diff options
Diffstat (limited to 'lib/isc')
377 files changed, 99631 insertions, 0 deletions
diff --git a/lib/isc/Atffile b/lib/isc/Atffile new file mode 100644 index 0000000..1edb838 --- /dev/null +++ b/lib/isc/Atffile @@ -0,0 +1,5 @@ +Content-Type: application/X-atf-atffile; version="1" + +prop: test-suite = bind9 + +tp: tests diff --git a/lib/isc/Kyuafile b/lib/isc/Kyuafile new file mode 100644 index 0000000..0739e3a --- /dev/null +++ b/lib/isc/Kyuafile @@ -0,0 +1,4 @@ +syntax(2) +test_suite('bind9') + +include('tests/Kyuafile') diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in new file mode 100644 index 0000000..ba53ef1 --- /dev/null +++ b/lib/isc/Makefile.in @@ -0,0 +1,146 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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@ + +@LIBISC_API@ + +@BIND9_MAKE_INCLUDES@ + +PROVIDER = @PKCS11_PROVIDER@ + +CINCLUDES = -I${srcdir}/unix/include \ + -I${srcdir}/@ISC_THREAD_DIR@/include \ + -I${srcdir}/@ISC_ARCH_DIR@/include \ + -I./include \ + -I${srcdir}/include ${DNS_INCLUDES} @ISC_OPENSSL_INC@ +CDEFINES = @CRYPTO@ -DPK11_LIB_LOCATION=\"${PROVIDER}\" +CWARNINGS = + +# Alphabetically +UNIXOBJS = @ISC_ISCIPV6_O@ @ISC_ISCPK11_API_O@ \ + unix/app.@O@ unix/dir.@O@ unix/entropy.@O@ unix/errno.@O@ \ + unix/errno2result.@O@ unix/file.@O@ unix/fsaccess.@O@ \ + unix/interfaceiter.@O@ unix/keyboard.@O@ unix/meminfo.@O@ \ + unix/net.@O@ unix/os.@O@ unix/resource.@O@ unix/socket.@O@ \ + unix/stdio.@O@ unix/stdtime.@O@ unix/strerror.@O@ \ + unix/syslog.@O@ unix/time.@O@ + +NLSOBJS = nls/msgcat.@O@ + +THREADOPTOBJS = @ISC_THREAD_DIR@/condition.@O@ @ISC_THREAD_DIR@/mutex.@O@ + +THREADOBJS = @THREADOPTOBJS@ @ISC_THREAD_DIR@/thread.@O@ + +WIN32OBJS = win32/condition.@O@ win32/dir.@O@ win32/errno.@O@ \ + win32/file.@O@ win32/fsaccess.@O@ \ + win32/meminfo.@O@ win32/once.@O@ \ + win32/stdtime.@O@ win32/thread.@O@ win32/time.@O@ + +# Alphabetically +OBJS = @ISC_EXTRA_OBJS@ @ISC_PK11_O@ @ISC_PK11_RESULT_O@ \ + aes.@O@ assertions.@O@ backtrace.@O@ base32.@O@ base64.@O@ \ + bind9.@O@ buffer.@O@ bufferlist.@O@ \ + commandline.@O@ counter.@O@ crc64.@O@ error.@O@ event.@O@ \ + hash.@O@ ht.@O@ heap.@O@ hex.@O@ hmacmd5.@O@ \ + hmacsha.@O@ httpd.@O@ inet_aton.@O@ iterated_hash.@O@ \ + lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \ + md5.@O@ mem.@O@ mutexblock.@O@ \ + netaddr.@O@ netscope.@O@ pool.@O@ ondestroy.@O@ \ + parseint.@O@ portset.@O@ quota.@O@ radix.@O@ random.@O@ \ + ratelimiter.@O@ refcount.@O@ region.@O@ regex.@O@ result.@O@ \ + rwlock.@O@ \ + safe.@O@ serial.@O@ sha1.@O@ sha2.@O@ sockaddr.@O@ stats.@O@ \ + string.@O@ strtoul.@O@ symtab.@O@ task.@O@ taskpool.@O@ \ + tm.@O@ timer.@O@ version.@O@ \ + ${UNIXOBJS} ${NLSOBJS} ${THREADOBJS} +SYMTBLOBJS = backtrace-emptytbl.@O@ + +CHACHASRCS = chacha_private.h + +# Alphabetically +SRCS = @ISC_EXTRA_SRCS@ @ISC_PK11_C@ @ISC_PK11_RESULT_C@ \ + aes.c assertions.c backtrace.c base32.c base64.c bind9.c \ + buffer.c bufferlist.c commandline.c counter.c crc64.c \ + error.c event.c hash.c ht.c heap.c hex.c hmacmd5.c \ + hmacsha.c httpd.c inet_aton.c iterated_hash.c \ + lex.c lfsr.c lib.c log.c \ + md5.c mem.c mutexblock.c \ + netaddr.c netscope.c pool.c ondestroy.c \ + parseint.c portset.c quota.c radix.c random.c ${CHACHASRCS} \ + ratelimiter.c refcount.c region.c regex.c result.c rwlock.c \ + safe.c serial.c sha1.c sha2.c sockaddr.c stats.c string.c \ + strtoul.c symtab.c task.c taskpool.c timer.c \ + tm.c version.c + +LIBS = @ISC_OPENSSL_LIBS@ @LIBS@ + +# Note: the order of SUBDIRS is important. +# Attempt to disable parallel processing. +.NOTPARALLEL: +.NO_PARALLEL: +SUBDIRS = include unix nls @ISC_THREAD_DIR@ @ISC_ARCH_DIR@ +TARGETS = timestamp +TESTDIRS = @UNITTESTS@ + +@BIND9_MAKE_RULES@ + +safe.@O@: safe.c + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} @CCNOOPT@ \ + -c ${srcdir}/safe.c + +version.@O@: version.c + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \ + -DVERSION=\"${VERSION}\" \ + -DLIBINTERFACE=${LIBINTERFACE} \ + -DLIBREVISION=${LIBREVISION} \ + -DLIBAGE=${LIBAGE} \ + -c ${srcdir}/version.c + +libisc.@SA@: ${OBJS} ${SYMTBLOBJS} + ${AR} ${ARFLAGS} $@ ${OBJS} ${SYMTBLOBJS} + ${RANLIB} $@ + +libisc-nosymtbl.@SA@: ${OBJS} + ${AR} ${ARFLAGS} $@ ${OBJS} + ${RANLIB} $@ + +libisc.la: ${OBJS} ${SYMTBLOBJS} + ${LIBTOOL_MODE_LINK} \ + ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libisc.la -rpath ${libdir} \ + -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \ + ${OBJS} ${SYMTBLOBJS} ${LIBS} + +libisc-nosymtbl.la: ${OBJS} + ${LIBTOOL_MODE_LINK} \ + ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libisc-nosymtbl.la -rpath ${libdir} \ + -version-info ${LIBINTERFACE}:${LIBREVISION}:${LIBAGE} \ + ${OBJS} ${LIBS} + +timestamp: libisc.@A@ libisc-nosymtbl.@A@ + touch timestamp + +testdirs: libisc.@A@ libisc-nosymtbl.@A@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${libdir} + +install:: timestamp installdirs + ${LIBTOOL_MODE_INSTALL} ${INSTALL_LIBRARY} libisc.@A@ ${DESTDIR}${libdir} + +uninstall:: + ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${libdir}/libisc.@A@ + +clean distclean:: + rm -f libisc.@A@ libisc-nosymtbl.@A@ libisc.la \ + libisc-nosymtbl.la timestamp diff --git a/lib/isc/aes.c b/lib/isc/aes.c new file mode 100644 index 0000000..2ca07f6 --- /dev/null +++ b/lib/isc/aes.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file isc/aes.c */ + +#include <config.h> + +#include <isc/assertions.h> +#include <isc/aes.h> +#include <isc/platform.h> +#include <isc/string.h> +#include <isc/types.h> +#include <isc/util.h> + +#ifdef ISC_PLATFORM_WANTAES +#if HAVE_OPENSSL_EVP_AES + +#include <openssl/opensslv.h> +#include <openssl/evp.h> + +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#define EVP_CIPHER_CTX_new() &(_context), EVP_CIPHER_CTX_init(&_context) +#define EVP_CIPHER_CTX_free(c) RUNTIME_CHECK(EVP_CIPHER_CTX_cleanup(c) == 1) +#endif + +void +isc_aes128_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + EVP_CIPHER_CTX _context; +#endif + EVP_CIPHER_CTX *c; + int len; + + c = EVP_CIPHER_CTX_new(); + RUNTIME_CHECK(c != NULL); + RUNTIME_CHECK(EVP_EncryptInit(c, EVP_aes_128_ecb(), key, NULL) == 1); + EVP_CIPHER_CTX_set_padding(c, 0); + RUNTIME_CHECK(EVP_EncryptUpdate(c, out, &len, in, + ISC_AES_BLOCK_LENGTH) == 1); + RUNTIME_CHECK(len == ISC_AES_BLOCK_LENGTH); + EVP_CIPHER_CTX_free(c); +} + +void +isc_aes192_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + EVP_CIPHER_CTX _context; +#endif + EVP_CIPHER_CTX *c; + int len; + + c = EVP_CIPHER_CTX_new(); + RUNTIME_CHECK(c != NULL); + RUNTIME_CHECK(EVP_EncryptInit(c, EVP_aes_192_ecb(), key, NULL) == 1); + EVP_CIPHER_CTX_set_padding(c, 0); + RUNTIME_CHECK(EVP_EncryptUpdate(c, out, &len, in, + ISC_AES_BLOCK_LENGTH) == 1); + RUNTIME_CHECK(len == ISC_AES_BLOCK_LENGTH); + EVP_CIPHER_CTX_free(c); +} + +void +isc_aes256_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out) +{ +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + EVP_CIPHER_CTX _context; +#endif + EVP_CIPHER_CTX *c; + int len; + + c = EVP_CIPHER_CTX_new(); + RUNTIME_CHECK(c != NULL); + RUNTIME_CHECK(EVP_EncryptInit(c, EVP_aes_256_ecb(), key, NULL) == 1); + EVP_CIPHER_CTX_set_padding(c, 0); + RUNTIME_CHECK(EVP_EncryptUpdate(c, out, &len, in, + ISC_AES_BLOCK_LENGTH) == 1); + RUNTIME_CHECK(len == ISC_AES_BLOCK_LENGTH); + EVP_CIPHER_CTX_free(c); +} + +#elif HAVE_OPENSSL_AES + +#include <openssl/aes.h> + +void +isc_aes128_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out) +{ + AES_KEY k; + + RUNTIME_CHECK(AES_set_encrypt_key(key, 128, &k) == 0); + AES_encrypt(in, out, &k); +} + +void +isc_aes192_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out) +{ + AES_KEY k; + + RUNTIME_CHECK(AES_set_encrypt_key(key, 192, &k) == 0); + AES_encrypt(in, out, &k); +} + +void +isc_aes256_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out) +{ + AES_KEY k; + + RUNTIME_CHECK(AES_set_encrypt_key(key, 256, &k) == 0); + AES_encrypt(in, out, &k); +} + +#elif PKCS11CRYPTO + +#include <pk11/pk11.h> +#include <pk11/internal.h> + +static CK_BBOOL truevalue = TRUE; +static CK_BBOOL falsevalue = FALSE; + +static void isc_aes_crypt(const unsigned char *key, CK_ULONG keylen, + const unsigned char *in, unsigned char *out); + +void +isc_aes128_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out) +{ + isc_aes_crypt(key, ISC_AES128_KEYLENGTH, in, out); +} + +void +isc_aes192_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out) +{ + isc_aes_crypt(key, ISC_AES192_KEYLENGTH, in, out); +} + +void +isc_aes256_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out) +{ + isc_aes_crypt(key, ISC_AES256_KEYLENGTH, in, out); +} + +static void +isc_aes_crypt(const unsigned char *key, CK_ULONG keylen, + const unsigned char *in, unsigned char *out) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_AES_ECB, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_AES; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_ENCRYPT, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_VALUE, NULL, keylen } + }; + CK_ULONG blocklen; + CK_BYTE_PTR pData; + pk11_context_t ctx; + + DE_CONST(key, keyTemplate[5].pValue); + RUNTIME_CHECK(pk11_get_session(&ctx, OP_AES, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + ctx.object = CK_INVALID_HANDLE; + PK11_FATALCHECK(pkcs_C_CreateObject, + (ctx.session, keyTemplate, + (CK_ULONG) 6, &ctx.object)); + INSIST(ctx.object != CK_INVALID_HANDLE); + PK11_FATALCHECK(pkcs_C_EncryptInit, + (ctx.session, &mech, ctx.object)); + + DE_CONST(in, pData); + blocklen = (CK_ULONG) ISC_AES_BLOCK_LENGTH; + PK11_FATALCHECK(pkcs_C_Encrypt, + (ctx.session, + pData, (CK_ULONG) ISC_AES_BLOCK_LENGTH, + out, &blocklen)); + RUNTIME_CHECK(blocklen == (CK_ULONG) ISC_AES_BLOCK_LENGTH); + + (void) pkcs_C_DestroyObject(ctx.session, ctx.object); + ctx.object = CK_INVALID_HANDLE; + pk11_return_session(&ctx); + +} + +#endif +#endif /* ISC_PLATFORM_WANTAES */ diff --git a/lib/isc/alpha/Makefile.in b/lib/isc/alpha/Makefile.in new file mode 100644 index 0000000..419cf9f --- /dev/null +++ b/lib/isc/alpha/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = include +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/alpha/include/Makefile.in b/lib/isc/alpha/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/alpha/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/alpha/include/isc/Makefile.in b/lib/isc/alpha/include/isc/Makefile.in new file mode 100644 index 0000000..97b6b41 --- /dev/null +++ b/lib/isc/alpha/include/isc/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = atomic.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/alpha/include/isc/atomic.h b/lib/isc/alpha/include/isc/atomic.h new file mode 100644 index 0000000..e704fef --- /dev/null +++ b/lib/isc/alpha/include/isc/atomic.h @@ -0,0 +1,180 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/* + * This code was written based on FreeBSD's kernel source whose copyright + * follows: + */ + +/*- + * Copyright (c) 1998 Doug Rabson + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/alpha/include/atomic.h,v 1.18.6.1 2004/09/13 21:52:04 wilko Exp $ + */ + +#ifndef ISC_ATOMIC_H +#define ISC_ATOMIC_H 1 + +#include <inttypes.h> + +#include <isc/platform.h> +#include <isc/types.h> + +#ifdef ISC_PLATFORM_USEOSFASM +#include <c_asm.h> + +#pragma intrinsic(asm) + +/* + * This routine atomically increments the value stored in 'p' by 'val', and + * returns the previous value. Memory access ordering around this function + * can be critical, so we add explicit memory block instructions at the + * beginning and the end of it (same for other functions). + */ +static inline int32_t +isc_atomic_xadd(int32_t *p, int32_t val) { + return (asm("mb;" + "1:" + "ldl_l %t0, 0(%a0);" /* load old value */ + "mov %t0, %v0;" /* copy the old value */ + "addl %t0, %a1, %t0;" /* calculate new value */ + "stl_c %t0, 0(%a0);" /* attempt to store */ + "beq %t0, 1b;" /* spin if failed */ + "mb;", + p, val)); +} + +/* + * This routine atomically stores the value 'val' in 'p'. + */ +static inline void +isc_atomic_store(int32_t *p, int32_t val) { + (void)asm("mb;" + "1:" + "ldl_l %t0, 0(%a0);" /* load old value */ + "mov %a1, %t0;" /* value to store */ + "stl_c %t0, 0(%a0);" /* attempt to store */ + "beq %t0, 1b;" /* spin if failed */ + "mb;", + p, val); +} + +/* + * This routine atomically replaces the value in 'p' with 'val', if the + * original value is equal to 'cmpval'. The original value is returned in any + * case. + */ +static inline int32_t +isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val) { + + return(asm("mb;" + "1:" + "ldl_l %t0, 0(%a0);" /* load old value */ + "mov %t0, %v0;" /* copy the old value */ + "cmpeq %t0, %a1, %t0;" /* compare */ + "beq %t0, 2f;" /* exit if not equal */ + "mov %a2, %t0;" /* value to store */ + "stl_c %t0, 0(%a0);" /* attempt to store */ + "beq %t0, 1b;" /* if it failed, spin */ + "2:" + "mb;", + p, cmpval, val)); +} +#elif defined (ISC_PLATFORM_USEGCCASM) +static inline int32_t +isc_atomic_xadd(int32_t *p, int32_t val) { + int32_t temp, prev; + + __asm__ volatile( + "mb;" + "1:" + "ldl_l %0, %1;" /* load old value */ + "mov %0, %2;" /* copy the old value */ + "addl %0, %3, %0;" /* calculate new value */ + "stl_c %0, %1;" /* attempt to store */ + "beq %0, 1b;" /* spin if failed */ + "mb;" + : "=&r"(temp), "+m"(*p), "=&r"(prev) + : "r"(val) + : "memory"); + + return (prev); +} + +static inline void +isc_atomic_store(int32_t *p, int32_t val) { + int32_t temp; + + __asm__ volatile( + "mb;" + "1:" + "ldl_l %0, %1;" /* load old value */ + "mov %2, %0;" /* value to store */ + "stl_c %0, %1;" /* attempt to store */ + "beq %0, 1b;" /* if it failed, spin */ + "mb;" + : "=&r"(temp), "+m"(*p) + : "r"(val) + : "memory"); +} + +static inline int32_t +isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val) { + int32_t temp, prev; + + __asm__ volatile( + "mb;" + "1:" + "ldl_l %0, %1;" /* load old value */ + "mov %0, %2;" /* copy the old value */ + "cmpeq %0, %3, %0;" /* compare */ + "beq %0, 2f;" /* exit if not equal */ + "mov %4, %0;" /* value to store */ + "stl_c %0, %1;" /* attempt to store */ + "beq %0, 1b;" /* if it failed, spin */ + "2:" + "mb;" + : "=&r"(temp), "+m"(*p), "=&r"(prev) + : "r"(cmpval), "r"(val) + : "memory"); + + return (prev); +} +#else + +#error "unsupported compiler. disable atomic ops by --disable-atomic" + +#endif + +#endif /* ISC_ATOMIC_H */ diff --git a/lib/isc/api b/lib/isc/api new file mode 100644 index 0000000..7b1b2b1 --- /dev/null +++ b/lib/isc/api @@ -0,0 +1,13 @@ +# LIBINTERFACE ranges +# 9.6: 50-59, 110-119 +# 9.7: 60-79 +# 9.8: 80-89, 120-129 +# 9.9: 90-109, 170-179 +# 9.9-sub: 130-139, 150-159, 200-209 +# 9.10: 140-149, 190-199 +# 9.10-sub: 180-189 +# 9.11: 160-169,1100-1199 +# 9.12: 1200-1299 +LIBINTERFACE = 1100 +LIBREVISION = 0 +LIBAGE = 0 diff --git a/lib/isc/app_api.c b/lib/isc/app_api.c new file mode 100644 index 0000000..425eb5a --- /dev/null +++ b/lib/isc/app_api.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <stdbool.h> +#include <unistd.h> + +#include <isc/app.h> +#include <isc/magic.h> +#include <isc/mutex.h> +#include <isc/once.h> +#include <isc/util.h> + +static isc_mutex_t createlock; +static isc_once_t once = ISC_ONCE_INIT; +static isc_appctxcreatefunc_t appctx_createfunc = NULL; +static bool is_running = false; + +#define ISCAPI_APPMETHODS_VALID(m) ISC_MAGIC_VALID(m, ISCAPI_APPMETHODS_MAGIC) + +static void +initialize(void) { + RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS); +} + +isc_result_t +isc_app_register(isc_appctxcreatefunc_t createfunc) { + isc_result_t result = ISC_R_SUCCESS; + + RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS); + + LOCK(&createlock); + if (appctx_createfunc == NULL) + appctx_createfunc = createfunc; + else + result = ISC_R_EXISTS; + UNLOCK(&createlock); + + return (result); +} + +isc_result_t +isc_appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp) { + isc_result_t result; + + if (isc_bind9) + return (isc__appctx_create(mctx, ctxp)); + + LOCK(&createlock); + + REQUIRE(appctx_createfunc != NULL); + result = (*appctx_createfunc)(mctx, ctxp); + + UNLOCK(&createlock); + + return (result); +} + +void +isc_appctx_destroy(isc_appctx_t **ctxp) { + REQUIRE(ctxp != NULL && ISCAPI_APPCTX_VALID(*ctxp)); + + if (isc_bind9) + isc__appctx_destroy(ctxp); + else + (*ctxp)->methods->ctxdestroy(ctxp); + + ENSURE(*ctxp == NULL); +} + +isc_result_t +isc_app_ctxstart(isc_appctx_t *ctx) { + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + + if (isc_bind9) + return (isc__app_ctxstart(ctx)); + + return (ctx->methods->ctxstart(ctx)); +} + +isc_result_t +isc_app_ctxrun(isc_appctx_t *ctx) { + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + + if (isc_bind9) + return (isc__app_ctxrun(ctx)); + + return (ctx->methods->ctxrun(ctx)); +} + +isc_result_t +isc_app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, + isc_task_t *task, isc_taskaction_t action, + void *arg) +{ + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + + if (isc_bind9) + return (isc__app_ctxonrun(ctx, mctx, task, action, arg)); + + return (ctx->methods->ctxonrun(ctx, mctx, task, action, arg)); +} + +isc_result_t +isc_app_ctxsuspend(isc_appctx_t *ctx) { + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + + if (isc_bind9) + return (isc__app_ctxsuspend(ctx)); + + return (ctx->methods->ctxsuspend(ctx)); +} + +isc_result_t +isc_app_ctxshutdown(isc_appctx_t *ctx) { + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + + if (isc_bind9) + return (isc__app_ctxshutdown(ctx)); + + return (ctx->methods->ctxshutdown(ctx)); +} + +void +isc_app_ctxfinish(isc_appctx_t *ctx) { + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + + if (isc_bind9) + isc__app_ctxfinish(ctx); + + ctx->methods->ctxfinish(ctx); +} + +void +isc_appctx_settaskmgr(isc_appctx_t *ctx, isc_taskmgr_t *taskmgr) { + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + REQUIRE(taskmgr != NULL); + + if (isc_bind9) + isc__appctx_settaskmgr(ctx, taskmgr); + + ctx->methods->settaskmgr(ctx, taskmgr); +} + +void +isc_appctx_setsocketmgr(isc_appctx_t *ctx, isc_socketmgr_t *socketmgr) { + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + REQUIRE(socketmgr != NULL); + + if (isc_bind9) + isc__appctx_setsocketmgr(ctx, socketmgr); + + ctx->methods->setsocketmgr(ctx, socketmgr); +} + +void +isc_appctx_settimermgr(isc_appctx_t *ctx, isc_timermgr_t *timermgr) { + REQUIRE(ISCAPI_APPCTX_VALID(ctx)); + REQUIRE(timermgr != NULL); + + if (isc_bind9) + isc__appctx_settimermgr(ctx, timermgr); + + ctx->methods->settimermgr(ctx, timermgr); +} + +isc_result_t +isc_app_start(void) { + if (isc_bind9) + return (isc__app_start()); + + return (ISC_R_NOTIMPLEMENTED); +} + +isc_result_t +isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + if (isc_bind9) + return (isc__app_onrun(mctx, task, action, arg)); + + return (ISC_R_NOTIMPLEMENTED); +} + +isc_result_t +isc_app_run() { + if (isc_bind9) { + isc_result_t result; + + is_running = true; + result = isc__app_run(); + is_running = false; + + return (result); + } + + return (ISC_R_NOTIMPLEMENTED); +} + +bool +isc_app_isrunning() { + return (is_running); +} + +isc_result_t +isc_app_shutdown(void) { + if (isc_bind9) + return (isc__app_shutdown()); + + return (ISC_R_NOTIMPLEMENTED); +} + +isc_result_t +isc_app_reload(void) { + if (isc_bind9) + return (isc__app_reload()); + + return (ISC_R_NOTIMPLEMENTED); +} + +void +isc_app_finish(void) { + if (!isc_bind9) + return; + + isc__app_finish(); +} + +void +isc_app_block(void) { + if (!isc_bind9) + return; + + isc__app_block(); +} + +void +isc_app_unblock(void) { + if (!isc_bind9) + return; + + isc__app_unblock(); +} diff --git a/lib/isc/assertions.c b/lib/isc/assertions.c new file mode 100644 index 0000000..b52342e --- /dev/null +++ b/lib/isc/assertions.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <isc/assertions.h> +#include <isc/backtrace.h> +#include <isc/msgs.h> +#include <isc/print.h> +#include <isc/result.h> + +/* + * The maximum number of stack frames to dump on assertion failure. + */ +#ifndef BACKTRACE_MAXFRAME +#define BACKTRACE_MAXFRAME 128 +#endif + +/*% + * Forward. + */ +static void +default_callback(const char *, int, isc_assertiontype_t, const char *); + +static isc_assertioncallback_t isc_assertion_failed_cb = default_callback; + +/*% + * Public. + */ + +/*% assertion failed handler */ +/* coverity[+kill] */ +void +isc_assertion_failed(const char *file, int line, isc_assertiontype_t type, + const char *cond) +{ + isc_assertion_failed_cb(file, line, type, cond); + abort(); + /* NOTREACHED */ +} + +/*% Set callback. */ +void +isc_assertion_setcallback(isc_assertioncallback_t cb) { + if (cb == NULL) + isc_assertion_failed_cb = default_callback; + else + isc_assertion_failed_cb = cb; +} + +/*% Type to Text */ +const char * +isc_assertion_typetotext(isc_assertiontype_t type) { + const char *result; + + /* + * These strings have purposefully not been internationalized + * because they are considered to essentially be keywords of + * the ISC development environment. + */ + switch (type) { + case isc_assertiontype_require: + result = "REQUIRE"; + break; + case isc_assertiontype_ensure: + result = "ENSURE"; + break; + case isc_assertiontype_insist: + result = "INSIST"; + break; + case isc_assertiontype_invariant: + result = "INVARIANT"; + break; + default: + result = NULL; + } + return (result); +} + +/* + * Private. + */ + +static void +default_callback(const char *file, int line, isc_assertiontype_t type, + const char *cond) +{ + void *tracebuf[BACKTRACE_MAXFRAME]; + int i, nframes; + const char *logsuffix = "."; + const char *fname; + isc_result_t result; + + result = isc_backtrace_gettrace(tracebuf, BACKTRACE_MAXFRAME, &nframes); + if (result == ISC_R_SUCCESS && nframes > 0) + logsuffix = ", back trace"; + + fprintf(stderr, "%s:%d: %s(%s) %s%s\n", + file, line, isc_assertion_typetotext(type), cond, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), logsuffix); + if (result == ISC_R_SUCCESS) { + for (i = 0; i < nframes; i++) { + unsigned long offset; + + fname = NULL; + result = isc_backtrace_getsymbol(tracebuf[i], &fname, + &offset); + if (result == ISC_R_SUCCESS) { + fprintf(stderr, "#%d %p in %s()+0x%lx\n", i, + tracebuf[i], fname, offset); + } else { + fprintf(stderr, "#%d %p in ??\n", i, + tracebuf[i]); + } + } + } + fflush(stderr); +} diff --git a/lib/isc/backtrace-emptytbl.c b/lib/isc/backtrace-emptytbl.c new file mode 100644 index 0000000..5822cfb --- /dev/null +++ b/lib/isc/backtrace-emptytbl.c @@ -0,0 +1,29 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +/* + * This file defines an empty (default) symbol table used in backtrace.c + * If the application wants to have a complete symbol table, it should redefine + * isc__backtrace_symtable with the complete table in some way, and link the + * version of the library not including this definition + * (e.g. libisc-nosymbol.a). + */ + +#include <config.h> + +#include <isc/backtrace.h> + +LIBISC_EXTERNAL_DATA const int isc__backtrace_nsymbols = 0; +LIBISC_EXTERNAL_DATA const + isc_backtrace_symmap_t isc__backtrace_symtable[] = { { NULL, "" } }; diff --git a/lib/isc/backtrace.c b/lib/isc/backtrace.c new file mode 100644 index 0000000..0ea1455 --- /dev/null +++ b/lib/isc/backtrace.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <string.h> +#include <stdlib.h> +#ifdef HAVE_LIBCTRACE +#include <execinfo.h> +#endif + +#include <isc/backtrace.h> +#include <isc/result.h> +#include <isc/util.h> + +#ifdef ISC_PLATFORM_USEBACKTRACE +/* + * Getting a back trace of a running process is tricky and highly platform + * dependent. Our current approach is as follows: + * 1. If the system library supports the "backtrace()" function, use it. + * 2. Otherwise, if the compiler is gcc and the architecture is x86_64 or IA64, + * then use gcc's (hidden) Unwind_Backtrace() function. Note that this + * function doesn't work for C programs on many other architectures. + * 3. Otherwise, if the architecture x86 or x86_64, try to unwind the stack + * frame following frame pointers. This assumes the executable binary + * compiled with frame pointers; this is not always true for x86_64 (rather, + * compiler optimizations often disable frame pointers). The validation + * checks in getnextframeptr() hopefully rejects bogus values stored in + * the RBP register in such a case. If the backtrace function itself crashes + * due to this problem, the whole package should be rebuilt with + * --disable-backtrace. + */ +#ifdef HAVE_LIBCTRACE +#define BACKTRACE_LIBC +#elif defined(__GNUC__) && (defined(__x86_64__) || defined(__ia64__)) +#define BACKTRACE_GCC +#elif defined(WIN32) +#define BACKTRACE_WIN32 +#elif defined(__x86_64__) || defined(__i386__) +#define BACKTRACE_X86STACK +#else +#define BACKTRACE_DISABLED +#endif /* HAVE_LIBCTRACE */ +#else /* !ISC_PLATFORM_USEBACKTRACE */ +#define BACKTRACE_DISABLED +#endif /* ISC_PLATFORM_USEBACKTRACE */ + +#ifdef BACKTRACE_LIBC +isc_result_t +isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { + int n; + + /* + * Validate the arguments: intentionally avoid using REQUIRE(). + * See notes in backtrace.h. + */ + if (addrs == NULL || nframes == NULL) + return (ISC_R_FAILURE); + + /* + * backtrace(3) includes this function itself in the address array, + * which should be eliminated from the returned sequence. + */ + n = backtrace(addrs, maxaddrs); + if (n < 2) + return (ISC_R_NOTFOUND); + n--; + memmove(addrs, &addrs[1], sizeof(void *) * n); + *nframes = n; + return (ISC_R_SUCCESS); +} +#elif defined(BACKTRACE_GCC) +extern int _Unwind_Backtrace(void* fn, void* a); +extern void* _Unwind_GetIP(void* ctx); + +typedef struct { + void **result; + int max_depth; + int skip_count; + int count; +} trace_arg_t; + +static int +btcallback(void *uc, void *opq) { + trace_arg_t *arg = (trace_arg_t *)opq; + + if (arg->skip_count > 0) + arg->skip_count--; + else + arg->result[arg->count++] = (void *)_Unwind_GetIP(uc); + if (arg->count == arg->max_depth) + return (5); /* _URC_END_OF_STACK */ + + return (0); /* _URC_NO_REASON */ +} + +isc_result_t +isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { + trace_arg_t arg; + + /* Argument validation: see above. */ + if (addrs == NULL || nframes == NULL) + return (ISC_R_FAILURE); + + arg.skip_count = 1; + arg.result = addrs; + arg.max_depth = maxaddrs; + arg.count = 0; + _Unwind_Backtrace(btcallback, &arg); + + *nframes = arg.count; + + return (ISC_R_SUCCESS); +} +#elif defined(BACKTRACE_WIN32) +isc_result_t +isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { + unsigned long ftc = (unsigned long)maxaddrs; + + *nframes = (int)CaptureStackBackTrace(1, ftc, addrs, NULL); + return ISC_R_SUCCESS; +} +#elif defined(BACKTRACE_X86STACK) +#ifdef __x86_64__ +static unsigned long +getrbp(void) { + __asm("movq %rbp, %rax\n"); +} +#endif + +static void ** +getnextframeptr(void **sp) { + void **newsp = (void **)*sp; + + /* + * Perform sanity check for the new frame pointer, derived from + * google glog. This can actually be bogus depending on compiler. + */ + + /* prohibit the stack frames from growing downwards */ + if (newsp <= sp) + return (NULL); + + /* A heuristics to reject "too large" frame: this actually happened. */ + if ((char *)newsp - (char *)sp > 100000) + return (NULL); + + /* + * Not sure if other checks used in glog are needed at this moment. + * For our purposes we don't have to consider non-contiguous frames, + * for example. + */ + + return (newsp); +} + +isc_result_t +isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { + int i = 0; + void **sp; + + /* Argument validation: see above. */ + if (addrs == NULL || nframes == NULL) + return (ISC_R_FAILURE); + +#ifdef __x86_64__ + sp = (void **)getrbp(); + if (sp == NULL) + return (ISC_R_NOTFOUND); + /* + * sp is the frame ptr of this function itself due to the call to + * getrbp(), so need to unwind one frame for consistency. + */ + sp = getnextframeptr(sp); +#else + /* + * i386: the frame pointer is stored 2 words below the address for the + * first argument. Note that the body of this function cannot be + * inlined since it depends on the address of the function argument. + */ + sp = (void **)&addrs - 2; +#endif + + while (sp != NULL && i < maxaddrs) { + addrs[i++] = *(sp + 1); + sp = getnextframeptr(sp); + } + + *nframes = i; + + return (ISC_R_SUCCESS); +} +#elif defined(BACKTRACE_DISABLED) +isc_result_t +isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes) { + /* Argument validation: see above. */ + if (addrs == NULL || nframes == NULL) + return (ISC_R_FAILURE); + + UNUSED(maxaddrs); + + return (ISC_R_NOTIMPLEMENTED); +} +#endif + +isc_result_t +isc_backtrace_getsymbolfromindex(int idx, const void **addrp, + const char **symbolp) +{ + REQUIRE(addrp != NULL && *addrp == NULL); + REQUIRE(symbolp != NULL && *symbolp == NULL); + + if (idx < 0 || idx >= isc__backtrace_nsymbols) + return (ISC_R_RANGE); + + *addrp = isc__backtrace_symtable[idx].addr; + *symbolp = isc__backtrace_symtable[idx].symbol; + return (ISC_R_SUCCESS); +} + +static int +symtbl_compare(const void *addr, const void *entryarg) { + const isc_backtrace_symmap_t *entry = entryarg; + const isc_backtrace_symmap_t *end = + &isc__backtrace_symtable[isc__backtrace_nsymbols - 1]; + + if (isc__backtrace_nsymbols == 1 || entry == end) { + if (addr >= entry->addr) { + /* + * If addr is equal to or larger than that of the last + * entry of the table, we cannot be sure if this is + * within a valid range so we consider it valid. + */ + return (0); + } + return (-1); + } + + /* entry + 1 is a valid entry from now on. */ + if (addr < entry->addr) + return (-1); + else if (addr >= (entry + 1)->addr) + return (1); + return (0); +} + +isc_result_t +isc_backtrace_getsymbol(const void *addr, const char **symbolp, + unsigned long *offsetp) +{ + isc_result_t result = ISC_R_SUCCESS; + isc_backtrace_symmap_t *found; + + /* + * Validate the arguments: intentionally avoid using REQUIRE(). + * See notes in backtrace.h. + */ + if (symbolp == NULL || *symbolp != NULL || offsetp == NULL) + return (ISC_R_FAILURE); + + if (isc__backtrace_nsymbols < 1) + return (ISC_R_NOTFOUND); + + /* + * Search the table for the entry that meets: + * entry.addr <= addr < next_entry.addr. + */ + found = bsearch(addr, isc__backtrace_symtable, isc__backtrace_nsymbols, + sizeof(isc__backtrace_symtable[0]), symtbl_compare); + if (found == NULL) + result = ISC_R_NOTFOUND; + else { + *symbolp = found->symbol; + *offsetp = (unsigned long) ((const char *)addr - + (char *)found->addr); + } + + return (result); +} diff --git a/lib/isc/base32.c b/lib/isc/base32.c new file mode 100644 index 0000000..bb8c2e1 --- /dev/null +++ b/lib/isc/base32.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/base32.h> +#include <isc/buffer.h> +#include <isc/lex.h> +#include <isc/region.h> +#include <isc/string.h> +#include <isc/util.h> + +#define RETERR(x) do { \ + isc_result_t _r = (x); \ + if (_r != ISC_R_SUCCESS) \ + return (_r); \ + } while (0) + + +/*@{*/ +/*! + * These static functions are also present in lib/dns/rdata.c. I'm not + * sure where they should go. -- bwelling + */ +static isc_result_t +str_totext(const char *source, isc_buffer_t *target); + +static isc_result_t +mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length); + +/*@}*/ + +static const char base32[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=abcdefghijklmnopqrstuvwxyz234567"; +static const char base32hex[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUV=0123456789abcdefghijklmnopqrstuv"; + +static isc_result_t +base32_totext(isc_region_t *source, int wordlength, const char *wordbreak, + isc_buffer_t *target, const char base[], char pad) +{ + char buf[9]; + unsigned int loops = 0; + + if (wordlength >= 0 && wordlength < 8) + wordlength = 8; + + memset(buf, 0, sizeof(buf)); + while (source->length > 0) { + buf[0] = base[((source->base[0]>>3)&0x1f)]; /* 5 + */ + if (source->length == 1) { + buf[1] = base[(source->base[0]<<2)&0x1c]; + buf[2] = buf[3] = buf[4] = pad; + buf[5] = buf[6] = buf[7] = pad; + RETERR(str_totext(buf, target)); + break; + } + buf[1] = base[((source->base[0]<<2)&0x1c)| /* 3 = 8 */ + ((source->base[1]>>6)&0x03)]; /* 2 + */ + buf[2] = base[((source->base[1]>>1)&0x1f)]; /* 5 + */ + if (source->length == 2) { + buf[3] = base[(source->base[1]<<4)&0x10]; + buf[4] = buf[5] = buf[6] = buf[7] = pad; + RETERR(str_totext(buf, target)); + break; + } + buf[3] = base[((source->base[1]<<4)&0x10)| /* 1 = 8 */ + ((source->base[2]>>4)&0x0f)]; /* 4 + */ + if (source->length == 3) { + buf[4] = base[(source->base[2]<<1)&0x1e]; + buf[5] = buf[6] = buf[7] = pad; + RETERR(str_totext(buf, target)); + break; + } + buf[4] = base[((source->base[2]<<1)&0x1e)| /* 4 = 8 */ + ((source->base[3]>>7)&0x01)]; /* 1 + */ + buf[5] = base[((source->base[3]>>2)&0x1f)]; /* 5 + */ + if (source->length == 4) { + buf[6] = base[(source->base[3]<<3)&0x18]; + buf[7] = pad; + RETERR(str_totext(buf, target)); + break; + } + buf[6] = base[((source->base[3]<<3)&0x18)| /* 2 = 8 */ + ((source->base[4]>>5)&0x07)]; /* 3 + */ + buf[7] = base[source->base[4]&0x1f]; /* 5 = 8 */ + RETERR(str_totext(buf, target)); + isc_region_consume(source, 5); + + loops++; + if (source->length != 0 && wordlength >= 0 && + (int)((loops + 1) * 8) >= wordlength) + { + loops = 0; + RETERR(str_totext(wordbreak, target)); + } + } + if (source->length > 0) + isc_region_consume(source, source->length); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_base32_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target) +{ + return (base32_totext(source, wordlength, wordbreak, target, + base32, '=')); +} + +isc_result_t +isc_base32hex_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target) +{ + return (base32_totext(source, wordlength, wordbreak, target, + base32hex, '=')); +} + +isc_result_t +isc_base32hexnp_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target) +{ + return (base32_totext(source, wordlength, wordbreak, target, + base32hex, 0)); +} + +/*% + * State of a base32 decoding process in progress. + */ +typedef struct { + int length; /*%< Desired length of binary data or -1 */ + isc_buffer_t *target; /*%< Buffer for resulting binary data */ + int digits; /*%< Number of buffered base32 digits */ + bool seen_end; /*%< True if "=" end marker seen */ + int val[8]; + const char *base; /*%< Which encoding we are using */ + int seen_32; /*%< Number of significant bytes if non zero */ + bool pad; /*%< Expect padding */ +} base32_decode_ctx_t; + +static inline void +base32_decode_init(base32_decode_ctx_t *ctx, int length, const char base[], + bool pad, isc_buffer_t *target) +{ + ctx->digits = 0; + ctx->seen_end = false; + ctx->seen_32 = 0; + ctx->length = length; + ctx->target = target; + ctx->base = base; + ctx->pad = pad; +} + +static inline isc_result_t +base32_decode_char(base32_decode_ctx_t *ctx, int c) { + const char *s; + unsigned int last; + + if (ctx->seen_end) + return (ISC_R_BADBASE32); + if ((s = strchr(ctx->base, c)) == NULL) + return (ISC_R_BADBASE32); + last = (unsigned int)(s - ctx->base); + + /* + * Handle lower case. + */ + if (last > 32) + last -= 33; + + /* + * Check that padding is contiguous. + */ + if (last != 32 && ctx->seen_32 != 0) + return (ISC_R_BADBASE32); + + /* + * If padding is not permitted flag padding as a error. + */ + if (last == 32 && !ctx->pad) + return (ISC_R_BADBASE32); + + /* + * Check that padding starts at the right place and that + * bits that should be zero are. + * Record how many significant bytes in answer (seen_32). + */ + if (last == 32 && ctx->seen_32 == 0) + switch (ctx->digits) { + case 0: + case 1: + return (ISC_R_BADBASE32); + case 2: + if ((ctx->val[1]&0x03) != 0) + return (ISC_R_BADBASE32); + ctx->seen_32 = 1; + break; + case 3: + return (ISC_R_BADBASE32); + case 4: + if ((ctx->val[3]&0x0f) != 0) + return (ISC_R_BADBASE32); + ctx->seen_32 = 3; + break; + case 5: + if ((ctx->val[4]&0x01) != 0) + return (ISC_R_BADBASE32); + ctx->seen_32 = 3; + break; + case 6: + return (ISC_R_BADBASE32); + case 7: + if ((ctx->val[6]&0x07) != 0) + return (ISC_R_BADBASE32); + ctx->seen_32 = 4; + break; + } + + /* + * Zero fill pad values. + */ + ctx->val[ctx->digits++] = (last == 32) ? 0 : last; + + if (ctx->digits == 8) { + int n = 5; + unsigned char buf[5]; + + if (ctx->seen_32 != 0) { + ctx->seen_end = true; + n = ctx->seen_32; + } + buf[0] = (ctx->val[0]<<3)|(ctx->val[1]>>2); + buf[1] = (ctx->val[1]<<6)|(ctx->val[2]<<1)|(ctx->val[3]>>4); + buf[2] = (ctx->val[3]<<4)|(ctx->val[4]>>1); + buf[3] = (ctx->val[4]<<7)|(ctx->val[5]<<2)|(ctx->val[6]>>3); + buf[4] = (ctx->val[6]<<5)|(ctx->val[7]); + RETERR(mem_tobuffer(ctx->target, buf, n)); + if (ctx->length >= 0) { + if (n > ctx->length) + return (ISC_R_BADBASE32); + else + ctx->length -= n; + } + ctx->digits = 0; + } + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +base32_decode_finish(base32_decode_ctx_t *ctx) { + + if (ctx->length > 0) + return (ISC_R_UNEXPECTEDEND); + /* + * Add missing padding if required. + */ + if (!ctx->pad && ctx->digits != 0) { + ctx->pad = true; + do { + RETERR(base32_decode_char(ctx, '=')); + } while (ctx->digits != 0); + } + if (ctx->digits != 0) + return (ISC_R_BADBASE32); + return (ISC_R_SUCCESS); +} + +static isc_result_t +base32_tobuffer(isc_lex_t *lexer, const char base[], bool pad, + isc_buffer_t *target, int length) +{ + base32_decode_ctx_t ctx; + isc_textregion_t *tr; + isc_token_t token; + bool eol; + + base32_decode_init(&ctx, length, base, pad, target); + + while (!ctx.seen_end && (ctx.length != 0)) { + unsigned int i; + + if (length > 0) + eol = false; + else + eol = true; + RETERR(isc_lex_getmastertoken(lexer, &token, + isc_tokentype_string, eol)); + if (token.type != isc_tokentype_string) + break; + tr = &token.value.as_textregion; + for (i = 0; i < tr->length; i++) + RETERR(base32_decode_char(&ctx, tr->base[i])); + } + if (ctx.length < 0 && !ctx.seen_end) + isc_lex_ungettoken(lexer, &token); + RETERR(base32_decode_finish(&ctx)); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_base32_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { + return (base32_tobuffer(lexer, base32, true, target, length)); +} + +isc_result_t +isc_base32hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { + return (base32_tobuffer(lexer, base32hex, true, target, length)); +} + +isc_result_t +isc_base32hexnp_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { + return (base32_tobuffer(lexer, base32hex, false, target, length)); +} + +static isc_result_t +base32_decodestring(const char *cstr, const char base[], bool pad, + isc_buffer_t *target) +{ + base32_decode_ctx_t ctx; + + base32_decode_init(&ctx, -1, base, pad, target); + for (;;) { + int c = *cstr++; + if (c == '\0') + break; + if (c == ' ' || c == '\t' || c == '\n' || c== '\r') + continue; + RETERR(base32_decode_char(&ctx, c)); + } + RETERR(base32_decode_finish(&ctx)); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_base32_decodestring(const char *cstr, isc_buffer_t *target) { + return (base32_decodestring(cstr, base32, true, target)); +} + +isc_result_t +isc_base32hex_decodestring(const char *cstr, isc_buffer_t *target) { + return (base32_decodestring(cstr, base32hex, true, target)); +} + +isc_result_t +isc_base32hexnp_decodestring(const char *cstr, isc_buffer_t *target) { + return (base32_decodestring(cstr, base32hex, false, target)); +} + +static isc_result_t +base32_decoderegion(isc_region_t *source, const char base[], + bool pad, isc_buffer_t *target) +{ + base32_decode_ctx_t ctx; + + base32_decode_init(&ctx, -1, base, pad, target); + while (source->length != 0) { + int c = *source->base; + RETERR(base32_decode_char(&ctx, c)); + isc_region_consume(source, 1); + } + RETERR(base32_decode_finish(&ctx)); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_base32_decoderegion(isc_region_t *source, isc_buffer_t *target) { + return (base32_decoderegion(source, base32, true, target)); +} + +isc_result_t +isc_base32hex_decoderegion(isc_region_t *source, isc_buffer_t *target) { + return (base32_decoderegion(source, base32hex, true, target)); +} + +isc_result_t +isc_base32hexnp_decoderegion(isc_region_t *source, isc_buffer_t *target) { + return (base32_decoderegion(source, base32hex, false, target)); +} + +static isc_result_t +str_totext(const char *source, isc_buffer_t *target) { + unsigned int l; + isc_region_t region; + + isc_buffer_availableregion(target, ®ion); + l = strlen(source); + + if (l > region.length) + return (ISC_R_NOSPACE); + + memmove(region.base, source, l); + isc_buffer_add(target, l); + return (ISC_R_SUCCESS); +} + +static isc_result_t +mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { + isc_region_t tr; + + isc_buffer_availableregion(target, &tr); + if (length > tr.length) + return (ISC_R_NOSPACE); + memmove(tr.base, base, length); + isc_buffer_add(target, length); + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/base64.c b/lib/isc/base64.c new file mode 100644 index 0000000..c7a1722 --- /dev/null +++ b/lib/isc/base64.c @@ -0,0 +1,247 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/base64.h> +#include <isc/buffer.h> +#include <isc/lex.h> +#include <isc/string.h> +#include <isc/util.h> + +#define RETERR(x) do { \ + isc_result_t _r = (x); \ + if (_r != ISC_R_SUCCESS) \ + return (_r); \ + } while (0) + + +/*@{*/ +/*! + * These static functions are also present in lib/dns/rdata.c. I'm not + * sure where they should go. -- bwelling + */ +static isc_result_t +str_totext(const char *source, isc_buffer_t *target); + +static isc_result_t +mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length); + +static const char base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +/*@}*/ + +isc_result_t +isc_base64_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target) +{ + char buf[5]; + unsigned int loops = 0; + + if (wordlength < 4) + wordlength = 4; + + memset(buf, 0, sizeof(buf)); + while (source->length > 2) { + buf[0] = base64[(source->base[0]>>2)&0x3f]; + buf[1] = base64[((source->base[0]<<4)&0x30)| + ((source->base[1]>>4)&0x0f)]; + buf[2] = base64[((source->base[1]<<2)&0x3c)| + ((source->base[2]>>6)&0x03)]; + buf[3] = base64[source->base[2]&0x3f]; + RETERR(str_totext(buf, target)); + isc_region_consume(source, 3); + + loops++; + if (source->length != 0 && + (int)((loops + 1) * 4) >= wordlength) + { + loops = 0; + RETERR(str_totext(wordbreak, target)); + } + } + if (source->length == 2) { + buf[0] = base64[(source->base[0]>>2)&0x3f]; + buf[1] = base64[((source->base[0]<<4)&0x30)| + ((source->base[1]>>4)&0x0f)]; + buf[2] = base64[((source->base[1]<<2)&0x3c)]; + buf[3] = '='; + RETERR(str_totext(buf, target)); + isc_region_consume(source, 2); + } else if (source->length == 1) { + buf[0] = base64[(source->base[0]>>2)&0x3f]; + buf[1] = base64[((source->base[0]<<4)&0x30)]; + buf[2] = buf[3] = '='; + RETERR(str_totext(buf, target)); + isc_region_consume(source, 1); + } + return (ISC_R_SUCCESS); +} + +/*% + * State of a base64 decoding process in progress. + */ +typedef struct { + int length; /*%< Desired length of binary data or -1 */ + isc_buffer_t *target; /*%< Buffer for resulting binary data */ + int digits; /*%< Number of buffered base64 digits */ + bool seen_end; /*%< True if "=" end marker seen */ + int val[4]; +} base64_decode_ctx_t; + +static inline void +base64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) +{ + ctx->digits = 0; + ctx->seen_end = false; + ctx->length = length; + ctx->target = target; +} + +static inline isc_result_t +base64_decode_char(base64_decode_ctx_t *ctx, int c) { + const char *s; + + if (ctx->seen_end) + return (ISC_R_BADBASE64); + if ((s = strchr(base64, c)) == NULL) + return (ISC_R_BADBASE64); + ctx->val[ctx->digits++] = (int)(s - base64); + if (ctx->digits == 4) { + int n; + unsigned char buf[3]; + if (ctx->val[0] == 64 || ctx->val[1] == 64) + return (ISC_R_BADBASE64); + if (ctx->val[2] == 64 && ctx->val[3] != 64) + return (ISC_R_BADBASE64); + /* + * Check that bits that should be zero are. + */ + if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) + return (ISC_R_BADBASE64); + /* + * We don't need to test for ctx->val[2] != 64 as + * the bottom two bits of 64 are zero. + */ + if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) + return (ISC_R_BADBASE64); + n = (ctx->val[2] == 64) ? 1 : + (ctx->val[3] == 64) ? 2 : 3; + if (n != 3) { + ctx->seen_end = true; + if (ctx->val[2] == 64) + ctx->val[2] = 0; + if (ctx->val[3] == 64) + ctx->val[3] = 0; + } + buf[0] = (ctx->val[0]<<2)|(ctx->val[1]>>4); + buf[1] = (ctx->val[1]<<4)|(ctx->val[2]>>2); + buf[2] = (ctx->val[2]<<6)|(ctx->val[3]); + RETERR(mem_tobuffer(ctx->target, buf, n)); + if (ctx->length >= 0) { + if (n > ctx->length) + return (ISC_R_BADBASE64); + else + ctx->length -= n; + } + ctx->digits = 0; + } + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +base64_decode_finish(base64_decode_ctx_t *ctx) { + if (ctx->length > 0) + return (ISC_R_UNEXPECTEDEND); + if (ctx->digits != 0) + return (ISC_R_BADBASE64); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { + base64_decode_ctx_t ctx; + isc_textregion_t *tr; + isc_token_t token; + bool eol; + + base64_decode_init(&ctx, length, target); + + while (!ctx.seen_end && (ctx.length != 0)) { + unsigned int i; + + if (length > 0) + eol = false; + else + eol = true; + RETERR(isc_lex_getmastertoken(lexer, &token, + isc_tokentype_string, eol)); + if (token.type != isc_tokentype_string) + break; + tr = &token.value.as_textregion; + for (i = 0; i < tr->length; i++) + RETERR(base64_decode_char(&ctx, tr->base[i])); + } + if (ctx.length < 0 && !ctx.seen_end) + isc_lex_ungettoken(lexer, &token); + RETERR(base64_decode_finish(&ctx)); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_base64_decodestring(const char *cstr, isc_buffer_t *target) { + base64_decode_ctx_t ctx; + + base64_decode_init(&ctx, -1, target); + for (;;) { + int c = *cstr++; + if (c == '\0') + break; + if (c == ' ' || c == '\t' || c == '\n' || c== '\r') + continue; + RETERR(base64_decode_char(&ctx, c)); + } + RETERR(base64_decode_finish(&ctx)); + return (ISC_R_SUCCESS); +} + +static isc_result_t +str_totext(const char *source, isc_buffer_t *target) { + unsigned int l; + isc_region_t region; + + isc_buffer_availableregion(target, ®ion); + l = strlen(source); + + if (l > region.length) + return (ISC_R_NOSPACE); + + memmove(region.base, source, l); + isc_buffer_add(target, l); + return (ISC_R_SUCCESS); +} + +static isc_result_t +mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { + isc_region_t tr; + + isc_buffer_availableregion(target, &tr); + if (length > tr.length) + return (ISC_R_NOSPACE); + memmove(tr.base, base, length); + isc_buffer_add(target, length); + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/bind9.c b/lib/isc/bind9.c new file mode 100644 index 0000000..e5b2fef --- /dev/null +++ b/lib/isc/bind9.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/bind9.h> + +/* + * This determines whether we are using the libisc/libdns libraries + * in BIND9 or in some other application. It is initialized to true + * and remains unchanged for BIND9 and related tools; export library + * clients will run isc_lib_register(), which sets it to false, + * overriding certain BIND9 behaviors. + */ +LIBISC_EXTERNAL_DATA bool isc_bind9 = true; diff --git a/lib/isc/buffer.c b/lib/isc/buffer.c new file mode 100644 index 0000000..9de1e06 --- /dev/null +++ b/lib/isc/buffer.c @@ -0,0 +1,649 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> +#include <stdarg.h> + +#include <isc/buffer.h> +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/region.h> +#include <isc/string.h> +#include <isc/util.h> + +void +isc__buffer_init(isc_buffer_t *b, void *base, unsigned int length) { + /* + * Make 'b' refer to the 'length'-byte region starting at 'base'. + * XXXDCL see the comment in buffer.h about base being const. + */ + + REQUIRE(b != NULL); + + ISC__BUFFER_INIT(b, base, length); +} + +void +isc__buffer_initnull(isc_buffer_t *b) { + /* + * Initialize a new buffer which has no backing store. This can + * later be grown as needed and swapped in place. + */ + + ISC__BUFFER_INIT(b, NULL, 0); +} + +void +isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length) { + /* + * Re-initialize the buffer enough to reconfigure the base of the + * buffer. We will swap in the new buffer, after copying any + * data we contain into the new buffer and adjusting all of our + * internal pointers. + * + * The buffer must not be smaller than the length of the original + * buffer. + */ + REQUIRE(b->length <= length); + REQUIRE(base != NULL); + REQUIRE(!b->autore); + + if (b->length > 0U) { + (void)memmove(base, b->base, b->length); + } + + b->base = base; + b->length = length; +} + +void +isc__buffer_invalidate(isc_buffer_t *b) { + /* + * Make 'b' an invalid buffer. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(!ISC_LINK_LINKED(b, link)); + REQUIRE(b->mctx == NULL); + + ISC__BUFFER_INVALIDATE(b); +} + +void +isc_buffer_setautorealloc(isc_buffer_t *b, bool enable) { + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(b->mctx != NULL); + b->autore = enable; +} + +void +isc__buffer_region(isc_buffer_t *b, isc_region_t *r) { + /* + * Make 'r' refer to the region of 'b'. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(r != NULL); + + ISC__BUFFER_REGION(b, r); +} + +void +isc__buffer_usedregion(const isc_buffer_t *b, isc_region_t *r) { + /* + * Make 'r' refer to the used region of 'b'. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(r != NULL); + + ISC__BUFFER_USEDREGION(b, r); +} + +void +isc__buffer_availableregion(isc_buffer_t *b, isc_region_t *r) { + /* + * Make 'r' refer to the available region of 'b'. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(r != NULL); + + ISC__BUFFER_AVAILABLEREGION(b, r); +} + +void +isc__buffer_add(isc_buffer_t *b, unsigned int n) { + /* + * Increase the 'used' region of 'b' by 'n' bytes. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(b->used + n <= b->length); + + ISC__BUFFER_ADD(b, n); +} + +void +isc__buffer_subtract(isc_buffer_t *b, unsigned int n) { + /* + * Decrease the 'used' region of 'b' by 'n' bytes. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(b->used >= n); + + ISC__BUFFER_SUBTRACT(b, n); +} + +void +isc__buffer_clear(isc_buffer_t *b) { + /* + * Make the used region empty. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + + ISC__BUFFER_CLEAR(b); +} + +void +isc__buffer_consumedregion(isc_buffer_t *b, isc_region_t *r) { + /* + * Make 'r' refer to the consumed region of 'b'. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(r != NULL); + + ISC__BUFFER_CONSUMEDREGION(b, r); +} + +void +isc__buffer_remainingregion(isc_buffer_t *b, isc_region_t *r) { + /* + * Make 'r' refer to the remaining region of 'b'. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(r != NULL); + + ISC__BUFFER_REMAININGREGION(b, r); +} + +void +isc__buffer_activeregion(isc_buffer_t *b, isc_region_t *r) { + /* + * Make 'r' refer to the active region of 'b'. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(r != NULL); + + ISC__BUFFER_ACTIVEREGION(b, r); +} + +void +isc__buffer_setactive(isc_buffer_t *b, unsigned int n) { + /* + * Sets the end of the active region 'n' bytes after current. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(b->current + n <= b->used); + + ISC__BUFFER_SETACTIVE(b, n); +} + +void +isc__buffer_first(isc_buffer_t *b) { + /* + * Make the consumed region empty. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + + ISC__BUFFER_FIRST(b); +} + +void +isc__buffer_forward(isc_buffer_t *b, unsigned int n) { + /* + * Increase the 'consumed' region of 'b' by 'n' bytes. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(b->current + n <= b->used); + + ISC__BUFFER_FORWARD(b, n); +} + +void +isc__buffer_back(isc_buffer_t *b, unsigned int n) { + /* + * Decrease the 'consumed' region of 'b' by 'n' bytes. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(n <= b->current); + + ISC__BUFFER_BACK(b, n); +} + +void +isc_buffer_compact(isc_buffer_t *b) { + unsigned int length; + void *src; + + /* + * Compact the used region by moving the remaining region so it occurs + * at the start of the buffer. The used region is shrunk by the size + * of the consumed region, and the consumed region is then made empty. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + + src = isc_buffer_current(b); + length = isc_buffer_remaininglength(b); + if (length > 0U) { + (void)memmove(b->base, src, (size_t)length); + } + + if (b->active > b->current) + b->active -= b->current; + else + b->active = 0; + b->current = 0; + b->used = length; +} + +uint8_t +isc_buffer_getuint8(isc_buffer_t *b) { + unsigned char *cp; + uint8_t result; + + /* + * Read an unsigned 8-bit integer from 'b' and return it. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(b->used - b->current >= 1); + + cp = isc_buffer_current(b); + b->current += 1; + result = ((uint8_t)(cp[0])); + + return (result); +} + +void +isc__buffer_putuint8(isc_buffer_t *b, uint8_t val) { + isc_result_t result; + REQUIRE(ISC_BUFFER_VALID(b)); + if (ISC_UNLIKELY(b->autore)) { + result = isc_buffer_reserve(&b, 1); + REQUIRE(result == ISC_R_SUCCESS); + } + REQUIRE(isc_buffer_availablelength(b) >= 1); + + ISC__BUFFER_PUTUINT8(b, val); +} + +uint16_t +isc_buffer_getuint16(isc_buffer_t *b) { + unsigned char *cp; + uint16_t result; + + /* + * Read an unsigned 16-bit integer in network byte order from 'b', + * convert it to host byte order, and return it. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(b->used - b->current >= 2); + + cp = isc_buffer_current(b); + b->current += 2; + result = ((unsigned int)(cp[0])) << 8; + result |= ((unsigned int)(cp[1])); + + return (result); +} + +void +isc__buffer_putuint16(isc_buffer_t *b, uint16_t val) { + isc_result_t result; + REQUIRE(ISC_BUFFER_VALID(b)); + if (ISC_UNLIKELY(b->autore)) { + result = isc_buffer_reserve(&b, 2); + REQUIRE(result == ISC_R_SUCCESS); + } + REQUIRE(isc_buffer_availablelength(b) >= 2); + + ISC__BUFFER_PUTUINT16(b, val); +} + +void +isc__buffer_putuint24(isc_buffer_t *b, uint32_t val) { + isc_result_t result; + REQUIRE(ISC_BUFFER_VALID(b)); + if (ISC_UNLIKELY(b->autore)) { + result = isc_buffer_reserve(&b, 3); + REQUIRE(result == ISC_R_SUCCESS); + } + REQUIRE(isc_buffer_availablelength(b) >= 3); + + ISC__BUFFER_PUTUINT24(b, val); +} + +uint32_t +isc_buffer_getuint32(isc_buffer_t *b) { + unsigned char *cp; + uint32_t result; + + /* + * Read an unsigned 32-bit integer in network byte order from 'b', + * convert it to host byte order, and return it. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(b->used - b->current >= 4); + + cp = isc_buffer_current(b); + b->current += 4; + result = ((unsigned int)(cp[0])) << 24; + result |= ((unsigned int)(cp[1])) << 16; + result |= ((unsigned int)(cp[2])) << 8; + result |= ((unsigned int)(cp[3])); + + return (result); +} + +void +isc__buffer_putuint32(isc_buffer_t *b, uint32_t val) { + isc_result_t result; + REQUIRE(ISC_BUFFER_VALID(b)); + if (ISC_UNLIKELY(b->autore)) { + result = isc_buffer_reserve(&b, 4); + REQUIRE(result == ISC_R_SUCCESS); + } + REQUIRE(isc_buffer_availablelength(b) >= 4); + + ISC__BUFFER_PUTUINT32(b, val); +} + +uint64_t +isc_buffer_getuint48(isc_buffer_t *b) { + unsigned char *cp; + uint64_t result; + + /* + * Read an unsigned 48-bit integer in network byte order from 'b', + * convert it to host byte order, and return it. + */ + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(b->used - b->current >= 6); + + cp = isc_buffer_current(b); + b->current += 6; + result = ((int64_t)(cp[0])) << 40; + result |= ((int64_t)(cp[1])) << 32; + result |= ((int64_t)(cp[2])) << 24; + result |= ((int64_t)(cp[3])) << 16; + result |= ((int64_t)(cp[4])) << 8; + result |= ((int64_t)(cp[5])); + + return (result); +} + +void +isc__buffer_putuint48(isc_buffer_t *b, uint64_t val) { + isc_result_t result; + uint16_t valhi; + uint32_t vallo; + + REQUIRE(ISC_BUFFER_VALID(b)); + if (ISC_UNLIKELY(b->autore)) { + result = isc_buffer_reserve(&b, 6); + REQUIRE(result == ISC_R_SUCCESS); + } + REQUIRE(isc_buffer_availablelength(b) >= 6); + + valhi = (uint16_t)(val >> 32); + vallo = (uint32_t)(val & 0xFFFFFFFF); + ISC__BUFFER_PUTUINT16(b, valhi); + ISC__BUFFER_PUTUINT32(b, vallo); +} + +void +isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base, + unsigned int length) +{ + isc_result_t result; + REQUIRE(ISC_BUFFER_VALID(b)); + if (ISC_UNLIKELY(b->autore)) { + result = isc_buffer_reserve(&b, length); + REQUIRE(result == ISC_R_SUCCESS); + } + REQUIRE(isc_buffer_availablelength(b) >= length); + + ISC__BUFFER_PUTMEM(b, base, length); +} + +void +isc__buffer_putstr(isc_buffer_t *b, const char *source) { + unsigned int l; + unsigned char *cp; + isc_result_t result; + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(source != NULL); + + /* + * Do not use ISC__BUFFER_PUTSTR(), so strlen is only done once. + */ + l = strlen(source); + if (ISC_UNLIKELY(b->autore)) { + result = isc_buffer_reserve(&b, l); + REQUIRE(result == ISC_R_SUCCESS); + } + REQUIRE(isc_buffer_availablelength(b) >= l); + + cp = isc_buffer_used(b); + memmove(cp, source, l); + b->used += l; +} + +void +isc_buffer_putdecint(isc_buffer_t *b, int64_t v) { + unsigned int l=0; + unsigned char *cp; + char buf[21]; + isc_result_t result; + + REQUIRE(ISC_BUFFER_VALID(b)); + + /* xxxwpk do it more low-level way ? */ + l = snprintf(buf, 21, "%" PRId64, v); + RUNTIME_CHECK(l <= 21); + if (ISC_UNLIKELY(b->autore)) { + result = isc_buffer_reserve(&b, l); + REQUIRE(result == ISC_R_SUCCESS); + } + REQUIRE(isc_buffer_availablelength(b) >= l); + + cp = isc_buffer_used(b); + memmove(cp, buf, l); + b->used += l; +} + +isc_result_t +isc_buffer_dup(isc_mem_t *mctx, isc_buffer_t **dstp, const isc_buffer_t *src) { + isc_buffer_t *dst = NULL; + isc_region_t region; + isc_result_t result; + + REQUIRE(dstp != NULL && *dstp == NULL); + REQUIRE(ISC_BUFFER_VALID(src)); + + isc_buffer_usedregion(src, ®ion); + + result = isc_buffer_allocate(mctx, &dst, region.length); + if (result != ISC_R_SUCCESS) + return (result); + + result = isc_buffer_copyregion(dst, ®ion); + RUNTIME_CHECK(result == ISC_R_SUCCESS); /* NOSPACE is impossible */ + *dstp = dst; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r) { + unsigned char *base; + unsigned int available; + isc_result_t result; + + REQUIRE(ISC_BUFFER_VALID(b)); + REQUIRE(r != NULL); + + /* + * XXXDCL + */ + base = isc_buffer_used(b); + available = isc_buffer_availablelength(b); + if (ISC_UNLIKELY(b->autore)) { + result = isc_buffer_reserve(&b, r->length); + if (result != ISC_R_SUCCESS) + return (result); + } + if (r->length > available) + return (ISC_R_NOSPACE); + if (r->length > 0U) { + memmove(base, r->base, r->length); + b->used += r->length; + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer, + unsigned int length) +{ + isc_buffer_t *dbuf; + unsigned char * bdata; + REQUIRE(dynbuffer != NULL); + REQUIRE(*dynbuffer == NULL); + + dbuf = isc_mem_get(mctx, sizeof(isc_buffer_t)); + if (dbuf == NULL) + return (ISC_R_NOMEMORY); + + bdata = isc_mem_get(mctx, length); + if (bdata == NULL) { + isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t)); + return (ISC_R_NOMEMORY); + } + + isc_buffer_init(dbuf, bdata, length); + dbuf->mctx = mctx; + + ENSURE(ISC_BUFFER_VALID(dbuf)); + + *dynbuffer = dbuf; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_buffer_reallocate(isc_buffer_t **dynbuffer, unsigned int length) { + unsigned char *bdata; + + REQUIRE(dynbuffer != NULL); + REQUIRE(ISC_BUFFER_VALID(*dynbuffer)); + REQUIRE((*dynbuffer)->mctx != NULL); + + if ((*dynbuffer)->length > length) + return (ISC_R_NOSPACE); + + /* + * XXXMUKS: This is far more expensive than plain realloc() as + * it doesn't remap pages, but does ordinary copy. So is + * isc_mem_reallocate(), which has additional issues. + */ + bdata = isc_mem_get((*dynbuffer)->mctx, length); + if (bdata == NULL) + return (ISC_R_NOMEMORY); + + memmove(bdata, (*dynbuffer)->base, (*dynbuffer)->length); + isc_mem_put((*dynbuffer)->mctx, (*dynbuffer)->base, + (*dynbuffer)->length); + + (*dynbuffer)->base = bdata; + (*dynbuffer)->length = length; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size) { + uint64_t len; + + REQUIRE(dynbuffer != NULL); + REQUIRE(ISC_BUFFER_VALID(*dynbuffer)); + + len = (*dynbuffer)->length; + if ((len - (*dynbuffer)->used) >= size) + return (ISC_R_SUCCESS); + + if ((*dynbuffer)->mctx == NULL) + return (ISC_R_NOSPACE); + + /* Round to nearest buffer size increment */ + len = size + (*dynbuffer)->used; + len = (len + ISC_BUFFER_INCR - 1 - ((len - 1) % ISC_BUFFER_INCR)); + + /* Cap at UINT_MAX */ + if (len > UINT_MAX) { + len = UINT_MAX; + } + + if ((len - (*dynbuffer)->used) < size) + return (ISC_R_NOMEMORY); + + return (isc_buffer_reallocate(dynbuffer, (unsigned int) len)); +} + +void +isc_buffer_free(isc_buffer_t **dynbuffer) { + isc_buffer_t *dbuf; + isc_mem_t *mctx; + + REQUIRE(dynbuffer != NULL); + REQUIRE(ISC_BUFFER_VALID(*dynbuffer)); + REQUIRE((*dynbuffer)->mctx != NULL); + + dbuf = *dynbuffer; + *dynbuffer = NULL; /* destroy external reference */ + mctx = dbuf->mctx; + dbuf->mctx = NULL; + + isc_mem_put(mctx, dbuf->base, dbuf->length); + isc_buffer_invalidate(dbuf); + isc_mem_put(mctx, dbuf, sizeof(isc_buffer_t)); +} diff --git a/lib/isc/bufferlist.c b/lib/isc/bufferlist.c new file mode 100644 index 0000000..f6ebdea --- /dev/null +++ b/lib/isc/bufferlist.c @@ -0,0 +1,57 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stddef.h> + +#include <isc/buffer.h> +#include <isc/bufferlist.h> +#include <isc/util.h> + +unsigned int +isc_bufferlist_usedcount(isc_bufferlist_t *bl) { + isc_buffer_t *buffer; + unsigned int length; + + REQUIRE(bl != NULL); + + length = 0; + buffer = ISC_LIST_HEAD(*bl); + while (buffer != NULL) { + REQUIRE(ISC_BUFFER_VALID(buffer)); + length += isc_buffer_usedlength(buffer); + buffer = ISC_LIST_NEXT(buffer, link); + } + + return (length); +} + +unsigned int +isc_bufferlist_availablecount(isc_bufferlist_t *bl) { + isc_buffer_t *buffer; + unsigned int length; + + REQUIRE(bl != NULL); + + length = 0; + buffer = ISC_LIST_HEAD(*bl); + while (buffer != NULL) { + REQUIRE(ISC_BUFFER_VALID(buffer)); + length += isc_buffer_availablelength(buffer); + buffer = ISC_LIST_NEXT(buffer, link); + } + + return (length); +} diff --git a/lib/isc/chacha_private.h b/lib/isc/chacha_private.h new file mode 100644 index 0000000..a6653c6 --- /dev/null +++ b/lib/isc/chacha_private.h @@ -0,0 +1,229 @@ +/* + * Taken from OpenBSD CVS src/lib/libc/crypt/chacha_private.h on + * May 12, 2014. + */ + +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +typedef unsigned char u8; +typedef unsigned int u32; + +typedef struct +{ + u32 input[16]; /* could be compressed */ +} chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U8V(v) ((u8)(v) & U8C(0xFF)) +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define U8TO32_LITTLE(p) \ + (((u32)((p)[0]) ) | \ + ((u32)((p)[1]) << 8) | \ + ((u32)((p)[2]) << 16) | \ + ((u32)((p)[3]) << 24)) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[16] = { 'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', + '2', '-', 'b', 'y', 't', 'e', ' ', 'k' }; +static const char tau[16] = { 'e', 'x', 'p', 'a', 'n', 'd', ' ', '1', + '6', '-', 'b', 'y', 't', 'e', ' ', 'k' }; + +static void +chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits,u32 ivbits) +{ + const char *constants; + + UNUSED(ivbits); + + x->input[4] = U8TO32_LITTLE(k + 0); + x->input[5] = U8TO32_LITTLE(k + 4); + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = U8TO32_LITTLE(k + 0); + x->input[9] = U8TO32_LITTLE(k + 4); + x->input[10] = U8TO32_LITTLE(k + 8); + x->input[11] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[1] = U8TO32_LITTLE(constants + 4); + x->input[2] = U8TO32_LITTLE(constants + 8); + x->input[3] = U8TO32_LITTLE(constants + 12); +} + +static void +chacha_ivsetup(chacha_ctx *x,const u8 *iv) +{ + x->input[12] = 0; + x->input[13] = 0; + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +static void +chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes) +{ + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = NULL; + u8 tmp[64]; + u_int i; + + if (!bytes) return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS(x0,j0); + x1 = PLUS(x1,j1); + x2 = PLUS(x2,j2); + x3 = PLUS(x3,j3); + x4 = PLUS(x4,j4); + x5 = PLUS(x5,j5); + x6 = PLUS(x6,j6); + x7 = PLUS(x7,j7); + x8 = PLUS(x8,j8); + x9 = PLUS(x9,j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + +#ifndef KEYSTREAM_ONLY + x0 = XOR(x0,U8TO32_LITTLE(m + 0)); + x1 = XOR(x1,U8TO32_LITTLE(m + 4)); + x2 = XOR(x2,U8TO32_LITTLE(m + 8)); + x3 = XOR(x3,U8TO32_LITTLE(m + 12)); + x4 = XOR(x4,U8TO32_LITTLE(m + 16)); + x5 = XOR(x5,U8TO32_LITTLE(m + 20)); + x6 = XOR(x6,U8TO32_LITTLE(m + 24)); + x7 = XOR(x7,U8TO32_LITTLE(m + 28)); + x8 = XOR(x8,U8TO32_LITTLE(m + 32)); + x9 = XOR(x9,U8TO32_LITTLE(m + 36)); + x10 = XOR(x10,U8TO32_LITTLE(m + 40)); + x11 = XOR(x11,U8TO32_LITTLE(m + 44)); + x12 = XOR(x12,U8TO32_LITTLE(m + 48)); + x13 = XOR(x13,U8TO32_LITTLE(m + 52)); + x14 = XOR(x14,U8TO32_LITTLE(m + 56)); + x15 = XOR(x15,U8TO32_LITTLE(m + 60)); +#endif + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0,x0); + U32TO8_LITTLE(c + 4,x1); + U32TO8_LITTLE(c + 8,x2); + U32TO8_LITTLE(c + 12,x3); + U32TO8_LITTLE(c + 16,x4); + U32TO8_LITTLE(c + 20,x5); + U32TO8_LITTLE(c + 24,x6); + U32TO8_LITTLE(c + 28,x7); + U32TO8_LITTLE(c + 32,x8); + U32TO8_LITTLE(c + 36,x9); + U32TO8_LITTLE(c + 40,x10); + U32TO8_LITTLE(c + 44,x11); + U32TO8_LITTLE(c + 48,x12); + U32TO8_LITTLE(c + 52,x13); + U32TO8_LITTLE(c + 56,x14); + U32TO8_LITTLE(c + 60,x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; +#ifndef KEYSTREAM_ONLY + m += 64; +#endif + } +} diff --git a/lib/isc/commandline.c b/lib/isc/commandline.c new file mode 100644 index 0000000..efee967 --- /dev/null +++ b/lib/isc/commandline.c @@ -0,0 +1,271 @@ +/* + * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +/*! \file + * This file was adapted from the NetBSD project's source tree, RCS ID: + * NetBSD: getopt.c,v 1.15 1999/09/20 04:39:37 lukem Exp + * + * The primary change has been to rename items to the ISC namespace + * and format in the ISC coding style. + */ + +#include <config.h> + +#include <stdbool.h> +#include <stdio.h> + +#include <isc/commandline.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/util.h> + +/*% Index into parent argv vector. */ +LIBISC_EXTERNAL_DATA int isc_commandline_index = 1; +/*% Character checked for validity. */ +LIBISC_EXTERNAL_DATA int isc_commandline_option; +/*% Argument associated with option. */ +LIBISC_EXTERNAL_DATA char *isc_commandline_argument; +/*% For printing error messages. */ +LIBISC_EXTERNAL_DATA char *isc_commandline_progname; +/*% Print error messages. */ +LIBISC_EXTERNAL_DATA bool isc_commandline_errprint = true; +/*% Reset processing. */ +LIBISC_EXTERNAL_DATA bool isc_commandline_reset = true; + +static char endopt = '\0'; + +#define BADOPT '?' +#define BADARG ':' +#define ENDOPT &endopt + +/*! + * getopt -- + * Parse argc/argv argument vector. + */ +int +isc_commandline_parse(int argc, char * const *argv, const char *options) { + static char *place = ENDOPT; + const char *option; /* Index into *options of option. */ + + REQUIRE(argc >= 0 && argv != NULL && options != NULL); + + /* + * Update scanning pointer, either because a reset was requested or + * the previous argv was finished. + */ + if (isc_commandline_reset || *place == '\0') { + if (isc_commandline_reset) { + isc_commandline_index = 1; + isc_commandline_reset = false; + } + + if (isc_commandline_progname == NULL) + isc_commandline_progname = argv[0]; + + if (isc_commandline_index >= argc || + *(place = argv[isc_commandline_index]) != '-') { + /* + * Index out of range or points to non-option. + */ + place = ENDOPT; + return (-1); + } + + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + /* + * Found '--' to signal end of options. Advance + * index to next argv, the first non-option. + */ + isc_commandline_index++; + place = ENDOPT; + return (-1); + } + } + + isc_commandline_option = *place++; + option = strchr(options, isc_commandline_option); + + /* + * Ensure valid option has been passed as specified by options string. + * '-:' is never a valid command line option because it could not + * distinguish ':' from the argument specifier in the options string. + */ + if (isc_commandline_option == ':' || option == NULL) { + if (*place == '\0') + isc_commandline_index++; + + if (isc_commandline_errprint && *options != ':') + fprintf(stderr, "%s: %s -- %c\n", + isc_commandline_progname, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_COMMANDLINE, + ISC_MSG_ILLEGALOPT, + "illegal option"), + isc_commandline_option); + + return (BADOPT); + } + + if (*++option != ':') { + /* + * Option does not take an argument. + */ + isc_commandline_argument = NULL; + + /* + * Skip to next argv if at the end of the current argv. + */ + if (*place == '\0') + ++isc_commandline_index; + + } else { + /* + * Option needs an argument. + */ + if (*place != '\0') + /* + * Option is in this argv, -D1 style. + */ + isc_commandline_argument = place; + + else if (argc > ++isc_commandline_index) + /* + * Option is next argv, -D 1 style. + */ + isc_commandline_argument = argv[isc_commandline_index]; + + else { + /* + * Argument needed, but no more argv. + */ + place = ENDOPT; + + /* + * Silent failure with "missing argument" return + * when ':' starts options string, per historical spec. + */ + if (*options == ':') + return (BADARG); + + if (isc_commandline_errprint) + fprintf(stderr, "%s: %s -- %c\n", + isc_commandline_progname, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_COMMANDLINE, + ISC_MSG_OPTNEEDARG, + "option requires " + "an argument"), + isc_commandline_option); + + return (BADOPT); + } + + place = ENDOPT; + + /* + * Point to argv that follows argument. + */ + isc_commandline_index++; + } + + return (isc_commandline_option); +} + +isc_result_t +isc_commandline_strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, + char ***argvp, unsigned int n) +{ + isc_result_t result; + + restart: + /* Discard leading whitespace. */ + while (*s == ' ' || *s == '\t') + s++; + + if (*s == '\0') { + /* We have reached the end of the string. */ + *argcp = n; + *argvp = isc_mem_get(mctx, n * sizeof(char *)); + if (*argvp == NULL) + return (ISC_R_NOMEMORY); + } else { + char *p = s; + while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') { + if (*p == '\n') { + *p = ' '; + goto restart; + } + p++; + } + + /* do "grouping", items between { and } are one arg */ + if (*p == '{') { + char *t = p; + /* + * shift all characters to left by 1 to get rid of '{' + */ + while (*t != '\0') { + t++; + *(t-1) = *t; + } + while (*p != '\0' && *p != '}') { + p++; + } + /* get rid of '}' character */ + if (*p == '}') { + *p = '\0'; + p++; + } + /* normal case, no "grouping" */ + } else if (*p != '\0') + *p++ = '\0'; + + result = isc_commandline_strtoargv(mctx, p, + argcp, argvp, n + 1); + if (result != ISC_R_SUCCESS) + return (result); + (*argvp)[n] = s; + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/counter.c b/lib/isc/counter.c new file mode 100644 index 0000000..8c70051 --- /dev/null +++ b/lib/isc/counter.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> +#include <stddef.h> + +#include <isc/counter.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/util.h> + +#define COUNTER_MAGIC ISC_MAGIC('C', 'n', 't', 'r') +#define VALID_COUNTER(r) ISC_MAGIC_VALID(r, COUNTER_MAGIC) + +struct isc_counter { + unsigned int magic; + isc_mem_t *mctx; + isc_mutex_t lock; + unsigned int references; + unsigned int limit; + unsigned int used; +}; + +isc_result_t +isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp) { + isc_result_t result; + isc_counter_t *counter; + + REQUIRE(counterp != NULL && *counterp == NULL); + + counter = isc_mem_get(mctx, sizeof(*counter)); + if (counter == NULL) + return (ISC_R_NOMEMORY); + + result = isc_mutex_init(&counter->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, counter, sizeof(*counter)); + return (result); + } + + counter->mctx = NULL; + isc_mem_attach(mctx, &counter->mctx); + + counter->references = 1; + counter->limit = limit; + counter->used = 0; + + counter->magic = COUNTER_MAGIC; + *counterp = counter; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_counter_increment(isc_counter_t *counter) { + isc_result_t result = ISC_R_SUCCESS; + + LOCK(&counter->lock); + counter->used++; + if (counter->limit != 0 && counter->used >= counter->limit) + result = ISC_R_QUOTA; + UNLOCK(&counter->lock); + + return (result); +} + +unsigned int +isc_counter_used(isc_counter_t *counter) { + REQUIRE(VALID_COUNTER(counter)); + + return (counter->used); +} + +void +isc_counter_setlimit(isc_counter_t *counter, int limit) { + REQUIRE(VALID_COUNTER(counter)); + + LOCK(&counter->lock); + counter->limit = limit; + UNLOCK(&counter->lock); +} + +void +isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp) { + REQUIRE(VALID_COUNTER(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + LOCK(&source->lock); + source->references++; + INSIST(source->references > 0); + UNLOCK(&source->lock); + + *targetp = source; +} + +static void +destroy(isc_counter_t *counter) { + counter->magic = 0; + isc_mutex_destroy(&counter->lock); + isc_mem_putanddetach(&counter->mctx, counter, sizeof(*counter)); +} + +void +isc_counter_detach(isc_counter_t **counterp) { + isc_counter_t *counter; + bool want_destroy = false; + + REQUIRE(counterp != NULL && *counterp != NULL); + counter = *counterp; + REQUIRE(VALID_COUNTER(counter)); + + *counterp = NULL; + + LOCK(&counter->lock); + INSIST(counter->references > 0); + counter->references--; + if (counter->references == 0) + want_destroy = true; + UNLOCK(&counter->lock); + + if (want_destroy) + destroy(counter); +} diff --git a/lib/isc/crc64.c b/lib/isc/crc64.c new file mode 100644 index 0000000..1db2d5d --- /dev/null +++ b/lib/isc/crc64.c @@ -0,0 +1,141 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include "config.h" + +#include <inttypes.h> + +#include <isc/assertions.h> +#include <isc/crc64.h> +#include <isc/string.h> +#include <isc/types.h> +#include <isc/util.h> + +/*%< + * ECMA-182 CRC64 polynomial. + */ +static const uint64_t crc64_table[256] = { + 0x0000000000000000ULL, 0x42F0E1EBA9EA3693ULL, 0x85E1C3D753D46D26ULL, + 0xC711223CFA3E5BB5ULL, 0x493366450E42ECDFULL, 0x0BC387AEA7A8DA4CULL, + 0xCCD2A5925D9681F9ULL, 0x8E224479F47CB76AULL, 0x9266CC8A1C85D9BEULL, + 0xD0962D61B56FEF2DULL, 0x17870F5D4F51B498ULL, 0x5577EEB6E6BB820BULL, + 0xDB55AACF12C73561ULL, 0x99A54B24BB2D03F2ULL, 0x5EB4691841135847ULL, + 0x1C4488F3E8F96ED4ULL, 0x663D78FF90E185EFULL, 0x24CD9914390BB37CULL, + 0xE3DCBB28C335E8C9ULL, 0xA12C5AC36ADFDE5AULL, 0x2F0E1EBA9EA36930ULL, + 0x6DFEFF5137495FA3ULL, 0xAAEFDD6DCD770416ULL, 0xE81F3C86649D3285ULL, + 0xF45BB4758C645C51ULL, 0xB6AB559E258E6AC2ULL, 0x71BA77A2DFB03177ULL, + 0x334A9649765A07E4ULL, 0xBD68D2308226B08EULL, 0xFF9833DB2BCC861DULL, + 0x388911E7D1F2DDA8ULL, 0x7A79F00C7818EB3BULL, 0xCC7AF1FF21C30BDEULL, + 0x8E8A101488293D4DULL, 0x499B3228721766F8ULL, 0x0B6BD3C3DBFD506BULL, + 0x854997BA2F81E701ULL, 0xC7B97651866BD192ULL, 0x00A8546D7C558A27ULL, + 0x4258B586D5BFBCB4ULL, 0x5E1C3D753D46D260ULL, 0x1CECDC9E94ACE4F3ULL, + 0xDBFDFEA26E92BF46ULL, 0x990D1F49C77889D5ULL, 0x172F5B3033043EBFULL, + 0x55DFBADB9AEE082CULL, 0x92CE98E760D05399ULL, 0xD03E790CC93A650AULL, + 0xAA478900B1228E31ULL, 0xE8B768EB18C8B8A2ULL, 0x2FA64AD7E2F6E317ULL, + 0x6D56AB3C4B1CD584ULL, 0xE374EF45BF6062EEULL, 0xA1840EAE168A547DULL, + 0x66952C92ECB40FC8ULL, 0x2465CD79455E395BULL, 0x3821458AADA7578FULL, + 0x7AD1A461044D611CULL, 0xBDC0865DFE733AA9ULL, 0xFF3067B657990C3AULL, + 0x711223CFA3E5BB50ULL, 0x33E2C2240A0F8DC3ULL, 0xF4F3E018F031D676ULL, + 0xB60301F359DBE0E5ULL, 0xDA050215EA6C212FULL, 0x98F5E3FE438617BCULL, + 0x5FE4C1C2B9B84C09ULL, 0x1D14202910527A9AULL, 0x93366450E42ECDF0ULL, + 0xD1C685BB4DC4FB63ULL, 0x16D7A787B7FAA0D6ULL, 0x5427466C1E109645ULL, + 0x4863CE9FF6E9F891ULL, 0x0A932F745F03CE02ULL, 0xCD820D48A53D95B7ULL, + 0x8F72ECA30CD7A324ULL, 0x0150A8DAF8AB144EULL, 0x43A04931514122DDULL, + 0x84B16B0DAB7F7968ULL, 0xC6418AE602954FFBULL, 0xBC387AEA7A8DA4C0ULL, + 0xFEC89B01D3679253ULL, 0x39D9B93D2959C9E6ULL, 0x7B2958D680B3FF75ULL, + 0xF50B1CAF74CF481FULL, 0xB7FBFD44DD257E8CULL, 0x70EADF78271B2539ULL, + 0x321A3E938EF113AAULL, 0x2E5EB66066087D7EULL, 0x6CAE578BCFE24BEDULL, + 0xABBF75B735DC1058ULL, 0xE94F945C9C3626CBULL, 0x676DD025684A91A1ULL, + 0x259D31CEC1A0A732ULL, 0xE28C13F23B9EFC87ULL, 0xA07CF2199274CA14ULL, + 0x167FF3EACBAF2AF1ULL, 0x548F120162451C62ULL, 0x939E303D987B47D7ULL, + 0xD16ED1D631917144ULL, 0x5F4C95AFC5EDC62EULL, 0x1DBC74446C07F0BDULL, + 0xDAAD56789639AB08ULL, 0x985DB7933FD39D9BULL, 0x84193F60D72AF34FULL, + 0xC6E9DE8B7EC0C5DCULL, 0x01F8FCB784FE9E69ULL, 0x43081D5C2D14A8FAULL, + 0xCD2A5925D9681F90ULL, 0x8FDAB8CE70822903ULL, 0x48CB9AF28ABC72B6ULL, + 0x0A3B7B1923564425ULL, 0x70428B155B4EAF1EULL, 0x32B26AFEF2A4998DULL, + 0xF5A348C2089AC238ULL, 0xB753A929A170F4ABULL, 0x3971ED50550C43C1ULL, + 0x7B810CBBFCE67552ULL, 0xBC902E8706D82EE7ULL, 0xFE60CF6CAF321874ULL, + 0xE224479F47CB76A0ULL, 0xA0D4A674EE214033ULL, 0x67C58448141F1B86ULL, + 0x253565A3BDF52D15ULL, 0xAB1721DA49899A7FULL, 0xE9E7C031E063ACECULL, + 0x2EF6E20D1A5DF759ULL, 0x6C0603E6B3B7C1CAULL, 0xF6FAE5C07D3274CDULL, + 0xB40A042BD4D8425EULL, 0x731B26172EE619EBULL, 0x31EBC7FC870C2F78ULL, + 0xBFC9838573709812ULL, 0xFD39626EDA9AAE81ULL, 0x3A28405220A4F534ULL, + 0x78D8A1B9894EC3A7ULL, 0x649C294A61B7AD73ULL, 0x266CC8A1C85D9BE0ULL, + 0xE17DEA9D3263C055ULL, 0xA38D0B769B89F6C6ULL, 0x2DAF4F0F6FF541ACULL, + 0x6F5FAEE4C61F773FULL, 0xA84E8CD83C212C8AULL, 0xEABE6D3395CB1A19ULL, + 0x90C79D3FEDD3F122ULL, 0xD2377CD44439C7B1ULL, 0x15265EE8BE079C04ULL, + 0x57D6BF0317EDAA97ULL, 0xD9F4FB7AE3911DFDULL, 0x9B041A914A7B2B6EULL, + 0x5C1538ADB04570DBULL, 0x1EE5D94619AF4648ULL, 0x02A151B5F156289CULL, + 0x4051B05E58BC1E0FULL, 0x87409262A28245BAULL, 0xC5B073890B687329ULL, + 0x4B9237F0FF14C443ULL, 0x0962D61B56FEF2D0ULL, 0xCE73F427ACC0A965ULL, + 0x8C8315CC052A9FF6ULL, 0x3A80143F5CF17F13ULL, 0x7870F5D4F51B4980ULL, + 0xBF61D7E80F251235ULL, 0xFD913603A6CF24A6ULL, 0x73B3727A52B393CCULL, + 0x31439391FB59A55FULL, 0xF652B1AD0167FEEAULL, 0xB4A25046A88DC879ULL, + 0xA8E6D8B54074A6ADULL, 0xEA16395EE99E903EULL, 0x2D071B6213A0CB8BULL, + 0x6FF7FA89BA4AFD18ULL, 0xE1D5BEF04E364A72ULL, 0xA3255F1BE7DC7CE1ULL, + 0x64347D271DE22754ULL, 0x26C49CCCB40811C7ULL, 0x5CBD6CC0CC10FAFCULL, + 0x1E4D8D2B65FACC6FULL, 0xD95CAF179FC497DAULL, 0x9BAC4EFC362EA149ULL, + 0x158E0A85C2521623ULL, 0x577EEB6E6BB820B0ULL, 0x906FC95291867B05ULL, + 0xD29F28B9386C4D96ULL, 0xCEDBA04AD0952342ULL, 0x8C2B41A1797F15D1ULL, + 0x4B3A639D83414E64ULL, 0x09CA82762AAB78F7ULL, 0x87E8C60FDED7CF9DULL, + 0xC51827E4773DF90EULL, 0x020905D88D03A2BBULL, 0x40F9E43324E99428ULL, + 0x2CFFE7D5975E55E2ULL, 0x6E0F063E3EB46371ULL, 0xA91E2402C48A38C4ULL, + 0xEBEEC5E96D600E57ULL, 0x65CC8190991CB93DULL, 0x273C607B30F68FAEULL, + 0xE02D4247CAC8D41BULL, 0xA2DDA3AC6322E288ULL, 0xBE992B5F8BDB8C5CULL, + 0xFC69CAB42231BACFULL, 0x3B78E888D80FE17AULL, 0x7988096371E5D7E9ULL, + 0xF7AA4D1A85996083ULL, 0xB55AACF12C735610ULL, 0x724B8ECDD64D0DA5ULL, + 0x30BB6F267FA73B36ULL, 0x4AC29F2A07BFD00DULL, 0x08327EC1AE55E69EULL, + 0xCF235CFD546BBD2BULL, 0x8DD3BD16FD818BB8ULL, 0x03F1F96F09FD3CD2ULL, + 0x41011884A0170A41ULL, 0x86103AB85A2951F4ULL, 0xC4E0DB53F3C36767ULL, + 0xD8A453A01B3A09B3ULL, 0x9A54B24BB2D03F20ULL, 0x5D45907748EE6495ULL, + 0x1FB5719CE1045206ULL, 0x919735E51578E56CULL, 0xD367D40EBC92D3FFULL, + 0x1476F63246AC884AULL, 0x568617D9EF46BED9ULL, 0xE085162AB69D5E3CULL, + 0xA275F7C11F7768AFULL, 0x6564D5FDE549331AULL, 0x279434164CA30589ULL, + 0xA9B6706FB8DFB2E3ULL, 0xEB46918411358470ULL, 0x2C57B3B8EB0BDFC5ULL, + 0x6EA7525342E1E956ULL, 0x72E3DAA0AA188782ULL, 0x30133B4B03F2B111ULL, + 0xF7021977F9CCEAA4ULL, 0xB5F2F89C5026DC37ULL, 0x3BD0BCE5A45A6B5DULL, + 0x79205D0E0DB05DCEULL, 0xBE317F32F78E067BULL, 0xFCC19ED95E6430E8ULL, + 0x86B86ED5267CDBD3ULL, 0xC4488F3E8F96ED40ULL, 0x0359AD0275A8B6F5ULL, + 0x41A94CE9DC428066ULL, 0xCF8B0890283E370CULL, 0x8D7BE97B81D4019FULL, + 0x4A6ACB477BEA5A2AULL, 0x089A2AACD2006CB9ULL, 0x14DEA25F3AF9026DULL, + 0x562E43B4931334FEULL, 0x913F6188692D6F4BULL, 0xD3CF8063C0C759D8ULL, + 0x5DEDC41A34BBEEB2ULL, 0x1F1D25F19D51D821ULL, + 0xD80C07CD676F8394ULL, 0x9AFCE626CE85B507ULL +}; + +void +isc_crc64_init(uint64_t *crc) { + REQUIRE(crc != NULL); + + *crc = 0xffffffffffffffffULL; +} + +void +isc_crc64_update(uint64_t *crc, const void *data, size_t len) { + const unsigned char *p = data; + int i; + + REQUIRE(crc != NULL); + REQUIRE(data != NULL); + + while (len-- > 0U) { + i = ((int) (*crc >> 56) ^ *p++) & 0xff; + *crc = crc64_table[i] ^ (*crc << 8); + } +} + + +void +isc_crc64_final(uint64_t *crc) { + REQUIRE(crc != NULL); + + *crc ^= 0xffffffffffffffffULL; +} diff --git a/lib/isc/entropy.c b/lib/isc/entropy.c new file mode 100644 index 0000000..ab2f617 --- /dev/null +++ b/lib/isc/entropy.c @@ -0,0 +1,1288 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file + * \brief + * This is the system independent part of the entropy module. It is + * compiled via inclusion from the relevant OS source file, ie, + * \link unix/entropy.c unix/entropy.c \endlink or win32/entropy.c. + * + * \author Much of this code is modeled after the NetBSD /dev/random implementation, + * written by Michael Graff <explorer@netbsd.org>. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdio.h> + +#include <isc/buffer.h> +#include <isc/entropy.h> +#include <isc/keyboard.h> +#include <isc/list.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/mutex.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/region.h> +#include <isc/safe.h> +#include <isc/sha1.h> +#include <isc/string.h> +#include <isc/time.h> +#include <isc/util.h> + +#ifdef PKCS11CRYPTO +#include <pk11/pk11.h> +#endif + +#define ENTROPY_MAGIC ISC_MAGIC('E', 'n', 't', 'e') +#define SOURCE_MAGIC ISC_MAGIC('E', 'n', 't', 's') + +#define VALID_ENTROPY(e) ISC_MAGIC_VALID(e, ENTROPY_MAGIC) +#define VALID_SOURCE(s) ISC_MAGIC_VALID(s, SOURCE_MAGIC) + +/*** + *** "constants." Do not change these unless you _really_ know what + *** you are doing. + ***/ + +/*% + * Size of entropy pool in 32-bit words. This _MUST_ be a power of 2. + */ +#define RND_POOLWORDS 128 +/*% Pool in bytes. */ +#define RND_POOLBYTES (RND_POOLWORDS * 4) +/*% Pool in bits. */ +#define RND_POOLBITS (RND_POOLWORDS * 32) + +/*% + * Number of bytes returned per hash. This must be true: + * threshold * 2 <= digest_size_in_bytes + */ +#define RND_ENTROPY_THRESHOLD 10 +#define THRESHOLD_BITS (RND_ENTROPY_THRESHOLD * 8) + +/*% + * Size of the input event queue in samples. + */ +#define RND_EVENTQSIZE 32 + +/*% + * The number of times we'll "reseed" for pseudorandom seeds. This is an + * extremely weak pseudorandom seed. If the caller is using lots of + * pseudorandom data and they cannot provide a stronger random source, + * there is little we can do other than hope they're smart enough to + * call _adddata() with something better than we can come up with. + */ +#define RND_INITIALIZE 128 + +/*% Entropy Pool */ +typedef struct { + uint32_t cursor; /*%< current add point in the pool */ + uint32_t entropy; /*%< current entropy estimate in bits */ + uint32_t pseudo; /*%< bits extracted in pseudorandom */ + uint32_t rotate; /*%< how many bits to rotate by */ + uint32_t pool[RND_POOLWORDS]; /*%< random pool data */ +} isc_entropypool_t; + +struct isc_entropy { + unsigned int magic; + isc_mem_t *mctx; + isc_mutex_t lock; + unsigned int refcnt; + uint32_t initialized; + uint32_t initcount; + isc_entropypool_t pool; + unsigned int nsources; + isc_entropysource_t *nextsource; + ISC_LIST(isc_entropysource_t) sources; +}; + +/*% Sample Queue */ +typedef struct { + uint32_t last_time; /*%< last time recorded */ + uint32_t last_delta; /*%< last delta value */ + uint32_t last_delta2; /*%< last delta2 value */ + uint32_t nsamples; /*%< number of samples filled in */ + uint32_t *samples; /*%< the samples */ + uint32_t *extra; /*%< extra samples added in */ +} sample_queue_t; + +typedef struct { + sample_queue_t samplequeue; +} isc_entropysamplesource_t; + +typedef struct { + bool start_called; + isc_entropystart_t startfunc; + isc_entropyget_t getfunc; + isc_entropystop_t stopfunc; + void *arg; + sample_queue_t samplequeue; +} isc_cbsource_t; + +typedef struct { + FILESOURCE_HANDLE_TYPE handle; +} isc_entropyfilesource_t; + +struct isc_entropysource { + unsigned int magic; + unsigned int type; + isc_entropy_t *ent; + uint32_t total; /*%< entropy from this source */ + ISC_LINK(isc_entropysource_t) link; + char name[32]; + bool bad; + bool warn_keyboard; + isc_keyboard_t kbd; + union { + isc_entropysamplesource_t sample; + isc_entropyfilesource_t file; + isc_cbsource_t callback; + isc_entropyusocketsource_t usocket; + } sources; +}; + +#define ENTROPY_SOURCETYPE_SAMPLE 1 /*%< Type is a sample source */ +#define ENTROPY_SOURCETYPE_FILE 2 /*%< Type is a file source */ +#define ENTROPY_SOURCETYPE_CALLBACK 3 /*%< Type is a callback source */ +#define ENTROPY_SOURCETYPE_USOCKET 4 /*%< Type is a Unix socket source */ + +/*@{*/ +/*% + * The random pool "taps" + */ +#define TAP1 99 +#define TAP2 59 +#define TAP3 31 +#define TAP4 9 +#define TAP5 7 +/*@}*/ + +/*@{*/ +/*% + * Declarations for function provided by the system dependent sources that + * include this file. + */ +static void +fillpool(isc_entropy_t *, unsigned int, bool); + +static int +wait_for_sources(isc_entropy_t *); + +static void +destroyfilesource(isc_entropyfilesource_t *source); + +static void +destroyusocketsource(isc_entropyusocketsource_t *source); + +/*@}*/ + +static void +samplequeue_release(isc_entropy_t *ent, sample_queue_t *sq) { + REQUIRE(sq->samples != NULL); + REQUIRE(sq->extra != NULL); + + isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4); + isc_mem_put(ent->mctx, sq->extra, RND_EVENTQSIZE * 4); + sq->samples = NULL; + sq->extra = NULL; +} + +static isc_result_t +samplesource_allocate(isc_entropy_t *ent, sample_queue_t *sq) { + sq->samples = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4); + if (sq->samples == NULL) + return (ISC_R_NOMEMORY); + + sq->extra = isc_mem_get(ent->mctx, RND_EVENTQSIZE * 4); + if (sq->extra == NULL) { + isc_mem_put(ent->mctx, sq->samples, RND_EVENTQSIZE * 4); + sq->samples = NULL; + return (ISC_R_NOMEMORY); + } + + sq->nsamples = 0; + + return (ISC_R_SUCCESS); +} + +/*% + * Add in entropy, even when the value we're adding in could be + * very large. + */ +static inline void +add_entropy(isc_entropy_t *ent, uint32_t entropy) { + /* clamp input. Yes, this must be done. */ + entropy = ISC_MIN(entropy, RND_POOLBITS); + /* Add in the entropy we already have. */ + entropy += ent->pool.entropy; + /* Clamp. */ + ent->pool.entropy = ISC_MIN(entropy, RND_POOLBITS); +} + +/*% + * Decrement the amount of entropy the pool has. + */ +static inline void +subtract_entropy(isc_entropy_t *ent, uint32_t entropy) { + entropy = ISC_MIN(entropy, ent->pool.entropy); + ent->pool.entropy -= entropy; +} + +/*! + * Add in entropy, even when the value we're adding in could be + * very large. + */ +static inline void +add_pseudo(isc_entropy_t *ent, uint32_t pseudo) { + /* clamp input. Yes, this must be done. */ + pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8); + /* Add in the pseudo we already have. */ + pseudo += ent->pool.pseudo; + /* Clamp. */ + ent->pool.pseudo = ISC_MIN(pseudo, RND_POOLBITS * 8); +} + +/*! + * Decrement the amount of pseudo the pool has. + */ +static inline void +subtract_pseudo(isc_entropy_t *ent, uint32_t pseudo) { + pseudo = ISC_MIN(pseudo, ent->pool.pseudo); + ent->pool.pseudo -= pseudo; +} + +/*! + * Add one word to the pool, rotating the input as needed. + */ +static inline void +entropypool_add_word(isc_entropypool_t *rp, uint32_t val) { + /* + * Steal some values out of the pool, and xor them into the + * word we were given. + * + * Mix the new value into the pool using xor. This will + * prevent the actual values from being known to the caller + * since the previous values are assumed to be unknown as well. + */ + val ^= rp->pool[(rp->cursor + TAP1) & (RND_POOLWORDS - 1)]; + val ^= rp->pool[(rp->cursor + TAP2) & (RND_POOLWORDS - 1)]; + val ^= rp->pool[(rp->cursor + TAP3) & (RND_POOLWORDS - 1)]; + val ^= rp->pool[(rp->cursor + TAP4) & (RND_POOLWORDS - 1)]; + val ^= rp->pool[(rp->cursor + TAP5) & (RND_POOLWORDS - 1)]; + if (rp->rotate == 0) + rp->pool[rp->cursor++] ^= val; + else + rp->pool[rp->cursor++] ^= + ((val << rp->rotate) | (val >> (32 - rp->rotate))); + + /* + * If we have looped around the pool, increment the rotate + * variable so the next value will get xored in rotated to + * a different position. + * Increment by a value that is relatively prime to the word size + * to try to spread the bits throughout the pool quickly when the + * pool is empty. + */ + if (rp->cursor == RND_POOLWORDS) { + rp->cursor = 0; + rp->rotate = (rp->rotate + 7) & 31; + } +} + +/*! + * Add a buffer's worth of data to the pool. + * + * Requires that the lock is held on the entropy pool. + */ +static void +entropypool_adddata(isc_entropy_t *ent, void *p, unsigned int len, + uint32_t entropy) +{ + uint32_t val; + unsigned long addr; + uint8_t *buf; + + /* Silly MSVC in 64 bit mode complains here... */ +#ifdef _WIN64 + addr = (unsigned long)((unsigned long long)p); +#else + addr = (unsigned long)p; +#endif + buf = p; + + if ((addr & 0x03U) != 0U) { + val = 0; + switch (len) { + case 3: + val = *buf++; + len--; + /* FALLTHROUGH */ + case 2: + val = val << 8 | *buf++; + len--; + /* FALLTHROUGH */ + case 1: + val = val << 8 | *buf++; + len--; + } + + entropypool_add_word(&ent->pool, val); + } + + for (; len > 3; len -= 4) { + val = *((uint32_t *)buf); + + entropypool_add_word(&ent->pool, val); + buf += 4; + } + + if (len != 0) { + val = 0; + switch (len) { + case 3: + val = *buf++; + /* FALLTHROUGH */ + case 2: + val = val << 8 | *buf++; + /* FALLTHROUGH */ + case 1: + val = val << 8 | *buf++; + } + + entropypool_add_word(&ent->pool, val); + } + + add_entropy(ent, entropy); + subtract_pseudo(ent, entropy); +} + +static inline void +reseed(isc_entropy_t *ent) { + isc_time_t t; + pid_t pid; + + if (ent->initcount == 0) { + pid = getpid(); + entropypool_adddata(ent, &pid, sizeof(pid), 0); + pid = getppid(); + entropypool_adddata(ent, &pid, sizeof(pid), 0); + } + + /*! + * After we've reseeded 100 times, only add new timing info every + * 50 requests. This will keep us from using lots and lots of + * CPU just to return bad pseudorandom data anyway. + */ + if (ent->initcount > 100) + if ((ent->initcount % 50) != 0) + return; + + TIME_NOW(&t); + entropypool_adddata(ent, &t, sizeof(t), 0); + ent->initcount++; +} + +static inline unsigned int +estimate_entropy(sample_queue_t *sq, uint32_t t) { + int32_t delta; + int32_t delta2; + int32_t delta3; + + /*! + * If the time counter has overflowed, calculate the real difference. + * If it has not, it is simpler. + */ + if (t < sq->last_time) + delta = UINT_MAX - sq->last_time + t; + else + delta = sq->last_time - t; + + if (delta < 0) + delta = -delta; + + /* + * Calculate the second and third order differentials + */ + delta2 = sq->last_delta - delta; + if (delta2 < 0) + delta2 = -delta2; + + delta3 = sq->last_delta2 - delta2; + if (delta3 < 0) + delta3 = -delta3; + + sq->last_time = t; + sq->last_delta = delta; + sq->last_delta2 = delta2; + + /* + * If any delta is 0, we got no entropy. If all are non-zero, we + * might have something. + */ + if (delta == 0 || delta2 == 0 || delta3 == 0) + return 0; + + /* + * We could find the smallest delta and claim we got log2(delta) + * bits, but for now return that we found 1 bit. + */ + return 1; +} + +static unsigned int +crunchsamples(isc_entropy_t *ent, sample_queue_t *sq) { + unsigned int ns; + unsigned int added; + + if (sq->nsamples < 6) + return (0); + + added = 0; + sq->last_time = sq->samples[0]; + sq->last_delta = 0; + sq->last_delta2 = 0; + + /* + * Prime the values by adding in the first 4 samples in. This + * should completely initialize the delta calculations. + */ + for (ns = 0; ns < 4; ns++) + (void)estimate_entropy(sq, sq->samples[ns]); + + for (ns = 4; ns < sq->nsamples; ns++) + added += estimate_entropy(sq, sq->samples[ns]); + + entropypool_adddata(ent, sq->samples, sq->nsamples * 4, added); + entropypool_adddata(ent, sq->extra, sq->nsamples * 4, 0); + + /* + * Move the last 4 samples into the first 4 positions, and start + * adding new samples from that point. + */ + for (ns = 0; ns < 4; ns++) { + sq->samples[ns] = sq->samples[sq->nsamples - 4 + ns]; + sq->extra[ns] = sq->extra[sq->nsamples - 4 + ns]; + } + + sq->nsamples = 4; + + return (added); +} + +static unsigned int +get_from_callback(isc_entropysource_t *source, unsigned int desired, + bool blocking) +{ + isc_entropy_t *ent = source->ent; + isc_cbsource_t *cbs = &source->sources.callback; + unsigned int added; + unsigned int got; + isc_result_t result; + + if (desired == 0) + return (0); + + if (source->bad) + return (0); + + if (!cbs->start_called && cbs->startfunc != NULL) { + result = cbs->startfunc(source, cbs->arg, blocking); + if (result != ISC_R_SUCCESS) + return (0); + cbs->start_called = true; + } + + added = 0; + result = ISC_R_SUCCESS; + while (desired > 0 && result == ISC_R_SUCCESS) { + result = cbs->getfunc(source, cbs->arg, blocking); + if (result == ISC_R_QUEUEFULL) { + got = crunchsamples(ent, &cbs->samplequeue); + added += got; + desired -= ISC_MIN(got, desired); + result = ISC_R_SUCCESS; + } else if (result != ISC_R_SUCCESS && + result != ISC_R_NOTBLOCKING) + source->bad = true; + + } + + return (added); +} + +/* + * Extract some number of bytes from the random pool, decreasing the + * estimate of randomness as each byte is extracted. + * + * Do this by stiring the pool and returning a part of hash as randomness. + * Note that no secrets are given away here since parts of the hash are + * xored together before returned. + * + * Honor the request from the caller to only return good data, any data, + * etc. + */ +isc_result_t +isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, + unsigned int *returned, unsigned int flags) +{ + unsigned int i; + isc_sha1_t hash; + unsigned char digest[ISC_SHA1_DIGESTLENGTH]; + uint32_t remain, deltae, count, total; + uint8_t *buf; + bool goodonly, partial, blocking; + + REQUIRE(VALID_ENTROPY(ent)); + REQUIRE(data != NULL); + REQUIRE(length > 0); + + goodonly = (flags & ISC_ENTROPY_GOODONLY); + partial = (flags & ISC_ENTROPY_PARTIAL); + blocking = (flags & ISC_ENTROPY_BLOCKING); + + REQUIRE(!partial || returned != NULL); + + LOCK(&ent->lock); + + remain = length; + buf = data; + total = 0; + while (remain != 0) { + count = ISC_MIN(remain, RND_ENTROPY_THRESHOLD); + + /* + * If we are extracting good data only, make certain we + * have enough data in our pool for this pass. If we don't, + * get some, and fail if we can't, and partial returns + * are not ok. + */ + if (goodonly) { + unsigned int fillcount; + + fillcount = ISC_MAX(remain * 8, count * 8); + + /* + * If, however, we have at least THRESHOLD_BITS + * of entropy in the pool, don't block here. It is + * better to drain the pool once in a while and + * then refill it than it is to constantly keep the + * pool full. + */ + if (ent->pool.entropy >= THRESHOLD_BITS) + fillpool(ent, fillcount, false); + else + fillpool(ent, fillcount, blocking); + + /* + * Verify that we got enough entropy to do one + * extraction. If we didn't, bail. + */ + if (ent->pool.entropy < THRESHOLD_BITS) { + if (!partial) + goto zeroize; + else + goto partial_output; + } + } else { + /* + * If we've extracted half our pool size in bits + * since the last refresh, try to refresh here. + */ + if (ent->initialized < THRESHOLD_BITS) + fillpool(ent, THRESHOLD_BITS, blocking); + else + fillpool(ent, 0, false); + + /* + * If we've not initialized with enough good random + * data, seed with our crappy code. + */ + if (ent->initialized < THRESHOLD_BITS) + reseed(ent); + } + + isc_sha1_init(&hash); + isc_sha1_update(&hash, (void *)(ent->pool.pool), + RND_POOLBYTES); + isc_sha1_final(&hash, digest); + + /* + * Stir the extracted data (all of it) back into the pool. + */ + entropypool_adddata(ent, digest, ISC_SHA1_DIGESTLENGTH, 0); + + for (i = 0; i < count; i++) + buf[i] = digest[i] ^ digest[i + RND_ENTROPY_THRESHOLD]; + + buf += count; + remain -= count; + + deltae = count * 8; + deltae = ISC_MIN(deltae, ent->pool.entropy); + total += deltae; + subtract_entropy(ent, deltae); + add_pseudo(ent, count * 8); + } + + partial_output: + isc_safe_memwipe(digest, sizeof(digest)); + + if (returned != NULL) + *returned = (length - remain); + + UNLOCK(&ent->lock); + + return (ISC_R_SUCCESS); + + zeroize: + /* put the entropy we almost extracted back */ + add_entropy(ent, total); + isc_safe_memwipe(data, length); + isc_safe_memwipe(digest, sizeof(digest)); + if (returned != NULL) + *returned = 0; + + UNLOCK(&ent->lock); + + return (ISC_R_NOENTROPY); +} + +static void +isc_entropypool_init(isc_entropypool_t *pool) { + pool->cursor = RND_POOLWORDS - 1; + pool->entropy = 0; + pool->pseudo = 0; + pool->rotate = 0; + memset(pool->pool, 0, RND_POOLBYTES); +} + +static void +isc_entropypool_invalidate(isc_entropypool_t *pool) { + pool->cursor = 0; + pool->entropy = 0; + pool->pseudo = 0; + pool->rotate = 0; + memset(pool->pool, 0, RND_POOLBYTES); +} + +isc_result_t +isc_entropy_create(isc_mem_t *mctx, isc_entropy_t **entp) { + isc_result_t result; + isc_entropy_t *ent; + + REQUIRE(mctx != NULL); + REQUIRE(entp != NULL && *entp == NULL); + + ent = isc_mem_get(mctx, sizeof(isc_entropy_t)); + if (ent == NULL) + return (ISC_R_NOMEMORY); + + /* + * We need a lock. + */ + result = isc_mutex_init(&ent->lock); + if (result != ISC_R_SUCCESS) + goto errout; + + /* + * From here down, no failures will/can occur. + */ + ISC_LIST_INIT(ent->sources); + ent->nextsource = NULL; + ent->nsources = 0; + ent->mctx = NULL; + isc_mem_attach(mctx, &ent->mctx); + ent->refcnt = 1; + ent->initialized = 0; + ent->initcount = 0; + ent->magic = ENTROPY_MAGIC; + + isc_entropypool_init(&ent->pool); + + *entp = ent; + return (ISC_R_SUCCESS); + + errout: + isc_mem_put(mctx, ent, sizeof(isc_entropy_t)); + + return (result); +} + +/*! + * Requires "ent" be locked. + */ +static void +destroysource(isc_entropysource_t **sourcep) { + isc_entropysource_t *source; + isc_entropy_t *ent; + isc_cbsource_t *cbs; + + source = *sourcep; + *sourcep = NULL; + ent = source->ent; + + ISC_LIST_UNLINK(ent->sources, source, link); + ent->nextsource = NULL; + REQUIRE(ent->nsources > 0); + ent->nsources--; + + switch (source->type) { + case ENTROPY_SOURCETYPE_FILE: + if (! source->bad) + destroyfilesource(&source->sources.file); + break; + case ENTROPY_SOURCETYPE_USOCKET: + if (! source->bad) + destroyusocketsource(&source->sources.usocket); + break; + case ENTROPY_SOURCETYPE_SAMPLE: + samplequeue_release(ent, &source->sources.sample.samplequeue); + break; + case ENTROPY_SOURCETYPE_CALLBACK: + cbs = &source->sources.callback; + if (cbs->start_called && cbs->stopfunc != NULL) { + cbs->stopfunc(source, cbs->arg); + cbs->start_called = false; + } + samplequeue_release(ent, &cbs->samplequeue); + break; + } + + isc_safe_memwipe(source, sizeof(*source)); + isc_mem_put(ent->mctx, source, sizeof(*source)); +} + +static inline bool +destroy_check(isc_entropy_t *ent) { + isc_entropysource_t *source; + + if (ent->refcnt > 0) + return (false); + + source = ISC_LIST_HEAD(ent->sources); + while (source != NULL) { + switch (source->type) { + case ENTROPY_SOURCETYPE_FILE: + case ENTROPY_SOURCETYPE_USOCKET: + break; + default: + return (false); + } + source = ISC_LIST_NEXT(source, link); + } + + return (true); +} + +static void +destroy(isc_entropy_t **entp) { + isc_entropy_t *ent; + isc_entropysource_t *source; + isc_mem_t *mctx; + + REQUIRE(entp != NULL && *entp != NULL); + ent = *entp; + *entp = NULL; + + LOCK(&ent->lock); + + REQUIRE(ent->refcnt == 0); + + /* + * Here, detach non-sample sources. + */ + source = ISC_LIST_HEAD(ent->sources); + while (source != NULL) { + switch(source->type) { + case ENTROPY_SOURCETYPE_FILE: + case ENTROPY_SOURCETYPE_USOCKET: + destroysource(&source); + break; + } + source = ISC_LIST_HEAD(ent->sources); + } + + /* + * If there are other types of sources, we've found a bug. + */ + REQUIRE(ISC_LIST_EMPTY(ent->sources)); + + mctx = ent->mctx; + + isc_entropypool_invalidate(&ent->pool); + + UNLOCK(&ent->lock); + + DESTROYLOCK(&ent->lock); + + isc_safe_memwipe(ent, sizeof(*ent)); + isc_mem_put(mctx, ent, sizeof(*ent)); + isc_mem_detach(&mctx); +} + +void +isc_entropy_destroysource(isc_entropysource_t **sourcep) { + isc_entropysource_t *source; + isc_entropy_t *ent; + bool killit; + + REQUIRE(sourcep != NULL); + REQUIRE(VALID_SOURCE(*sourcep)); + + source = *sourcep; + *sourcep = NULL; + + ent = source->ent; + REQUIRE(VALID_ENTROPY(ent)); + + LOCK(&ent->lock); + + destroysource(&source); + + killit = destroy_check(ent); + + UNLOCK(&ent->lock); + + if (killit) + destroy(&ent); +} + +isc_result_t +isc_entropy_createcallbacksource(isc_entropy_t *ent, + isc_entropystart_t start, + isc_entropyget_t get, + isc_entropystop_t stop, + void *arg, + isc_entropysource_t **sourcep) +{ + isc_result_t result; + isc_entropysource_t *source; + isc_cbsource_t *cbs; + + REQUIRE(VALID_ENTROPY(ent)); + REQUIRE(get != NULL); + REQUIRE(sourcep != NULL && *sourcep == NULL); + + LOCK(&ent->lock); + + source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); + if (source == NULL) { + result = ISC_R_NOMEMORY; + goto errout; + } + source->bad = false; + + cbs = &source->sources.callback; + + result = samplesource_allocate(ent, &cbs->samplequeue); + if (result != ISC_R_SUCCESS) + goto errout; + + cbs->start_called = false; + cbs->startfunc = start; + cbs->getfunc = get; + cbs->stopfunc = stop; + cbs->arg = arg; + + /* + * From here down, no failures can occur. + */ + source->magic = SOURCE_MAGIC; + source->type = ENTROPY_SOURCETYPE_CALLBACK; + source->ent = ent; + source->total = 0; + memset(source->name, 0, sizeof(source->name)); + ISC_LINK_INIT(source, link); + + /* + * Hook it into the entropy system. + */ + ISC_LIST_APPEND(ent->sources, source, link); + ent->nsources++; + + *sourcep = source; + + UNLOCK(&ent->lock); + return (ISC_R_SUCCESS); + + errout: + if (source != NULL) + isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); + + UNLOCK(&ent->lock); + + return (result); +} + +void +isc_entropy_stopcallbacksources(isc_entropy_t *ent) { + isc_entropysource_t *source; + isc_cbsource_t *cbs; + + REQUIRE(VALID_ENTROPY(ent)); + + LOCK(&ent->lock); + + source = ISC_LIST_HEAD(ent->sources); + while (source != NULL) { + if (source->type == ENTROPY_SOURCETYPE_CALLBACK) { + cbs = &source->sources.callback; + if (cbs->start_called && cbs->stopfunc != NULL) { + cbs->stopfunc(source, cbs->arg); + cbs->start_called = false; + } + } + + source = ISC_LIST_NEXT(source, link); + } + + UNLOCK(&ent->lock); +} + +isc_result_t +isc_entropy_createsamplesource(isc_entropy_t *ent, + isc_entropysource_t **sourcep) +{ + isc_result_t result; + isc_entropysource_t *source; + sample_queue_t *sq; + + REQUIRE(VALID_ENTROPY(ent)); + REQUIRE(sourcep != NULL && *sourcep == NULL); + + LOCK(&ent->lock); + + source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); + if (source == NULL) { + result = ISC_R_NOMEMORY; + goto errout; + } + + sq = &source->sources.sample.samplequeue; + result = samplesource_allocate(ent, sq); + if (result != ISC_R_SUCCESS) + goto errout; + + /* + * From here down, no failures can occur. + */ + source->magic = SOURCE_MAGIC; + source->type = ENTROPY_SOURCETYPE_SAMPLE; + source->ent = ent; + source->total = 0; + memset(source->name, 0, sizeof(source->name)); + ISC_LINK_INIT(source, link); + + /* + * Hook it into the entropy system. + */ + ISC_LIST_APPEND(ent->sources, source, link); + ent->nsources++; + + *sourcep = source; + + UNLOCK(&ent->lock); + return (ISC_R_SUCCESS); + + errout: + if (source != NULL) + isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); + + UNLOCK(&ent->lock); + + return (result); +} + +/*! + * Add a sample, and return ISC_R_SUCCESS if the queue has become full, + * ISC_R_NOENTROPY if it has space remaining, and ISC_R_NOMORE if the + * queue was full when this function was called. + */ +static isc_result_t +addsample(sample_queue_t *sq, uint32_t sample, uint32_t extra) { + if (sq->nsamples >= RND_EVENTQSIZE) + return (ISC_R_NOMORE); + + sq->samples[sq->nsamples] = sample; + sq->extra[sq->nsamples] = extra; + sq->nsamples++; + + if (sq->nsamples >= RND_EVENTQSIZE) + return (ISC_R_QUEUEFULL); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_entropy_addsample(isc_entropysource_t *source, uint32_t sample, + uint32_t extra) +{ + isc_entropy_t *ent; + sample_queue_t *sq; + unsigned int entropy; + isc_result_t result; + + REQUIRE(VALID_SOURCE(source)); + + ent = source->ent; + + LOCK(&ent->lock); + + sq = &source->sources.sample.samplequeue; + result = addsample(sq, sample, extra); + if (result == ISC_R_QUEUEFULL) { + entropy = crunchsamples(ent, sq); + add_entropy(ent, entropy); + } + + UNLOCK(&ent->lock); + + return (result); +} + +isc_result_t +isc_entropy_addcallbacksample(isc_entropysource_t *source, uint32_t sample, + uint32_t extra) +{ + sample_queue_t *sq; + isc_result_t result; + + REQUIRE(VALID_SOURCE(source)); + REQUIRE(source->type == ENTROPY_SOURCETYPE_CALLBACK); + + sq = &source->sources.callback.samplequeue; + result = addsample(sq, sample, extra); + + return (result); +} + +void +isc_entropy_putdata(isc_entropy_t *ent, void *data, unsigned int length, + uint32_t entropy) +{ + REQUIRE(VALID_ENTROPY(ent)); + + LOCK(&ent->lock); + + entropypool_adddata(ent, data, length, entropy); + + if (ent->initialized < THRESHOLD_BITS) + ent->initialized = THRESHOLD_BITS; + + UNLOCK(&ent->lock); +} + +static void +dumpstats(isc_entropy_t *ent, FILE *out) { + fprintf(out, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_ENTROPY, + ISC_MSG_ENTROPYSTATS, + "Entropy pool %p: refcnt %u cursor %u," + " rotate %u entropy %u pseudo %u nsources %u" + " nextsource %p initialized %u initcount %u\n"), + ent, ent->refcnt, + ent->pool.cursor, ent->pool.rotate, + ent->pool.entropy, ent->pool.pseudo, + ent->nsources, ent->nextsource, ent->initialized, + ent->initcount); +} + +/* + * This function ignores locking. Use at your own risk. + */ +void +isc_entropy_stats(isc_entropy_t *ent, FILE *out) { + REQUIRE(VALID_ENTROPY(ent)); + + LOCK(&ent->lock); + dumpstats(ent, out); + UNLOCK(&ent->lock); +} + +unsigned int +isc_entropy_status(isc_entropy_t *ent) { + unsigned int estimate; + + LOCK(&ent->lock); + estimate = ent->pool.entropy; + UNLOCK(&ent->lock); + + return estimate; +} + +void +isc_entropy_attach(isc_entropy_t *ent, isc_entropy_t **entp) { + REQUIRE(VALID_ENTROPY(ent)); + REQUIRE(entp != NULL && *entp == NULL); + + LOCK(&ent->lock); + + ent->refcnt++; + *entp = ent; + + UNLOCK(&ent->lock); +} + +void +isc_entropy_detach(isc_entropy_t **entp) { + isc_entropy_t *ent; + bool killit; + + REQUIRE(entp != NULL && VALID_ENTROPY(*entp)); + ent = *entp; + *entp = NULL; + + LOCK(&ent->lock); + + REQUIRE(ent->refcnt > 0); + ent->refcnt--; + + killit = destroy_check(ent); + + UNLOCK(&ent->lock); + + if (killit) + destroy(&ent); +} + +static isc_result_t +kbdstart(isc_entropysource_t *source, void *arg, bool blocking) { + /* + * The intent of "first" is to provide a warning message only once + * during the run of a program that might try to gather keyboard + * entropy multiple times. + */ + static bool first = true; + + UNUSED(arg); + + if (! blocking) + return (ISC_R_NOENTROPY); + + if (first) { + if (source->warn_keyboard) + fprintf(stderr, "You must use the keyboard to create " + "entropy, since your system is lacking\n" + "/dev/random (or equivalent)\n\n"); + first = false; + } + fprintf(stderr, "start typing:\n"); + + return (isc_keyboard_open(&source->kbd)); +} + +static void +kbdstop(isc_entropysource_t *source, void *arg) { + + UNUSED(arg); + + if (! isc_keyboard_canceled(&source->kbd)) + fprintf(stderr, "stop typing.\r\n"); + + (void)isc_keyboard_close(&source->kbd, 3); +} + +static isc_result_t +kbdget(isc_entropysource_t *source, void *arg, bool blocking) { + isc_result_t result; + isc_time_t t; + uint32_t sample; + uint32_t extra; + unsigned char c; + + UNUSED(arg); + + if (!blocking) + return (ISC_R_NOTBLOCKING); + + result = isc_keyboard_getchar(&source->kbd, &c); + if (result != ISC_R_SUCCESS) + return (result); + + TIME_NOW(&t); + + sample = isc_time_nanoseconds(&t); + extra = c; + + result = isc_entropy_addcallbacksample(source, sample, extra); + if (result != ISC_R_SUCCESS) { + fprintf(stderr, "\r\n"); + return (result); + } + + fprintf(stderr, "."); + fflush(stderr); + + return (result); +} + +isc_result_t +isc_entropy_usebestsource(isc_entropy_t *ectx, isc_entropysource_t **source, + const char *randomfile, int use_keyboard) +{ + isc_result_t result; + isc_result_t final_result = ISC_R_NOENTROPY; + bool userfile = true; + + REQUIRE(VALID_ENTROPY(ectx)); + REQUIRE(source != NULL && *source == NULL); + REQUIRE(use_keyboard == ISC_ENTROPY_KEYBOARDYES || + use_keyboard == ISC_ENTROPY_KEYBOARDNO || + use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE); + +#ifdef PKCS11CRYPTO + if (randomfile != NULL) + pk11_rand_seed_fromfile(randomfile); +#endif + +#ifdef PATH_RANDOMDEV + if (randomfile == NULL) { + randomfile = PATH_RANDOMDEV; + userfile = false; + } +#endif + + if (randomfile != NULL && use_keyboard != ISC_ENTROPY_KEYBOARDYES) { + result = isc_entropy_createfilesource(ectx, randomfile); + if (result == ISC_R_SUCCESS && + use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE) + use_keyboard = ISC_ENTROPY_KEYBOARDNO; + if (result != ISC_R_SUCCESS && userfile) + return (result); + + final_result = result; + } + + if (use_keyboard != ISC_ENTROPY_KEYBOARDNO) { + result = isc_entropy_createcallbacksource(ectx, kbdstart, + kbdget, kbdstop, + NULL, source); + if (result == ISC_R_SUCCESS) + (*source)->warn_keyboard = + (use_keyboard == ISC_ENTROPY_KEYBOARDMAYBE); + + if (final_result != ISC_R_SUCCESS) + final_result = result; + } + + /* + * final_result is ISC_R_SUCCESS if at least one source of entropy + * could be started, otherwise it is the error from the most recently + * failed operation (or ISC_R_NOENTROPY if PATH_RANDOMDEV is not + * defined and use_keyboard is ISC_ENTROPY_KEYBOARDNO). + */ + return (final_result); +} diff --git a/lib/isc/error.c b/lib/isc/error.c new file mode 100644 index 0000000..b0fe500 --- /dev/null +++ b/lib/isc/error.c @@ -0,0 +1,100 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <isc/error.h> +#include <isc/msgs.h> +#include <isc/print.h> + +/*% Default unexpected callback. */ +static void +default_unexpected_callback(const char *, int, const char *, va_list) + ISC_FORMAT_PRINTF(3, 0); + +/*% Default fatal callback. */ +static void +default_fatal_callback(const char *, int, const char *, va_list) + ISC_FORMAT_PRINTF(3, 0); + +/*% unexpected_callback */ +static isc_errorcallback_t unexpected_callback = default_unexpected_callback; +static isc_errorcallback_t fatal_callback = default_fatal_callback; + +void +isc_error_setunexpected(isc_errorcallback_t cb) { + if (cb == NULL) + unexpected_callback = default_unexpected_callback; + else + unexpected_callback = cb; +} + +void +isc_error_setfatal(isc_errorcallback_t cb) { + if (cb == NULL) + fatal_callback = default_fatal_callback; + else + fatal_callback = cb; +} + +void +isc_error_unexpected(const char *file, int line, const char *format, ...) { + va_list args; + + va_start(args, format); + (unexpected_callback)(file, line, format, args); + va_end(args); +} + +void +isc_error_fatal(const char *file, int line, const char *format, ...) { + va_list args; + + va_start(args, format); + (fatal_callback)(file, line, format, args); + va_end(args); + abort(); +} + +void +isc_error_runtimecheck(const char *file, int line, const char *expression) { + isc_error_fatal(file, line, "RUNTIME_CHECK(%s) %s", expression, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); +} + +static void +default_unexpected_callback(const char *file, int line, const char *format, + va_list args) +{ + fprintf(stderr, "%s:%d: ", file, line); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); +} + +static void +default_fatal_callback(const char *file, int line, const char *format, + va_list args) +{ + fprintf(stderr, "%s:%d: %s: ", file, line, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FATALERROR, "fatal error")); + vfprintf(stderr, format, args); + fprintf(stderr, "\n"); + fflush(stderr); +} diff --git a/lib/isc/event.c b/lib/isc/event.c new file mode 100644 index 0000000..500b6fd --- /dev/null +++ b/lib/isc/event.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! + * \file + */ + +#include <config.h> + +#include <isc/event.h> +#include <isc/mem.h> +#include <isc/util.h> + +/*** + *** Events. + ***/ + +static void +destroy(isc_event_t *event) { + isc_mem_t *mctx = event->ev_destroy_arg; + + isc_mem_put(mctx, event, event->ev_size); +} + +isc_event_t * +isc_event_allocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type, + isc_taskaction_t action, void *arg, size_t size) +{ + isc_event_t *event; + + REQUIRE(size >= sizeof(struct isc_event)); + REQUIRE(action != NULL); + + event = isc_mem_get(mctx, size); + if (event == NULL) + return (NULL); + + ISC_EVENT_INIT(event, size, 0, NULL, type, action, arg, + sender, destroy, mctx); + + return (event); +} + +isc_event_t * +isc_event_constallocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type, + isc_taskaction_t action, const void *arg, size_t size) +{ + isc_event_t *event; + void *deconst_arg; + + REQUIRE(size >= sizeof(struct isc_event)); + REQUIRE(action != NULL); + + event = isc_mem_get(mctx, size); + if (event == NULL) + return (NULL); + + /* + * Removing the const attribute from "arg" is the best of two + * evils here. If the event->ev_arg member is made const, then + * it affects a great many users of the task/event subsystem + * which are not passing in an "arg" which starts its life as + * const. Changing isc_event_allocate() and isc_task_onshutdown() + * to not have "arg" prototyped as const (which is quite legitimate, + * because neither of those functions modify arg) can cause + * compiler whining anytime someone does want to use a const + * arg that they themselves never modify, such as with + * gcc -Wwrite-strings and using a string "arg". + */ + DE_CONST(arg, deconst_arg); + + ISC_EVENT_INIT(event, size, 0, NULL, type, action, deconst_arg, + sender, destroy, mctx); + + return (event); +} + +void +isc_event_free(isc_event_t **eventp) { + isc_event_t *event; + + REQUIRE(eventp != NULL); + event = *eventp; + REQUIRE(event != NULL); + + REQUIRE(!ISC_LINK_LINKED(event, ev_link)); + REQUIRE(!ISC_LINK_LINKED(event, ev_ratelink)); + + if (event->ev_destroy != NULL) + (event->ev_destroy)(event); + + *eventp = NULL; +} diff --git a/lib/isc/fsaccess.c b/lib/isc/fsaccess.c new file mode 100644 index 0000000..deaffd8 --- /dev/null +++ b/lib/isc/fsaccess.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file + * \brief + * This file contains the OS-independent functionality of the API. + */ + +#include <stdbool.h> + +#include <isc/fsaccess.h> +#include <isc/print.h> +#include <isc/result.h> +#include <isc/util.h> + +/*! + * Shorthand. Maybe ISC__FSACCESS_PERMISSIONBITS should not even be in + * <isc/fsaccess.h>. Could check consistency with sizeof(isc_fsaccess_t) + * and the number of bits in each function. + */ +#define STEP (ISC__FSACCESS_PERMISSIONBITS) +#define GROUP (STEP) +#define OTHER (STEP * 2) + +void +isc_fsaccess_add(int trustee, int permission, isc_fsaccess_t *access) { + REQUIRE(trustee <= 0x7); + REQUIRE(permission <= 0xFF); + + if ((trustee & ISC_FSACCESS_OWNER) != 0) + *access |= permission; + + if ((trustee & ISC_FSACCESS_GROUP) != 0) + *access |= (permission << GROUP); + + if ((trustee & ISC_FSACCESS_OTHER) != 0) + *access |= (permission << OTHER); +} + +void +isc_fsaccess_remove(int trustee, int permission, isc_fsaccess_t *access) { + REQUIRE(trustee <= 0x7); + REQUIRE(permission <= 0xFF); + + + if ((trustee & ISC_FSACCESS_OWNER) != 0) + *access &= ~permission; + + if ((trustee & ISC_FSACCESS_GROUP) != 0) + *access &= ~(permission << GROUP); + + if ((trustee & ISC_FSACCESS_OTHER) != 0) + *access &= ~(permission << OTHER); +} + +static isc_result_t +check_bad_bits(isc_fsaccess_t access, bool is_dir) { + isc_fsaccess_t bits; + + /* + * Check for disallowed user bits. + */ + if (is_dir) + bits = ISC_FSACCESS_READ | + ISC_FSACCESS_WRITE | + ISC_FSACCESS_EXECUTE; + else + bits = ISC_FSACCESS_CREATECHILD | + ISC_FSACCESS_ACCESSCHILD | + ISC_FSACCESS_DELETECHILD | + ISC_FSACCESS_LISTDIRECTORY; + + /* + * Set group bad bits. + */ + bits |= bits << STEP; + /* + * Set other bad bits. + */ + bits |= bits << STEP; + + if ((access & bits) != 0) { + if (is_dir) + return (ISC_R_NOTFILE); + else + return (ISC_R_NOTDIRECTORY); + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/hash.c b/lib/isc/hash.c new file mode 100644 index 0000000..54be2fa --- /dev/null +++ b/lib/isc/hash.c @@ -0,0 +1,579 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file + * Some portion of this code was derived from universal hash function + * libraries of Rice University. +\section license UH Universal Hashing Library + +Copyright ((c)) 2002, Rice University +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Rice University (RICE) nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + +This software is provided by RICE and the contributors on an "as is" +basis, without any representations or warranties of any kind, express +or implied including, but not limited to, representations or +warranties of non-infringement, merchantability or fitness for a +particular purpose. In no event shall RICE or contributors be liable +for any direct, indirect, incidental, special, exemplary, or +consequential damages (including, but not limited to, procurement of +substitute goods or services; loss of use, data, or profits; or +business interruption) however caused and on any theory of liability, +whether in contract, strict liability, or tort (including negligence +or otherwise) arising in any way out of the use of this software, even +if advised of the possibility of such damage. +*/ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/entropy.h> +#include <isc/hash.h> +#include <isc/mem.h> +#include <isc/magic.h> +#include <isc/mutex.h> +#include <isc/once.h> +#include <isc/random.h> +#include <isc/refcount.h> +#include <isc/string.h> +#include <isc/util.h> + +#define HASH_MAGIC ISC_MAGIC('H', 'a', 's', 'h') +#define VALID_HASH(h) ISC_MAGIC_VALID((h), HASH_MAGIC) + +/*% + * A large 32-bit prime number that specifies the range of the hash output. + */ +#define PRIME32 0xFFFFFFFB /* 2^32 - 5 */ + +/*@{*/ +/*% + * Types of random seed and hash accumulator. Perhaps they can be system + * dependent. + */ +typedef uint32_t hash_accum_t; +typedef uint16_t hash_random_t; +/*@}*/ + +/*% isc hash structure */ +struct isc_hash { + unsigned int magic; + isc_mem_t *mctx; + isc_mutex_t lock; + bool initialized; + isc_refcount_t refcnt; + isc_entropy_t *entropy; /*%< entropy source */ + size_t limit; /*%< upper limit of key length */ + size_t vectorlen; /*%< size of the vector below */ + hash_random_t *rndvector; /*%< random vector for universal hashing */ +}; + +static isc_mutex_t createlock; +static isc_once_t once = ISC_ONCE_INIT; + +LIBISC_EXTERNAL_DATA isc_hash_t *isc_hashctx = NULL; + +static unsigned char maptolower[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +isc_result_t +isc_hash_ctxcreate(isc_mem_t *mctx, isc_entropy_t *entropy, + size_t limit, isc_hash_t **hctxp) +{ + isc_result_t result; + isc_hash_t *hctx; + size_t vlen; + hash_random_t *rv; + hash_accum_t overflow_limit; + + REQUIRE(mctx != NULL); + REQUIRE(hctxp != NULL && *hctxp == NULL); + + /* + * Overflow check. Since our implementation only does a modulo + * operation at the last stage of hash calculation, the accumulator + * must not overflow. + */ + overflow_limit = + 1 << (((sizeof(hash_accum_t) - sizeof(hash_random_t))) * 8); + if (overflow_limit < (limit + 1) * 0xff) + return (ISC_R_RANGE); + + hctx = isc_mem_get(mctx, sizeof(isc_hash_t)); + if (hctx == NULL) + return (ISC_R_NOMEMORY); + + vlen = sizeof(hash_random_t) * (limit + 1); + rv = isc_mem_get(mctx, vlen); + if (rv == NULL) { + result = ISC_R_NOMEMORY; + goto errout; + } + + /* + * We need a lock. + */ + result = isc_mutex_init(&hctx->lock); + if (result != ISC_R_SUCCESS) + goto errout; + + /* + * From here down, no failures will/can occur. + */ + hctx->magic = HASH_MAGIC; + hctx->mctx = NULL; + isc_mem_attach(mctx, &hctx->mctx); + hctx->initialized = false; + result = isc_refcount_init(&hctx->refcnt, 1); + if (result != ISC_R_SUCCESS) + goto cleanup_lock; + hctx->entropy = NULL; + hctx->limit = limit; + hctx->vectorlen = vlen; + hctx->rndvector = rv; + + if (entropy != NULL) + isc_entropy_attach(entropy, &hctx->entropy); + + *hctxp = hctx; + return (ISC_R_SUCCESS); + + cleanup_lock: + DESTROYLOCK(&hctx->lock); + errout: + isc_mem_put(mctx, hctx, sizeof(isc_hash_t)); + if (rv != NULL) + isc_mem_put(mctx, rv, vlen); + + return (result); +} + +static void +initialize_lock(void) { + RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS); +} + +isc_result_t +isc_hash_create(isc_mem_t *mctx, isc_entropy_t *entropy, size_t limit) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(mctx != NULL); + INSIST(isc_hashctx == NULL); + + RUNTIME_CHECK(isc_once_do(&once, initialize_lock) == ISC_R_SUCCESS); + + LOCK(&createlock); + + if (isc_hashctx == NULL) + result = isc_hash_ctxcreate(mctx, entropy, limit, + &isc_hashctx); + + UNLOCK(&createlock); + + return (result); +} + +void +isc_hash_ctxinit(isc_hash_t *hctx) { + LOCK(&hctx->lock); + + if (hctx->initialized == true) + goto out; + + if (hctx->entropy != NULL) { + isc_result_t result; + + result = isc_entropy_getdata(hctx->entropy, + hctx->rndvector, + (unsigned int)hctx->vectorlen, + NULL, 0); + INSIST(result == ISC_R_SUCCESS); + } else { + uint32_t pr; + size_t i, copylen; + unsigned char *p; + + p = (unsigned char *)hctx->rndvector; + for (i = 0; i < hctx->vectorlen; i += copylen, p += copylen) { + isc_random_get(&pr); + if (i + sizeof(pr) <= hctx->vectorlen) + copylen = sizeof(pr); + else + copylen = hctx->vectorlen - i; + + memmove(p, &pr, copylen); + } + INSIST(p == (unsigned char *)hctx->rndvector + + hctx->vectorlen); + } + + hctx->initialized = true; + + out: + UNLOCK(&hctx->lock); +} + +void +isc_hash_init(void) { + INSIST(isc_hashctx != NULL && VALID_HASH(isc_hashctx)); + + isc_hash_ctxinit(isc_hashctx); +} + +void +isc_hash_ctxattach(isc_hash_t *hctx, isc_hash_t **hctxp) { + REQUIRE(VALID_HASH(hctx)); + REQUIRE(hctxp != NULL && *hctxp == NULL); + + isc_refcount_increment(&hctx->refcnt, NULL); + *hctxp = hctx; +} + +static void +destroy(isc_hash_t **hctxp) { + isc_hash_t *hctx; + isc_mem_t *mctx; + + REQUIRE(hctxp != NULL && *hctxp != NULL); + hctx = *hctxp; + *hctxp = NULL; + + LOCK(&hctx->lock); + + isc_refcount_destroy(&hctx->refcnt); + + mctx = hctx->mctx; + if (hctx->entropy != NULL) + isc_entropy_detach(&hctx->entropy); + if (hctx->rndvector != NULL) + isc_mem_put(mctx, hctx->rndvector, hctx->vectorlen); + + UNLOCK(&hctx->lock); + + DESTROYLOCK(&hctx->lock); + + memset(hctx, 0, sizeof(isc_hash_t)); + isc_mem_put(mctx, hctx, sizeof(isc_hash_t)); + isc_mem_detach(&mctx); +} + +void +isc_hash_ctxdetach(isc_hash_t **hctxp) { + isc_hash_t *hctx; + unsigned int refs; + + REQUIRE(hctxp != NULL && VALID_HASH(*hctxp)); + hctx = *hctxp; + + isc_refcount_decrement(&hctx->refcnt, &refs); + if (refs == 0) + destroy(&hctx); + + *hctxp = NULL; +} + +void +isc_hash_destroy(void) { + unsigned int refs; + + INSIST(isc_hashctx != NULL && VALID_HASH(isc_hashctx)); + + isc_refcount_decrement(&isc_hashctx->refcnt, &refs); + INSIST(refs == 0); + + destroy(&isc_hashctx); +} + +static inline unsigned int +hash_calc(isc_hash_t *hctx, const unsigned char *key, unsigned int keylen, + bool case_sensitive) +{ + hash_accum_t partial_sum = 0; + hash_random_t *p = hctx->rndvector; + unsigned int i = 0; + + /* Make it sure that the hash context is initialized. */ + if (hctx->initialized == false) + isc_hash_ctxinit(hctx); + + if (case_sensitive) { + for (i = 0; i < keylen; i++) + partial_sum += key[i] * (hash_accum_t)p[i]; + } else { + for (i = 0; i < keylen; i++) + partial_sum += maptolower[key[i]] * (hash_accum_t)p[i]; + } + + partial_sum += p[i]; + + return ((unsigned int)(partial_sum % PRIME32)); +} + +unsigned int +isc_hash_ctxcalc(isc_hash_t *hctx, const unsigned char *key, + unsigned int keylen, bool case_sensitive) +{ + REQUIRE(hctx != NULL && VALID_HASH(hctx)); + REQUIRE(keylen <= hctx->limit); + + return (hash_calc(hctx, key, keylen, case_sensitive)); +} + +unsigned int +isc_hash_calc(const unsigned char *key, unsigned int keylen, + bool case_sensitive) +{ + INSIST(isc_hashctx != NULL && VALID_HASH(isc_hashctx)); + REQUIRE(keylen <= isc_hashctx->limit); + + return (hash_calc(isc_hashctx, key, keylen, case_sensitive)); +} + +void +isc__hash_setvec(const uint16_t *vec) { + int i; + hash_random_t *p; + + if (isc_hashctx == NULL) + return; + + p = isc_hashctx->rndvector; + for (i = 0; i < 256; i++) { + p[i] = vec[i]; + } +} + +static uint32_t fnv_offset_basis; +static isc_once_t fnv_once = ISC_ONCE_INIT; +static bool fnv_initialized = false; + +static void +fnv_initialize(void) { + /* + * This function should not leave fnv_offset_basis set to + * 0. Also, after this function has been called, if it is called + * again, it should not change fnv_offset_basis. + */ + while (fnv_offset_basis == 0) { + isc_random_get(&fnv_offset_basis); + } + + fnv_initialized = true; +} + +const void * +isc_hash_get_initializer(void) { + if (ISC_UNLIKELY(!fnv_initialized)) + RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) == ISC_R_SUCCESS); + + return (&fnv_offset_basis); +} + +void +isc_hash_set_initializer(const void *initializer) { + REQUIRE(initializer != NULL); + + /* + * Ensure that fnv_initialize() is not called after + * isc_hash_set_initializer() is called. + */ + if (ISC_UNLIKELY(!fnv_initialized)) + RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) == ISC_R_SUCCESS); + + fnv_offset_basis = *((const unsigned int *) initializer); +} + +uint32_t +isc_hash_function(const void *data, size_t length, + bool case_sensitive, + const uint32_t *previous_hashp) +{ + uint32_t hval; + const unsigned char *bp; + const unsigned char *be; + + REQUIRE(length == 0 || data != NULL); + + if (ISC_UNLIKELY(!fnv_initialized)) + RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) == ISC_R_SUCCESS); + + hval = ISC_UNLIKELY(previous_hashp != NULL) ? + *previous_hashp : fnv_offset_basis; + + if (length == 0) + return (hval); + + bp = (const unsigned char *) data; + be = bp + length; + + /* + * Fowler-Noll-Vo FNV-1a hash function. + * + * NOTE: A random FNV offset basis is used by default to avoid + * collision attacks as the hash function is reversible. This + * makes the mapping non-deterministic, but the distribution in + * the domain is still uniform. + */ + + if (case_sensitive) { + while (bp <= be - 4) { + hval ^= bp[0]; + hval *= 16777619; + hval ^= bp[1]; + hval *= 16777619; + hval ^= bp[2]; + hval *= 16777619; + hval ^= bp[3]; + hval *= 16777619; + bp += 4; + } + while (bp < be) { + hval ^= *bp++; + hval *= 16777619; + } + } else { + while (bp <= be - 4) { + hval ^= maptolower[bp[0]]; + hval *= 16777619; + hval ^= maptolower[bp[1]]; + hval *= 16777619; + hval ^= maptolower[bp[2]]; + hval *= 16777619; + hval ^= maptolower[bp[3]]; + hval *= 16777619; + bp += 4; + } + while (bp < be) { + hval ^= maptolower[*bp++]; + hval *= 16777619; + } + } + + return (hval); +} + +uint32_t +isc_hash_function_reverse(const void *data, size_t length, + bool case_sensitive, + const uint32_t *previous_hashp) +{ + uint32_t hval; + const unsigned char *bp; + const unsigned char *be; + + REQUIRE(length == 0 || data != NULL); + + if (ISC_UNLIKELY(!fnv_initialized)) + RUNTIME_CHECK(isc_once_do(&fnv_once, fnv_initialize) == ISC_R_SUCCESS); + + hval = ISC_UNLIKELY(previous_hashp != NULL) ? + *previous_hashp : fnv_offset_basis; + + if (length == 0) + return (hval); + + bp = (const unsigned char *) data; + be = bp + length; + + /* + * Fowler-Noll-Vo FNV-1a hash function. + * + * NOTE: A random FNV offset basis is used by default to avoid + * collision attacks as the hash function is reversible. This + * makes the mapping non-deterministic, but the distribution in + * the domain is still uniform. + */ + + if (case_sensitive) { + while (be >= bp + 4) { + be -= 4; + hval ^= be[3]; + hval *= 16777619; + hval ^= be[2]; + hval *= 16777619; + hval ^= be[1]; + hval *= 16777619; + hval ^= be[0]; + hval *= 16777619; + } + while (--be >= bp) { + hval ^= *be; + hval *= 16777619; + } + } else { + while (be >= bp + 4) { + be -= 4; + hval ^= maptolower[be[3]]; + hval *= 16777619; + hval ^= maptolower[be[2]]; + hval *= 16777619; + hval ^= maptolower[be[1]]; + hval *= 16777619; + hval ^= maptolower[be[0]]; + hval *= 16777619; + } + while (--be >= bp) { + hval ^= maptolower[*be]; + hval *= 16777619; + } + } + + return (hval); +} diff --git a/lib/isc/heap.c b/lib/isc/heap.c new file mode 100644 index 0000000..22fa81c --- /dev/null +++ b/lib/isc/heap.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file + * Heap implementation of priority queues adapted from the following: + * + * \li "Introduction to Algorithms," Cormen, Leiserson, and Rivest, + * MIT Press / McGraw Hill, 1990, ISBN 0-262-03141-8, chapter 7. + * + * \li "Algorithms," Second Edition, Sedgewick, Addison-Wesley, 1988, + * ISBN 0-201-06673-4, chapter 11. + */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/heap.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/string.h> /* Required for memmove. */ +#include <isc/util.h> + +/*@{*/ +/*% + * Note: to make heap_parent and heap_left easy to compute, the first + * element of the heap array is not used; i.e. heap subscripts are 1-based, + * not 0-based. The parent is index/2, and the left-child is index*2. + * The right child is index*2+1. + */ +#define heap_parent(i) ((i) >> 1) +#define heap_left(i) ((i) << 1) +/*@}*/ + +#define SIZE_INCREMENT 1024 + +#define HEAP_MAGIC ISC_MAGIC('H', 'E', 'A', 'P') +#define VALID_HEAP(h) ISC_MAGIC_VALID(h, HEAP_MAGIC) + +/*% + * When the heap is in a consistent state, the following invariant + * holds true: for every element i > 1, heap_parent(i) has a priority + * higher than or equal to that of i. + */ +#define HEAPCONDITION(i) ((i) == 1 || \ + ! heap->compare(heap->array[(i)], \ + heap->array[heap_parent(i)])) + +/*% ISC heap structure. */ +struct isc_heap { + unsigned int magic; + isc_mem_t * mctx; + unsigned int size; + unsigned int size_increment; + unsigned int last; + void **array; + isc_heapcompare_t compare; + isc_heapindex_t index; +}; + +#ifdef ISC_HEAP_CHECK +static void +heap_check(isc_heap_t *heap) { + unsigned int i; + for (i = 1; i <= heap->last; i++) { + INSIST(HEAPCONDITION(i)); + } +} +#else +#define heap_check(x) (void)0 +#endif + +isc_result_t +isc_heap_create(isc_mem_t *mctx, isc_heapcompare_t compare, + isc_heapindex_t idx, unsigned int size_increment, + isc_heap_t **heapp) +{ + isc_heap_t *heap; + + REQUIRE(heapp != NULL && *heapp == NULL); + REQUIRE(compare != NULL); + + heap = isc_mem_get(mctx, sizeof(*heap)); + if (heap == NULL) + return (ISC_R_NOMEMORY); + heap->magic = HEAP_MAGIC; + heap->size = 0; + heap->mctx = NULL; + isc_mem_attach(mctx, &heap->mctx); + if (size_increment == 0) + heap->size_increment = SIZE_INCREMENT; + else + heap->size_increment = size_increment; + heap->last = 0; + heap->array = NULL; + heap->compare = compare; + heap->index = idx; + + *heapp = heap; + + return (ISC_R_SUCCESS); +} + +void +isc_heap_destroy(isc_heap_t **heapp) { + isc_heap_t *heap; + + REQUIRE(heapp != NULL); + heap = *heapp; + REQUIRE(VALID_HEAP(heap)); + + if (heap->array != NULL) + isc_mem_put(heap->mctx, heap->array, + heap->size * sizeof(void *)); + heap->magic = 0; + isc_mem_putanddetach(&heap->mctx, heap, sizeof(*heap)); + + *heapp = NULL; +} + +static bool +resize(isc_heap_t *heap) { + void **new_array; + unsigned int new_size; + + REQUIRE(VALID_HEAP(heap)); + + new_size = heap->size + heap->size_increment; + new_array = isc_mem_get(heap->mctx, new_size * sizeof(void *)); + if (new_array == NULL) + return (false); + if (heap->array != NULL) { + memmove(new_array, heap->array, heap->size * sizeof(void *)); + isc_mem_put(heap->mctx, heap->array, + heap->size * sizeof(void *)); + } + heap->size = new_size; + heap->array = new_array; + + return (true); +} + +static void +float_up(isc_heap_t *heap, unsigned int i, void *elt) { + unsigned int p; + + for (p = heap_parent(i) ; + i > 1 && heap->compare(elt, heap->array[p]) ; + i = p, p = heap_parent(i)) { + heap->array[i] = heap->array[p]; + if (heap->index != NULL) + (heap->index)(heap->array[i], i); + } + heap->array[i] = elt; + if (heap->index != NULL) + (heap->index)(heap->array[i], i); + + INSIST(HEAPCONDITION(i)); + heap_check(heap); +} + +static void +sink_down(isc_heap_t *heap, unsigned int i, void *elt) { + unsigned int j, size, half_size; + size = heap->last; + half_size = size / 2; + while (i <= half_size) { + /* Find the smallest of the (at most) two children. */ + j = heap_left(i); + if (j < size && heap->compare(heap->array[j+1], + heap->array[j])) + j++; + if (heap->compare(elt, heap->array[j])) + break; + heap->array[i] = heap->array[j]; + if (heap->index != NULL) + (heap->index)(heap->array[i], i); + i = j; + } + heap->array[i] = elt; + if (heap->index != NULL) + (heap->index)(heap->array[i], i); + + INSIST(HEAPCONDITION(i)); + heap_check(heap); +} + +isc_result_t +isc_heap_insert(isc_heap_t *heap, void *elt) { + unsigned int new_last; + + REQUIRE(VALID_HEAP(heap)); + + heap_check(heap); + new_last = heap->last + 1; + RUNTIME_CHECK(new_last > 0); /* overflow check */ + if (new_last >= heap->size && !resize(heap)) + return (ISC_R_NOMEMORY); + heap->last = new_last; + + float_up(heap, new_last, elt); + + return (ISC_R_SUCCESS); +} + +void +isc_heap_delete(isc_heap_t *heap, unsigned int idx) { + void *elt; + bool less; + + REQUIRE(VALID_HEAP(heap)); + REQUIRE(idx >= 1 && idx <= heap->last); + + heap_check(heap); + if (heap->index != NULL) + (heap->index)(heap->array[idx], 0); + if (idx == heap->last) { + heap->array[heap->last] = NULL; + heap->last--; + heap_check(heap); + } else { + elt = heap->array[heap->last]; + heap->array[heap->last] = NULL; + heap->last--; + + less = heap->compare(elt, heap->array[idx]); + heap->array[idx] = elt; + if (less) + float_up(heap, idx, heap->array[idx]); + else + sink_down(heap, idx, heap->array[idx]); + } +} + +void +isc_heap_increased(isc_heap_t *heap, unsigned int idx) { + REQUIRE(VALID_HEAP(heap)); + REQUIRE(idx >= 1 && idx <= heap->last); + + float_up(heap, idx, heap->array[idx]); +} + +void +isc_heap_decreased(isc_heap_t *heap, unsigned int idx) { + REQUIRE(VALID_HEAP(heap)); + REQUIRE(idx >= 1 && idx <= heap->last); + + sink_down(heap, idx, heap->array[idx]); +} + +void * +isc_heap_element(isc_heap_t *heap, unsigned int idx) { + REQUIRE(VALID_HEAP(heap)); + REQUIRE(idx >= 1); + + heap_check(heap); + if (idx <= heap->last) + return (heap->array[idx]); + return (NULL); +} + +void +isc_heap_foreach(isc_heap_t *heap, isc_heapaction_t action, void *uap) { + unsigned int i; + + REQUIRE(VALID_HEAP(heap)); + REQUIRE(action != NULL); + + for (i = 1 ; i <= heap->last ; i++) + (action)(heap->array[i], uap); +} diff --git a/lib/isc/hex.c b/lib/isc/hex.c new file mode 100644 index 0000000..6d231ab --- /dev/null +++ b/lib/isc/hex.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <ctype.h> +#include <stdbool.h> + +#include <isc/buffer.h> +#include <isc/hex.h> +#include <isc/lex.h> +#include <isc/string.h> +#include <isc/util.h> + +#define RETERR(x) do { \ + isc_result_t _r = (x); \ + if (_r != ISC_R_SUCCESS) \ + return (_r); \ + } while (0) + + +/* + * BEW: These static functions are copied from lib/dns/rdata.c. + */ +static isc_result_t +str_totext(const char *source, isc_buffer_t *target); + +static isc_result_t +mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length); + +static const char hex[] = "0123456789ABCDEF"; + +isc_result_t +isc_hex_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target) +{ + char buf[3]; + unsigned int loops = 0; + + if (wordlength < 2) + wordlength = 2; + + memset(buf, 0, sizeof(buf)); + while (source->length > 0) { + buf[0] = hex[(source->base[0] >> 4) & 0xf]; + buf[1] = hex[(source->base[0]) & 0xf]; + RETERR(str_totext(buf, target)); + isc_region_consume(source, 1); + + loops++; + if (source->length != 0 && + (int)((loops + 1) * 2) >= wordlength) + { + loops = 0; + RETERR(str_totext(wordbreak, target)); + } + } + return (ISC_R_SUCCESS); +} + +/*% + * State of a hex decoding process in progress. + */ +typedef struct { + int length; /*%< Desired length of binary data or -1 */ + isc_buffer_t *target; /*%< Buffer for resulting binary data */ + int digits; /*%< Number of buffered hex digits */ + int val[2]; +} hex_decode_ctx_t; + +static inline void +hex_decode_init(hex_decode_ctx_t *ctx, int length, isc_buffer_t *target) +{ + ctx->digits = 0; + ctx->length = length; + ctx->target = target; +} + +static inline isc_result_t +hex_decode_char(hex_decode_ctx_t *ctx, int c) { + const char *s; + + if ((s = strchr(hex, toupper(c))) == NULL) + return (ISC_R_BADHEX); + ctx->val[ctx->digits++] = (int)(s - hex); + if (ctx->digits == 2) { + unsigned char num; + + num = (ctx->val[0] << 4) + (ctx->val[1]); + RETERR(mem_tobuffer(ctx->target, &num, 1)); + if (ctx->length >= 0) { + if (ctx->length == 0) + return (ISC_R_BADHEX); + else + ctx->length -= 1; + } + ctx->digits = 0; + } + return (ISC_R_SUCCESS); +} + +static inline isc_result_t +hex_decode_finish(hex_decode_ctx_t *ctx) { + if (ctx->length > 0) + return (ISC_R_UNEXPECTEDEND); + if (ctx->digits != 0) + return (ISC_R_BADHEX); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { + hex_decode_ctx_t ctx; + isc_textregion_t *tr; + isc_token_t token; + bool eol; + + hex_decode_init(&ctx, length, target); + + while (ctx.length != 0) { + unsigned int i; + + if (length > 0) + eol = false; + else + eol = true; + RETERR(isc_lex_getmastertoken(lexer, &token, + isc_tokentype_string, eol)); + if (token.type != isc_tokentype_string) + break; + tr = &token.value.as_textregion; + for (i = 0; i < tr->length; i++) + RETERR(hex_decode_char(&ctx, tr->base[i])); + } + if (ctx.length < 0) + isc_lex_ungettoken(lexer, &token); + RETERR(hex_decode_finish(&ctx)); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_hex_decodestring(const char *cstr, isc_buffer_t *target) { + hex_decode_ctx_t ctx; + + hex_decode_init(&ctx, -1, target); + for (;;) { + int c = *cstr++; + if (c == '\0') + break; + if (c == ' ' || c == '\t' || c == '\n' || c== '\r') + continue; + RETERR(hex_decode_char(&ctx, c)); + } + RETERR(hex_decode_finish(&ctx)); + return (ISC_R_SUCCESS); +} + +static isc_result_t +str_totext(const char *source, isc_buffer_t *target) { + unsigned int l; + isc_region_t region; + + isc_buffer_availableregion(target, ®ion); + l = strlen(source); + + if (l > region.length) + return (ISC_R_NOSPACE); + + memmove(region.base, source, l); + isc_buffer_add(target, l); + return (ISC_R_SUCCESS); +} + +static isc_result_t +mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { + isc_region_t tr; + + isc_buffer_availableregion(target, &tr); + if (length > tr.length) + return (ISC_R_NOSPACE); + memmove(tr.base, base, length); + isc_buffer_add(target, length); + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/hmacmd5.c b/lib/isc/hmacmd5.c new file mode 100644 index 0000000..0aa270d --- /dev/null +++ b/lib/isc/hmacmd5.c @@ -0,0 +1,423 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: hmacmd5.c,v 1.16 2009/02/06 23:47:42 tbox Exp $ */ + +/*! \file + * This code implements the HMAC-MD5 keyed hash algorithm + * described in RFC2104. + */ + +#include "config.h" + +#include <pk11/site.h> + +#ifndef PK11_MD5_DISABLE + +#include <stdbool.h> + +#include <isc/assertions.h> +#include <isc/hmacmd5.h> +#include <isc/md5.h> +#include <isc/platform.h> +#include <isc/safe.h> +#include <isc/string.h> +#include <isc/types.h> +#include <isc/util.h> + +#if PKCS11CRYPTO +#include <pk11/internal.h> +#include <pk11/pk11.h> +#endif + +#ifdef ISC_PLATFORM_OPENSSLHASH +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#define HMAC_CTX_new() &(ctx->_ctx), HMAC_CTX_init(&(ctx->_ctx)) +#define HMAC_CTX_free(ptr) HMAC_CTX_cleanup(ptr) +#endif + +void +isc_hmacmd5_init(isc_hmacmd5_t *ctx, const unsigned char *key, + unsigned int len) +{ + ctx->ctx = HMAC_CTX_new(); + RUNTIME_CHECK(ctx->ctx != NULL); + RUNTIME_CHECK(HMAC_Init_ex(ctx->ctx, (const void *) key, + (int) len, EVP_md5(), NULL) == 1); +} + +void +isc_hmacmd5_invalidate(isc_hmacmd5_t *ctx) { + if (ctx->ctx == NULL) + return; + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; +} + +void +isc_hmacmd5_update(isc_hmacmd5_t *ctx, const unsigned char *buf, + unsigned int len) +{ + RUNTIME_CHECK(HMAC_Update(ctx->ctx, buf, (int) len) == 1); +} + +void +isc_hmacmd5_sign(isc_hmacmd5_t *ctx, unsigned char *digest) { + RUNTIME_CHECK(HMAC_Final(ctx->ctx, digest, NULL) == 1); + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; +} + +#elif PKCS11CRYPTO + +#ifndef PK11_MD5_HMAC_REPLACE + +static CK_BBOOL truevalue = TRUE; +static CK_BBOOL falsevalue = FALSE; + +void +isc_hmacmd5_init(isc_hmacmd5_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_MD5_HMAC, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_MD5_HMAC; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_VALUE, NULL, (CK_ULONG) len } + }; +#ifdef PK11_PAD_HMAC_KEYS + CK_BYTE keypad[ISC_MD5_DIGESTLENGTH]; + + if (len < ISC_MD5_DIGESTLENGTH) { + memset(keypad, 0, ISC_MD5_DIGESTLENGTH); + memmove(keypad, key, len); + keyTemplate[5].pValue = keypad; + keyTemplate[5].ulValueLen = ISC_MD5_DIGESTLENGTH; + } else + DE_CONST(key, keyTemplate[5].pValue); +#else + DE_CONST(key, keyTemplate[5].pValue); +#endif + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + ctx->object = CK_INVALID_HANDLE; + PK11_FATALCHECK(pkcs_C_CreateObject, + (ctx->session, keyTemplate, + (CK_ULONG) 6, &ctx->object)); + INSIST(ctx->object != CK_INVALID_HANDLE); + PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object)); +} + +void +isc_hmacmd5_invalidate(isc_hmacmd5_t *ctx) { + CK_BYTE garbage[ISC_MD5_DIGESTLENGTH]; + CK_ULONG len = ISC_MD5_DIGESTLENGTH; + + if (ctx->handle == NULL) + return; + (void) pkcs_C_SignFinal(ctx->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); +} + +void +isc_hmacmd5_update(isc_hmacmd5_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_SignUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacmd5_sign(isc_hmacmd5_t *ctx, unsigned char *digest) { + CK_RV rv; + CK_ULONG len = ISC_MD5_DIGESTLENGTH; + + PK11_FATALCHECK(pkcs_C_SignFinal, + (ctx->session, (CK_BYTE_PTR) digest, &len)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); +} +#else +/* Replace missing CKM_MD5_HMAC PKCS#11 mechanism */ + +#define PADLEN 64 +#define IPAD 0x36 +#define OPAD 0x5C + +void +isc_hmacmd5_init(isc_hmacmd5_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_MD5, NULL, 0 }; + unsigned char ipad[PADLEN]; + unsigned int i; + + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + RUNTIME_CHECK((ctx->key = pk11_mem_get(PADLEN)) != NULL); + if (len > PADLEN) { + CK_BYTE_PTR kPart; + CK_ULONG kl; + + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + DE_CONST(key, kPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, kPart, (CK_ULONG) len)); + kl = ISC_MD5_DIGESTLENGTH; + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, (CK_BYTE_PTR) ctx->key, &kl)); + } else + memmove(ctx->key, key, len); + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + memset(ipad, IPAD, PADLEN); + for (i = 0; i < PADLEN; i++) + ipad[i] ^= ctx->key[i]; + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, ipad, (CK_ULONG) PADLEN)); +} + +void +isc_hmacmd5_invalidate(isc_hmacmd5_t *ctx) { + if (ctx->key != NULL) + pk11_mem_put(ctx->key, PADLEN); + ctx->key = NULL; + isc_md5_invalidate(ctx); +} + +void +isc_hmacmd5_update(isc_hmacmd5_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacmd5_sign(isc_hmacmd5_t *ctx, unsigned char *digest) { + CK_RV rv; + CK_MECHANISM mech = { CKM_MD5, NULL, 0 }; + CK_ULONG len = ISC_MD5_DIGESTLENGTH; + CK_BYTE opad[PADLEN]; + unsigned int i; + + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, (CK_BYTE_PTR) digest, + (CK_ULONG_PTR) &len)); + memset(opad, OPAD, PADLEN); + for (i = 0; i < PADLEN; i++) + opad[i] ^= ctx->key[i]; + pk11_mem_put(ctx->key, PADLEN); + ctx->key = NULL; + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, opad, (CK_ULONG) PADLEN)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, (CK_BYTE_PTR) digest, len)); + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, + (CK_BYTE_PTR) digest, + (CK_ULONG_PTR) &len)); + pk11_return_session(ctx); +} +#endif + +#else + +#define PADLEN 64 +#define IPAD 0x36 +#define OPAD 0x5C + +/*! + * Start HMAC-MD5 process. Initialize an md5 context and digest the key. + */ +void +isc_hmacmd5_init(isc_hmacmd5_t *ctx, const unsigned char *key, + unsigned int len) +{ + unsigned char ipad[PADLEN]; + int i; + + memset(ctx->key, 0, sizeof(ctx->key)); + if (len > sizeof(ctx->key)) { + isc_md5_t md5ctx; + isc_md5_init(&md5ctx); + isc_md5_update(&md5ctx, key, len); + isc_md5_final(&md5ctx, ctx->key); + } else + memmove(ctx->key, key, len); + + isc_md5_init(&ctx->md5ctx); + memset(ipad, IPAD, sizeof(ipad)); + for (i = 0; i < PADLEN; i++) + ipad[i] ^= ctx->key[i]; + isc_md5_update(&ctx->md5ctx, ipad, sizeof(ipad)); +} + +void +isc_hmacmd5_invalidate(isc_hmacmd5_t *ctx) { + isc_md5_invalidate(&ctx->md5ctx); + isc_safe_memwipe(ctx->key, sizeof(ctx->key)); +} + +/*! + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +isc_hmacmd5_update(isc_hmacmd5_t *ctx, const unsigned char *buf, + unsigned int len) +{ + isc_md5_update(&ctx->md5ctx, buf, len); +} + +/*! + * Compute signature - finalize MD5 operation and reapply MD5. + */ +void +isc_hmacmd5_sign(isc_hmacmd5_t *ctx, unsigned char *digest) { + unsigned char opad[PADLEN]; + int i; + + isc_md5_final(&ctx->md5ctx, digest); + + memset(opad, OPAD, sizeof(opad)); + for (i = 0; i < PADLEN; i++) + opad[i] ^= ctx->key[i]; + + isc_md5_init(&ctx->md5ctx); + isc_md5_update(&ctx->md5ctx, opad, sizeof(opad)); + isc_md5_update(&ctx->md5ctx, digest, ISC_MD5_DIGESTLENGTH); + isc_md5_final(&ctx->md5ctx, digest); + isc_hmacmd5_invalidate(ctx); +} + +#endif /* !ISC_PLATFORM_OPENSSLHASH */ + +/*! + * Verify signature - finalize MD5 operation and reapply MD5, then + * compare to the supplied digest. + */ +bool +isc_hmacmd5_verify(isc_hmacmd5_t *ctx, unsigned char *digest) { + return (isc_hmacmd5_verify2(ctx, digest, ISC_MD5_DIGESTLENGTH)); +} + +bool +isc_hmacmd5_verify2(isc_hmacmd5_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_MD5_DIGESTLENGTH]; + + REQUIRE(len <= ISC_MD5_DIGESTLENGTH); + isc_hmacmd5_sign(ctx, newdigest); + return (isc_safe_memequal(digest, newdigest, len)); +} + +/* + * Check for MD5 support; if it does not work, raise a fatal error. + * + * Use the first test vector from RFC 2104, with a second round using + * a too-short key. + * + * Standard use is testing 0 and expecting result true. + * Testing use is testing 1..4 and expecting result false. + */ +bool +isc_hmacmd5_check(int testing) { + isc_hmacmd5_t ctx; + unsigned char key[] = { /* 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b */ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b + }; + unsigned char input[] = { /* "Hi There" */ + 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 + }; + unsigned char expected[] = { + 0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c, + 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d + }; + unsigned char expected2[] = { + 0xad, 0xb8, 0x48, 0x05, 0xb8, 0x8d, 0x03, 0xe5, + 0x90, 0x1e, 0x4b, 0x05, 0x69, 0xce, 0x35, 0xea + }; + bool result; + + /* + * Introduce a fault for testing. + */ + switch (testing) { + case 0: + default: + break; + case 1: + key[0] ^= 0x01; + break; + case 2: + input[0] ^= 0x01; + break; + case 3: + expected[0] ^= 0x01; + break; + case 4: + expected2[0] ^= 0x01; + break; + } + + /* + * These functions do not return anything; any failure will be fatal. + */ + isc_hmacmd5_init(&ctx, key, 16U); + isc_hmacmd5_update(&ctx, input, 8U); + result = isc_hmacmd5_verify2(&ctx, expected, sizeof(expected)); + if (!result) { + return (result); + } + + /* Second round using a byte key */ + isc_hmacmd5_init(&ctx, key, 1U); + isc_hmacmd5_update(&ctx, input, 8U); + return (isc_hmacmd5_verify2(&ctx, expected2, sizeof(expected2))); +} + +#else /* !PK11_MD5_DISABLE */ +#ifdef WIN32 +/* Make the Visual Studio linker happy */ +#include <isc/util.h> + +void isc_hmacmd5_init() { INSIST(0); } +void isc_hmacmd5_invalidate() { INSIST(0); } +void isc_hmacmd5_sign() { INSIST(0); } +void isc_hmacmd5_update() { INSIST(0); } +void isc_hmacmd5_verify() { INSIST(0); } +void isc_hmacmd5_verify2() { INSIST(0); } +void isc_hmacmd5_check() { INSIST(0); } +#endif +#endif /* PK11_MD5_DISABLE */ diff --git a/lib/isc/hmacsha.c b/lib/isc/hmacsha.c new file mode 100644 index 0000000..2839f4f --- /dev/null +++ b/lib/isc/hmacsha.c @@ -0,0 +1,1575 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id$ */ + +/* + * This code implements the HMAC-SHA1, HMAC-SHA224, HMAC-SHA256, HMAC-SHA384 + * and HMAC-SHA512 keyed hash algorithm described in RFC 2104 and + * draft-ietf-dnsext-tsig-sha-01.txt. + */ + +#include "config.h" + +#include <stdbool.h> + +#include <isc/assertions.h> +#include <isc/hmacsha.h> +#include <isc/platform.h> +#include <isc/safe.h> +#include <isc/sha1.h> +#include <isc/sha2.h> +#include <isc/string.h> +#include <isc/types.h> +#include <isc/util.h> + +#if PKCS11CRYPTO +#include <pk11/internal.h> +#include <pk11/pk11.h> +#endif + +#ifdef ISC_PLATFORM_OPENSSLHASH +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#define HMAC_CTX_new() &(ctx->_ctx), HMAC_CTX_init(&(ctx->_ctx)) +#define HMAC_CTX_free(ptr) HMAC_CTX_cleanup(ptr) +#endif + +void +isc_hmacsha1_init(isc_hmacsha1_t *ctx, const unsigned char *key, + unsigned int len) +{ + ctx->ctx = HMAC_CTX_new(); + RUNTIME_CHECK(ctx->ctx != NULL); + RUNTIME_CHECK(HMAC_Init_ex(ctx->ctx, (const void *) key, + (int) len, EVP_sha1(), NULL) == 1); +} + +void +isc_hmacsha1_invalidate(isc_hmacsha1_t *ctx) { + if (ctx->ctx == NULL) + return; + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; +} + +void +isc_hmacsha1_update(isc_hmacsha1_t *ctx, const unsigned char *buf, + unsigned int len) +{ + RUNTIME_CHECK(HMAC_Update(ctx->ctx, buf, (int) len) == 1); +} + +void +isc_hmacsha1_sign(isc_hmacsha1_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA1_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA1_DIGESTLENGTH); + + RUNTIME_CHECK(HMAC_Final(ctx->ctx, newdigest, NULL) == 1); + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} + +void +isc_hmacsha224_init(isc_hmacsha224_t *ctx, const unsigned char *key, + unsigned int len) +{ + ctx->ctx = HMAC_CTX_new(); + RUNTIME_CHECK(ctx->ctx != NULL); + RUNTIME_CHECK(HMAC_Init_ex(ctx->ctx, (const void *) key, + (int) len, EVP_sha224(), NULL) == 1); +} + +void +isc_hmacsha224_invalidate(isc_hmacsha224_t *ctx) { + if (ctx->ctx == NULL) + return; + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; +} + +void +isc_hmacsha224_update(isc_hmacsha224_t *ctx, const unsigned char *buf, + unsigned int len) +{ + RUNTIME_CHECK(HMAC_Update(ctx->ctx, buf, (int) len) == 1); +} + +void +isc_hmacsha224_sign(isc_hmacsha224_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA224_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA224_DIGESTLENGTH); + + RUNTIME_CHECK(HMAC_Final(ctx->ctx, newdigest, NULL) == 1); + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} + +void +isc_hmacsha256_init(isc_hmacsha256_t *ctx, const unsigned char *key, + unsigned int len) +{ + ctx->ctx = HMAC_CTX_new(); + RUNTIME_CHECK(ctx->ctx != NULL); + RUNTIME_CHECK(HMAC_Init_ex(ctx->ctx, (const void *) key, + (int) len, EVP_sha256(), NULL) == 1); +} + +void +isc_hmacsha256_invalidate(isc_hmacsha256_t *ctx) { + if (ctx->ctx == NULL) + return; + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; +} + +void +isc_hmacsha256_update(isc_hmacsha256_t *ctx, const unsigned char *buf, + unsigned int len) +{ + RUNTIME_CHECK(HMAC_Update(ctx->ctx, buf, (int) len) == 1); +} + +void +isc_hmacsha256_sign(isc_hmacsha256_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA256_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA256_DIGESTLENGTH); + + RUNTIME_CHECK(HMAC_Final(ctx->ctx, newdigest, NULL) == 1); + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} + +void +isc_hmacsha384_init(isc_hmacsha384_t *ctx, const unsigned char *key, + unsigned int len) +{ + ctx->ctx = HMAC_CTX_new(); + RUNTIME_CHECK(ctx->ctx != NULL); + RUNTIME_CHECK(HMAC_Init_ex(ctx->ctx, (const void *) key, + (int) len, EVP_sha384(), NULL) == 1); +} + +void +isc_hmacsha384_invalidate(isc_hmacsha384_t *ctx) { + if (ctx->ctx == NULL) + return; + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; +} + +void +isc_hmacsha384_update(isc_hmacsha384_t *ctx, const unsigned char *buf, + unsigned int len) +{ + RUNTIME_CHECK(HMAC_Update(ctx->ctx, buf, (int) len) == 1); +} + +void +isc_hmacsha384_sign(isc_hmacsha384_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA384_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA384_DIGESTLENGTH); + + RUNTIME_CHECK(HMAC_Final(ctx->ctx, newdigest, NULL) == 1); + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} + +void +isc_hmacsha512_init(isc_hmacsha512_t *ctx, const unsigned char *key, + unsigned int len) +{ + ctx->ctx = HMAC_CTX_new(); + RUNTIME_CHECK(ctx->ctx != NULL); + RUNTIME_CHECK(HMAC_Init_ex(ctx->ctx, (const void *) key, + (int) len, EVP_sha512(), NULL) == 1); +} + +void +isc_hmacsha512_invalidate(isc_hmacsha512_t *ctx) { + if (ctx->ctx == NULL) + return; + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; +} + +void +isc_hmacsha512_update(isc_hmacsha512_t *ctx, const unsigned char *buf, + unsigned int len) +{ + RUNTIME_CHECK(HMAC_Update(ctx->ctx, buf, (int) len) == 1); +} + +void +isc_hmacsha512_sign(isc_hmacsha512_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA512_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA512_DIGESTLENGTH); + + RUNTIME_CHECK(HMAC_Final(ctx->ctx, newdigest, NULL) == 1); + HMAC_CTX_free(ctx->ctx); + ctx->ctx = NULL; + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} + +#elif PKCS11CRYPTO + +#if defined(PK11_SHA_1_HMAC_REPLACE) || \ + defined(PK11_SHA224_HMAC_REPLACE) || \ + defined(PK11_SHA256_HMAC_REPLACE) || \ + defined(PK11_SHA384_HMAC_REPLACE) || \ + defined(PK11_SHA512_HMAC_REPLACE) +#define IPAD 0x36 +#define OPAD 0x5C +#endif + +#if !defined(PK11_SHA_1_HMAC_REPLACE) && \ + !defined(PK11_SHA224_HMAC_REPLACE) && \ + !defined(PK11_SHA256_HMAC_REPLACE) && \ + !defined(PK11_SHA384_HMAC_REPLACE) && \ + !defined(PK11_SHA512_HMAC_REPLACE) +static CK_BBOOL truevalue = TRUE; +static CK_BBOOL falsevalue = FALSE; +#endif + +#ifndef PK11_SHA_1_HMAC_REPLACE +void +isc_hmacsha1_init(isc_hmacsha1_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA_1_HMAC, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_SHA_1_HMAC; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_VALUE, NULL, (CK_ULONG) len } + }; +#ifdef PK11_PAD_HMAC_KEYS + CK_BYTE keypad[ISC_SHA1_DIGESTLENGTH]; + + if (len < ISC_SHA1_DIGESTLENGTH) { + memset(keypad, 0, ISC_SHA1_DIGESTLENGTH); + memmove(keypad, key, len); + keyTemplate[5].pValue = keypad; + keyTemplate[5].ulValueLen = ISC_SHA1_DIGESTLENGTH; + } else + DE_CONST(key, keyTemplate[5].pValue); +#else + DE_CONST(key, keyTemplate[5].pValue); +#endif + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + ctx->object = CK_INVALID_HANDLE; + PK11_FATALCHECK(pkcs_C_CreateObject, + (ctx->session, keyTemplate, + (CK_ULONG) 6, &ctx->object)); + INSIST(ctx->object != CK_INVALID_HANDLE); + PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object)); +} + +void +isc_hmacsha1_invalidate(isc_hmacsha1_t *ctx) { + CK_BYTE garbage[ISC_SHA1_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA1_DIGESTLENGTH; + + if (ctx->handle == NULL) + return; + (void) pkcs_C_SignFinal(ctx->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); +} + +void +isc_hmacsha1_update(isc_hmacsha1_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_SignUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha1_sign(isc_hmacsha1_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA1_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA1_DIGESTLENGTH; + + REQUIRE(len <= ISC_SHA1_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#else +void +isc_hmacsha1_init(isc_hmacsha1_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA_1, NULL, 0 }; + unsigned char ipad[ISC_SHA1_BLOCK_LENGTH]; + unsigned int i; + + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + RUNTIME_CHECK((ctx->key = pk11_mem_get(ISC_SHA1_BLOCK_LENGTH)) + != NULL); + if (len > ISC_SHA1_BLOCK_LENGTH) { + CK_BYTE_PTR kPart; + CK_ULONG kl; + + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + DE_CONST(key, kPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, kPart, (CK_ULONG) len)); + kl = ISC_SHA1_DIGESTLENGTH; + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, (CK_BYTE_PTR) ctx->key, &kl)); + } else + memmove(ctx->key, key, len); + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + memset(ipad, IPAD, ISC_SHA1_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA1_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, ipad, + (CK_ULONG) ISC_SHA1_BLOCK_LENGTH)); +} + +void +isc_hmacsha1_invalidate(isc_hmacsha1_t *ctx) { + if (ctx->key != NULL) + pk11_mem_put(ctx->key, ISC_SHA1_BLOCK_LENGTH); + ctx->key = NULL; + isc_sha1_invalidate(ctx); +} + +void +isc_hmacsha1_update(isc_hmacsha1_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha1_sign(isc_hmacsha1_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA1_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA1_DIGESTLENGTH; + CK_MECHANISM mech = { CKM_SHA_1, NULL, 0 }; + CK_BYTE opad[ISC_SHA1_BLOCK_LENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA1_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + memset(opad, OPAD, ISC_SHA1_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA1_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + pk11_mem_put(ctx->key, ISC_SHA1_BLOCK_LENGTH); + ctx->key = NULL; + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, opad, + (CK_ULONG) ISC_SHA1_BLOCK_LENGTH)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, (CK_BYTE_PTR) newdigest, psl)); + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#endif + +#ifndef PK11_SHA224_HMAC_REPLACE +void +isc_hmacsha224_init(isc_hmacsha224_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA224_HMAC, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_SHA224_HMAC; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_VALUE, NULL, (CK_ULONG) len } + }; +#ifdef PK11_PAD_HMAC_KEYS + CK_BYTE keypad[ISC_SHA224_DIGESTLENGTH]; + + if (len < ISC_SHA224_DIGESTLENGTH) { + memset(keypad, 0, ISC_SHA224_DIGESTLENGTH); + memmove(keypad, key, len); + keyTemplate[5].pValue = keypad; + keyTemplate[5].ulValueLen = ISC_SHA224_DIGESTLENGTH; + } else + DE_CONST(key, keyTemplate[5].pValue); +#else + DE_CONST(key, keyTemplate[5].pValue); +#endif + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + ctx->object = CK_INVALID_HANDLE; + PK11_FATALCHECK(pkcs_C_CreateObject, + (ctx->session, keyTemplate, + (CK_ULONG) 6, &ctx->object)); + INSIST(ctx->object != CK_INVALID_HANDLE); + PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object)); +} + +void +isc_hmacsha224_invalidate(isc_hmacsha224_t *ctx) { + CK_BYTE garbage[ISC_SHA224_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA224_DIGESTLENGTH; + + if (ctx->handle == NULL) + return; + (void) pkcs_C_SignFinal(ctx->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); +} + +void +isc_hmacsha224_update(isc_hmacsha224_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_SignUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha224_sign(isc_hmacsha224_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA224_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA224_DIGESTLENGTH; + + REQUIRE(len <= ISC_SHA224_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#else +void +isc_hmacsha224_init(isc_hmacsha224_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA224, NULL, 0 }; + unsigned char ipad[ISC_SHA224_BLOCK_LENGTH]; + unsigned int i; + + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + RUNTIME_CHECK((ctx->key = pk11_mem_get(ISC_SHA224_BLOCK_LENGTH)) + != NULL); + if (len > ISC_SHA224_BLOCK_LENGTH) { + CK_BYTE_PTR kPart; + CK_ULONG kl; + + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + DE_CONST(key, kPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, kPart, (CK_ULONG) len)); + kl = ISC_SHA224_DIGESTLENGTH; + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, (CK_BYTE_PTR) ctx->key, &kl)); + } else + memmove(ctx->key, key, len); + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + memset(ipad, IPAD, ISC_SHA224_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA224_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, ipad, + (CK_ULONG) ISC_SHA224_BLOCK_LENGTH)); +} + +void +isc_hmacsha224_invalidate(isc_hmacsha224_t *ctx) { + if (ctx->key != NULL) + pk11_mem_put(ctx->key, ISC_SHA224_BLOCK_LENGTH); + ctx->key = NULL; + isc_sha224_invalidate(ctx); +} + +void +isc_hmacsha224_update(isc_hmacsha224_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha224_sign(isc_hmacsha224_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA224_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA224_DIGESTLENGTH; + CK_MECHANISM mech = { CKM_SHA224, NULL, 0 }; + CK_BYTE opad[ISC_SHA224_BLOCK_LENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA224_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + memset(opad, OPAD, ISC_SHA224_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA224_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + pk11_mem_put(ctx->key, ISC_SHA224_BLOCK_LENGTH); + ctx->key = NULL; + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, opad, + (CK_ULONG) ISC_SHA224_BLOCK_LENGTH)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, (CK_BYTE_PTR) newdigest, psl)); + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#endif + +#ifndef PK11_SHA256_HMAC_REPLACE +void +isc_hmacsha256_init(isc_hmacsha256_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA256_HMAC, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_SHA256_HMAC; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_VALUE, NULL, (CK_ULONG) len } + }; +#ifdef PK11_PAD_HMAC_KEYS + CK_BYTE keypad[ISC_SHA256_DIGESTLENGTH]; + + if (len < ISC_SHA256_DIGESTLENGTH) { + memset(keypad, 0, ISC_SHA256_DIGESTLENGTH); + memmove(keypad, key, len); + keyTemplate[5].pValue = keypad; + keyTemplate[5].ulValueLen = ISC_SHA256_DIGESTLENGTH; + } else + DE_CONST(key, keyTemplate[5].pValue); +#else + DE_CONST(key, keyTemplate[5].pValue); +#endif + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + ctx->object = CK_INVALID_HANDLE; + PK11_FATALCHECK(pkcs_C_CreateObject, + (ctx->session, keyTemplate, + (CK_ULONG) 6, &ctx->object)); + INSIST(ctx->object != CK_INVALID_HANDLE); + PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object)); +} + +void +isc_hmacsha256_invalidate(isc_hmacsha256_t *ctx) { + CK_BYTE garbage[ISC_SHA256_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA256_DIGESTLENGTH; + + if (ctx->handle == NULL) + return; + (void) pkcs_C_SignFinal(ctx->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); +} + +void +isc_hmacsha256_update(isc_hmacsha256_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_SignUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha256_sign(isc_hmacsha256_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA256_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA256_DIGESTLENGTH; + + REQUIRE(len <= ISC_SHA256_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#else +void +isc_hmacsha256_init(isc_hmacsha256_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA256, NULL, 0 }; + unsigned char ipad[ISC_SHA256_BLOCK_LENGTH]; + unsigned int i; + + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + RUNTIME_CHECK((ctx->key = pk11_mem_get(ISC_SHA256_BLOCK_LENGTH)) + != NULL); + if (len > ISC_SHA256_BLOCK_LENGTH) { + CK_BYTE_PTR kPart; + CK_ULONG kl; + + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + DE_CONST(key, kPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, kPart, (CK_ULONG) len)); + kl = ISC_SHA256_DIGESTLENGTH; + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, (CK_BYTE_PTR) ctx->key, &kl)); + } else + memmove(ctx->key, key, len); + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + memset(ipad, IPAD, ISC_SHA256_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA256_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, ipad, + (CK_ULONG) ISC_SHA256_BLOCK_LENGTH)); +} + +void +isc_hmacsha256_invalidate(isc_hmacsha256_t *ctx) { + if (ctx->key != NULL) + pk11_mem_put(ctx->key, ISC_SHA256_BLOCK_LENGTH); + ctx->key = NULL; + isc_sha256_invalidate(ctx); +} + +void +isc_hmacsha256_update(isc_hmacsha256_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha256_sign(isc_hmacsha256_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA256_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA256_DIGESTLENGTH; + CK_MECHANISM mech = { CKM_SHA256, NULL, 0 }; + CK_BYTE opad[ISC_SHA256_BLOCK_LENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA256_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + memset(opad, OPAD, ISC_SHA256_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA256_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + pk11_mem_put(ctx->key, ISC_SHA256_BLOCK_LENGTH); + ctx->key = NULL; + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, opad, + (CK_ULONG) ISC_SHA256_BLOCK_LENGTH)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, (CK_BYTE_PTR) newdigest, psl)); + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#endif + +#ifndef PK11_SHA384_HMAC_REPLACE +void +isc_hmacsha384_init(isc_hmacsha384_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA384_HMAC, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_SHA384_HMAC; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_VALUE, NULL, (CK_ULONG) len } + }; +#ifdef PK11_PAD_HMAC_KEYS + CK_BYTE keypad[ISC_SHA384_DIGESTLENGTH]; + + if (len < ISC_SHA384_DIGESTLENGTH) { + memset(keypad, 0, ISC_SHA384_DIGESTLENGTH); + memmove(keypad, key, len); + keyTemplate[5].pValue = keypad; + keyTemplate[5].ulValueLen = ISC_SHA384_DIGESTLENGTH; + } else + DE_CONST(key, keyTemplate[5].pValue); +#else + DE_CONST(key, keyTemplate[5].pValue); +#endif + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + ctx->object = CK_INVALID_HANDLE; + PK11_FATALCHECK(pkcs_C_CreateObject, + (ctx->session, keyTemplate, + (CK_ULONG) 6, &ctx->object)); + INSIST(ctx->object != CK_INVALID_HANDLE); + PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object)); +} + +void +isc_hmacsha384_invalidate(isc_hmacsha384_t *ctx) { + CK_BYTE garbage[ISC_SHA384_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA384_DIGESTLENGTH; + + if (ctx->handle == NULL) + return; + (void) pkcs_C_SignFinal(ctx->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); +} + +void +isc_hmacsha384_update(isc_hmacsha384_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_SignUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha384_sign(isc_hmacsha384_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA384_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA384_DIGESTLENGTH; + + REQUIRE(len <= ISC_SHA384_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#else +void +isc_hmacsha384_init(isc_hmacsha384_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA384, NULL, 0 }; + unsigned char ipad[ISC_SHA384_BLOCK_LENGTH]; + unsigned int i; + + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + RUNTIME_CHECK((ctx->key = pk11_mem_get(ISC_SHA384_BLOCK_LENGTH)) + != NULL); + if (len > ISC_SHA384_BLOCK_LENGTH) { + CK_BYTE_PTR kPart; + CK_ULONG kl; + + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + DE_CONST(key, kPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, kPart, (CK_ULONG) len)); + kl = ISC_SHA384_DIGESTLENGTH; + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, (CK_BYTE_PTR) ctx->key, &kl)); + } else + memmove(ctx->key, key, len); + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + memset(ipad, IPAD, ISC_SHA384_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA384_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, ipad, + (CK_ULONG) ISC_SHA384_BLOCK_LENGTH)); +} + +void +isc_hmacsha384_invalidate(isc_hmacsha384_t *ctx) { + if (ctx->key != NULL) + pk11_mem_put(ctx->key, ISC_SHA384_BLOCK_LENGTH); + ctx->key = NULL; + isc_sha384_invalidate(ctx); +} + +void +isc_hmacsha384_update(isc_hmacsha384_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha384_sign(isc_hmacsha384_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA384_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA384_DIGESTLENGTH; + CK_MECHANISM mech = { CKM_SHA384, NULL, 0 }; + CK_BYTE opad[ISC_SHA384_BLOCK_LENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA384_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + memset(opad, OPAD, ISC_SHA384_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA384_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + pk11_mem_put(ctx->key, ISC_SHA384_BLOCK_LENGTH); + ctx->key = NULL; + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, opad, + (CK_ULONG) ISC_SHA384_BLOCK_LENGTH)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, (CK_BYTE_PTR) newdigest, psl)); + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#endif + +#ifndef PK11_SHA512_HMAC_REPLACE +void +isc_hmacsha512_init(isc_hmacsha512_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA512_HMAC, NULL, 0 }; + CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; + CK_KEY_TYPE keyType = CKK_SHA512_HMAC; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_VALUE, NULL, (CK_ULONG) len } + }; +#ifdef PK11_PAD_HMAC_KEYS + CK_BYTE keypad[ISC_SHA512_DIGESTLENGTH]; + + if (len < ISC_SHA512_DIGESTLENGTH) { + memset(keypad, 0, ISC_SHA512_DIGESTLENGTH); + memmove(keypad, key, len); + keyTemplate[5].pValue = keypad; + keyTemplate[5].ulValueLen = ISC_SHA512_DIGESTLENGTH; + } else + DE_CONST(key, keyTemplate[5].pValue); +#else + DE_CONST(key, keyTemplate[5].pValue); +#endif + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + ctx->object = CK_INVALID_HANDLE; + PK11_FATALCHECK(pkcs_C_CreateObject, + (ctx->session, keyTemplate, + (CK_ULONG) 6, &ctx->object)); + INSIST(ctx->object != CK_INVALID_HANDLE); + PK11_FATALCHECK(pkcs_C_SignInit, (ctx->session, &mech, ctx->object)); +} + +void +isc_hmacsha512_invalidate(isc_hmacsha512_t *ctx) { + CK_BYTE garbage[ISC_SHA512_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA512_DIGESTLENGTH; + + if (ctx->handle == NULL) + return; + (void) pkcs_C_SignFinal(ctx->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); +} + +void +isc_hmacsha512_update(isc_hmacsha512_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_SignUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha512_sign(isc_hmacsha512_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA512_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA512_DIGESTLENGTH; + + REQUIRE(len <= ISC_SHA512_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_SignFinal, (ctx->session, newdigest, &psl)); + if (ctx->object != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(ctx->session, ctx->object); + ctx->object = CK_INVALID_HANDLE; + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#else +void +isc_hmacsha512_init(isc_hmacsha512_t *ctx, const unsigned char *key, + unsigned int len) +{ + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA512, NULL, 0 }; + unsigned char ipad[ISC_SHA512_BLOCK_LENGTH]; + unsigned int i; + + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + RUNTIME_CHECK((ctx->key = pk11_mem_get(ISC_SHA512_BLOCK_LENGTH)) + != NULL); + if (len > ISC_SHA512_BLOCK_LENGTH) { + CK_BYTE_PTR kPart; + CK_ULONG kl; + + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + DE_CONST(key, kPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, kPart, (CK_ULONG) len)); + kl = ISC_SHA512_DIGESTLENGTH; + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, (CK_BYTE_PTR) ctx->key, &kl)); + } else + memmove(ctx->key, key, len); + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + memset(ipad, IPAD, ISC_SHA512_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA512_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, ipad, + (CK_ULONG) ISC_SHA512_BLOCK_LENGTH)); +} + +void +isc_hmacsha512_invalidate(isc_hmacsha512_t *ctx) { + if (ctx->key != NULL) + pk11_mem_put(ctx->key, ISC_SHA512_BLOCK_LENGTH); + ctx->key = NULL; + isc_sha512_invalidate(ctx); +} + +void +isc_hmacsha512_update(isc_hmacsha512_t *ctx, const unsigned char *buf, + unsigned int len) +{ + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_hmacsha512_sign(isc_hmacsha512_t *ctx, unsigned char *digest, size_t len) { + CK_RV rv; + CK_BYTE newdigest[ISC_SHA512_DIGESTLENGTH]; + CK_ULONG psl = ISC_SHA512_DIGESTLENGTH; + CK_MECHANISM mech = { CKM_SHA512, NULL, 0 }; + CK_BYTE opad[ISC_SHA512_BLOCK_LENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA512_DIGESTLENGTH); + + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + memset(opad, OPAD, ISC_SHA512_BLOCK_LENGTH); + for (i = 0; i < ISC_SHA512_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + pk11_mem_put(ctx->key, ISC_SHA512_BLOCK_LENGTH); + ctx->key = NULL; + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, opad, + (CK_ULONG) ISC_SHA512_BLOCK_LENGTH)); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, (CK_BYTE_PTR) newdigest, psl)); + PK11_FATALCHECK(pkcs_C_DigestFinal, (ctx->session, newdigest, &psl)); + pk11_return_session(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#endif + +#else + +#define IPAD 0x36 +#define OPAD 0x5C + +/* + * Start HMAC-SHA1 process. Initialize an sha1 context and digest the key. + */ +void +isc_hmacsha1_init(isc_hmacsha1_t *ctx, const unsigned char *key, + unsigned int len) +{ + unsigned char ipad[ISC_SHA1_BLOCK_LENGTH]; + unsigned int i; + + memset(ctx->key, 0, sizeof(ctx->key)); + if (len > sizeof(ctx->key)) { + isc_sha1_t sha1ctx; + isc_sha1_init(&sha1ctx); + isc_sha1_update(&sha1ctx, key, len); + isc_sha1_final(&sha1ctx, ctx->key); + } else + memmove(ctx->key, key, len); + + isc_sha1_init(&ctx->sha1ctx); + memset(ipad, IPAD, sizeof(ipad)); + for (i = 0; i < ISC_SHA1_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + isc_sha1_update(&ctx->sha1ctx, ipad, sizeof(ipad)); +} + +void +isc_hmacsha1_invalidate(isc_hmacsha1_t *ctx) { + isc_sha1_invalidate(&ctx->sha1ctx); + isc_safe_memwipe(ctx, sizeof(*ctx)); +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +isc_hmacsha1_update(isc_hmacsha1_t *ctx, const unsigned char *buf, + unsigned int len) +{ + isc_sha1_update(&ctx->sha1ctx, buf, len); +} + +/* + * Compute signature - finalize SHA1 operation and reapply SHA1. + */ +void +isc_hmacsha1_sign(isc_hmacsha1_t *ctx, unsigned char *digest, size_t len) { + unsigned char opad[ISC_SHA1_BLOCK_LENGTH]; + unsigned char newdigest[ISC_SHA1_DIGESTLENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA1_DIGESTLENGTH); + isc_sha1_final(&ctx->sha1ctx, newdigest); + + memset(opad, OPAD, sizeof(opad)); + for (i = 0; i < ISC_SHA1_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + + isc_sha1_init(&ctx->sha1ctx); + isc_sha1_update(&ctx->sha1ctx, opad, sizeof(opad)); + isc_sha1_update(&ctx->sha1ctx, newdigest, ISC_SHA1_DIGESTLENGTH); + isc_sha1_final(&ctx->sha1ctx, newdigest); + isc_hmacsha1_invalidate(ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} + +/* + * Start HMAC-SHA224 process. Initialize an sha224 context and digest the key. + */ +void +isc_hmacsha224_init(isc_hmacsha224_t *ctx, const unsigned char *key, + unsigned int len) +{ + unsigned char ipad[ISC_SHA224_BLOCK_LENGTH]; + unsigned int i; + + memset(ctx->key, 0, sizeof(ctx->key)); + if (len > sizeof(ctx->key)) { + isc_sha224_t sha224ctx; + isc_sha224_init(&sha224ctx); + isc_sha224_update(&sha224ctx, key, len); + isc_sha224_final(ctx->key, &sha224ctx); + } else + memmove(ctx->key, key, len); + + isc_sha224_init(&ctx->sha224ctx); + memset(ipad, IPAD, sizeof(ipad)); + for (i = 0; i < ISC_SHA224_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + isc_sha224_update(&ctx->sha224ctx, ipad, sizeof(ipad)); +} + +void +isc_hmacsha224_invalidate(isc_hmacsha224_t *ctx) { + isc_safe_memwipe(ctx, sizeof(*ctx)); +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +isc_hmacsha224_update(isc_hmacsha224_t *ctx, const unsigned char *buf, + unsigned int len) +{ + isc_sha224_update(&ctx->sha224ctx, buf, len); +} + +/* + * Compute signature - finalize SHA224 operation and reapply SHA224. + */ +void +isc_hmacsha224_sign(isc_hmacsha224_t *ctx, unsigned char *digest, size_t len) { + unsigned char opad[ISC_SHA224_BLOCK_LENGTH]; + unsigned char newdigest[ISC_SHA224_DIGESTLENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA224_DIGESTLENGTH); + isc_sha224_final(newdigest, &ctx->sha224ctx); + + memset(opad, OPAD, sizeof(opad)); + for (i = 0; i < ISC_SHA224_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + + isc_sha224_init(&ctx->sha224ctx); + isc_sha224_update(&ctx->sha224ctx, opad, sizeof(opad)); + isc_sha224_update(&ctx->sha224ctx, newdigest, ISC_SHA224_DIGESTLENGTH); + isc_sha224_final(newdigest, &ctx->sha224ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} + +/* + * Start HMAC-SHA256 process. Initialize an sha256 context and digest the key. + */ +void +isc_hmacsha256_init(isc_hmacsha256_t *ctx, const unsigned char *key, + unsigned int len) +{ + unsigned char ipad[ISC_SHA256_BLOCK_LENGTH]; + unsigned int i; + + memset(ctx->key, 0, sizeof(ctx->key)); + if (len > sizeof(ctx->key)) { + isc_sha256_t sha256ctx; + isc_sha256_init(&sha256ctx); + isc_sha256_update(&sha256ctx, key, len); + isc_sha256_final(ctx->key, &sha256ctx); + } else + memmove(ctx->key, key, len); + + isc_sha256_init(&ctx->sha256ctx); + memset(ipad, IPAD, sizeof(ipad)); + for (i = 0; i < ISC_SHA256_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + isc_sha256_update(&ctx->sha256ctx, ipad, sizeof(ipad)); +} + +void +isc_hmacsha256_invalidate(isc_hmacsha256_t *ctx) { + isc_safe_memwipe(ctx, sizeof(*ctx)); +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +isc_hmacsha256_update(isc_hmacsha256_t *ctx, const unsigned char *buf, + unsigned int len) +{ + isc_sha256_update(&ctx->sha256ctx, buf, len); +} + +/* + * Compute signature - finalize SHA256 operation and reapply SHA256. + */ +void +isc_hmacsha256_sign(isc_hmacsha256_t *ctx, unsigned char *digest, size_t len) { + unsigned char opad[ISC_SHA256_BLOCK_LENGTH]; + unsigned char newdigest[ISC_SHA256_DIGESTLENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA256_DIGESTLENGTH); + isc_sha256_final(newdigest, &ctx->sha256ctx); + + memset(opad, OPAD, sizeof(opad)); + for (i = 0; i < ISC_SHA256_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + + isc_sha256_init(&ctx->sha256ctx); + isc_sha256_update(&ctx->sha256ctx, opad, sizeof(opad)); + isc_sha256_update(&ctx->sha256ctx, newdigest, ISC_SHA256_DIGESTLENGTH); + isc_sha256_final(newdigest, &ctx->sha256ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} + +/* + * Start HMAC-SHA384 process. Initialize an sha384 context and digest the key. + */ +void +isc_hmacsha384_init(isc_hmacsha384_t *ctx, const unsigned char *key, + unsigned int len) +{ + unsigned char ipad[ISC_SHA384_BLOCK_LENGTH]; + unsigned int i; + + memset(ctx->key, 0, sizeof(ctx->key)); + if (len > sizeof(ctx->key)) { + isc_sha384_t sha384ctx; + isc_sha384_init(&sha384ctx); + isc_sha384_update(&sha384ctx, key, len); + isc_sha384_final(ctx->key, &sha384ctx); + } else + memmove(ctx->key, key, len); + + isc_sha384_init(&ctx->sha384ctx); + memset(ipad, IPAD, sizeof(ipad)); + for (i = 0; i < ISC_SHA384_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + isc_sha384_update(&ctx->sha384ctx, ipad, sizeof(ipad)); +} + +void +isc_hmacsha384_invalidate(isc_hmacsha384_t *ctx) { + isc_safe_memwipe(ctx, sizeof(*ctx)); +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +isc_hmacsha384_update(isc_hmacsha384_t *ctx, const unsigned char *buf, + unsigned int len) +{ + isc_sha384_update(&ctx->sha384ctx, buf, len); +} + +/* + * Compute signature - finalize SHA384 operation and reapply SHA384. + */ +void +isc_hmacsha384_sign(isc_hmacsha384_t *ctx, unsigned char *digest, size_t len) { + unsigned char opad[ISC_SHA384_BLOCK_LENGTH]; + unsigned char newdigest[ISC_SHA384_DIGESTLENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA384_DIGESTLENGTH); + isc_sha384_final(newdigest, &ctx->sha384ctx); + + memset(opad, OPAD, sizeof(opad)); + for (i = 0; i < ISC_SHA384_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + + isc_sha384_init(&ctx->sha384ctx); + isc_sha384_update(&ctx->sha384ctx, opad, sizeof(opad)); + isc_sha384_update(&ctx->sha384ctx, newdigest, ISC_SHA384_DIGESTLENGTH); + isc_sha384_final(newdigest, &ctx->sha384ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} + +/* + * Start HMAC-SHA512 process. Initialize an sha512 context and digest the key. + */ +void +isc_hmacsha512_init(isc_hmacsha512_t *ctx, const unsigned char *key, + unsigned int len) +{ + unsigned char ipad[ISC_SHA512_BLOCK_LENGTH]; + unsigned int i; + + memset(ctx->key, 0, sizeof(ctx->key)); + if (len > sizeof(ctx->key)) { + isc_sha512_t sha512ctx; + isc_sha512_init(&sha512ctx); + isc_sha512_update(&sha512ctx, key, len); + isc_sha512_final(ctx->key, &sha512ctx); + } else + memmove(ctx->key, key, len); + + isc_sha512_init(&ctx->sha512ctx); + memset(ipad, IPAD, sizeof(ipad)); + for (i = 0; i < ISC_SHA512_BLOCK_LENGTH; i++) + ipad[i] ^= ctx->key[i]; + isc_sha512_update(&ctx->sha512ctx, ipad, sizeof(ipad)); +} + +void +isc_hmacsha512_invalidate(isc_hmacsha512_t *ctx) { + isc_safe_memwipe(ctx, sizeof(*ctx)); +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +isc_hmacsha512_update(isc_hmacsha512_t *ctx, const unsigned char *buf, + unsigned int len) +{ + isc_sha512_update(&ctx->sha512ctx, buf, len); +} + +/* + * Compute signature - finalize SHA512 operation and reapply SHA512. + */ +void +isc_hmacsha512_sign(isc_hmacsha512_t *ctx, unsigned char *digest, size_t len) { + unsigned char opad[ISC_SHA512_BLOCK_LENGTH]; + unsigned char newdigest[ISC_SHA512_DIGESTLENGTH]; + unsigned int i; + + REQUIRE(len <= ISC_SHA512_DIGESTLENGTH); + isc_sha512_final(newdigest, &ctx->sha512ctx); + + memset(opad, OPAD, sizeof(opad)); + for (i = 0; i < ISC_SHA512_BLOCK_LENGTH; i++) + opad[i] ^= ctx->key[i]; + + isc_sha512_init(&ctx->sha512ctx); + isc_sha512_update(&ctx->sha512ctx, opad, sizeof(opad)); + isc_sha512_update(&ctx->sha512ctx, newdigest, ISC_SHA512_DIGESTLENGTH); + isc_sha512_final(newdigest, &ctx->sha512ctx); + memmove(digest, newdigest, len); + isc_safe_memwipe(newdigest, sizeof(newdigest)); +} +#endif /* !ISC_PLATFORM_OPENSSLHASH */ + +/* + * Verify signature - finalize SHA1 operation and reapply SHA1, then + * compare to the supplied digest. + */ +bool +isc_hmacsha1_verify(isc_hmacsha1_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA1_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA1_DIGESTLENGTH); + isc_hmacsha1_sign(ctx, newdigest, ISC_SHA1_DIGESTLENGTH); + return (isc_safe_memequal(digest, newdigest, len)); +} + +/* + * Verify signature - finalize SHA224 operation and reapply SHA224, then + * compare to the supplied digest. + */ +bool +isc_hmacsha224_verify(isc_hmacsha224_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA224_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA224_DIGESTLENGTH); + isc_hmacsha224_sign(ctx, newdigest, ISC_SHA224_DIGESTLENGTH); + return (isc_safe_memequal(digest, newdigest, len)); +} + +/* + * Verify signature - finalize SHA256 operation and reapply SHA256, then + * compare to the supplied digest. + */ +bool +isc_hmacsha256_verify(isc_hmacsha256_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA256_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA256_DIGESTLENGTH); + isc_hmacsha256_sign(ctx, newdigest, ISC_SHA256_DIGESTLENGTH); + return (isc_safe_memequal(digest, newdigest, len)); +} + +/* + * Verify signature - finalize SHA384 operation and reapply SHA384, then + * compare to the supplied digest. + */ +bool +isc_hmacsha384_verify(isc_hmacsha384_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA384_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA384_DIGESTLENGTH); + isc_hmacsha384_sign(ctx, newdigest, ISC_SHA384_DIGESTLENGTH); + return (isc_safe_memequal(digest, newdigest, len)); +} + +/* + * Verify signature - finalize SHA512 operation and reapply SHA512, then + * compare to the supplied digest. + */ +bool +isc_hmacsha512_verify(isc_hmacsha512_t *ctx, unsigned char *digest, size_t len) { + unsigned char newdigest[ISC_SHA512_DIGESTLENGTH]; + + REQUIRE(len <= ISC_SHA512_DIGESTLENGTH); + isc_hmacsha512_sign(ctx, newdigest, ISC_SHA512_DIGESTLENGTH); + return (isc_safe_memequal(digest, newdigest, len)); +} + +/* + * Check for SHA-1 support; if it does not work, raise a fatal error. + * + * Use the first test vector from RFC 2104, with a second round using + * a too-short key. + * + * Standard use is testing 0 and expecting result true. + * Testing use is testing 1..4 and expecting result false. + */ +bool +isc_hmacsha1_check(int testing) { + isc_hmacsha1_t ctx; + unsigned char key[] = { /* 20*0x0b */ + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b + }; + unsigned char input[] = { /* "Hi There" */ + 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 + }; + unsigned char expected[] = { + 0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, + 0xe2, 0x8b, 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e, + 0xf1, 0x46, 0xbe, 0x00 + }; + unsigned char expected2[] = { + 0xa0, 0x75, 0xe0, 0x5f, 0x7f, 0x17, 0x9d, 0x34, + 0xb2, 0xab, 0xc5, 0x19, 0x8f, 0x38, 0x62, 0x36, + 0x42, 0xbd, 0xec, 0xde + }; + bool result; + + /* + * Introduce a fault for testing. + */ + switch (testing) { + case 0: + default: + break; + case 1: + key[0] ^= 0x01; + break; + case 2: + input[0] ^= 0x01; + break; + case 3: + expected[0] ^= 0x01; + break; + case 4: + expected2[0] ^= 0x01; + break; + } + + /* + * These functions do not return anything; any failure will be fatal. + */ + isc_hmacsha1_init(&ctx, key, 20U); + isc_hmacsha1_update(&ctx, input, 8U); + result = isc_hmacsha1_verify(&ctx, expected, sizeof(expected)); + if (!result) { + return (result); + } + + /* Second round using a byte key */ + isc_hmacsha1_init(&ctx, key, 1U); + isc_hmacsha1_update(&ctx, input, 8U); + return (isc_hmacsha1_verify(&ctx, expected2, sizeof(expected2))); +} diff --git a/lib/isc/ht.c b/lib/isc/ht.c new file mode 100644 index 0000000..b3c6825 --- /dev/null +++ b/lib/isc/ht.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <inttypes.h> +#include <string.h> + +#include <isc/hash.h> +#include <isc/ht.h> +#include <isc/types.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/result.h> +#include <isc/util.h> + + +typedef struct isc_ht_node isc_ht_node_t; + +#define ISC_HT_MAGIC ISC_MAGIC('H', 'T', 'a', 'b') +#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC) + +struct isc_ht_node { + void *value; + isc_ht_node_t *next; + size_t keysize; + unsigned char key[FLEXIBLE_ARRAY_MEMBER]; +}; + +struct isc_ht { + unsigned int magic; + isc_mem_t *mctx; + size_t size; + size_t mask; + unsigned int count; + isc_ht_node_t **table; +}; + +struct isc_ht_iter { + isc_ht_t *ht; + size_t i; + isc_ht_node_t *cur; +}; + +isc_result_t +isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits) { + isc_ht_t *ht = NULL; + size_t i; + + REQUIRE(htp != NULL && *htp == NULL); + REQUIRE(mctx != NULL); + REQUIRE(bits >= 1 && bits <= (sizeof(size_t)*8 - 1)); + + ht = isc_mem_get(mctx, sizeof(struct isc_ht)); + if (ht == NULL) { + return (ISC_R_NOMEMORY); + } + + ht->mctx = NULL; + isc_mem_attach(mctx, &ht->mctx); + + ht->size = ((size_t)1<<bits); + ht->mask = ((size_t)1<<bits)-1; + ht->count = 0; + + ht->table = isc_mem_get(ht->mctx, ht->size * sizeof(isc_ht_node_t*)); + if (ht->table == NULL) { + isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht)); + return (ISC_R_NOMEMORY); + } + + for (i = 0; i < ht->size; i++) { + ht->table[i] = NULL; + } + + ht->magic = ISC_HT_MAGIC; + + *htp = ht; + return (ISC_R_SUCCESS); +} + +void +isc_ht_destroy(isc_ht_t **htp) { + isc_ht_t *ht; + size_t i; + + REQUIRE(htp != NULL); + + ht = *htp; + REQUIRE(ISC_HT_VALID(ht)); + + ht->magic = 0; + + for (i = 0; i < ht->size; i++) { + isc_ht_node_t *node = ht->table[i]; + while (node != NULL) { + isc_ht_node_t *next = node->next; + ht->count--; + isc_mem_put(ht->mctx, node, + offsetof(isc_ht_node_t, key) + + node->keysize); + node = next; + } + } + + INSIST(ht->count == 0); + + isc_mem_put(ht->mctx, ht->table, ht->size * sizeof(isc_ht_node_t*)); + isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht)); + + *htp = NULL; +} + +isc_result_t +isc_ht_add(isc_ht_t *ht, const unsigned char *key, + uint32_t keysize, void *value) +{ + isc_ht_node_t *node; + uint32_t hash; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + + hash = isc_hash_function(key, keysize, true, NULL); + node = ht->table[hash & ht->mask]; + while (node != NULL) { + if (keysize == node->keysize && + memcmp(key, node->key, keysize) == 0) { + return (ISC_R_EXISTS); + } + node = node->next; + } + + node = isc_mem_get(ht->mctx, offsetof(isc_ht_node_t, key) + keysize); + if (node == NULL) + return (ISC_R_NOMEMORY); + + memmove(node->key, key, keysize); + node->keysize = keysize; + node->next = ht->table[hash & ht->mask]; + node->value = value; + + ht->count++; + ht->table[hash & ht->mask] = node; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_ht_find(const isc_ht_t *ht, const unsigned char *key, + uint32_t keysize, void **valuep) +{ + isc_ht_node_t *node; + uint32_t hash; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + REQUIRE(valuep != NULL); + + hash = isc_hash_function(key, keysize, true, NULL); + node = ht->table[hash & ht->mask]; + while (node != NULL) { + if (keysize == node->keysize && + memcmp(key, node->key, keysize) == 0) { + *valuep = node->value; + return (ISC_R_SUCCESS); + } + node = node->next; + } + + return (ISC_R_NOTFOUND); +} + +isc_result_t +isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize) { + isc_ht_node_t *node, *prev; + uint32_t hash; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + + prev = NULL; + hash = isc_hash_function(key, keysize, true, NULL); + node = ht->table[hash & ht->mask]; + while (node != NULL) { + if (keysize == node->keysize && + memcmp(key, node->key, keysize) == 0) { + if (prev == NULL) + ht->table[hash & ht->mask] = node->next; + else + prev->next = node->next; + isc_mem_put(ht->mctx, node, + offsetof(isc_ht_node_t, key) + + node->keysize); + ht->count--; + + return (ISC_R_SUCCESS); + } + + prev = node; + node = node->next; + } + return (ISC_R_NOTFOUND); +} + +isc_result_t +isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) { + isc_ht_iter_t *it; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(itp != NULL && *itp == NULL); + + it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t)); + if (it == NULL) + return (ISC_R_NOMEMORY); + + it->ht = ht; + it->i = 0; + it->cur = NULL; + + *itp = it; + + return (ISC_R_SUCCESS); +} + +void +isc_ht_iter_destroy(isc_ht_iter_t **itp) { + isc_ht_iter_t *it; + isc_ht_t *ht; + + REQUIRE(itp != NULL && *itp != NULL); + + it = *itp; + ht = it->ht; + isc_mem_put(ht->mctx, it, sizeof(isc_ht_iter_t)); + + *itp = NULL; +} + +isc_result_t +isc_ht_iter_first(isc_ht_iter_t *it) { + REQUIRE(it != NULL); + + it->i = 0; + while (it->i < it->ht->size && it->ht->table[it->i] == NULL) + it->i++; + + if (it->i == it->ht->size) + return (ISC_R_NOMORE); + + it->cur = it->ht->table[it->i]; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_ht_iter_next(isc_ht_iter_t *it) { + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); + + it->cur = it->cur->next; + if (it->cur == NULL) { + do { + it->i++; + } while (it->i < it->ht->size && it->ht->table[it->i] == NULL); + if (it->i >= it->ht->size) + return (ISC_R_NOMORE); + it->cur = it->ht->table[it->i]; + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) { + isc_result_t result = ISC_R_SUCCESS; + isc_ht_node_t *to_delete = NULL; + isc_ht_node_t *prev = NULL; + isc_ht_node_t *node = NULL; + uint32_t hash; + isc_ht_t *ht; + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); + to_delete = it->cur; + ht = it->ht; + + it->cur = it->cur->next; + if (it->cur == NULL) { + do { + it->i++; + } while (it->i < ht->size && ht->table[it->i] == NULL); + if (it->i >= ht->size) + result = ISC_R_NOMORE; + else + it->cur = ht->table[it->i]; + } + + hash = isc_hash_function(to_delete->key, to_delete->keysize, true, + NULL); + node = ht->table[hash & ht->mask]; + while (node != to_delete) { + prev = node; + node = node->next; + INSIST(node != NULL); + } + + if (prev == NULL) + ht->table[hash & ht->mask] = node->next; + else + prev->next = node->next; + isc_mem_put(ht->mctx, node, + offsetof(isc_ht_node_t, key) + node->keysize); + ht->count--; + + return (result); +} + +void +isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) { + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); + *valuep = it->cur->value; +} + +void +isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize) +{ + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); + *key = it->cur->key; + *keysize = it->cur->keysize; +} + +unsigned int +isc_ht_count(isc_ht_t *ht) { + REQUIRE(ISC_HT_VALID(ht)); + + return(ht->count); +} diff --git a/lib/isc/httpd.c b/lib/isc/httpd.c new file mode 100644 index 0000000..06e7a8b --- /dev/null +++ b/lib/isc/httpd.c @@ -0,0 +1,1273 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> +#include <string.h> + +#include <isc/buffer.h> +#include <isc/httpd.h> +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/socket.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/time.h> +#include <isc/util.h> + +#ifdef HAVE_ZLIB +#include <zlib.h> +#endif + +/*% + * TODO: + * + * o Put in better checks to make certain things are passed in correctly. + * This includes a magic number for externally-visible structures, + * checking for NULL-ness before dereferencing, etc. + * o Make the URL processing external functions which will fill-in a buffer + * structure we provide, or return an error and we will render a generic + * page and close the client. + */ + +#define MSHUTTINGDOWN(cm) ((cm->flags & ISC_HTTPDMGR_FLAGSHUTTINGDOWN) != 0) +#define MSETSHUTTINGDOWN(cm) (cm->flags |= ISC_HTTPDMGR_FLAGSHUTTINGDOWN) + +#ifdef DEBUG_HTTPD +#define ENTER(x) do { fprintf(stderr, "ENTER %s\n", (x)); } while (0) +#define EXIT(x) do { fprintf(stderr, "EXIT %s\n", (x)); } while (0) +#define NOTICE(x) do { fprintf(stderr, "NOTICE %s\n", (x)); } while (0) +#else +#define ENTER(x) do { } while(0) +#define EXIT(x) do { } while(0) +#define NOTICE(x) do { } while(0) +#endif + +#define HTTP_RECVLEN 1024 +#define HTTP_SENDGROW 1024 +#define HTTP_SEND_MAXLEN 10240 + +#define HTTPD_CLOSE 0x0001 /* Got a Connection: close header */ +#define HTTPD_FOUNDHOST 0x0002 /* Got a Host: header */ +#define HTTPD_KEEPALIVE 0x0004 /* Got a Connection: Keep-Alive */ +#define HTTPD_ACCEPT_DEFLATE 0x0008 + +/*% http client */ +struct isc_httpd { + isc_httpdmgr_t *mgr; /*%< our parent */ + ISC_LINK(isc_httpd_t) link; + unsigned int state; + isc_socket_t *sock; + + /*% + * Received data state. + */ + char recvbuf[HTTP_RECVLEN]; /*%< receive buffer */ + uint32_t recvlen; /*%< length recv'd */ + char *headers; /*%< set in process_request() */ + unsigned int method; + char *url; + char *querystring; + char *protocol; + + /* + * Flags on the httpd client. + */ + int flags; + + /*% + * Transmit data state. + * + * This is the data buffer we will transmit. + * + * This free function pointer is filled in by the rendering function + * we call. The free function is called after the data is transmitted + * to the client. + * + * The bufflist is the list of buffers we are currently transmitting. + * The headerbuffer is where we render our headers to. If we run out of + * space when rendering a header, we will change the size of our + * buffer. We will not free it until we are finished, and will + * allocate an additional HTTP_SENDGROW bytes per header space grow. + * + * We currently use three buffers total, one for the headers (which + * we manage), another for the client to fill in (which it manages, + * it provides the space for it, etc) -- we will pass that buffer + * structure back to the caller, who is responsible for managing the + * space it may have allocated as backing store for it. This second + * buffer is bodybuffer, and we only allocate the buffer itself, not + * the backing store. + * The third buffer is compbuffer, managed by us, that contains the + * compressed HTTP data, if compression is used. + * + */ + isc_bufferlist_t bufflist; + isc_buffer_t headerbuffer; + isc_buffer_t compbuffer; + + const char *mimetype; + unsigned int retcode; + const char *retmsg; + isc_buffer_t bodybuffer; + isc_httpdfree_t *freecb; + void *freecb_arg; +}; + +/*% lightweight socket manager for httpd output */ +struct isc_httpdmgr { + isc_mem_t *mctx; + isc_socket_t *sock; /*%< listening socket */ + isc_task_t *task; /*%< owning task */ + isc_timermgr_t *timermgr; + + isc_httpdclientok_t *client_ok; /*%< client validator */ + isc_httpdondestroy_t *ondestroy; /*%< cleanup callback */ + void *cb_arg; /*%< argument for the above */ + + unsigned int flags; + ISC_LIST(isc_httpd_t) running; /*%< running clients */ + + isc_mutex_t lock; + + ISC_LIST(isc_httpdurl_t) urls; /*%< urls we manage */ + isc_httpdaction_t *render_404; + isc_httpdaction_t *render_500; +}; + +/*% + * HTTP methods. + */ +#define ISC_HTTPD_METHODUNKNOWN 0 +#define ISC_HTTPD_METHODGET 1 +#define ISC_HTTPD_METHODPOST 2 + +/*% + * Client states. + * + * _IDLE The client is not doing anything at all. This state should + * only occur just after creation, and just before being + * destroyed. + * + * _RECV The client is waiting for data after issuing a socket recv(). + * + * _RECVDONE Data has been received, and is being processed. + * + * _SEND All data for a response has completed, and a reply was + * sent via a socket send() call. + * + * _SENDDONE Send is completed. + * + * Badly formatted state table: + * + * IDLE -> RECV when client has a recv() queued. + * + * RECV -> RECVDONE when recvdone event received. + * + * RECVDONE -> SEND if the data for a reply is at hand. + * + * SEND -> RECV when a senddone event was received. + * + * At any time -> RECV on error. If RECV fails, the client will + * self-destroy, closing the socket and freeing memory. + */ +#define ISC_HTTPD_STATEIDLE 0 +#define ISC_HTTPD_STATERECV 1 +#define ISC_HTTPD_STATERECVDONE 2 +#define ISC_HTTPD_STATESEND 3 +#define ISC_HTTPD_STATESENDDONE 4 + +#define ISC_HTTPD_ISRECV(c) ((c)->state == ISC_HTTPD_STATERECV) +#define ISC_HTTPD_ISRECVDONE(c) ((c)->state == ISC_HTTPD_STATERECVDONE) +#define ISC_HTTPD_ISSEND(c) ((c)->state == ISC_HTTPD_STATESEND) +#define ISC_HTTPD_ISSENDDONE(c) ((c)->state == ISC_HTTPD_STATESENDDONE) + +/*% + * Overall magic test that means we're not idle. + */ +#define ISC_HTTPD_SETRECV(c) ((c)->state = ISC_HTTPD_STATERECV) +#define ISC_HTTPD_SETRECVDONE(c) ((c)->state = ISC_HTTPD_STATERECVDONE) +#define ISC_HTTPD_SETSEND(c) ((c)->state = ISC_HTTPD_STATESEND) +#define ISC_HTTPD_SETSENDDONE(c) ((c)->state = ISC_HTTPD_STATESENDDONE) + +static void isc_httpd_accept(isc_task_t *, isc_event_t *); +static void isc_httpd_recvdone(isc_task_t *, isc_event_t *); +static void isc_httpd_senddone(isc_task_t *, isc_event_t *); +static void destroy_client(isc_httpd_t **); +static isc_result_t process_request(isc_httpd_t *, int); +static void httpdmgr_destroy(isc_httpdmgr_t *); +static isc_result_t grow_headerspace(isc_httpd_t *); +static void reset_client(isc_httpd_t *httpd); + +static isc_httpdaction_t render_404; +static isc_httpdaction_t render_500; + +static void (*finishhook)(void) = NULL; + +static void +destroy_client(isc_httpd_t **httpdp) { + isc_httpd_t *httpd = *httpdp; + isc_httpdmgr_t *httpdmgr = httpd->mgr; + isc_region_t r; + + *httpdp = NULL; + + LOCK(&httpdmgr->lock); + + isc_socket_detach(&httpd->sock); + ISC_LIST_UNLINK(httpdmgr->running, httpd, link); + + isc_buffer_region(&httpd->headerbuffer, &r); + if (r.length > 0) { + isc_mem_put(httpdmgr->mctx, r.base, r.length); + } + + isc_buffer_region(&httpd->compbuffer, &r); + if (r.length > 0) { + isc_mem_put(httpdmgr->mctx, r.base, r.length); + } + + isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t)); + + UNLOCK(&httpdmgr->lock); + + if (finishhook != NULL) + finishhook(); + + httpdmgr_destroy(httpdmgr); +} + +isc_result_t +isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task, + isc_httpdclientok_t *client_ok, + isc_httpdondestroy_t *ondestroy, void *cb_arg, + isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdmgrp) +{ + isc_result_t result; + isc_httpdmgr_t *httpdmgr; + + REQUIRE(mctx != NULL); + REQUIRE(sock != NULL); + REQUIRE(task != NULL); + REQUIRE(tmgr != NULL); + REQUIRE(httpdmgrp != NULL && *httpdmgrp == NULL); + + httpdmgr = isc_mem_get(mctx, sizeof(isc_httpdmgr_t)); + if (httpdmgr == NULL) + return (ISC_R_NOMEMORY); + + result = isc_mutex_init(&httpdmgr->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t)); + return (result); + } + httpdmgr->mctx = NULL; + isc_mem_attach(mctx, &httpdmgr->mctx); + httpdmgr->sock = NULL; + isc_socket_attach(sock, &httpdmgr->sock); + httpdmgr->task = NULL; + isc_task_attach(task, &httpdmgr->task); + httpdmgr->timermgr = tmgr; /* XXXMLG no attach function? */ + httpdmgr->client_ok = client_ok; + httpdmgr->ondestroy = ondestroy; + httpdmgr->cb_arg = cb_arg; + httpdmgr->flags = 0; + + ISC_LIST_INIT(httpdmgr->running); + ISC_LIST_INIT(httpdmgr->urls); + + /* XXXMLG ignore errors on isc_socket_listen() */ + result = isc_socket_listen(sock, SOMAXCONN); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_socket_listen() failed: %s", + isc_result_totext(result)); + goto cleanup; + } + + (void)isc_socket_filter(sock, "httpready"); + + result = isc_socket_accept(sock, task, isc_httpd_accept, httpdmgr); + if (result != ISC_R_SUCCESS) + goto cleanup; + + httpdmgr->render_404 = render_404; + httpdmgr->render_500 = render_500; + + *httpdmgrp = httpdmgr; + return (ISC_R_SUCCESS); + + cleanup: + isc_task_detach(&httpdmgr->task); + isc_socket_detach(&httpdmgr->sock); + isc_mem_detach(&httpdmgr->mctx); + (void)isc_mutex_destroy(&httpdmgr->lock); + isc_mem_put(mctx, httpdmgr, sizeof(isc_httpdmgr_t)); + return (result); +} + +static void +httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) { + isc_mem_t *mctx; + isc_httpdurl_t *url; + + ENTER("httpdmgr_destroy"); + + LOCK(&httpdmgr->lock); + + if (!MSHUTTINGDOWN(httpdmgr)) { + NOTICE("httpdmgr_destroy not shutting down yet"); + UNLOCK(&httpdmgr->lock); + return; + } + + /* + * If all clients are not shut down, don't do anything yet. + */ + if (!ISC_LIST_EMPTY(httpdmgr->running)) { + NOTICE("httpdmgr_destroy clients still active"); + UNLOCK(&httpdmgr->lock); + return; + } + + NOTICE("httpdmgr_destroy detaching socket, task, and timermgr"); + + isc_socket_detach(&httpdmgr->sock); + isc_task_detach(&httpdmgr->task); + httpdmgr->timermgr = NULL; + + /* + * Clear out the list of all actions we know about. Just free the + * memory. + */ + url = ISC_LIST_HEAD(httpdmgr->urls); + while (url != NULL) { + isc_mem_free(httpdmgr->mctx, url->url); + ISC_LIST_UNLINK(httpdmgr->urls, url, link); + isc_mem_put(httpdmgr->mctx, url, sizeof(isc_httpdurl_t)); + url = ISC_LIST_HEAD(httpdmgr->urls); + } + + UNLOCK(&httpdmgr->lock); + (void)isc_mutex_destroy(&httpdmgr->lock); + + if (httpdmgr->ondestroy != NULL) + (httpdmgr->ondestroy)(httpdmgr->cb_arg); + + mctx = httpdmgr->mctx; + isc_mem_putanddetach(&mctx, httpdmgr, sizeof(isc_httpdmgr_t)); + + EXIT("httpdmgr_destroy"); +} + +#define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen) +#define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN) + +/* + * Look for the given header in headers. + * If value is specified look for it terminated with a character in eov. + */ +static bool +have_header(isc_httpd_t *httpd, const char *header, const char *value, + const char *eov) +{ + char *cr, *nl, *h; + size_t hlen, vlen = 0; + + h = httpd->headers; + hlen = strlen(header); + if (value != NULL) { + INSIST(eov != NULL); + vlen = strlen(value); + } + + for (;;) { + if (strncasecmp(h, header, hlen) != 0) { + /* + * Skip to next line; + */ + cr = strchr(h, '\r'); + if (cr != NULL && cr[1] == '\n') + cr++; + nl = strchr(h, '\n'); + + /* last header? */ + h = cr; + if (h == NULL || (nl != NULL && nl < h)) + h = nl; + if (h == NULL) + return (false); + h++; + continue; + } + + if (value == NULL) + return (true); + + /* + * Skip optional leading white space. + */ + h += hlen; + while (*h == ' ' || *h == '\t') + h++; + /* + * Terminate token search on NULL or EOL. + */ + while (*h != 0 && *h != '\r' && *h != '\n') { + if (strncasecmp(h, value, vlen) == 0) + if (strchr(eov, h[vlen]) != NULL) + return (true); + /* + * Skip to next token. + */ + h += strcspn(h, eov); + if (h[0] == '\r' && h[1] == '\n') + h++; + if (h[0] != 0) + h++; + } + return (false); + } +} + +static isc_result_t +process_request(isc_httpd_t *httpd, int length) { + char *s; + char *p; + int delim; + + ENTER("request"); + + httpd->recvlen += length; + + httpd->recvbuf[httpd->recvlen] = 0; + httpd->headers = NULL; + + /* + * If we don't find a blank line in our buffer, return that we need + * more data. + */ + s = strstr(httpd->recvbuf, "\r\n\r\n"); + delim = 2; + if (s == NULL) { + s = strstr(httpd->recvbuf, "\n\n"); + delim = 1; + } + if (s == NULL) + return (ISC_R_NOTFOUND); + + /* + * NUL terminate request at the blank line. + */ + s[delim] = 0; + + /* + * Determine if this is a POST or GET method. Any other values will + * cause an error to be returned. + */ + if (strncmp(httpd->recvbuf, "GET ", 4) == 0) { + httpd->method = ISC_HTTPD_METHODGET; + p = httpd->recvbuf + 4; + } else if (strncmp(httpd->recvbuf, "POST ", 5) == 0) { + httpd->method = ISC_HTTPD_METHODPOST; + p = httpd->recvbuf + 5; + } else { + return (ISC_R_RANGE); + } + + /* + * From now on, p is the start of our buffer. + */ + + /* + * Extract the URL. + */ + s = p; + while (LENGTHOK(s) && BUFLENOK(s) && + (*s != '\n' && *s != '\r' && *s != '\0' && *s != ' ')) + s++; + if (!LENGTHOK(s)) + return (ISC_R_NOTFOUND); + if (!BUFLENOK(s)) + return (ISC_R_NOMEMORY); + *s = 0; + + /* + * Make the URL relative. + */ + if ((strncmp(p, "http:/", 6) == 0) + || (strncmp(p, "https:/", 7) == 0)) { + /* Skip first / */ + while (*p != '/' && *p != 0) + p++; + if (*p == 0) + return (ISC_R_RANGE); + p++; + /* Skip second / */ + while (*p != '/' && *p != 0) + p++; + if (*p == 0) + return (ISC_R_RANGE); + p++; + /* Find third / */ + while (*p != '/' && *p != 0) + p++; + if (*p == 0) { + p--; + *p = '/'; + } + } + + httpd->url = p; + p = s + 1; + s = p; + + /* + * Now, see if there is a ? mark in the URL. If so, this is + * part of the query string, and we will split it from the URL. + */ + httpd->querystring = strchr(httpd->url, '?'); + if (httpd->querystring != NULL) { + *(httpd->querystring) = 0; + httpd->querystring++; + } + + /* + * Extract the HTTP/1.X protocol. We will bounce on anything but + * HTTP/1.0 or HTTP/1.1 for now. + */ + while (LENGTHOK(s) && BUFLENOK(s) && + (*s != '\n' && *s != '\r' && *s != '\0')) + s++; + if (!LENGTHOK(s)) + return (ISC_R_NOTFOUND); + if (!BUFLENOK(s)) + return (ISC_R_NOMEMORY); + /* + * Check that we have the expected eol delimiter. + */ + if (strncmp(s, delim == 1 ? "\n" : "\r\n", delim) != 0) + return (ISC_R_RANGE); + *s = 0; + if ((strncmp(p, "HTTP/1.0", 8) != 0) + && (strncmp(p, "HTTP/1.1", 8) != 0)) + return (ISC_R_RANGE); + httpd->protocol = p; + p = s + delim; /* skip past eol */ + s = p; + + httpd->headers = s; + + if (have_header(httpd, "Connection:", "close", ", \t\r\n")) + httpd->flags |= HTTPD_CLOSE; + + if (have_header(httpd, "Host:", NULL, NULL)) + httpd->flags |= HTTPD_FOUNDHOST; + + if (strncmp(httpd->protocol, "HTTP/1.0", 8) == 0) { + if (have_header(httpd, "Connection:", "Keep-Alive", + ", \t\r\n")) + httpd->flags |= HTTPD_KEEPALIVE; + else + httpd->flags |= HTTPD_CLOSE; + } + + /* + * Check for Accept-Encoding: + */ +#ifdef HAVE_ZLIB + if (have_header(httpd, "Accept-Encoding:", "deflate", ";, \t\r\n")) + httpd->flags |= HTTPD_ACCEPT_DEFLATE; +#endif + + /* + * Standards compliance hooks here. + */ + if (strcmp(httpd->protocol, "HTTP/1.1") == 0 + && ((httpd->flags & HTTPD_FOUNDHOST) == 0)) + return (ISC_R_RANGE); + + EXIT("request"); + + return (ISC_R_SUCCESS); +} + +static void +isc_httpd_accept(isc_task_t *task, isc_event_t *ev) { + isc_result_t result; + isc_httpdmgr_t *httpdmgr = ev->ev_arg; + isc_httpd_t *httpd; + isc_region_t r; + isc_socket_newconnev_t *nev = (isc_socket_newconnev_t *)ev; + isc_sockaddr_t peeraddr; + char *headerdata; + + ENTER("accept"); + + LOCK(&httpdmgr->lock); + if (MSHUTTINGDOWN(httpdmgr)) { + NOTICE("accept shutting down, goto out"); + goto out; + } + + if (nev->result == ISC_R_CANCELED) { + NOTICE("accept canceled, goto out"); + goto out; + } + + if (nev->result != ISC_R_SUCCESS) { + /* XXXMLG log failure */ + NOTICE("accept returned failure, goto requeue"); + goto requeue; + } + + (void)isc_socket_getpeername(nev->newsocket, &peeraddr); + if (httpdmgr->client_ok != NULL && + !(httpdmgr->client_ok)(&peeraddr, httpdmgr->cb_arg)) { + isc_socket_detach(&nev->newsocket); + goto requeue; + } + + httpd = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpd_t)); + if (httpd == NULL) { + /* XXXMLG log failure */ + NOTICE("accept failed to allocate memory, goto requeue"); + isc_socket_detach(&nev->newsocket); + goto requeue; + } + + httpd->mgr = httpdmgr; + ISC_LINK_INIT(httpd, link); + ISC_LIST_APPEND(httpdmgr->running, httpd, link); + ISC_HTTPD_SETRECV(httpd); + httpd->sock = nev->newsocket; + isc_socket_setname(httpd->sock, "httpd", NULL); + httpd->flags = 0; + + /* + * Initialize the buffer for our headers. + */ + headerdata = isc_mem_get(httpdmgr->mctx, HTTP_SENDGROW); + if (headerdata == NULL) { + isc_mem_put(httpdmgr->mctx, httpd, sizeof(isc_httpd_t)); + isc_socket_detach(&nev->newsocket); + goto requeue; + } + isc_buffer_init(&httpd->headerbuffer, headerdata, HTTP_SENDGROW); + + ISC_LIST_INIT(httpd->bufflist); + + isc_buffer_initnull(&httpd->compbuffer); + isc_buffer_initnull(&httpd->bodybuffer); + reset_client(httpd); + + r.base = (unsigned char *)httpd->recvbuf; + r.length = HTTP_RECVLEN - 1; + result = isc_socket_recv(httpd->sock, &r, 1, task, isc_httpd_recvdone, + httpd); + /* FIXME!!! */ + POST(result); + NOTICE("accept queued recv on socket"); + + requeue: + result = isc_socket_accept(httpdmgr->sock, task, isc_httpd_accept, + httpdmgr); + if (result != ISC_R_SUCCESS) { + /* XXXMLG what to do? Log failure... */ + NOTICE("accept could not reaccept due to failure"); + } + + out: + UNLOCK(&httpdmgr->lock); + + httpdmgr_destroy(httpdmgr); + + isc_event_free(&ev); + + EXIT("accept"); +} + +static isc_result_t +render_404(const char *url, isc_httpdurl_t *urlinfo, + const char *querystring, const char *headers, void *arg, + unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) +{ + static char msg[] = "No such URL.\r\n"; + + UNUSED(url); + UNUSED(urlinfo); + UNUSED(querystring); + UNUSED(headers); + UNUSED(arg); + + *retcode = 404; + *retmsg = "No such URL"; + *mimetype = "text/plain"; + isc_buffer_reinit(b, msg, strlen(msg)); + isc_buffer_add(b, strlen(msg)); + *freecb = NULL; + *freecb_args = NULL; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +render_500(const char *url, isc_httpdurl_t *urlinfo, + const char *querystring, const char *headers, void *arg, + unsigned int *retcode, const char **retmsg, + const char **mimetype, isc_buffer_t *b, + isc_httpdfree_t **freecb, void **freecb_args) +{ + static char msg[] = "Internal server failure.\r\n"; + + UNUSED(url); + UNUSED(urlinfo); + UNUSED(querystring); + UNUSED(headers); + UNUSED(arg); + + *retcode = 500; + *retmsg = "Internal server failure"; + *mimetype = "text/plain"; + isc_buffer_reinit(b, msg, strlen(msg)); + isc_buffer_add(b, strlen(msg)); + *freecb = NULL; + *freecb_args = NULL; + + return (ISC_R_SUCCESS); +} + +#ifdef HAVE_ZLIB +/*%< + * Reallocates compbuffer to size, does nothing if compbuffer is already + * larger than size. + * + * Requires: + *\li httpd a valid isc_httpd_t object + * + * Returns: + *\li #ISC_R_SUCCESS -- all is well. + *\li #ISC_R_NOMEMORY -- not enough memory to extend buffer + */ +static isc_result_t +alloc_compspace(isc_httpd_t *httpd, unsigned int size) { + char *newspace; + isc_region_t r; + + isc_buffer_region(&httpd->compbuffer, &r); + if (size < r.length) { + return (ISC_R_SUCCESS); + } + + newspace = isc_mem_get(httpd->mgr->mctx, size); + if (newspace == NULL) + return (ISC_R_NOMEMORY); + isc_buffer_reinit(&httpd->compbuffer, newspace, size); + + if (r.base != NULL) { + isc_mem_put(httpd->mgr->mctx, r.base, r.length); + } + + return (ISC_R_SUCCESS); +} + +/*%< + * Tries to compress httpd->bodybuffer to httpd->compbuffer, extending it + * if necessary. + * + * Requires: + *\li httpd a valid isc_httpd_t object + * + * Returns: + *\li #ISC_R_SUCCESS -- all is well. + *\li #ISC_R_NOMEMORY -- not enough memory to compress data + *\li #ISC_R_FAILURE -- error during compression or compressed + * data would be larger than input data + */ +static isc_result_t +isc_httpd_compress(isc_httpd_t *httpd) { + z_stream zstr; + isc_region_t r; + isc_result_t result; + int ret; + int inputlen; + + inputlen = isc_buffer_usedlength(&httpd->bodybuffer); + result = alloc_compspace(httpd, inputlen); + if (result != ISC_R_SUCCESS) { + return result; + } + isc_buffer_region(&httpd->compbuffer, &r); + + /* + * We're setting output buffer size to input size so it fails if the + * compressed data size would be bigger than the input size. + */ + memset(&zstr, 0, sizeof(zstr)); + zstr.total_in = zstr.avail_in = + zstr.total_out = zstr.avail_out = inputlen; + + zstr.next_in = isc_buffer_base(&httpd->bodybuffer); + zstr.next_out = r.base; + + ret = deflateInit(&zstr, Z_DEFAULT_COMPRESSION); + if (ret == Z_OK) { + ret = deflate(&zstr, Z_FINISH); + } + deflateEnd(&zstr); + if (ret == Z_STREAM_END) { + isc_buffer_add(&httpd->compbuffer, inputlen - zstr.avail_out); + return (ISC_R_SUCCESS); + } else { + return (ISC_R_FAILURE); + } +} +#endif + +static void +isc_httpd_recvdone(isc_task_t *task, isc_event_t *ev) { + isc_region_t r; + isc_result_t result; + isc_httpd_t *httpd = ev->ev_arg; + isc_socketevent_t *sev = (isc_socketevent_t *)ev; + isc_httpdurl_t *url; + isc_time_t now; + bool is_compressed = false; + char datebuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; + + ENTER("recv"); + + INSIST(ISC_HTTPD_ISRECV(httpd)); + + if (sev->result != ISC_R_SUCCESS) { + NOTICE("recv destroying client"); + destroy_client(&httpd); + goto out; + } + + result = process_request(httpd, sev->n); + if (result == ISC_R_NOTFOUND) { + if (httpd->recvlen >= HTTP_RECVLEN - 1) { + destroy_client(&httpd); + goto out; + } + r.base = (unsigned char *)httpd->recvbuf + httpd->recvlen; + r.length = HTTP_RECVLEN - httpd->recvlen - 1; + /* check return code? */ + (void)isc_socket_recv(httpd->sock, &r, 1, task, + isc_httpd_recvdone, httpd); + goto out; + } else if (result != ISC_R_SUCCESS) { + destroy_client(&httpd); + goto out; + } + + ISC_HTTPD_SETSEND(httpd); + + /* + * XXXMLG Call function here. Provide an add-header function + * which will append the common headers to a response we generate. + */ + isc_buffer_initnull(&httpd->bodybuffer); + isc_time_now(&now); + isc_time_formathttptimestamp(&now, datebuf, sizeof(datebuf)); + url = ISC_LIST_HEAD(httpd->mgr->urls); + while (url != NULL) { + if (strcmp(httpd->url, url->url) == 0) + break; + url = ISC_LIST_NEXT(url, link); + } + if (url == NULL) + result = httpd->mgr->render_404(httpd->url, NULL, + httpd->querystring, + NULL, NULL, + &httpd->retcode, + &httpd->retmsg, + &httpd->mimetype, + &httpd->bodybuffer, + &httpd->freecb, + &httpd->freecb_arg); + else + result = url->action(httpd->url, url, + httpd->querystring, + httpd->headers, + url->action_arg, + &httpd->retcode, &httpd->retmsg, + &httpd->mimetype, &httpd->bodybuffer, + &httpd->freecb, &httpd->freecb_arg); + if (result != ISC_R_SUCCESS) { + result = httpd->mgr->render_500(httpd->url, url, + httpd->querystring, + NULL, NULL, + &httpd->retcode, + &httpd->retmsg, + &httpd->mimetype, + &httpd->bodybuffer, + &httpd->freecb, + &httpd->freecb_arg); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + } + +#ifdef HAVE_ZLIB + if (httpd->flags & HTTPD_ACCEPT_DEFLATE) { + result = isc_httpd_compress(httpd); + if (result == ISC_R_SUCCESS) { + is_compressed = true; + } + } +#endif + + isc_httpd_response(httpd); + if ((httpd->flags & HTTPD_KEEPALIVE) != 0) + isc_httpd_addheader(httpd, "Connection", "Keep-Alive"); + isc_httpd_addheader(httpd, "Content-Type", httpd->mimetype); + isc_httpd_addheader(httpd, "Date", datebuf); + isc_httpd_addheader(httpd, "Expires", datebuf); + + if (url != NULL && url->isstatic) { + char loadbuf[ISC_FORMATHTTPTIMESTAMP_SIZE]; + isc_time_formathttptimestamp(&url->loadtime, + loadbuf, sizeof(loadbuf)); + isc_httpd_addheader(httpd, "Last-Modified", loadbuf); + isc_httpd_addheader(httpd, "Cache-Control: public", NULL); + } else { + isc_httpd_addheader(httpd, "Last-Modified", datebuf); + isc_httpd_addheader(httpd, "Pragma: no-cache", NULL); + isc_httpd_addheader(httpd, "Cache-Control: no-cache", NULL); + } + + isc_httpd_addheader(httpd, "Server: libisc", NULL); + + if (is_compressed == true) { + isc_httpd_addheader(httpd, "Content-Encoding", "deflate"); + isc_httpd_addheaderuint(httpd, "Content-Length", + isc_buffer_usedlength(&httpd->compbuffer)); + } else { + isc_httpd_addheaderuint(httpd, "Content-Length", + isc_buffer_usedlength(&httpd->bodybuffer)); + } + + isc_httpd_endheaders(httpd); /* done */ + + ISC_LIST_APPEND(httpd->bufflist, &httpd->headerbuffer, link); + /* + * Link the data buffer into our send queue, should we have any data + * rendered into it. If no data is present, we won't do anything + * with the buffer. + */ + if (is_compressed == true) { + ISC_LIST_APPEND(httpd->bufflist, &httpd->compbuffer, link); + } else { + if (isc_buffer_length(&httpd->bodybuffer) > 0) { + ISC_LIST_APPEND(httpd->bufflist, &httpd->bodybuffer, link); + } + } + + /* check return code? */ + (void)isc_socket_sendv(httpd->sock, &httpd->bufflist, task, + isc_httpd_senddone, httpd); + + out: + isc_event_free(&ev); + EXIT("recv"); +} + +void +isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdmgrp) { + isc_httpdmgr_t *httpdmgr; + isc_httpd_t *httpd; + httpdmgr = *httpdmgrp; + *httpdmgrp = NULL; + + ENTER("isc_httpdmgr_shutdown"); + + LOCK(&httpdmgr->lock); + + MSETSHUTTINGDOWN(httpdmgr); + + isc_socket_cancel(httpdmgr->sock, httpdmgr->task, ISC_SOCKCANCEL_ALL); + + httpd = ISC_LIST_HEAD(httpdmgr->running); + while (httpd != NULL) { + isc_socket_cancel(httpd->sock, httpdmgr->task, + ISC_SOCKCANCEL_ALL); + httpd = ISC_LIST_NEXT(httpd, link); + } + + UNLOCK(&httpdmgr->lock); + + EXIT("isc_httpdmgr_shutdown"); +} + +static isc_result_t +grow_headerspace(isc_httpd_t *httpd) { + char *newspace; + unsigned int newlen; + isc_region_t r; + + isc_buffer_region(&httpd->headerbuffer, &r); + newlen = r.length + HTTP_SENDGROW; + if (newlen > HTTP_SEND_MAXLEN) + return (ISC_R_NOSPACE); + + newspace = isc_mem_get(httpd->mgr->mctx, newlen); + if (newspace == NULL) + return (ISC_R_NOMEMORY); + + isc_buffer_reinit(&httpd->headerbuffer, newspace, newlen); + + isc_mem_put(httpd->mgr->mctx, r.base, r.length); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_httpd_response(isc_httpd_t *httpd) { + isc_result_t result; + unsigned int needlen; + + needlen = strlen(httpd->protocol) + 1; /* protocol + space */ + needlen += 3 + 1; /* room for response code, always 3 bytes */ + needlen += strlen(httpd->retmsg) + 2; /* return msg + CRLF */ + + while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { + result = grow_headerspace(httpd); + if (result != ISC_R_SUCCESS) + return (result); + } + + snprintf(isc_buffer_used(&httpd->headerbuffer), + (int)isc_buffer_availablelength(&httpd->headerbuffer), + "%s %03u %s\r\n", httpd->protocol, httpd->retcode, + httpd->retmsg); + isc_buffer_add(&httpd->headerbuffer, needlen); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_httpd_addheader(isc_httpd_t *httpd, const char *name, + const char *val) +{ + isc_result_t result; + unsigned int needlen; + + needlen = strlen(name); /* name itself */ + if (val != NULL) + needlen += 2 + strlen(val); /* :<space> and val */ + needlen += 2; /* CRLF */ + + while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { + result = grow_headerspace(httpd); + if (result != ISC_R_SUCCESS) + return (result); + } + + if (val != NULL) + snprintf(isc_buffer_used(&httpd->headerbuffer), + isc_buffer_availablelength(&httpd->headerbuffer), + "%s: %s\r\n", name, val); + else + snprintf(isc_buffer_used(&httpd->headerbuffer), + isc_buffer_availablelength(&httpd->headerbuffer), + "%s\r\n", name); + + isc_buffer_add(&httpd->headerbuffer, needlen); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_httpd_endheaders(isc_httpd_t *httpd) { + isc_result_t result; + + while (isc_buffer_availablelength(&httpd->headerbuffer) < 2) { + result = grow_headerspace(httpd); + if (result != ISC_R_SUCCESS) + return (result); + } + + snprintf(isc_buffer_used(&httpd->headerbuffer), + isc_buffer_availablelength(&httpd->headerbuffer), "\r\n"); + isc_buffer_add(&httpd->headerbuffer, 2); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val) { + isc_result_t result; + unsigned int needlen; + char buf[sizeof "18446744073709551616"]; + + snprintf(buf, sizeof(buf), "%d", val); + + needlen = strlen(name); /* name itself */ + needlen += 2 + strlen(buf); /* :<space> and val */ + needlen += 2; /* CRLF */ + + while (isc_buffer_availablelength(&httpd->headerbuffer) < needlen) { + result = grow_headerspace(httpd); + if (result != ISC_R_SUCCESS) + return (result); + } + + snprintf(isc_buffer_used(&httpd->headerbuffer), + isc_buffer_availablelength(&httpd->headerbuffer), + "%s: %s\r\n", name, buf); + + isc_buffer_add(&httpd->headerbuffer, needlen); + + return (ISC_R_SUCCESS); +} + +static void +isc_httpd_senddone(isc_task_t *task, isc_event_t *ev) { + isc_httpd_t *httpd = ev->ev_arg; + isc_region_t r; + isc_socketevent_t *sev = (isc_socketevent_t *)ev; + + ENTER("senddone"); + INSIST(ISC_HTTPD_ISSEND(httpd)); + + /* + * First, unlink our header buffer from the socket's bufflist. This + * is sort of an evil hack, since we know our buffer will be there, + * and we know it's address, so we can just remove it directly. + */ + NOTICE("senddone unlinked header"); + ISC_LIST_UNLINK(sev->bufferlist, &httpd->headerbuffer, link); + + /* + * We will always want to clean up our receive buffer, even if we + * got an error on send or we are shutting down. + * + * We will pass in the buffer only if there is data in it. If + * there is no data, we will pass in a NULL. + */ + if (httpd->freecb != NULL) { + isc_buffer_t *b = NULL; + if (isc_buffer_length(&httpd->bodybuffer) > 0) { + b = &httpd->bodybuffer; + httpd->freecb(b, httpd->freecb_arg); + } + NOTICE("senddone free callback performed"); + } + if (ISC_LINK_LINKED(&httpd->bodybuffer, link)) { + ISC_LIST_UNLINK(sev->bufferlist, &httpd->bodybuffer, link); + NOTICE("senddone body buffer unlinked"); + } else if (ISC_LINK_LINKED(&httpd->compbuffer, link)) { + ISC_LIST_UNLINK(sev->bufferlist, &httpd->compbuffer, link); + NOTICE("senddone compressed data unlinked and freed"); + } + + if (sev->result != ISC_R_SUCCESS) { + destroy_client(&httpd); + goto out; + } + + if ((httpd->flags & HTTPD_CLOSE) != 0) { + destroy_client(&httpd); + goto out; + } + + ISC_HTTPD_SETRECV(httpd); + + NOTICE("senddone restarting recv on socket"); + + reset_client(httpd); + + r.base = (unsigned char *)httpd->recvbuf; + r.length = HTTP_RECVLEN - 1; + /* check return code? */ + (void)isc_socket_recv(httpd->sock, &r, 1, task, + isc_httpd_recvdone, httpd); + +out: + isc_event_free(&ev); + EXIT("senddone"); +} + +static void +reset_client(isc_httpd_t *httpd) { + /* + * Catch errors here. We MUST be in RECV mode, and we MUST NOT have + * any outstanding buffers. If we have buffers, we have a leak. + */ + INSIST(ISC_HTTPD_ISRECV(httpd)); + INSIST(!ISC_LINK_LINKED(&httpd->headerbuffer, link)); + INSIST(!ISC_LINK_LINKED(&httpd->bodybuffer, link)); + + httpd->recvbuf[0] = 0; + httpd->recvlen = 0; + httpd->headers = NULL; + httpd->method = ISC_HTTPD_METHODUNKNOWN; + httpd->url = NULL; + httpd->querystring = NULL; + httpd->protocol = NULL; + httpd->flags = 0; + + isc_buffer_clear(&httpd->headerbuffer); + isc_buffer_clear(&httpd->compbuffer); + isc_buffer_invalidate(&httpd->bodybuffer); +} + +isc_result_t +isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, + isc_httpdaction_t *func, void *arg) +{ + return (isc_httpdmgr_addurl2(httpdmgr, url, false, func, arg)); +} + +isc_result_t +isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url, + bool isstatic, + isc_httpdaction_t *func, void *arg) +{ + isc_httpdurl_t *item; + + if (url == NULL) { + httpdmgr->render_404 = func; + return (ISC_R_SUCCESS); + } + + item = isc_mem_get(httpdmgr->mctx, sizeof(isc_httpdurl_t)); + if (item == NULL) + return (ISC_R_NOMEMORY); + + item->url = isc_mem_strdup(httpdmgr->mctx, url); + if (item->url == NULL) { + isc_mem_put(httpdmgr->mctx, item, sizeof(isc_httpdurl_t)); + return (ISC_R_NOMEMORY); + } + + item->action = func; + item->action_arg = arg; + item->isstatic = isstatic; + isc_time_now(&item->loadtime); + + ISC_LINK_INIT(item, link); + ISC_LIST_APPEND(httpdmgr->urls, item, link); + + return (ISC_R_SUCCESS); +} + +void +isc_httpd_setfinishhook(void (*fn)(void)) +{ + finishhook = fn; +} diff --git a/lib/isc/ia64/Makefile.in b/lib/isc/ia64/Makefile.in new file mode 100644 index 0000000..419cf9f --- /dev/null +++ b/lib/isc/ia64/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = include +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/ia64/include/Makefile.in b/lib/isc/ia64/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/ia64/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/ia64/include/isc/Makefile.in b/lib/isc/ia64/include/isc/Makefile.in new file mode 100644 index 0000000..97b6b41 --- /dev/null +++ b/lib/isc/ia64/include/isc/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = atomic.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/ia64/include/isc/atomic.h b/lib/isc/ia64/include/isc/atomic.h new file mode 100644 index 0000000..e2f4331 --- /dev/null +++ b/lib/isc/ia64/include/isc/atomic.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_ATOMIC_H +#define ISC_ATOMIC_H 1 + +#include <inttypes.h> + +#include <isc/platform.h> +#include <isc/types.h> + +#ifdef ISC_PLATFORM_USEGCCASM +/* + * This routine atomically increments the value stored in 'p' by 'val', and + * returns the previous value. + * + * Open issue: can 'fetchadd' make the code faster for some particular values + * (e.g., 1 and -1)? + */ +static inline int32_t +#ifdef __GNUC__ +__attribute__ ((unused)) +#endif +isc_atomic_xadd(int32_t *p, int32_t val) +{ + int32_t prev, swapped; + + for (prev = *(volatile int32_t *)p; ; prev = swapped) { + swapped = prev + val; + __asm__ volatile( + "mov ar.ccv=%2;;" + "cmpxchg4.acq %0=%4,%3,ar.ccv" + : "=r" (swapped), "=m" (*p) + : "r" (prev), "r" (swapped), "m" (*p) + : "memory"); + if (swapped == prev) + break; + } + + return (prev); +} + +/* + * This routine atomically stores the value 'val' in 'p'. + */ +static inline void +#ifdef __GNUC__ +__attribute__ ((unused)) +#endif +isc_atomic_store(int32_t *p, int32_t val) +{ + __asm__ volatile( + "st4.rel %0=%1" + : "=m" (*p) + : "r" (val) + : "memory" + ); +} + +/* + * This routine atomically replaces the value in 'p' with 'val', if the + * original value is equal to 'cmpval'. The original value is returned in any + * case. + */ +static inline int32_t +#ifdef __GNUC__ +__attribute__ ((unused)) +#endif +isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val) +{ + int32_t ret; + + __asm__ volatile( + "mov ar.ccv=%2;;" + "cmpxchg4.acq %0=%4,%3,ar.ccv" + : "=r" (ret), "=m" (*p) + : "r" (cmpval), "r" (val), "m" (*p) + : "memory"); + + return (ret); +} +#else /* !ISC_PLATFORM_USEGCCASM */ + +#error "unsupported compiler. disable atomic ops by --disable-atomic" + +#endif +#endif /* ISC_ATOMIC_H */ diff --git a/lib/isc/include/Makefile.in b/lib/isc/include/Makefile.in new file mode 100644 index 0000000..3b21e6e --- /dev/null +++ b/lib/isc/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 pk11 pkcs11 +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/include/isc/Makefile.in b/lib/isc/include/isc/Makefile.in new file mode 100644 index 0000000..4698206 --- /dev/null +++ b/lib/isc/include/isc/Makefile.in @@ -0,0 +1,59 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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@ + +# +# Only list headers that are to be installed and are not +# machine generated. The latter are handled specially in the +# install target below. +# +HEADERS = aes.h app.h assertions.h boolean.h backtrace.h base32.h base64.h \ + bind9.h buffer.h bufferlist.h commandline.h \ + counter.h crc64.h deprecated.h entropy.h errno.h \ + error.h event.h eventclass.h file.h formatcheck.h \ + fsaccess.h hash.h heap.h hex.h hmacmd5.h hmacsha.h \ + ht.h httpd.h int.h interfaceiter.h @ISC_IPV6_H@ iterated_hash.h \ + json.h lang.h lex.h lfsr.h lib.h likely.h list.h \ + log.h magic.h md5.h mem.h meminfo.h msgcat.h msgs.h \ + mutexblock.h netaddr.h netscope.h ondestroy.h os.h \ + parseint.h pool.h portset.h print.h queue.h quota.h \ + radix.h random.h ratelimiter.h refcount.h regex.h \ + region.h resource.h result.h resultclass.h rwlock.h \ + safe.h serial.h sha1.h sha2.h sockaddr.h socket.h \ + stats.h stdio.h stdlib.h string.h symtab.h task.h \ + taskpool.h timer.h tm.h types.h util.h version.h \ + xml.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + ${INSTALL_DATA} platform.h ${DESTDIR}${includedir}/isc + +uninstall:: + rm -f ${DESTDIR}${includedir}/isc/platform.h + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done + +distclean:: + rm -f platform.h diff --git a/lib/isc/include/isc/aes.h b/lib/isc/include/isc/aes.h new file mode 100644 index 0000000..75f2a2b --- /dev/null +++ b/lib/isc/include/isc/aes.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file isc/aes.h */ + +#ifndef ISC_AES_H +#define ISC_AES_H 1 + +#include <isc/lang.h> +#include <isc/platform.h> +#include <isc/types.h> + +#define ISC_AES128_KEYLENGTH 16U +#define ISC_AES192_KEYLENGTH 24U +#define ISC_AES256_KEYLENGTH 32U +#define ISC_AES_BLOCK_LENGTH 16U + +#ifdef ISC_PLATFORM_WANTAES + +ISC_LANG_BEGINDECLS + +void +isc_aes128_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out); + +void +isc_aes192_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out); + +void +isc_aes256_crypt(const unsigned char *key, const unsigned char *in, + unsigned char *out); + +ISC_LANG_ENDDECLS + +#endif /* ISC_PLATFORM_WANTAES */ + +#endif /* ISC_AES_H */ diff --git a/lib/isc/include/isc/app.h b/lib/isc/include/isc/app.h new file mode 100644 index 0000000..5ed2750 --- /dev/null +++ b/lib/isc/include/isc/app.h @@ -0,0 +1,385 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_APP_H +#define ISC_APP_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/app.h + * \brief ISC Application Support + * + * Dealing with program termination can be difficult, especially in a + * multithreaded program. The routines in this module help coordinate + * the shutdown process. They are used as follows by the initial (main) + * thread of the application: + * + *\li isc_app_start(); Call very early in main(), before + * any other threads have been created. + * + *\li isc_app_run(); This will post any on-run events, + * and then block until application + * shutdown is requested. A shutdown + * request is made by calling + * isc_app_shutdown(), or by sending + * SIGINT or SIGTERM to the process. + * After isc_app_run() returns, the + * application should shutdown itself. + * + *\li isc_app_finish(); Call very late in main(). + * + * Applications that want to use SIGHUP/isc_app_reload() to trigger reloading + * should check the result of isc_app_run() and call the reload routine if + * the result is ISC_R_RELOAD. They should then call isc_app_run() again + * to resume waiting for reload or termination. + * + * Use of this module is not required. In particular, isc_app_start() is + * NOT an ISC library initialization routine. + * + * This module also supports per-thread 'application contexts'. With this + * mode, a thread-based application will have a separate context, in which + * it uses other ISC library services such as tasks or timers. Signals are + * not caught in this mode, so that the application can handle the signals + * in its preferred way. + * + * \li MP: + * Clients must ensure that isc_app_start(), isc_app_run(), and + * isc_app_finish() are called at most once. isc_app_shutdown() + * is safe to use by any thread (provided isc_app_start() has been + * called previously). + * + * The same note applies to isc_app_ctxXXX() functions, but in this case + * it's a per-thread restriction. For example, a thread with an + * application context must ensure that isc_app_ctxstart() with the + * context is called at most once. + * + * \li Reliability: + * No anticipated impact. + * + * \li Resources: + * None. + * + * \li Security: + * No anticipated impact. + * + * \li Standards: + * None. + */ + +#include <stdbool.h> + +#include <isc/eventclass.h> +#include <isc/lang.h> +#include <isc/magic.h> +#include <isc/result.h> + +/*** + *** Types + ***/ + +typedef isc_event_t isc_appevent_t; + +#define ISC_APPEVENT_FIRSTEVENT (ISC_EVENTCLASS_APP + 0) +#define ISC_APPEVENT_SHUTDOWN (ISC_EVENTCLASS_APP + 1) +#define ISC_APPEVENT_LASTEVENT (ISC_EVENTCLASS_APP + 65535) + +/*% + * app module methods. Only app driver implementations use this structure. + * Other clients should use the top-level interfaces (i.e., isc_app_xxx + * functions). magic must be ISCAPI_APPMETHODS_MAGIC. + */ +typedef struct isc_appmethods { + void (*ctxdestroy)(isc_appctx_t **ctxp); + isc_result_t (*ctxstart)(isc_appctx_t *ctx); + isc_result_t (*ctxrun)(isc_appctx_t *ctx); + isc_result_t (*ctxsuspend)(isc_appctx_t *ctx); + isc_result_t (*ctxshutdown)(isc_appctx_t *ctx); + void (*ctxfinish)(isc_appctx_t *ctx); + void (*settaskmgr)(isc_appctx_t *ctx, + isc_taskmgr_t *timermgr); + void (*setsocketmgr)(isc_appctx_t *ctx, + isc_socketmgr_t *timermgr); + void (*settimermgr)(isc_appctx_t *ctx, + isc_timermgr_t *timermgr); + isc_result_t (*ctxonrun)(isc_appctx_t *ctx, isc_mem_t *mctx, + isc_task_t *task, isc_taskaction_t action, + void *arg); +} isc_appmethods_t; + +/*% + * This structure is actually just the common prefix of an application context + * implementation's version of an isc_appctx_t. + * \brief + * Direct use of this structure by clients is forbidden. app implementations + * may change the structure. 'magic' must be ISCAPI_APPCTX_MAGIC for any + * of the isc_app_ routines to work. app implementations must maintain + * all app context invariants. + */ +struct isc_appctx { + unsigned int impmagic; + unsigned int magic; + isc_appmethods_t *methods; +}; + +#define ISCAPI_APPCTX_MAGIC ISC_MAGIC('A','a','p','c') +#define ISCAPI_APPCTX_VALID(c) ((c) != NULL && \ + (c)->magic == ISCAPI_APPCTX_MAGIC) + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_app_ctxstart(isc_appctx_t *ctx); + +isc_result_t +isc_app_start(void); +/*!< + * \brief Start an ISC library application. + * + * Notes: + * This call should be made before any other ISC library call, and as + * close to the beginning of the application as possible. + * + * Requires: + *\li 'ctx' is a valid application context (for app_ctxstart()). + */ + +isc_result_t +isc_app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, isc_task_t *task, + isc_taskaction_t action, void *arg); +isc_result_t +isc_app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action, + void *arg); +/*!< + * \brief Request delivery of an event when the application is run. + * + * Requires: + *\li isc_app_start() has been called. + *\li 'ctx' is a valid application context (for app_ctxonrun()). + * + * Returns: + * ISC_R_SUCCESS + * ISC_R_NOMEMORY + */ + +isc_result_t +isc_app_ctxrun(isc_appctx_t *ctx); + +isc_result_t +isc_app_run(void); +/*!< + * \brief Run an ISC library application. + * + * Notes: + *\li The caller (typically the initial thread of an application) will + * block until shutdown is requested. When the call returns, the + * caller should start shutting down the application. + * + * Requires: + *\li isc_app_[ctx]start() has been called. + * + * Ensures: + *\li Any events requested via isc_app_onrun() will have been posted (in + * FIFO order) before isc_app_run() blocks. + *\li 'ctx' is a valid application context (for app_ctxrun()). + * + * Returns: + *\li ISC_R_SUCCESS Shutdown has been requested. + *\li ISC_R_RELOAD Reload has been requested. + */ + +bool +isc_app_isrunning(void); +/*!< + * \brief Return if the ISC library application is running. + * + * Returns: + *\li true App is running. + *\li false App is not running. + */ + +isc_result_t +isc_app_ctxshutdown(isc_appctx_t *ctx); + +isc_result_t +isc_app_shutdown(void); +/*!< + * \brief Request application shutdown. + * + * Notes: + *\li It is safe to call isc_app_shutdown() multiple times. Shutdown will + * only be triggered once. + * + * Requires: + *\li isc_app_[ctx]run() has been called. + *\li 'ctx' is a valid application context (for app_ctxshutdown()). + * + * Returns: + *\li ISC_R_SUCCESS + *\li ISC_R_UNEXPECTED + */ + +isc_result_t +isc_app_ctxsuspend(isc_appctx_t *ctx); +/*!< + * \brief This has the same behavior as isc_app_ctxsuspend(). + */ + +isc_result_t +isc_app_reload(void); +/*!< + * \brief Request application reload. + * + * Requires: + *\li isc_app_run() has been called. + * + * Returns: + *\li ISC_R_SUCCESS + *\li ISC_R_UNEXPECTED + */ + +void +isc_app_ctxfinish(isc_appctx_t *ctx); + +void +isc_app_finish(void); +/*!< + * \brief Finish an ISC library application. + * + * Notes: + *\li This call should be made at or near the end of main(). + * + * Requires: + *\li isc_app_start() has been called. + *\li 'ctx' is a valid application context (for app_ctxfinish()). + * + * Ensures: + *\li Any resources allocated by isc_app_start() have been released. + */ + +void +isc_app_block(void); +/*!< + * \brief Indicate that a blocking operation will be performed. + * + * Notes: + *\li If a blocking operation is in process, a call to isc_app_shutdown() + * or an external signal will abort the program, rather than allowing + * clean shutdown. This is primarily useful for reading user input. + * + * Requires: + * \li isc_app_start() has been called. + * \li No other blocking operations are in progress. + */ + +void +isc_app_unblock(void); +/*!< + * \brief Indicate that a blocking operation is complete. + * + * Notes: + * \li When a blocking operation has completed, return the program to a + * state where a call to isc_app_shutdown() or an external signal will + * shutdown normally. + * + * Requires: + * \li isc_app_start() has been called. + * \li isc_app_block() has been called by the same thread. + */ + +isc_result_t +isc_appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp); +/*!< + * \brief Create an application context. + * + * Requires: + *\li 'mctx' is a valid memory context. + *\li 'ctxp' != NULL && *ctxp == NULL. + */ + +void +isc_appctx_destroy(isc_appctx_t **ctxp); +/*!< + * \brief Destroy an application context. + * + * Requires: + *\li '*ctxp' is a valid application context. + * + * Ensures: + *\li *ctxp == NULL. + */ + +void +isc_appctx_settaskmgr(isc_appctx_t *ctx, isc_taskmgr_t *taskmgr); +/*!< + * \brief Associate a task manager with an application context. + * + * This must be done before running tasks within the application context. + * + * Requires: + *\li 'ctx' is a valid application context. + *\li 'taskmgr' is a valid task manager. + */ + +void +isc_appctx_setsocketmgr(isc_appctx_t *ctx, isc_socketmgr_t *socketmgr); +/*!< + * \brief Associate a socket manager with an application context. + * + * This must be done before handling socket events within the application + * context. + * + * Requires: + *\li 'ctx' is a valid application context. + *\li 'socketmgr' is a valid socket manager. + */ + +void +isc_appctx_settimermgr(isc_appctx_t *ctx, isc_timermgr_t *timermgr); +/*!< + * \brief Associate a socket timer with an application context. + * + * This must be done before handling timer events within the application + * context. + * + * Requires: + *\li 'ctx' is a valid application context. + *\li 'timermgr' is a valid timer manager. + */ + +/*%< + * See isc_appctx_create() above. + */ +typedef isc_result_t +(*isc_appctxcreatefunc_t)(isc_mem_t *mctx, isc_appctx_t **ctxp); + +isc_result_t +isc_app_register(isc_appctxcreatefunc_t createfunc); +/*%< + * Register a new application implementation and add it to the list of + * supported implementations. This function must be called when a different + * event library is used than the one contained in the ISC library. + */ + +isc_result_t +isc__app_register(void); +/*%< + * A short cut function that specifies the application module in the ISC + * library for isc_app_register(). An application that uses the ISC library + * usually do not have to care about this function: it would call + * isc_lib_register(), which internally calls this function. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_APP_H */ diff --git a/lib/isc/include/isc/assertions.h b/lib/isc/include/isc/assertions.h new file mode 100644 index 0000000..e5719d9 --- /dev/null +++ b/lib/isc/include/isc/assertions.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file isc/assertions.h + */ + +#ifndef ISC_ASSERTIONS_H +#define ISC_ASSERTIONS_H 1 + +#include <isc/lang.h> +#include <isc/likely.h> +#include <isc/platform.h> + +ISC_LANG_BEGINDECLS + +/*% isc assertion type */ +typedef enum { + isc_assertiontype_require, + isc_assertiontype_ensure, + isc_assertiontype_insist, + isc_assertiontype_invariant +} isc_assertiontype_t; + +typedef void (*isc_assertioncallback_t)(const char *, int, isc_assertiontype_t, + const char *); + +/* coverity[+kill] */ +ISC_PLATFORM_NORETURN_PRE +void isc_assertion_failed(const char *, int, isc_assertiontype_t, + const char *) ISC_PLATFORM_NORETURN_POST; + +void +isc_assertion_setcallback(isc_assertioncallback_t); + +const char * +isc_assertion_typetotext(isc_assertiontype_t type); + +#if defined(ISC_CHECK_ALL) || defined(__COVERITY__) +#define ISC_CHECK_REQUIRE 1 +#define ISC_CHECK_ENSURE 1 +#define ISC_CHECK_INSIST 1 +#define ISC_CHECK_INVARIANT 1 +#endif + +#if defined(ISC_CHECK_NONE) && !defined(__COVERITY__) +#define ISC_CHECK_REQUIRE 0 +#define ISC_CHECK_ENSURE 0 +#define ISC_CHECK_INSIST 0 +#define ISC_CHECK_INVARIANT 0 +#endif + +#ifndef ISC_CHECK_REQUIRE +#define ISC_CHECK_REQUIRE 1 +#endif + +#ifndef ISC_CHECK_ENSURE +#define ISC_CHECK_ENSURE 1 +#endif + +#ifndef ISC_CHECK_INSIST +#define ISC_CHECK_INSIST 1 +#endif + +#ifndef ISC_CHECK_INVARIANT +#define ISC_CHECK_INVARIANT 1 +#endif + +#if ISC_CHECK_REQUIRE != 0 +#define ISC_REQUIRE(cond) \ + ((void) (ISC_LIKELY(cond) || \ + ((isc_assertion_failed)(__FILE__, __LINE__, \ + isc_assertiontype_require, \ + #cond), 0))) +#else +#define ISC_REQUIRE(cond) ((void) 0) +#endif /* ISC_CHECK_REQUIRE */ + +#if ISC_CHECK_ENSURE != 0 +#define ISC_ENSURE(cond) \ + ((void) (ISC_LIKELY(cond) || \ + ((isc_assertion_failed)(__FILE__, __LINE__, \ + isc_assertiontype_ensure, \ + #cond), 0))) +#else +#define ISC_ENSURE(cond) ((void) 0) +#endif /* ISC_CHECK_ENSURE */ + +#if ISC_CHECK_INSIST != 0 +#define ISC_INSIST(cond) \ + ((void) (ISC_LIKELY(cond) || \ + ((isc_assertion_failed)(__FILE__, __LINE__, \ + isc_assertiontype_insist, \ + #cond), 0))) +#else +#define ISC_INSIST(cond) ((void) 0) +#endif /* ISC_CHECK_INSIST */ + +#if ISC_CHECK_INVARIANT != 0 +#define ISC_INVARIANT(cond) \ + ((void) (ISC_LIKELY(cond) || \ + ((isc_assertion_failed)(__FILE__, __LINE__, \ + isc_assertiontype_invariant, \ + #cond), 0))) +#else +#define ISC_INVARIANT(cond) ((void) 0) +#endif /* ISC_CHECK_INVARIANT */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_ASSERTIONS_H */ diff --git a/lib/isc/include/isc/backtrace.h b/lib/isc/include/isc/backtrace.h new file mode 100644 index 0000000..27ab9da --- /dev/null +++ b/lib/isc/include/isc/backtrace.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file isc/backtrace.h + * \brief provide a back trace of the running process to help debug problems. + * + * This module tries to get a back trace of the process using some platform + * dependent way when available. It also manages an internal symbol table + * that maps function addresses used in the process to their textual symbols. + * This module is expected to be used to help debug when some fatal error + * happens. + * + * IMPORTANT NOTE: since the (major) intended use case of this module is + * dumping a back trace on a fatal error, normally followed by self termination, + * functions defined in this module generally doesn't employ assertion checks + * (if it did, a program bug could cause infinite recursive calls to a + * backtrace function). These functions still perform minimal checks and return + * ISC_R_FAILURE if they detect an error, but the caller should therefore be + * very careful about the use of these functions, and generally discouraged to + * use them except in an exit path. The exception is + * isc_backtrace_getsymbolfromindex(), which is expected to be used in a + * non-error-handling context and validates arguments with assertion checks. + */ + +#ifndef ISC_BACKTRACE_H +#define ISC_BACKTRACE_H 1 + +/*** + *** Imports + ***/ + +#include <isc/types.h> + +/*** + *** Types + ***/ +struct isc_backtrace_symmap { + void *addr; + const char *symbol; +}; + +LIBISC_EXTERNAL_DATA extern const int isc__backtrace_nsymbols; +LIBISC_EXTERNAL_DATA extern const + isc_backtrace_symmap_t isc__backtrace_symtable[]; + +/*** + *** Functions + ***/ + +ISC_LANG_BEGINDECLS +isc_result_t +isc_backtrace_gettrace(void **addrs, int maxaddrs, int *nframes); +/*%< + * Get a back trace of the running process above this function itself. On + * success, addrs[i] will store the address of the call point of the i-th + * stack frame (addrs[0] is the caller of this function). *nframes will store + * the total number of frames. + * + * Requires (note that these are not ensured by assertion checks, see above): + * + *\li 'addrs' is a valid array containing at least 'maxaddrs' void * entries. + * + *\li 'nframes' must be non NULL. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_FAILURE + *\li #ISC_R_NOTFOUND + *\li #ISC_R_NOTIMPLEMENTED + */ + +isc_result_t +isc_backtrace_getsymbolfromindex(int index, const void **addrp, + const char **symbolp); +/*%< + * Returns the content of the internal symbol table of the given index. + * On success, *addrsp and *symbolp point to the address and the symbol of + * the 'index'th entry of the table, respectively. If 'index' is not in the + * range of the symbol table, ISC_R_RANGE will be returned. + * + * Requires + * + *\li 'addrp' must be non NULL && '*addrp' == NULL. + * + *\li 'symbolp' must be non NULL && '*symbolp' == NULL. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_RANGE + */ + +isc_result_t +isc_backtrace_getsymbol(const void *addr, const char **symbolp, + unsigned long *offsetp); +/*%< + * Searches the internal symbol table for the symbol that most matches the + * given 'addr'. On success, '*symbolp' will point to the name of function + * to which the address 'addr' belong, and '*offsetp' will store the offset + * from the function's entry address to 'addr'. + * + * Requires (note that these are not ensured by assertion checks, see above): + * + *\li 'symbolp' must be non NULL && '*symbolp' == NULL. + * + *\li 'offsetp' must be non NULL. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_FAILURE + *\li #ISC_R_NOTFOUND + */ +ISC_LANG_ENDDECLS + +#endif /* ISC_BACKTRACE_H */ diff --git a/lib/isc/include/isc/base32.h b/lib/isc/include/isc/base32.h new file mode 100644 index 0000000..96fe2ce --- /dev/null +++ b/lib/isc/include/isc/base32.h @@ -0,0 +1,135 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_BASE32_H +#define ISC_BASE32_H 1 + +/*! \file */ + +/* + * Routines for manipulating base 32 and base 32 hex encoded data. + * Based on RFC 4648. + * + * Base 32 hex preserves the sort order of data when it is encoded / + * decoded. + * + * Base 32 hex "np" is base 32 hex but no padding is produced or accepted. + */ + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/*** + *** Functions + ***/ + +isc_result_t +isc_base32_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target); +isc_result_t +isc_base32hex_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target); +isc_result_t +isc_base32hexnp_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target); +/*!< + * \brief Convert data into base32 encoded text. + * + * Notes: + *\li The base32 encoded text in 'target' will be divided into + * words of at most 'wordlength' characters, separated by + * the 'wordbreak' string. No parentheses will surround + * the text. + * + * Requires: + *\li 'source' is a region containing binary data + *\li 'target' is a text buffer containing available space + *\li 'wordbreak' points to a null-terminated string of + * zero or more whitespace characters + * + * Ensures: + *\li target will contain the base32 encoded version of the data + * in source. The 'used' pointer in target will be advanced as + * necessary. + */ + +isc_result_t +isc_base32_decodestring(const char *cstr, isc_buffer_t *target); +isc_result_t +isc_base32hex_decodestring(const char *cstr, isc_buffer_t *target); +isc_result_t +isc_base32hexnp_decodestring(const char *cstr, isc_buffer_t *target); +/*!< + * \brief Decode a null-terminated string in base32, base32hex, or + * base32hex non-padded. + * + * Requires: + *\li 'cstr' is non-null. + *\li 'target' is a valid buffer. + * + * Returns: + *\li #ISC_R_SUCCESS -- the entire decoded representation of 'cstring' + * fit in 'target'. + *\li #ISC_R_BADBASE32 -- 'cstr' is not a valid base32 encoding. + * + * Other error returns are any possible error code from: + *\li isc_lex_create(), + *\li isc_lex_openbuffer(), + *\li isc_base32_tobuffer(). + */ + +isc_result_t +isc_base32_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length); +isc_result_t +isc_base32hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length); +isc_result_t +isc_base32hexnp_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length); +/*!< + * \brief Convert text encoded in base32, base32hex, or base32hex + * non-padded from a lexer context into data. + * + * Requires: + *\li 'lex' is a valid lexer context + *\li 'target' is a buffer containing binary data + *\li 'length' is an integer + * + * Ensures: + *\li target will contain the data represented by the base32 encoded + * string parsed by the lexer. No more than length bytes will be read, + * if length is positive. The 'used' pointer in target will be + * advanced as necessary. + */ + +isc_result_t +isc_base32_decoderegion(isc_region_t *source, isc_buffer_t *target); +isc_result_t +isc_base32hex_decoderegion(isc_region_t *source, isc_buffer_t *target); +isc_result_t +isc_base32hexnp_decoderegion(isc_region_t *source, isc_buffer_t *target); +/*!< + * \brief Decode a packed (no white space permitted) region in + * base32, base32hex or base32hex non-padded. + * + * Requires: + *\li 'source' is a valid region. + *\li 'target' is a valid buffer. + * + * Returns: + *\li #ISC_R_SUCCESS -- the entire decoded representation of 'cstring' + * fit in 'target'. + *\li #ISC_R_BADBASE32 -- 'source' is not a valid base32 encoding. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_BASE32_H */ diff --git a/lib/isc/include/isc/base64.h b/lib/isc/include/isc/base64.h new file mode 100644 index 0000000..1504d3d --- /dev/null +++ b/lib/isc/include/isc/base64.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_BASE64_H +#define ISC_BASE64_H 1 + +/*! \file isc/base64.h */ + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/*** + *** Functions + ***/ + +isc_result_t +isc_base64_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target); +/*!< + * \brief Convert data into base64 encoded text. + * + * Notes: + *\li The base64 encoded text in 'target' will be divided into + * words of at most 'wordlength' characters, separated by + * the 'wordbreak' string. No parentheses will surround + * the text. + * + * Requires: + *\li 'source' is a region containing binary data + *\li 'target' is a text buffer containing available space + *\li 'wordbreak' points to a null-terminated string of + * zero or more whitespace characters + * + * Ensures: + *\li target will contain the base64 encoded version of the data + * in source. The 'used' pointer in target will be advanced as + * necessary. + */ + +isc_result_t +isc_base64_decodestring(const char *cstr, isc_buffer_t *target); +/*!< + * \brief Decode a null-terminated base64 string. + * + * Requires: + *\li 'cstr' is non-null. + *\li 'target' is a valid buffer. + * + * Returns: + *\li #ISC_R_SUCCESS -- the entire decoded representation of 'cstring' + * fit in 'target'. + *\li #ISC_R_BADBASE64 -- 'cstr' is not a valid base64 encoding. + * + * Other error returns are any possible error code from: + *\li isc_lex_create(), + *\li isc_lex_openbuffer(), + *\li isc_base64_tobuffer(). + */ + +isc_result_t +isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length); +/*!< + * \brief Convert base64 encoded text from a lexer context into data. + * + * Requires: + *\li 'lex' is a valid lexer context + *\li 'target' is a buffer containing binary data + *\li 'length' is an integer + * + * Ensures: + *\li target will contain the data represented by the base64 encoded + * string parsed by the lexer. No more than length bytes will be read, + * if length is positive. The 'used' pointer in target will be + * advanced as necessary. + */ + + + +ISC_LANG_ENDDECLS + +#endif /* ISC_BASE64_H */ diff --git a/lib/isc/include/isc/bind9.h b/lib/isc/include/isc/bind9.h new file mode 100644 index 0000000..5a2837f --- /dev/null +++ b/lib/isc/include/isc/bind9.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_BIND9_H +#define ISC_BIND9_H 1 + +#include <stdbool.h> + +#include <isc/platform.h> + +/* + * This determines whether we are using the libisc/libdns libraries + * in BIND9 or in some other application. For BIND9 (named and related + * tools) it must be set to true at runtime. Export library clients + * will call isc_lib_register(), which will set it to false. + */ +LIBISC_EXTERNAL_DATA extern bool isc_bind9; + +#endif /* ISC_BIND9_H */ diff --git a/lib/isc/include/isc/boolean.h b/lib/isc/include/isc/boolean.h new file mode 100644 index 0000000..2eebefa --- /dev/null +++ b/lib/isc/include/isc/boolean.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file isc/boolean.h */ + +#define isc_boolean_t bool +#define isc_boolean_false false +#define isc_boolean_true true + +#define ISC_FALSE false +#define ISC_TRUE true +#define ISC_TF(x) (!!(x)) diff --git a/lib/isc/include/isc/buffer.h b/lib/isc/include/isc/buffer.h new file mode 100644 index 0000000..caaac57 --- /dev/null +++ b/lib/isc/include/isc/buffer.h @@ -0,0 +1,1047 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_BUFFER_H +#define ISC_BUFFER_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/buffer.h + * + * \brief A buffer is a region of memory, together with a set of related subregions. + * Buffers are used for parsing and I/O operations. + * + * The 'used region' and the 'available' region are disjoint, and their + * union is the buffer's region. The used region extends from the beginning + * of the buffer region to the last used byte. The available region + * extends from one byte greater than the last used byte to the end of the + * buffer's region. The size of the used region can be changed using various + * buffer commands. Initially, the used region is empty. + * + * The used region is further subdivided into two disjoint regions: the + * 'consumed region' and the 'remaining region'. The union of these two + * regions is the used region. The consumed region extends from the beginning + * of the used region to the byte before the 'current' offset (if any). The + * 'remaining' region the current pointer to the end of the used + * region. The size of the consumed region can be changed using various + * buffer commands. Initially, the consumed region is empty. + * + * The 'active region' is an (optional) subregion of the remaining region. + * It extends from the current offset to an offset in the remaining region + * that is selected with isc_buffer_setactive(). Initially, the active region + * is empty. If the current offset advances beyond the chosen offset, the + * active region will also be empty. + * + * \verbatim + * /------------entire length---------------\ + * /----- used region -----\/-- available --\ + * +----------------------------------------+ + * | consumed | remaining | | + * +----------------------------------------+ + * a b c d e + * + * a == base of buffer. + * b == current pointer. Can be anywhere between a and d. + * c == active pointer. Meaningful between b and d. + * d == used pointer. + * e == length of buffer. + * + * a-e == entire length of buffer. + * a-d == used region. + * a-b == consumed region. + * b-d == remaining region. + * b-c == optional active region. + *\endverbatim + * + * The following invariants are maintained by all routines: + * + *\code + * length > 0 + * + * base is a valid pointer to length bytes of memory + * + * 0 <= used <= length + * + * 0 <= current <= used + * + * 0 <= active <= used + * (although active < current implies empty active region) + *\endcode + * + * \li MP: + * Buffers have no synchronization. Clients must ensure exclusive + * access. + * + * \li Reliability: + * No anticipated impact. + * + * \li Resources: + * Memory: 1 pointer + 6 unsigned integers per buffer. + * + * \li Security: + * No anticipated impact. + * + * \li Standards: + * None. + */ + +/*** + *** Imports + ***/ + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/assertions.h> +#include <isc/formatcheck.h> +#include <isc/lang.h> +#include <isc/likely.h> +#include <isc/magic.h> +#include <isc/types.h> + +/*! + * To make many functions be inline macros (via \#define) define this. + * If it is undefined, a function will be used. + */ +/* #define ISC_BUFFER_USEINLINE */ + + +ISC_LANG_BEGINDECLS + +/*@{*/ +/*! + *** Magic numbers + ***/ +#define ISC_BUFFER_MAGIC 0x42756621U /* Buf!. */ +#define ISC_BUFFER_VALID(b) ISC_MAGIC_VALID(b, ISC_BUFFER_MAGIC) +/*@}*/ + +/*! + * Size granularity for dynamically resizeable buffers; when reserving + * space in a buffer, we round the allocated buffer length up to the + * nearest * multiple of this value. + */ +#define ISC_BUFFER_INCR 2048 + +/* + * The following macros MUST be used only on valid buffers. It is the + * caller's responsibility to ensure this by using the ISC_BUFFER_VALID + * check above, or by calling another isc_buffer_*() function (rather than + * another macro.) + */ + +/*@{*/ +/*! + * Fundamental buffer elements. (A through E in the introductory comment.) + */ +#define isc_buffer_base(b) ((void *)(b)->base) /*a*/ +#define isc_buffer_current(b) \ + ((void *)((unsigned char *)(b)->base + (b)->current)) /*b*/ +#define isc_buffer_active(b) \ + ((void *)((unsigned char *)(b)->base + (b)->active)) /*c*/ +#define isc_buffer_used(b) \ + ((void *)((unsigned char *)(b)->base + (b)->used)) /*d*/ +#define isc_buffer_length(b) ((b)->length) /*e*/ +/*@}*/ + +/*@{*/ +/*! + * Derived lengths. (Described in the introductory comment.) + */ +#define isc_buffer_usedlength(b) ((b)->used) /* d-a */ +#define isc_buffer_consumedlength(b) ((b)->current) /* b-a */ +#define isc_buffer_remaininglength(b) ((b)->used - (b)->current) /* d-b */ +#define isc_buffer_activelength(b) ((b)->active - (b)->current) /* c-b */ +#define isc_buffer_availablelength(b) ((b)->length - (b)->used) /* e-d */ +/*@}*/ + +/*! + * Note that the buffer structure is public. This is principally so buffer + * operations can be implemented using macros. Applications are strongly + * discouraged from directly manipulating the structure. + */ + +struct isc_buffer { + unsigned int magic; + void *base; + /*@{*/ + /*! The following integers are byte offsets from 'base'. */ + unsigned int length; + unsigned int used; + unsigned int current; + unsigned int active; + /*@}*/ + /*! linkable */ + ISC_LINK(isc_buffer_t) link; + /*! private internal elements */ + isc_mem_t *mctx; + /* automatically realloc buffer at put* */ + bool autore; +}; + +/*** + *** Functions + ***/ + +isc_result_t +isc_buffer_allocate(isc_mem_t *mctx, isc_buffer_t **dynbuffer, + unsigned int length); +/*!< + * \brief Allocate a dynamic linkable buffer which has "length" bytes in the + * data region. + * + * Requires: + *\li "mctx" is valid. + * + *\li "dynbuffer" is non-NULL, and "*dynbuffer" is NULL. + * + * Returns: + *\li ISC_R_SUCCESS - success + *\li ISC_R_NOMEMORY - no memory available + * + * Note: + *\li Changing the buffer's length field is not permitted. + */ + +isc_result_t +isc_buffer_reallocate(isc_buffer_t **dynbuffer, unsigned int length); +/*!< + * \brief Reallocate the buffer to be "length" bytes long. The buffer + * pointer may move when you call this function. + * + * Requires: + *\li "dynbuffer" is not NULL. + * + *\li "*dynbuffer" is a valid dynamic buffer. + * + *\li 'length' > current length of buffer. + * + * Returns: + *\li ISC_R_SUCCESS - success + *\li ISC_R_NOMEMORY - no memory available + * + * Ensures: + *\li "*dynbuffer" will be valid on return and will contain all the + * original data. However, the buffer pointer may be moved during + * reallocation. + */ + +isc_result_t +isc_buffer_reserve(isc_buffer_t **dynbuffer, unsigned int size); +/*!< + * \brief Make "size" bytes of space available in the buffer. The buffer + * pointer may move when you call this function. + * + * Requires: + *\li "dynbuffer" is not NULL. + * + *\li "*dynbuffer" is a valid dynamic buffer. + * + * Returns: + *\li ISC_R_SUCCESS - success + *\li ISC_R_NOMEMORY - no memory available + * + * Ensures: + *\li "*dynbuffer" will be valid on return and will contain all the + * original data. However, the buffer pointer may be moved during + * reallocation. + */ + +void +isc_buffer_free(isc_buffer_t **dynbuffer); +/*!< + * \brief Release resources allocated for a dynamic buffer. + * + * Requires: + *\li "dynbuffer" is not NULL. + * + *\li "*dynbuffer" is a valid dynamic buffer. + * + * Ensures: + *\li "*dynbuffer" will be NULL on return, and all memory associated with + * the dynamic buffer is returned to the memory context used in + * isc_buffer_allocate(). + */ + +void +isc__buffer_init(isc_buffer_t *b, void *base, unsigned int length); +/*!< + * \brief Make 'b' refer to the 'length'-byte region starting at base. + * + * Requires: + * + *\li 'length' > 0 + * + *\li 'base' is a pointer to a sequence of 'length' bytes. + * + */ + +void +isc__buffer_initnull(isc_buffer_t *b); +/*!< + *\brief Initialize a buffer 'b' with a null data and zero length/ + */ + +void +isc_buffer_reinit(isc_buffer_t *b, void *base, unsigned int length); +/*!< + * \brief Make 'b' refer to the 'length'-byte region starting at base. + * Any existing data will be copied. + * + * Requires: + * + *\li 'length' > 0 AND length >= previous length + * + *\li 'base' is a pointer to a sequence of 'length' bytes. + * + */ + +void +isc__buffer_invalidate(isc_buffer_t *b); +/*!< + * \brief Make 'b' an invalid buffer. + * + * Requires: + *\li 'b' is a valid buffer. + * + * Ensures: + *\li If assertion checking is enabled, future attempts to use 'b' without + * calling isc_buffer_init() on it will cause an assertion failure. + */ + +void +isc_buffer_setautorealloc(isc_buffer_t *b, bool enable); +/*!< + * \brief Enable or disable autoreallocation on 'b'. + * + * Requires: + *\li 'b' is a valid dynamic buffer (b->mctx != NULL). + * + */ + +void +isc__buffer_region(isc_buffer_t *b, isc_region_t *r); +/*!< + * \brief Make 'r' refer to the region of 'b'. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li 'r' points to a region structure. + */ + +void +isc__buffer_usedregion(const isc_buffer_t *b, isc_region_t *r); +/*!< + * \brief Make 'r' refer to the used region of 'b'. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li 'r' points to a region structure. + */ + +void +isc__buffer_availableregion(isc_buffer_t *b, isc_region_t *r); +/*!< + * \brief Make 'r' refer to the available region of 'b'. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li 'r' points to a region structure. + */ + +void +isc__buffer_add(isc_buffer_t *b, unsigned int n); +/*!< + * \brief Increase the 'used' region of 'b' by 'n' bytes. + * + * Requires: + * + *\li 'b' is a valid buffer + * + *\li used + n <= length + * + */ + +void +isc__buffer_subtract(isc_buffer_t *b, unsigned int n); +/*!< + * \brief Decrease the 'used' region of 'b' by 'n' bytes. + * + * Requires: + * + *\li 'b' is a valid buffer + * + *\li used >= n + * + */ + +void +isc__buffer_clear(isc_buffer_t *b); +/*!< + * \brief Make the used region empty. + * + * Requires: + * + *\li 'b' is a valid buffer + * + * Ensures: + * + *\li used = 0 + * + */ + +void +isc__buffer_consumedregion(isc_buffer_t *b, isc_region_t *r); +/*!< + * \brief Make 'r' refer to the consumed region of 'b'. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li 'r' points to a region structure. + */ + +void +isc__buffer_remainingregion(isc_buffer_t *b, isc_region_t *r); +/*!< + * \brief Make 'r' refer to the remaining region of 'b'. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li 'r' points to a region structure. + */ + +void +isc__buffer_activeregion(isc_buffer_t *b, isc_region_t *r); +/*!< + * \brief Make 'r' refer to the active region of 'b'. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li 'r' points to a region structure. + */ + +void +isc__buffer_setactive(isc_buffer_t *b, unsigned int n); +/*!< + * \brief Sets the end of the active region 'n' bytes after current. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li current + n <= used + */ + +void +isc__buffer_first(isc_buffer_t *b); +/*!< + * \brief Make the consumed region empty. + * + * Requires: + * + *\li 'b' is a valid buffer + * + * Ensures: + * + *\li current == 0 + * + */ + +void +isc__buffer_forward(isc_buffer_t *b, unsigned int n); +/*!< + * \brief Increase the 'consumed' region of 'b' by 'n' bytes. + * + * Requires: + * + *\li 'b' is a valid buffer + * + *\li current + n <= used + * + */ + +void +isc__buffer_back(isc_buffer_t *b, unsigned int n); +/*!< + * \brief Decrease the 'consumed' region of 'b' by 'n' bytes. + * + * Requires: + * + *\li 'b' is a valid buffer + * + *\li n <= current + * + */ + +void +isc_buffer_compact(isc_buffer_t *b); +/*!< + * \brief Compact the used region by moving the remaining region so it occurs + * at the start of the buffer. The used region is shrunk by the size of + * the consumed region, and the consumed region is then made empty. + * + * Requires: + * + *\li 'b' is a valid buffer + * + * Ensures: + * + *\li current == 0 + * + *\li The size of the used region is now equal to the size of the remaining + * region (as it was before the call). The contents of the used region + * are those of the remaining region (as it was before the call). + */ + +uint8_t +isc_buffer_getuint8(isc_buffer_t *b); +/*!< + * \brief Read an unsigned 8-bit integer from 'b' and return it. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li The length of the available region of 'b' is at least 1. + * + * Ensures: + * + *\li The current pointer in 'b' is advanced by 1. + * + * Returns: + * + *\li A 8-bit unsigned integer. + */ + +void +isc__buffer_putuint8(isc_buffer_t *b, uint8_t val); +/*!< + * \brief Store an unsigned 8-bit integer from 'val' into 'b'. + * + * Requires: + *\li 'b' is a valid buffer. + * + *\li The length of the unused region of 'b' is at least 1 + * or the buffer has autoreallocation enabled. + * + * Ensures: + *\li The used pointer in 'b' is advanced by 1. + */ + +uint16_t +isc_buffer_getuint16(isc_buffer_t *b); +/*!< + * \brief Read an unsigned 16-bit integer in network byte order from 'b', convert + * it to host byte order, and return it. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li The length of the available region of 'b' is at least 2 + * or the buffer has autoreallocation enabled. + * + * Ensures: + * + *\li The current pointer in 'b' is advanced by 2. + * + * Returns: + * + *\li A 16-bit unsigned integer. + */ + +void +isc__buffer_putuint16(isc_buffer_t *b, uint16_t val); +/*!< + * \brief Store an unsigned 16-bit integer in host byte order from 'val' + * into 'b' in network byte order. + * + * Requires: + *\li 'b' is a valid buffer. + * + *\li The length of the unused region of 'b' is at least 2 + * or the buffer has autoreallocation enabled. + * + * Ensures: + *\li The used pointer in 'b' is advanced by 2. + */ + +uint32_t +isc_buffer_getuint32(isc_buffer_t *b); +/*!< + * \brief Read an unsigned 32-bit integer in network byte order from 'b', convert + * it to host byte order, and return it. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li The length of the available region of 'b' is at least 4. + * + * Ensures: + * + *\li The current pointer in 'b' is advanced by 4. + * + * Returns: + * + *\li A 32-bit unsigned integer. + */ + +void +isc__buffer_putuint32(isc_buffer_t *b, uint32_t val); +/*!< + * \brief Store an unsigned 32-bit integer in host byte order from 'val' + * into 'b' in network byte order. + * + * Requires: + *\li 'b' is a valid buffer. + * + *\li The length of the unused region of 'b' is at least 4 + * or the buffer has autoreallocation enabled. + * + * Ensures: + *\li The used pointer in 'b' is advanced by 4. + */ + +uint64_t +isc_buffer_getuint48(isc_buffer_t *b); +/*!< + * \brief Read an unsigned 48-bit integer in network byte order from 'b', + * convert it to host byte order, and return it. + * + * Requires: + * + *\li 'b' is a valid buffer. + * + *\li The length of the available region of 'b' is at least 6. + * + * Ensures: + * + *\li The current pointer in 'b' is advanced by 6. + * + * Returns: + * + *\li A 48-bit unsigned integer (stored in a 64-bit integer). + */ + +void +isc__buffer_putuint48(isc_buffer_t *b, uint64_t val); +/*!< + * \brief Store an unsigned 48-bit integer in host byte order from 'val' + * into 'b' in network byte order. + * + * Requires: + *\li 'b' is a valid buffer. + * + *\li The length of the unused region of 'b' is at least 6 + * or the buffer has autoreallocation enabled. + * + * Ensures: + *\li The used pointer in 'b' is advanced by 6. + */ + +void +isc__buffer_putuint24(isc_buffer_t *b, uint32_t val); +/*!< + * Store an unsigned 24-bit integer in host byte order from 'val' + * into 'b' in network byte order. + * + * Requires: + *\li 'b' is a valid buffer. + * + * The length of the unused region of 'b' is at least 3 + * or the buffer has autoreallocation enabled. + * + * Ensures: + *\li The used pointer in 'b' is advanced by 3. + */ + +void +isc__buffer_putmem(isc_buffer_t *b, const unsigned char *base, + unsigned int length); +/*!< + * \brief Copy 'length' bytes of memory at 'base' into 'b'. + * + * Requires: + *\li 'b' is a valid buffer, and it has at least 'length' + * or the buffer has autoreallocation enabled. + * + *\li 'base' points to 'length' bytes of valid memory. + * + */ + +void +isc__buffer_putstr(isc_buffer_t *b, const char *source); +/*!< + * \brief Copy 'source' into 'b', not including terminating NUL. + * + * Requires: + *\li 'b' is a valid buffer. + * + *\li 'source' to be a valid NULL terminated string. + * + *\li strlen(source) <= isc_buffer_available(b) || b->mctx != NULL + */ + +void +isc_buffer_putdecint(isc_buffer_t *b, int64_t v); +/*!< + * \brief Put decimal representation of 'v' in b + * + * Requires: + *\li 'b' is a valid buffer. + * + *\li strlen(dec(v)) <= isc_buffer_available(b) || b->mctx != NULL + */ + + + +isc_result_t +isc_buffer_copyregion(isc_buffer_t *b, const isc_region_t *r); +/*!< + * \brief Copy the contents of 'r' into 'b'. + * + * Requires: + *\li 'b' is a valid buffer. + * + *\li 'r' is a valid region. + * + * Returns: + * + *\li ISC_R_SUCCESS + *\li ISC_R_NOSPACE The available region of 'b' is not + * big enough. + */ + +isc_result_t +isc_buffer_dup(isc_mem_t *mctx, isc_buffer_t **dstp, const isc_buffer_t *src); +/*!< + * \brief Allocate 'dst' and copy used contents of 'src' into it + * + * Requires: + *\li 'dstp' is not NULL and *dst is NULL + *\li 'src' is a valid buffer. + * + * Returns: + * + *\li ISC_R_SUCCESS + *\li ISC_R_NOSPACE The available region of 'b' is not + * big enough. + */ + +ISC_LANG_ENDDECLS + +/* + * Inline macro versions of the functions. These should never be called + * directly by an application, but will be used by the functions within + * buffer.c. The callers should always use "isc_buffer_*()" names, never + * ones beginning with "isc__" + */ + +/*! \note + * XXXDCL Something more could be done with initializing buffers that + * point to const data. For example, isc_buffer_constinit() could + * set a new boolean flag in the buffer structure indicating whether + * the buffer was initialized with that function. * Then if the + * boolean were true, the isc_buffer_put* functions could assert a + * contractual requirement for a non-const buffer. + * + * One drawback is that the isc_buffer_* functions (macros) that return + * pointers would still need to return non-const pointers to avoid compiler + * warnings, so it would be up to code that uses them to have to deal + * with the possibility that the buffer was initialized as const -- + * a problem that they *already* have to deal with but have absolutely + * no ability to. With a new isc_buffer_isconst() function returning + * true/false, they could at least assert a contractual requirement for + * non-const buffers when needed. + */ +#define ISC__BUFFER_INIT(_b, _base, _length) \ + do { \ + (_b)->base = _base; \ + (_b)->length = (_length); \ + (_b)->used = 0; \ + (_b)->current = 0; \ + (_b)->active = 0; \ + (_b)->mctx = NULL; \ + ISC_LINK_INIT(_b, link); \ + (_b)->magic = ISC_BUFFER_MAGIC; \ + (_b)->autore = false; \ + } while (0) + +#define ISC__BUFFER_INITNULL(_b) ISC__BUFFER_INIT(_b, NULL, 0) + +#define ISC__BUFFER_INVALIDATE(_b) \ + do { \ + (_b)->magic = 0; \ + (_b)->base = NULL; \ + (_b)->length = 0; \ + (_b)->used = 0; \ + (_b)->current = 0; \ + (_b)->active = 0; \ + } while (0) + +#define ISC__BUFFER_REGION(_b, _r) \ + do { \ + (_r)->base = (_b)->base; \ + (_r)->length = (_b)->length; \ + } while (0) + +#define ISC__BUFFER_USEDREGION(_b, _r) \ + do { \ + (_r)->base = (_b)->base; \ + (_r)->length = (_b)->used; \ + } while (0) + +#define ISC__BUFFER_AVAILABLEREGION(_b, _r) \ + do { \ + (_r)->base = isc_buffer_used(_b); \ + (_r)->length = isc_buffer_availablelength(_b); \ + } while (0) + +#define ISC__BUFFER_ADD(_b, _n) \ + do { \ + (_b)->used += (_n); \ + } while (0) + +#define ISC__BUFFER_SUBTRACT(_b, _n) \ + do { \ + (_b)->used -= (_n); \ + if ((_b)->current > (_b)->used) \ + (_b)->current = (_b)->used; \ + if ((_b)->active > (_b)->used) \ + (_b)->active = (_b)->used; \ + } while (0) + +#define ISC__BUFFER_CLEAR(_b) \ + do { \ + (_b)->used = 0; \ + (_b)->current = 0; \ + (_b)->active = 0; \ + } while (0) + +#define ISC__BUFFER_CONSUMEDREGION(_b, _r) \ + do { \ + (_r)->base = (_b)->base; \ + (_r)->length = (_b)->current; \ + } while (0) + +#define ISC__BUFFER_REMAININGREGION(_b, _r) \ + do { \ + (_r)->base = isc_buffer_current(_b); \ + (_r)->length = isc_buffer_remaininglength(_b); \ + } while (0) + +#define ISC__BUFFER_ACTIVEREGION(_b, _r) \ + do { \ + if ((_b)->current < (_b)->active) { \ + (_r)->base = isc_buffer_current(_b); \ + (_r)->length = isc_buffer_activelength(_b); \ + } else { \ + (_r)->base = NULL; \ + (_r)->length = 0; \ + } \ + } while (0) + +#define ISC__BUFFER_SETACTIVE(_b, _n) \ + do { \ + (_b)->active = (_b)->current + (_n); \ + } while (0) + +#define ISC__BUFFER_FIRST(_b) \ + do { \ + (_b)->current = 0; \ + } while (0) + +#define ISC__BUFFER_FORWARD(_b, _n) \ + do { \ + (_b)->current += (_n); \ + } while (0) + +#define ISC__BUFFER_BACK(_b, _n) \ + do { \ + (_b)->current -= (_n); \ + } while (0) + +#define ISC__BUFFER_PUTMEM(_b, _base, _length) \ + do { \ + if (ISC_UNLIKELY((_b)->autore)) { \ + isc_buffer_t *_tmp = _b; \ + ISC_REQUIRE(isc_buffer_reserve(&_tmp, _length) \ + == ISC_R_SUCCESS); \ + } \ + ISC_REQUIRE(isc_buffer_availablelength(_b) >= (unsigned int) _length); \ + if (_length > 0U) { \ + memmove(isc_buffer_used(_b), (_base), (_length)); \ + (_b)->used += (_length); \ + } \ + } while (0) + +#define ISC__BUFFER_PUTSTR(_b, _source) \ + do { \ + unsigned int _length; \ + unsigned char *_cp; \ + _length = (unsigned int)strlen(_source); \ + if (ISC_UNLIKELY((_b)->autore)) { \ + isc_buffer_t *_tmp = _b; \ + ISC_REQUIRE(isc_buffer_reserve(&_tmp, _length) \ + == ISC_R_SUCCESS); \ + } \ + ISC_REQUIRE(isc_buffer_availablelength(_b) >= _length); \ + _cp = isc_buffer_used(_b); \ + memmove(_cp, (_source), _length); \ + (_b)->used += (_length); \ + } while (0) + +#define ISC__BUFFER_PUTUINT8(_b, _val) \ + do { \ + unsigned char *_cp; \ + /* evaluate (_val) only once */ \ + uint8_t _val2 = (_val); \ + if (ISC_UNLIKELY((_b)->autore)) { \ + isc_buffer_t *_tmp = _b; \ + ISC_REQUIRE(isc_buffer_reserve(&_tmp, 1) \ + == ISC_R_SUCCESS); \ + } \ + ISC_REQUIRE(isc_buffer_availablelength(_b) >= 1U); \ + _cp = isc_buffer_used(_b); \ + (_b)->used++; \ + _cp[0] = _val2; \ + } while (0) + +#define ISC__BUFFER_PUTUINT16(_b, _val) \ + do { \ + unsigned char *_cp; \ + /* evaluate (_val) only once */ \ + uint16_t _val2 = (_val); \ + if (ISC_UNLIKELY((_b)->autore)) { \ + isc_buffer_t *_tmp = _b; \ + ISC_REQUIRE(isc_buffer_reserve(&_tmp, 2) \ + == ISC_R_SUCCESS); \ + } \ + ISC_REQUIRE(isc_buffer_availablelength(_b) >= 2U); \ + _cp = isc_buffer_used(_b); \ + (_b)->used += 2; \ + _cp[0] = _val2 >> 8; \ + _cp[1] = _val2; \ + } while (0) + +#define ISC__BUFFER_PUTUINT24(_b, _val) \ + do { \ + unsigned char *_cp; \ + /* evaluate (_val) only once */ \ + uint32_t _val2 = (_val); \ + if (ISC_UNLIKELY((_b)->autore)) { \ + isc_buffer_t *_tmp = _b; \ + ISC_REQUIRE(isc_buffer_reserve(&_tmp, 3) \ + == ISC_R_SUCCESS); \ + } \ + ISC_REQUIRE(isc_buffer_availablelength(_b) >= 3U); \ + _cp = isc_buffer_used(_b); \ + (_b)->used += 3; \ + _cp[0] = _val2 >> 16; \ + _cp[1] = _val2 >> 8; \ + _cp[2] = _val2; \ + } while (0) + +#define ISC__BUFFER_PUTUINT32(_b, _val) \ + do { \ + unsigned char *_cp; \ + /* evaluate (_val) only once */ \ + uint32_t _val2 = (_val); \ + if (ISC_UNLIKELY((_b)->autore)) { \ + isc_buffer_t *_tmp = _b; \ + ISC_REQUIRE(isc_buffer_reserve(&_tmp, 4) \ + == ISC_R_SUCCESS); \ + } \ + ISC_REQUIRE(isc_buffer_availablelength(_b) >= 4U); \ + _cp = isc_buffer_used(_b); \ + (_b)->used += 4; \ + _cp[0] = _val2 >> 24; \ + _cp[1] = _val2 >> 16; \ + _cp[2] = _val2 >> 8; \ + _cp[3] = _val2; \ + } while (0) + +#if defined(ISC_BUFFER_USEINLINE) +#define isc_buffer_init ISC__BUFFER_INIT +#define isc_buffer_initnull ISC__BUFFER_INITNULL +#define isc_buffer_invalidate ISC__BUFFER_INVALIDATE +#define isc_buffer_region ISC__BUFFER_REGION +#define isc_buffer_usedregion ISC__BUFFER_USEDREGION +#define isc_buffer_availableregion ISC__BUFFER_AVAILABLEREGION +#define isc_buffer_add ISC__BUFFER_ADD +#define isc_buffer_subtract ISC__BUFFER_SUBTRACT +#define isc_buffer_clear ISC__BUFFER_CLEAR +#define isc_buffer_consumedregion ISC__BUFFER_CONSUMEDREGION +#define isc_buffer_remainingregion ISC__BUFFER_REMAININGREGION +#define isc_buffer_activeregion ISC__BUFFER_ACTIVEREGION +#define isc_buffer_setactive ISC__BUFFER_SETACTIVE +#define isc_buffer_first ISC__BUFFER_FIRST +#define isc_buffer_forward ISC__BUFFER_FORWARD +#define isc_buffer_back ISC__BUFFER_BACK +#define isc_buffer_putmem ISC__BUFFER_PUTMEM +#define isc_buffer_putstr ISC__BUFFER_PUTSTR +#define isc_buffer_putuint8 ISC__BUFFER_PUTUINT8 +#define isc_buffer_putuint16 ISC__BUFFER_PUTUINT16 +#define isc_buffer_putuint24 ISC__BUFFER_PUTUINT24 +#define isc_buffer_putuint32 ISC__BUFFER_PUTUINT32 +#else +#define isc_buffer_init isc__buffer_init +#define isc_buffer_initnull isc__buffer_initnull +#define isc_buffer_invalidate isc__buffer_invalidate +#define isc_buffer_region isc__buffer_region +#define isc_buffer_usedregion isc__buffer_usedregion +#define isc_buffer_availableregion isc__buffer_availableregion +#define isc_buffer_add isc__buffer_add +#define isc_buffer_subtract isc__buffer_subtract +#define isc_buffer_clear isc__buffer_clear +#define isc_buffer_consumedregion isc__buffer_consumedregion +#define isc_buffer_remainingregion isc__buffer_remainingregion +#define isc_buffer_activeregion isc__buffer_activeregion +#define isc_buffer_setactive isc__buffer_setactive +#define isc_buffer_first isc__buffer_first +#define isc_buffer_forward isc__buffer_forward +#define isc_buffer_back isc__buffer_back +#define isc_buffer_putmem isc__buffer_putmem +#define isc_buffer_putstr isc__buffer_putstr +#define isc_buffer_putuint8 isc__buffer_putuint8 +#define isc_buffer_putuint16 isc__buffer_putuint16 +#define isc_buffer_putuint24 isc__buffer_putuint24 +#define isc_buffer_putuint32 isc__buffer_putuint32 +#endif + +#define isc_buffer_constinit(_b, _d, _l) \ + do { \ + union { void *_var; const void *_const; } _deconst; \ + _deconst._const = (_d); \ + isc_buffer_init((_b), _deconst._var, (_l)); \ + } while (0) + +/* + * No inline method for this one (yet). + */ +#define isc_buffer_putuint48 isc__buffer_putuint48 + +#endif /* ISC_BUFFER_H */ diff --git a/lib/isc/include/isc/bufferlist.h b/lib/isc/include/isc/bufferlist.h new file mode 100644 index 0000000..ffb4986 --- /dev/null +++ b/lib/isc/include/isc/bufferlist.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_BUFFERLIST_H +#define ISC_BUFFERLIST_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/bufferlist.h + * + * + *\brief Buffer lists have no synchronization. Clients must ensure exclusive + * access. + * + * \li Reliability: + * No anticipated impact. + + * \li Security: + * No anticipated impact. + * + * \li Standards: + * None. + */ + +/*** + *** Imports + ***/ + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/*** + *** Functions + ***/ + +unsigned int +isc_bufferlist_usedcount(isc_bufferlist_t *bl); +/*!< + * \brief Return the length of the sum of all used regions of all buffers in + * the buffer list 'bl' + * + * Requires: + * + *\li 'bl' is not NULL. + * + * Returns: + *\li sum of all used regions' lengths. + */ + +unsigned int +isc_bufferlist_availablecount(isc_bufferlist_t *bl); +/*!< + * \brief Return the length of the sum of all available regions of all buffers in + * the buffer list 'bl' + * + * Requires: + * + *\li 'bl' is not NULL. + * + * Returns: + *\li sum of all available regions' lengths. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_BUFFERLIST_H */ diff --git a/lib/isc/include/isc/commandline.h b/lib/isc/include/isc/commandline.h new file mode 100644 index 0000000..a1a256e --- /dev/null +++ b/lib/isc/include/isc/commandline.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_COMMANDLINE_H +#define ISC_COMMANDLINE_H 1 + +/*! \file isc/commandline.h */ + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/platform.h> +#include <isc/result.h> + +/*% Index into parent argv vector. */ +LIBISC_EXTERNAL_DATA extern int isc_commandline_index; +/*% Character checked for validity. */ +LIBISC_EXTERNAL_DATA extern int isc_commandline_option; +/*% Argument associated with option. */ +LIBISC_EXTERNAL_DATA extern char *isc_commandline_argument; +/*% For printing error messages. */ +LIBISC_EXTERNAL_DATA extern char *isc_commandline_progname; +/*% Print error message. */ +LIBISC_EXTERNAL_DATA extern bool isc_commandline_errprint; +/*% Reset getopt. */ +LIBISC_EXTERNAL_DATA extern bool isc_commandline_reset; + +ISC_LANG_BEGINDECLS + +int +isc_commandline_parse(int argc, char * const *argv, const char *options); +/*%< + * Parse a command line (similar to getopt()) + */ + +isc_result_t +isc_commandline_strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, + char ***argvp, unsigned int n); +/*%< + * Tokenize the string "s" into whitespace-separated words, + * returning the number of words in '*argcp' and an array + * of pointers to the words in '*argvp'. The caller + * must free the array using isc_mem_free(). The string + * is modified in-place. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_COMMANDLINE_H */ diff --git a/lib/isc/include/isc/counter.h b/lib/isc/include/isc/counter.h new file mode 100644 index 0000000..28d9c79 --- /dev/null +++ b/lib/isc/include/isc/counter.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_COUNTER_H +#define ISC_COUNTER_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/counter.h + * + * \brief The isc_counter_t object is a simplified version of the + * isc_quota_t object; it tracks the consumption of limited + * resources, returning an error condition when the quota is + * exceeded. However, unlike isc_quota_t, attaching and detaching + * from a counter object does not increment or decrement the counter. + */ + +/*** + *** Imports. + ***/ + +#include <isc/lang.h> +#include <isc/mutex.h> +#include <isc/types.h> + +/***** + ***** Types. + *****/ + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_counter_create(isc_mem_t *mctx, int limit, isc_counter_t **counterp); +/*%< + * Allocate and initialize a counter object. + */ + +isc_result_t +isc_counter_increment(isc_counter_t *counter); +/*%< + * Increment the counter. + * + * If the counter limit is nonzero and has been reached, then + * return ISC_R_QUOTA, otherwise ISC_R_SUCCESS. (The counter is + * incremented regardless of return value.) + */ + +unsigned int +isc_counter_used(isc_counter_t *counter); +/*%< + * Return the current counter value. + */ + +void +isc_counter_setlimit(isc_counter_t *counter, int limit); +/*%< + * Set the counter limit. + */ + +void +isc_counter_attach(isc_counter_t *source, isc_counter_t **targetp); +/*%< + * Attach to a counter object, increasing its reference counter. + */ + +void +isc_counter_detach(isc_counter_t **counterp); +/*%< + * Detach (and destroy if reference counter has dropped to zero) + * a counter object. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_COUNTER_H */ diff --git a/lib/isc/include/isc/crc64.h b/lib/isc/include/isc/crc64.h new file mode 100644 index 0000000..ed5860b --- /dev/null +++ b/lib/isc/include/isc/crc64.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_CRC64_H +#define ISC_CRC64_H 1 + +/*! \file isc/crc64.h + * \brief CRC64 in C + */ + +#include <inttypes.h> + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +void +isc_crc64_init(uint64_t *crc); +/*% + * Initialize a new CRC. + * + * Requires: + * * 'crc' is not NULL. + */ + +void +isc_crc64_update(uint64_t *crc, const void *data, size_t len); +/*% + * Add data to the CRC. + * + * Requires: + * * 'crc' is not NULL. + * * 'data' is not NULL. + */ + +void +isc_crc64_final(uint64_t *crc); +/*% + * Finalize the CRC. + * + * Requires: + * * 'crc' is not NULL. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_CRC64_H */ diff --git a/lib/isc/include/isc/deprecated.h b/lib/isc/include/isc/deprecated.h new file mode 100644 index 0000000..d485e20 --- /dev/null +++ b/lib/isc/include/isc/deprecated.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_DEPRECATED_H +#define ISC_DEPRECATED_H + +#if (__GNUC__ + 0) > 3 +#define ISC_DEPRECATED __attribute__((deprecated)) +#else +#define ISC_DEPRECATED /* none */ +#endif /* __GNUC__ > 3*/ + +#endif diff --git a/lib/isc/include/isc/entropy.h b/lib/isc/include/isc/entropy.h new file mode 100644 index 0000000..4bba8e1 --- /dev/null +++ b/lib/isc/include/isc/entropy.h @@ -0,0 +1,309 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: entropy.h,v 1.35 2009/10/19 02:37:08 marka Exp $ */ + +#ifndef ISC_ENTROPY_H +#define ISC_ENTROPY_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/entropy.h + * \brief The entropy API + * + * \li MP: + * The entropy object is locked internally. All callbacks into + * application-provided functions (for setup, gathering, and + * shutdown of sources) are guaranteed to be called with the + * entropy API lock held. This means these functions are + * not permitted to call back into the entropy API. + * + * \li Reliability: + * No anticipated impact. + * + * \li Resources: + * A buffer, used as an entropy pool. + * + * \li Security: + * While this code is believed to implement good entropy gathering + * and distribution, it has not been reviewed by a cryptographic + * expert. + * Since the added entropy is only as good as the sources used, + * this module could hand out bad data and never know it. + * + * \li Standards: + * None. + */ + +/*** + *** Imports + ***/ + +#include <stdbool.h> +#include <stdio.h> + +#include <isc/lang.h> +#include <isc/types.h> + +/*@{*/ +/*% Entropy callback function. */ +typedef isc_result_t (*isc_entropystart_t)(isc_entropysource_t *source, + void *arg, bool blocking); +typedef isc_result_t (*isc_entropyget_t)(isc_entropysource_t *source, + void *arg, bool blocking); +typedef void (*isc_entropystop_t)(isc_entropysource_t *source, void *arg); +/*@}*/ + +/*** + *** Flags. + ***/ + +/*! + * \brief + * Extract only "good" data; return failure if there is not enough + * data available and there are no sources which we can poll to get + * data, or those sources are empty. + * + * + */ +#define ISC_ENTROPY_GOODONLY 0x00000001U +/*! + * \brief + * Extract as much good data as possible, but if there isn't enough + * at hand, return what is available. This flag only makes sense + * when used with _GOODONLY. + */ +#define ISC_ENTROPY_PARTIAL 0x00000002U +/*! + * \brief + * Block the task until data is available. This is contrary to the + * ISC task system, where tasks should never block. However, if + * this is a special purpose application where blocking a task is + * acceptable (say, an offline zone signer) this flag may be set. + * This flag only makes sense when used with _GOODONLY, and will + * block regardless of the setting for _PARTIAL. + */ +#define ISC_ENTROPY_BLOCKING 0x00000004U + +/*! + * \brief + * Estimate the amount of entropy contained in the sample pool. + * If this is not set, the source will be gathered and periodically + * mixed into the entropy pool, but no increment in contained entropy + * will be assumed. This flag only makes sense on sample sources. + */ +#define ISC_ENTROPYSOURCE_ESTIMATE 0x00000001U + +/* + * For use with isc_entropy_usebestsource(). + */ +/*! + * \brief + * Use the keyboard as the only entropy source. + */ +#define ISC_ENTROPY_KEYBOARDYES 1 +/*! + * \brief + * Never use the keyboard as an entropy source. + */ +#define ISC_ENTROPY_KEYBOARDNO 2 +/*! + * \brief + * Use the keyboard as an entropy source only if opening the + * random device fails. + */ +#define ISC_ENTROPY_KEYBOARDMAYBE 3 + +ISC_LANG_BEGINDECLS + +/*** + *** Functions + ***/ + +isc_result_t +isc_entropy_create(isc_mem_t *mctx, isc_entropy_t **entp); +/*!< + * \brief Create a new entropy object. + */ + +void +isc_entropy_attach(isc_entropy_t *ent, isc_entropy_t **entp); +/*!< + * Attaches to an entropy object. + */ + +void +isc_entropy_detach(isc_entropy_t **entp); +/*!< + * \brief Detaches from an entropy object. + */ + +isc_result_t +isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname); +/*!< + * \brief Create a new entropy source from a file. + * + * The file is assumed to contain good randomness, and will be mixed directly + * into the pool with every byte adding 8 bits of entropy. + * + * The file will be put into non-blocking mode, so it may be a device file, + * such as /dev/random. /dev/urandom should not be used here if it can + * be avoided, since it will always provide data even if it isn't good. + * We will make as much pseudorandom data as we need internally if our + * caller asks for it. + * + * If we hit end-of-file, we will stop reading from this source. Callers + * who require strong random data will get failure when our pool drains. + * The file will never be opened/read again once EOF is reached. + */ + +void +isc_entropy_destroysource(isc_entropysource_t **sourcep); +/*!< + * \brief Removes an entropy source from the entropy system. + */ + +isc_result_t +isc_entropy_createsamplesource(isc_entropy_t *ent, + isc_entropysource_t **sourcep); +/*!< + * \brief Create an entropy source that consists of samples. Each sample is + * added to the source via isc_entropy_addsamples(), below. + */ + +isc_result_t +isc_entropy_createcallbacksource(isc_entropy_t *ent, + isc_entropystart_t start, + isc_entropyget_t get, + isc_entropystop_t stop, + void *arg, + isc_entropysource_t **sourcep); +/*!< + * \brief Create an entropy source that is polled via a callback. + * + * This would + * be used when keyboard input is used, or a GUI input method. It can + * also be used to hook in any external entropy source. + * + * Samples are added via isc_entropy_addcallbacksample(), below. + * _addcallbacksample() is the only function which may be called from + * within an entropy API callback function. + */ + +void +isc_entropy_stopcallbacksources(isc_entropy_t *ent); +/*!< + * \brief Call the stop functions for callback sources that have had their + * start functions called. + */ + +/*@{*/ +isc_result_t +isc_entropy_addcallbacksample(isc_entropysource_t *source, uint32_t sample, + uint32_t extra); +isc_result_t +isc_entropy_addsample(isc_entropysource_t *source, uint32_t sample, + uint32_t extra); +/*!< + * \brief Add a sample to the sample source. + * + * The sample MUST be a timestamp + * that increases over time, with the exception of wrap-around for + * extremely high resolution timers which will quickly wrap-around + * a 32-bit integer. + * + * The "extra" parameter is used only to add a bit more unpredictable + * data. It is not used other than included in the hash of samples. + * + * When in an entropy API callback function, _addcallbacksource() must be + * used. At all other times, _addsample() must be used. + */ +/*@}*/ + +isc_result_t +isc_entropy_getdata(isc_entropy_t *ent, void *data, unsigned int length, + unsigned int *returned, unsigned int flags); +/*!< + * \brief Extract data from the entropy pool. This may load the pool from various + * sources. + * + * Do this by stiring the pool and returning a part of hash as randomness. + * Note that no secrets are given away here since parts of the hash are + * xored together before returned. + * + * Honor the request from the caller to only return good data, any data, + * etc. + */ + +void +isc_entropy_putdata(isc_entropy_t *ent, void *data, unsigned int length, + uint32_t entropy); +/*!< + * \brief Add "length" bytes in "data" to the entropy pool, incrementing the + * pool's entropy count by "entropy." + * + * These bytes will prime the pseudorandom portion even if no entropy is + * actually added. + */ + +void +isc_entropy_stats(isc_entropy_t *ent, FILE *out); +/*!< + * \brief Dump some (trivial) stats to the stdio stream "out". + */ + +unsigned int +isc_entropy_status(isc_entropy_t *end); +/* + * Returns the number of bits the pool currently contains. This is just + * an estimate. + */ + +isc_result_t +isc_entropy_usebestsource(isc_entropy_t *ectx, isc_entropysource_t **source, + const char *randomfile, int use_keyboard); +/*!< + * \brief Use whatever source of entropy is best. + * + * Notes: + *\li If "randomfile" is not NULL, open it with + * isc_entropy_createfilesource(). + * + *\li If "randomfile" is NULL and the system's random device was detected + * when the program was configured and built, open that device with + * isc_entropy_createfilesource(). + * + *\li If "use_keyboard" is #ISC_ENTROPY_KEYBOARDYES, then always open + * the keyboard as an entropy source (possibly in addition to + * "randomfile" or the random device). + * + *\li If "use_keyboard" is #ISC_ENTROPY_KEYBOARDMAYBE, open the keyboard only + * if opening the random file/device fails. A message will be + * printed describing the need for keyboard input. + * + *\li If "use_keyboard" is #ISC_ENTROPY_KEYBOARDNO, the keyboard will + * never be opened. + * + * Returns: + *\li #ISC_R_SUCCESS if at least one source of entropy could be started. + * + *\li #ISC_R_NOENTROPY if use_keyboard is #ISC_ENTROPY_KEYBOARDNO and + * there is no random device pathname compiled into the program. + * + *\li A return code from isc_entropy_createfilesource() or + * isc_entropy_createcallbacksource(). + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_ENTROPY_H */ diff --git a/lib/isc/include/isc/errno.h b/lib/isc/include/isc/errno.h new file mode 100644 index 0000000..7777b8d --- /dev/null +++ b/lib/isc/include/isc/errno.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_ERRNO_H +#define ISC_ERRNO_H 1 + +/*! \file isc/file.h */ + +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_errno_toresult(int err); +/*!< + * \brief Convert a POSIX errno value to an ISC result code. + */ +ISC_LANG_ENDDECLS + +#endif /* ISC_ERRNO_H */ diff --git a/lib/isc/include/isc/error.h b/lib/isc/include/isc/error.h new file mode 100644 index 0000000..87bb6ba --- /dev/null +++ b/lib/isc/include/isc/error.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_ERROR_H +#define ISC_ERROR_H 1 + +/*! \file isc/error.h */ + +#include <stdarg.h> + +#include <isc/formatcheck.h> +#include <isc/lang.h> +#include <isc/likely.h> +#include <isc/platform.h> + +ISC_LANG_BEGINDECLS + +typedef void (*isc_errorcallback_t)(const char *, int, const char *, va_list); + +/*% set unexpected error */ +void +isc_error_setunexpected(isc_errorcallback_t); + +/*% set fatal error */ +void +isc_error_setfatal(isc_errorcallback_t); + +/*% unexpected error */ +void +isc_error_unexpected(const char *, int, const char *, ...) + ISC_FORMAT_PRINTF(3, 4); + +/*% fatal error */ +ISC_PLATFORM_NORETURN_PRE void +isc_error_fatal(const char *, int, const char *, ...) +ISC_FORMAT_PRINTF(3, 4) ISC_PLATFORM_NORETURN_POST; + +/*% runtimecheck error */ +ISC_PLATFORM_NORETURN_PRE void +isc_error_runtimecheck(const char *, int, const char *) ISC_PLATFORM_NORETURN_POST; + +#define ISC_ERROR_RUNTIMECHECK(cond) \ + ((void) (ISC_LIKELY(cond) || \ + ((isc_error_runtimecheck)(__FILE__, __LINE__, #cond), 0))) + +ISC_LANG_ENDDECLS + +#endif /* ISC_ERROR_H */ diff --git a/lib/isc/include/isc/event.h b/lib/isc/include/isc/event.h new file mode 100644 index 0000000..22f735d --- /dev/null +++ b/lib/isc/include/isc/event.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_EVENT_H +#define ISC_EVENT_H 1 + +/*! \file isc/event.h */ + +#include <isc/lang.h> +#include <isc/types.h> + +/***** + ***** Events. + *****/ + +typedef void (*isc_eventdestructor_t)(isc_event_t *); + +#define ISC_EVENT_COMMON(ltype) \ + size_t ev_size; \ + unsigned int ev_attributes; \ + void * ev_tag; \ + isc_eventtype_t ev_type; \ + isc_taskaction_t ev_action; \ + void * ev_arg; \ + void * ev_sender; \ + isc_eventdestructor_t ev_destroy; \ + void * ev_destroy_arg; \ + ISC_LINK(ltype) ev_link; \ + ISC_LINK(ltype) ev_ratelink + +/*% + * Attributes matching a mask of 0x000000ff are reserved for the task library's + * definition. Attributes of 0xffffff00 may be used by the application + * or non-ISC libraries. + */ +#define ISC_EVENTATTR_NOPURGE 0x00000001 + +/*% + * The ISC_EVENTATTR_CANCELED attribute is intended to indicate + * that an event is delivered as a result of a canceled operation + * rather than successful completion, by mutual agreement + * between the sender and receiver. It is not set or used by + * the task system. + */ +#define ISC_EVENTATTR_CANCELED 0x00000002 + +#define ISC_EVENT_INIT(event, sz, at, ta, ty, ac, ar, sn, df, da) \ +do { \ + (event)->ev_size = (sz); \ + (event)->ev_attributes = (at); \ + (event)->ev_tag = (ta); \ + (event)->ev_type = (ty); \ + (event)->ev_action = (ac); \ + (event)->ev_arg = (ar); \ + (event)->ev_sender = (sn); \ + (event)->ev_destroy = (df); \ + (event)->ev_destroy_arg = (da); \ + ISC_LINK_INIT((event), ev_link); \ + ISC_LINK_INIT((event), ev_ratelink); \ +} while (0) + +/*% + * This structure is public because "subclassing" it may be useful when + * defining new event types. + */ +struct isc_event { + ISC_EVENT_COMMON(struct isc_event); +}; + +#define ISC_EVENTTYPE_FIRSTEVENT 0x00000000 +#define ISC_EVENTTYPE_LASTEVENT 0xffffffff + +#define ISC_EVENT_PTR(p) ((isc_event_t **)(void *)(p)) + +ISC_LANG_BEGINDECLS + +isc_event_t * +isc_event_allocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type, + isc_taskaction_t action, void *arg, size_t size); +isc_event_t * +isc_event_constallocate(isc_mem_t *mctx, void *sender, isc_eventtype_t type, + isc_taskaction_t action, const void *arg, size_t size); +/*%< + * Allocate an event structure. + * + * Allocate and initialize in a structure with initial elements + * defined by: + * + * \code + * struct { + * ISC_EVENT_COMMON(struct isc_event); + * ... + * }; + * \endcode + * + * Requires: + *\li 'size' >= sizeof(struct isc_event) + *\li 'action' to be non NULL + * + * Returns: + *\li a pointer to a initialized structure of the requested size. + *\li NULL if unable to allocate memory. + */ + +void +isc_event_free(isc_event_t **); + +ISC_LANG_ENDDECLS + +#endif /* ISC_EVENT_H */ diff --git a/lib/isc/include/isc/eventclass.h b/lib/isc/include/isc/eventclass.h new file mode 100644 index 0000000..15ad345 --- /dev/null +++ b/lib/isc/include/isc/eventclass.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_EVENTCLASS_H +#define ISC_EVENTCLASS_H 1 + +/*! \file isc/eventclass.h + ***** Registry of Predefined Event Type Classes + *****/ + +/*% + * An event class is an unsigned 16 bit number. Each class may contain up + * to 65536 events. An event type is formed by adding the event number + * within the class to the class number. + * + */ + +#define ISC_EVENTCLASS(eclass) ((eclass) << 16) + +/*@{*/ +/*! + * Classes < 1024 are reserved for ISC use. + * Event classes >= 1024 and <= 65535 are reserved for application use. + */ + +#define ISC_EVENTCLASS_TASK ISC_EVENTCLASS(0) +#define ISC_EVENTCLASS_TIMER ISC_EVENTCLASS(1) +#define ISC_EVENTCLASS_SOCKET ISC_EVENTCLASS(2) +#define ISC_EVENTCLASS_FILE ISC_EVENTCLASS(3) +#define ISC_EVENTCLASS_DNS ISC_EVENTCLASS(4) +#define ISC_EVENTCLASS_APP ISC_EVENTCLASS(5) +#define ISC_EVENTCLASS_OMAPI ISC_EVENTCLASS(6) +#define ISC_EVENTCLASS_RATELIMITER ISC_EVENTCLASS(7) +#define ISC_EVENTCLASS_ISCCC ISC_EVENTCLASS(8) +/*@}*/ + +#endif /* ISC_EVENTCLASS_H */ diff --git a/lib/isc/include/isc/file.h b/lib/isc/include/isc/file.h new file mode 100644 index 0000000..0797f02 --- /dev/null +++ b/lib/isc/include/isc/file.h @@ -0,0 +1,398 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_FILE_H +#define ISC_FILE_H 1 + +/*! \file isc/file.h */ + +#include <stdbool.h> +#include <stdio.h> + +#include <isc/lang.h> +#include <isc/stat.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_file_settime(const char *file, isc_time_t *time); + +isc_result_t +isc_file_mode(const char *file, mode_t *modep); + +isc_result_t +isc_file_getmodtime(const char *file, isc_time_t *time); +/*!< + * \brief Get the time of last modification of a file. + * + * Notes: + *\li The time that is set is relative to the (OS-specific) epoch, as are + * all isc_time_t structures. + * + * Requires: + *\li file != NULL. + *\li time != NULL. + * + * Ensures: + *\li If the file could not be accessed, 'time' is unchanged. + * + * Returns: + *\li #ISC_R_SUCCESS + * Success. + *\li #ISC_R_NOTFOUND + * No such file exists. + *\li #ISC_R_INVALIDFILE + * The path specified was not usable by the operating system. + *\li #ISC_R_NOPERM + * The file's metainformation could not be retrieved because + * permission was denied to some part of the file's path. + *\li #ISC_R_IOERROR + * Hardware error interacting with the filesystem. + *\li #ISC_R_UNEXPECTED + * Something totally unexpected happened. + * + */ + +isc_result_t +isc_file_mktemplate(const char *path, char *buf, size_t buflen); +/*!< + * \brief Generate a template string suitable for use with isc_file_openunique(). + * + * Notes: + *\li This function is intended to make creating temporary files + * portable between different operating systems. + * + *\li The path is prepended to an implementation-defined string and + * placed into buf. The string has no path characters in it, + * and its maximum length is 14 characters plus a NUL. Thus + * buflen should be at least strlen(path) + 15 characters or + * an error will be returned. + * + * Requires: + *\li buf != NULL. + * + * Ensures: + *\li If result == #ISC_R_SUCCESS: + * buf contains a string suitable for use as the template argument + * to isc_file_openunique(). + * + *\li If result != #ISC_R_SUCCESS: + * buf is unchanged. + * + * Returns: + *\li #ISC_R_SUCCESS Success. + *\li #ISC_R_NOSPACE buflen indicates buf is too small for the catenation + * of the path with the internal template string. + */ + +isc_result_t +isc_file_openunique(char *templet, FILE **fp); +isc_result_t +isc_file_openuniqueprivate(char *templet, FILE **fp); +isc_result_t +isc_file_openuniquemode(char *templet, int mode, FILE **fp); +isc_result_t +isc_file_bopenunique(char *templet, FILE **fp); +isc_result_t +isc_file_bopenuniqueprivate(char *templet, FILE **fp); +isc_result_t +isc_file_bopenuniquemode(char *templet, int mode, FILE **fp); +/*!< + * \brief Create and open a file with a unique name based on 'templet'. + * isc_file_bopen*() open the file in binary mode in Windows. + * isc_file_open*() open the file in text mode in Windows. + * + * Notes: + *\li 'template' is a reserved work in C++. If you want to complain + * about the spelling of 'templet', first look it up in the + * Merriam-Webster English dictionary. (http://www.m-w.com/) + * + *\li This function works by using the template to generate file names. + * The template must be a writable string, as it is modified in place. + * Trailing X characters in the file name (full file name on Unix, + * basename on Win32 -- eg, tmp-XXXXXX vs XXXXXX.tmp, respectively) + * are replaced with ASCII characters until a non-existent filename + * is found. If the template does not include pathname information, + * the files in the working directory of the program are searched. + * + *\li isc_file_mktemplate is a good, portable way to get a template. + * + * Requires: + *\li 'fp' is non-NULL and '*fp' is NULL. + * + *\li 'template' is non-NULL, and of a form suitable for use by + * the system as described above. + * + * Ensures: + *\li If result is #ISC_R_SUCCESS: + * *fp points to an stream opening in stdio's "w+" mode. + * + *\li If result is not #ISC_R_SUCCESS: + * *fp is NULL. + * + * No file is open. Even if one was created (but unable + * to be reopened as a stdio FILE pointer) then it has been + * removed. + * + *\li This function does *not* ensure that the template string has not been + * modified, even if the operation was unsuccessful. + * + * Returns: + *\li #ISC_R_SUCCESS + * Success. + *\li #ISC_R_EXISTS + * No file with a unique name could be created based on the + * template. + *\li #ISC_R_INVALIDFILE + * The path specified was not usable by the operating system. + *\li #ISC_R_NOPERM + * The file could not be created because permission was denied + * to some part of the file's path. + *\li #ISC_R_IOERROR + * Hardware error interacting with the filesystem. + *\li #ISC_R_UNEXPECTED + * Something totally unexpected happened. + */ + +isc_result_t +isc_file_remove(const char *filename); +/*!< + * \brief Remove the file named by 'filename'. + */ + +isc_result_t +isc_file_rename(const char *oldname, const char *newname); +/*!< + * \brief Rename the file 'oldname' to 'newname'. + */ + +bool +isc_file_exists(const char *pathname); +/*!< + * \brief Return #true if the calling process can tell that the given file exists. + * Will not return true if the calling process has insufficient privileges + * to search the entire path. + */ + +bool +isc_file_isabsolute(const char *filename); +/*!< + * \brief Return #true if the given file name is absolute. + */ + +isc_result_t +isc_file_isplainfile(const char *name); + +isc_result_t +isc_file_isplainfilefd(int fd); +/*!< + * \brief Check that the file is a plain file + * + * Returns: + *\li #ISC_R_SUCCESS + * Success. The file is a plain file. + *\li #ISC_R_INVALIDFILE + * The path specified was not usable by the operating system. + *\li #ISC_R_FILENOTFOUND + * The file does not exist. This return code comes from + * errno=ENOENT when stat returns -1. This code is mentioned + * here, because in logconf.c, it is the one rcode that is + * permitted in addition to ISC_R_SUCCESS. This is done since + * the next call in logconf.c is to isc_stdio_open(), which + * will create the file if it can. + *\li other ISC_R_* errors translated from errno + * These occur when stat returns -1 and an errno. + */ + +isc_result_t +isc_file_isdirectory(const char *name); +/*!< + * \brief Check that 'name' exists and is a directory. + * + * Returns: + *\li #ISC_R_SUCCESS + * Success, file is a directory. + *\li #ISC_R_INVALIDFILE + * File is not a directory. + *\li #ISC_R_FILENOTFOUND + * File does not exist. + *\li other ISC_R_* errors translated from errno + * These occur when stat returns -1 and an errno. + */ + +bool +isc_file_iscurrentdir(const char *filename); +/*!< + * \brief Return #true if the given file name is the current directory ("."). + */ + +bool +isc_file_ischdiridempotent(const char *filename); +/*%< + * Return #true if calling chdir(filename) multiple times will give + * the same result as calling it once. + */ + +const char * +isc_file_basename(const char *filename); +/*%< + * Return the final component of the path in the file name. + */ + +isc_result_t +isc_file_progname(const char *filename, char *buf, size_t buflen); +/*!< + * \brief Given an operating system specific file name "filename" + * referring to a program, return the canonical program name. + * + * Any directory prefix or executable file name extension (if + * used on the OS in case) is stripped. On systems where program + * names are case insensitive, the name is canonicalized to all + * lower case. The name is written to 'buf', an array of 'buflen' + * chars, and null terminated. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOSPACE The name did not fit in 'buf'. + */ + +isc_result_t +isc_file_template(const char *path, const char *templet, char *buf, + size_t buflen); +/*%< + * Create an OS specific template using 'path' to define the directory + * 'templet' to describe the filename and store the result in 'buf' + * such that path can be renamed to buf atomically. + */ + +isc_result_t +isc_file_renameunique(const char *file, char *templet); +/*%< + * Rename 'file' using 'templet' as a template for the new file name. + */ + +isc_result_t +isc_file_absolutepath(const char *filename, char *path, size_t pathlen); +/*%< + * Given a file name, return the fully qualified path to the file. + */ + +/* + * XXX We should also have a isc_file_writeeopen() function + * for safely open a file in a publicly writable directory + * (see write_open() in BIND 8's ns_config.c). + */ + +isc_result_t +isc_file_truncate(const char *filename, isc_offset_t size); +/*%< + * Truncate/extend the file specified to 'size' bytes. + */ + +isc_result_t +isc_file_safecreate(const char *filename, FILE **fp); +/*%< + * Open 'filename' for writing, truncating if necessary. Ensure that + * if it existed it was a normal file. If creating the file, ensure + * that only the owner can read/write it. + */ + +isc_result_t +isc_file_splitpath(isc_mem_t *mctx, const char *path, + char **dirname, char const **basename); +/*%< + * Split a path into dirname and basename. If 'path' contains no slash + * (or, on windows, backslash), then '*dirname' is set to ".". + * + * Allocates memory for '*dirname', which can be freed with isc_mem_free(). + * + * Returns: + * - ISC_R_SUCCESS on success + * - ISC_R_INVALIDFILE if 'path' is empty or ends with '/' + * - ISC_R_NOMEMORY if unable to allocate memory + */ + +isc_result_t +isc_file_getsize(const char *file, off_t *size); +/*%< + * Return the size of the file (stored in the parameter pointed + * to by 'size') in bytes. + * + * Returns: + * - ISC_R_SUCCESS on success + */ + +isc_result_t +isc_file_getsizefd(int fd, off_t *size); +/*%< + * Return the size of the file (stored in the parameter pointed + * to by 'size') in bytes. + * + * Returns: + * - ISC_R_SUCCESS on success + */ + +void * +isc_file_mmap(void *addr, size_t len, int prot, + int flags, int fd, off_t offset); +/*%< + * Portable front-end to mmap(). If mmap() is not defined on this + * platform, then we simulate it by calling malloc() and read(). + * (In this event, the addr, prot, and flags parameters are ignored). + */ + +int +isc_file_munmap(void *addr, size_t len); +/*%< + * Portable front-end to munmap(). If munmap() is not defined on + * this platform, then we simply free the memory. + */ + +isc_result_t +isc_file_sanitize(const char *dir, const char *base, const char *ext, + char *path, size_t length); +/*%< + * Generate a sanitized filename, such as for MKEYS or NZF files. + * + * Historically, MKEYS and NZF files used SHA256 hashes of the view + * name for the filename; this was to deal with the possibility of + * forbidden characters such as "/" being in a view name, and to + * avoid problems with case-insensitive file systems. + * + * Given a basename 'base' and an extension 'ext', this function checks + * for the existence of file using the old-style name format in directory + * 'dir'. If found, it returns the path to that file. If there is no + * file already in place, a new pathname is generated; if the basename + * contains any excluded characters, then a truncated SHA256 hash is + * used, otherwise the basename is used. The path name is copied + * into 'path', which must point to a buffer of at least 'length' + * bytes. + * + * Requires: + * - base != NULL + * - path != NULL + * + * Returns: + * - ISC_R_SUCCESS on success + * - ISC_R_NOSPACE if the resulting path would be longer than 'length' + */ + +bool +isc_file_isdirwritable(const char *path); +/*%< + * Return true if the path is a directory and is writable + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_FILE_H */ diff --git a/lib/isc/include/isc/formatcheck.h b/lib/isc/include/isc/formatcheck.h new file mode 100644 index 0000000..162c16e --- /dev/null +++ b/lib/isc/include/isc/formatcheck.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_FORMATCHECK_H +#define ISC_FORMATCHECK_H 1 + +/*! \file isc/formatcheck.h */ + +/*% + * ISC_FORMAT_PRINTF(). + * + * \li fmt is the location of the format string parameter. + * \li args is the location of the first argument (or 0 for no argument checking). + * + * Note: + * \li The first parameter is 1, not 0. + */ +#ifdef __GNUC__ +#define ISC_FORMAT_PRINTF(fmt, args) __attribute__((__format__(__printf__, fmt, args))) +#else +#define ISC_FORMAT_PRINTF(fmt, args) +#endif + +#endif /* ISC_FORMATCHECK_H */ diff --git a/lib/isc/include/isc/fsaccess.h b/lib/isc/include/isc/fsaccess.h new file mode 100644 index 0000000..d842aaf --- /dev/null +++ b/lib/isc/include/isc/fsaccess.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_FSACCESS_H +#define ISC_FSACCESS_H 1 + +/*! \file isc/fsaccess.h + * \brief The ISC filesystem access module encapsulates the setting of file + * and directory access permissions into one API that is meant to be + * portable to multiple operating systems. + * + * The two primary operating system flavors that are initially accommodated + * are POSIX and Windows NT 4.0 and later. The Windows NT access model is + * considerable more flexible than POSIX's model (as much as I am loathe to + * admit it), and so the ISC API has a higher degree of complexity than would + * be needed to simply address POSIX's needs. + * + * The full breadth of NT's flexibility is not available either, for the + * present time. Much of it is to provide compatibility with what Unix + * programmers are expecting. This is also due to not yet really needing all + * of the functionality of an NT system (or, for that matter, a POSIX system) + * in BIND9, and so resolving how to handle the various incompatibilities has + * been a purely theoretical exercise with no operational experience to + * indicate how flawed the thinking may be. + * + * Some of the more notable dumbing down of NT for this API includes: + * + *\li Each of FILE_READ_DATA and FILE_READ_EA are set with #ISC_FSACCESS_READ. + * + * \li All of FILE_WRITE_DATA, FILE_WRITE_EA and FILE_APPEND_DATA are + * set with #ISC_FSACCESS_WRITE. FILE_WRITE_ATTRIBUTES is not set + * so as to be consistent with Unix, where only the owner of the file + * or the superuser can change the attributes/mode of a file. + * + * \li Both of FILE_ADD_FILE and FILE_ADD_SUBDIRECTORY are set with + * #ISC_FSACCESS_CREATECHILD. This is similar to setting the WRITE + * permission on a Unix directory. + * + * \li SYNCHRONIZE is always set for files and directories, unless someone + * can give me a reason why this is a bad idea. + * + * \li READ_CONTROL and FILE_READ_ATTRIBUTES are always set; this is + * consistent with Unix, where any file or directory can be stat()'d + * unless the directory path disallows complete access somewhere along + * the way. + * + * \li WRITE_DAC is only set for the owner. This too is consistent with + * Unix, and is tighter security than allowing anyone else to be + * able to set permissions. + * + * \li DELETE is only set for the owner. On Unix the ability to delete + * a file is controlled by the directory permissions, but it isn't + * currently clear to me what happens on NT if the directory has + * FILE_DELETE_CHILD set but a file within it does not have DELETE + * set. Always setting DELETE on the file/directory for the owner + * gives maximum flexibility to the owner without exposing the + * file to deletion by others. + * + * \li WRITE_OWNER is never set. This too is consistent with Unix, + * and is also tighter security than allowing anyone to change the + * ownership of the file apart from the superu..ahem, Administrator. + * + * \li Inheritance is set to NO_INHERITANCE. + * + * Unix's dumbing down includes: + * + * \li The sticky bit cannot be set. + * + * \li setuid and setgid cannot be set. + * + * \li Only regular files and directories can be set. + * + * The rest of this comment discusses a few of the incompatibilities + * between the two systems that need more thought if this API is to + * be extended to accommodate them. + * + * The Windows standard access right "DELETE" doesn't have a direct + * equivalent in the Unix world, so it isn't clear what should be done + * with it. + * + * The Unix sticky bit is not supported. While NT does have a concept + * of allowing users to create files in a directory but not delete or + * rename them, it does not have a concept of allowing them to be deleted + * if they are owned by the user trying to delete/rename. While it is + * probable that something could be cobbled together in NT 5 with inheritance, + * it can't really be done in NT 4 as a single property that you could + * set on a directory. You'd need to coordinate something with file creation + * so that every file created had DELETE set for the owner but noone else. + * + * On Unix systems, setting #ISC_FSACCESS_LISTDIRECTORY sets READ. + * ... setting either #ISC_FSACCESS_CREATECHILD or #ISC_FSACCESS_DELETECHILD + * sets WRITE. + * ... setting #ISC_FSACCESS_ACCESSCHILD sets EXECUTE. + * + * On NT systems, setting #ISC_FSACCESS_LISTDIRECTORY sets FILE_LIST_DIRECTORY. + * ... setting #ISC_FSACCESS_CREATECHILD sets FILE_CREATE_CHILD independently. + * ... setting #ISC_FSACCESS_DELETECHILD sets FILE_DELETE_CHILD independently. + * ... setting #ISC_FSACCESS_ACCESSCHILD sets FILE_TRAVERSE. + * + * Unresolved: XXXDCL + * \li What NT access right controls the ability to rename a file? + * \li How does DELETE work? If a directory has FILE_DELETE_CHILD but a + * file or directory within it does not have DELETE, is that file + * or directory deletable? + * \li To implement isc_fsaccess_get(), mapping an existing Unix permission + * mode_t back to an isc_fsaccess_t is pretty trivial; however, mapping + * an NT DACL could be impossible to do in a responsible way. + * \li Similarly, trying to implement the functionality of being able to + * say "add group writability to whatever permissions already exist" + * could be tricky on NT because of the order-of-entry issue combined + * with possibly having one or more matching ACEs already explicitly + * granting or denying access. Because this functionality is + * not yet needed by the ISC, no code has been written to try to + * solve this problem. + */ + +#include <inttypes.h> + +#include <isc/lang.h> +#include <isc/types.h> + +/* + * Trustees. + */ +#define ISC_FSACCESS_OWNER 0x1 /*%< User account. */ +#define ISC_FSACCESS_GROUP 0x2 /*%< Primary group owner. */ +#define ISC_FSACCESS_OTHER 0x4 /*%< Not the owner or the group owner. */ +#define ISC_FSACCESS_WORLD 0x7 /*%< User, Group, Other. */ + +/* + * Types of permission. + */ +#define ISC_FSACCESS_READ 0x00000001 /*%< File only. */ +#define ISC_FSACCESS_WRITE 0x00000002 /*%< File only. */ +#define ISC_FSACCESS_EXECUTE 0x00000004 /*%< File only. */ +#define ISC_FSACCESS_CREATECHILD 0x00000008 /*%< Dir only. */ +#define ISC_FSACCESS_DELETECHILD 0x00000010 /*%< Dir only. */ +#define ISC_FSACCESS_LISTDIRECTORY 0x00000020 /*%< Dir only. */ +#define ISC_FSACCESS_ACCESSCHILD 0x00000040 /*%< Dir only. */ + +/*% + * Adding any permission bits beyond 0x200 would mean typedef'ing + * isc_fsaccess_t as uint64_t, and redefining this value to + * reflect the new range of permission types, Probably to 21 for + * maximum flexibility. The number of bits has to accommodate all of + * the permission types, and three full sets of them have to fit + * within an isc_fsaccess_t. + */ +#define ISC__FSACCESS_PERMISSIONBITS 10 + +ISC_LANG_BEGINDECLS + +void +isc_fsaccess_add(int trustee, int permission, isc_fsaccess_t *access); + +void +isc_fsaccess_remove(int trustee, int permission, isc_fsaccess_t *access); + +isc_result_t +isc_fsaccess_set(const char *path, isc_fsaccess_t access); + +ISC_LANG_ENDDECLS + +#endif /* ISC_FSACCESS_H */ diff --git a/lib/isc/include/isc/hash.h b/lib/isc/include/isc/hash.h new file mode 100644 index 0000000..fd558f7 --- /dev/null +++ b/lib/isc/include/isc/hash.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_HASH_H +#define ISC_HASH_H 1 + +#include <stdbool.h> + +#include <isc/deprecated.h> +#include <isc/types.h> + +/***** + ***** Module Info + *****/ + +/*! \file isc/hash.h + * + * \brief The hash API + * provides an unpredictable hash value for variable length data. + * A hash object contains a random vector (which is hidden from clients + * of this API) to make the actual hash value unpredictable. + * + * The algorithm used in the API guarantees the probability of hash + * collision; in the current implementation, as long as the values stored + * in the random vector are unpredictable, the probability of hash + * collision between arbitrary two different values is at most 1/2^16. + * + * Although the API is generic about the hash keys, it mainly expects + * DNS names (and sometimes IPv4/v6 addresses) as inputs. It has an + * upper limit of the input length, and may run slow to calculate the + * hash values for large inputs. + * + * This API is designed to be general so that it can provide multiple + * different hash contexts that have different random vectors. However, + * it should be typical to have a single context for an entire system. + * To support such cases, the API also provides a single-context mode. + * + * \li MP: + * The hash object is almost read-only. Once the internal random vector + * is initialized, no write operation will occur, and there will be no + * need to lock the object to calculate actual hash values. + * + * \li Reliability: + * In some cases this module uses low-level data copy to initialize the + * random vector. Errors in this part are likely to crash the server or + * corrupt memory. + * + * \li Resources: + * A buffer, used as a random vector for calculating hash values. + * + * \li Security: + * This module intends to provide unpredictable hash values in + * adversarial environments in order to avoid denial of service attacks + * to hash buckets. + * Its unpredictability relies on the quality of entropy to build the + * random vector. + * + * \li Standards: + * None. + */ + +/*** + *** Imports + ***/ + +#include <isc/types.h> + +/*** + *** Functions + ***/ +ISC_LANG_BEGINDECLS + +LIBISC_EXTERNAL_DATA extern isc_hash_t *isc_hashctx; + +isc_result_t +isc_hash_ctxcreate(isc_mem_t *mctx, isc_entropy_t *entropy, size_t limit, + isc_hash_t **hctx); +isc_result_t +isc_hash_create(isc_mem_t *mctx, isc_entropy_t *entropy, size_t limit); +/*!< + * \brief Create a new hash object. + * + * isc_hash_ctxcreate() creates a different object. + * + * isc_hash_create() creates a module-internal object to support the + * single-context mode. It should be called only once. + * + * 'entropy' must be NULL or a valid entropy object. If 'entropy' is NULL, + * pseudo random values will be used to build the random vector, which may + * weaken security. + * + * 'limit' specifies the maximum number of hash keys. If it is too large, + * these functions may fail. + */ + +void +isc_hash_ctxattach(isc_hash_t *hctx, isc_hash_t **hctxp) + ISC_DEPRECATED; +/*!< + * \brief Attach to a hash object. + * + * This function is only necessary for the multiple-context mode. + */ + +void +isc_hash_ctxdetach(isc_hash_t **hctxp) + ISC_DEPRECATED; +/*!< + * \brief Detach from a hash object. + * + * This function is for the multiple-context mode, and takes a valid + * hash object as an argument. + */ + +void +isc_hash_destroy(void); +/*!< + * \brief This function is for the single-context mode, and is expected to be used + * as a counterpart of isc_hash_create(). + * + * A valid module-internal hash object must have been created, and this + * function should be called only once. + */ + +/*@{*/ +void +isc_hash_ctxinit(isc_hash_t *hctx); +void +isc_hash_init(void); +/*!< + * \brief Initialize a hash object. + * + * It fills in the random vector with a proper + * source of entropy, which is typically from the entropy object specified + * at the creation. Thus, it is desirable to call these functions after + * initializing the entropy object with some good entropy sources. + * + * These functions should be called before the first hash calculation. + * + * isc_hash_ctxinit() is for the multiple-context mode, and takes a valid hash + * object as an argument. + * + * isc_hash_init() is for the single-context mode. A valid module-internal + * hash object must have been created, and this function should be called only + * once. + */ +/*@}*/ + +/*@{*/ +unsigned int +isc_hash_ctxcalc(isc_hash_t *hctx, const unsigned char *key, + unsigned int keylen, bool case_sensitive) + ISC_DEPRECATED; +unsigned int +isc_hash_calc(const unsigned char *key, unsigned int keylen, + bool case_sensitive) + ISC_DEPRECATED; +/*!< + * \brief Calculate a hash value. + * + * isc_hash_ctxinit() is for the multiple-context mode, and takes a valid hash + * object as an argument. + * + * isc_hash_init() is for the single-context mode. A valid module-internal + * hash object must have been created. + * + * 'key' is the hash key, which is a variable length buffer. + * + * 'keylen' specifies the key length, which must not be larger than the limit + * specified for the corresponding hash object. + * + * 'case_sensitive' specifies whether the hash key should be treated as + * case_sensitive values. It should typically be false if the hash key + * is a DNS name. + */ +/*@}*/ + +void +isc__hash_setvec(const uint16_t *vec) + ISC_DEPRECATED; + +/*!< + * \brief Set the contents of the random vector used in hashing. + * + * WARNING: This function is meant to be used only in testing code. It + * must not be used anywhere in normally running code. + * + * The hash context must have been created beforehand, otherwise this + * function is a nop. + * + * 'vec' is not documented here on purpose. You should know what you are + * doing before using this function. + */ + +const void * +isc_hash_get_initializer(void); + +void +isc_hash_set_initializer(const void *initializer); + +uint32_t +isc_hash_function(const void *data, size_t length, + bool case_sensitive, + const uint32_t *previous_hashp); +uint32_t +isc_hash_function_reverse(const void *data, size_t length, + bool case_sensitive, + const uint32_t *previous_hashp); +/*!< + * \brief Calculate a hash over data. + * + * This hash function is useful for hashtables. The hash function is + * opaque and not important to the caller. The returned hash values are + * non-deterministic and will have different mapping every time a + * process using this library is run, but will have uniform + * distribution. + * + * isc_hash_function() calculates the hash from start to end over the + * input data. isc_hash_function_reverse() calculates the hash from the + * end to the start over the input data. The difference in order is + * useful in incremental hashing; for example, a previously hashed + * value for 'com' can be used as input when hashing 'example.com'. + * + * This is a new variant of isc_hash_calc() and will supercede + * isc_hash_calc() eventually. + * + * 'data' is the data to be hashed. + * + * 'length' is the size of the data to be hashed. + * + * 'case_sensitive' specifies whether the hash key should be treated as + * case_sensitive values. It should typically be false if the hash key + * is a DNS name. + * + * 'previous_hashp' is a pointer to a previous hash value returned by + * this function. It can be used to perform incremental hashing. NULL + * must be passed during first calls. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_HASH_H */ diff --git a/lib/isc/include/isc/heap.h b/lib/isc/include/isc/heap.h new file mode 100644 index 0000000..747287b --- /dev/null +++ b/lib/isc/include/isc/heap.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_HEAP_H +#define ISC_HEAP_H 1 + +/*! \file isc/heap.h */ + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/*% + * The comparison function returns true if the first argument has + * higher priority than the second argument, and false otherwise. + */ +typedef bool (*isc_heapcompare_t)(void *, void *); + +/*% + * The index function allows the client of the heap to receive a callback + * when an item's index number changes. This allows it to maintain + * sync with its external state, but still delete itself, since deletions + * from the heap require the index be provided. + */ +typedef void (*isc_heapindex_t)(void *, unsigned int); + +/*% + * The heapaction function is used when iterating over the heap. + * + * NOTE: The heap structure CANNOT BE MODIFIED during the call to + * isc_heap_foreach(). + */ +typedef void (*isc_heapaction_t)(void *, void *); + +typedef struct isc_heap isc_heap_t; + +isc_result_t +isc_heap_create(isc_mem_t *mctx, isc_heapcompare_t compare, + isc_heapindex_t index, unsigned int size_increment, + isc_heap_t **heapp); +/*!< + * \brief Create a new heap. The heap is implemented using a space-efficient + * storage method. When the heap elements are deleted space is not freed + * but will be reused when new elements are inserted. + * + * Heap elements are indexed from 1. + * + * Requires: + *\li "mctx" is valid. + *\li "compare" is a function which takes two void * arguments and + * returns true if the first argument has a higher priority than + * the second, and false otherwise. + *\li "index" is a function which takes a void *, and an unsigned int + * argument. This function will be called whenever an element's + * index value changes, so it may continue to delete itself from the + * heap. This option may be NULL if this functionality is unneeded. + *\li "size_increment" is a hint about how large the heap should grow + * when resizing is needed. If this is 0, a default size will be + * used, which is currently 1024, allowing space for an additional 1024 + * heap elements to be inserted before adding more space. + *\li "heapp" is not NULL, and "*heap" is NULL. + * + * Returns: + *\li ISC_R_SUCCESS - success + *\li ISC_R_NOMEMORY - insufficient memory + */ + +void +isc_heap_destroy(isc_heap_t **heapp); +/*!< + * \brief Destroys a heap. + * + * Requires: + *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. + */ + +isc_result_t +isc_heap_insert(isc_heap_t *heap, void *elt); +/*!< + * \brief Inserts a new element into a heap. + * + * Requires: + *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. + */ + +void +isc_heap_delete(isc_heap_t *heap, unsigned int index); +/*!< + * \brief Deletes an element from a heap, by element index. + * + * Requires: + *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. + *\li "index" is a valid element index, as provided by the "index" callback + * provided during heap creation. + */ + +void +isc_heap_increased(isc_heap_t *heap, unsigned int index); +/*!< + * \brief Indicates to the heap that an element's priority has increased. + * This function MUST be called whenever an element has increased in priority. + * + * Requires: + *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. + *\li "index" is a valid element index, as provided by the "index" callback + * provided during heap creation. + */ + +void +isc_heap_decreased(isc_heap_t *heap, unsigned int index); +/*!< + * \brief Indicates to the heap that an element's priority has decreased. + * This function MUST be called whenever an element has decreased in priority. + * + * Requires: + *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. + *\li "index" is a valid element index, as provided by the "index" callback + * provided during heap creation. + */ + +void * +isc_heap_element(isc_heap_t *heap, unsigned int index); +/*!< + * \brief Returns the element for a specific element index. + * + * Requires: + *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. + *\li "index" is a valid element index, as provided by the "index" callback + * provided during heap creation. + * + * Returns: + *\li A pointer to the element for the element index. + */ + +void +isc_heap_foreach(isc_heap_t *heap, isc_heapaction_t action, void *uap); +/*!< + * \brief Iterate over the heap, calling an action for each element. The + * order of iteration is not sorted. + * + * Requires: + *\li "heapp" is not NULL and "*heap" points to a valid isc_heap_t. + *\li "action" is not NULL, and is a function which takes two arguments. + * The first is a void *, representing the element, and the second is + * "uap" as provided to isc_heap_foreach. + *\li "uap" is a caller-provided argument, and may be NULL. + * + * Note: + *\li The heap structure CANNOT be modified during this iteration. The only + * safe function to call while iterating the heap is isc_heap_element(). + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_HEAP_H */ diff --git a/lib/isc/include/isc/hex.h b/lib/isc/include/isc/hex.h new file mode 100644 index 0000000..3e09e59 --- /dev/null +++ b/lib/isc/include/isc/hex.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_HEX_H +#define ISC_HEX_H 1 + +/*! \file isc/hex.h */ + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/*** + *** Functions + ***/ + +isc_result_t +isc_hex_totext(isc_region_t *source, int wordlength, + const char *wordbreak, isc_buffer_t *target); +/*!< + * \brief Convert data into hex encoded text. + * + * Notes: + *\li The hex encoded text in 'target' will be divided into + * words of at most 'wordlength' characters, separated by + * the 'wordbreak' string. No parentheses will surround + * the text. + * + * Requires: + *\li 'source' is a region containing binary data + *\li 'target' is a text buffer containing available space + *\li 'wordbreak' points to a null-terminated string of + * zero or more whitespace characters + * + * Ensures: + *\li target will contain the hex encoded version of the data + * in source. The 'used' pointer in target will be advanced as + * necessary. + */ + +isc_result_t +isc_hex_decodestring(const char *cstr, isc_buffer_t *target); +/*!< + * \brief Decode a null-terminated hex string. + * + * Requires: + *\li 'cstr' is non-null. + *\li 'target' is a valid buffer. + * + * Returns: + *\li #ISC_R_SUCCESS -- the entire decoded representation of 'cstring' + * fit in 'target'. + *\li #ISC_R_BADHEX -- 'cstr' is not a valid hex encoding. + * + * Other error returns are any possible error code from: + * isc_lex_create(), + * isc_lex_openbuffer(), + * isc_hex_tobuffer(). + */ + +isc_result_t +isc_hex_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length); +/*!< + * \brief Convert hex encoded text from a lexer context into data. + * + * Requires: + *\li 'lex' is a valid lexer context + *\li 'target' is a buffer containing binary data + *\li 'length' is an integer + * + * Ensures: + *\li target will contain the data represented by the hex encoded + * string parsed by the lexer. No more than length bytes will be read, + * if length is positive. The 'used' pointer in target will be + * advanced as necessary. + */ + + +ISC_LANG_ENDDECLS + +#endif /* ISC_HEX_H */ diff --git a/lib/isc/include/isc/hmacmd5.h b/lib/isc/include/isc/hmacmd5.h new file mode 100644 index 0000000..ca2ec56 --- /dev/null +++ b/lib/isc/include/isc/hmacmd5.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file isc/hmacmd5.h + * \brief This is the header file for the HMAC-MD5 keyed hash algorithm + * described in RFC2104. + */ + +#ifndef ISC_HMACMD5_H +#define ISC_HMACMD5_H 1 + +#include <pk11/site.h> + +#ifndef PK11_MD5_DISABLE + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/md5.h> +#include <isc/platform.h> +#include <isc/types.h> + +#define ISC_HMACMD5_KEYLENGTH 64 + +#ifdef ISC_PLATFORM_OPENSSLHASH +#include <openssl/opensslv.h> +#include <openssl/hmac.h> + +typedef struct { + HMAC_CTX *ctx; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + HMAC_CTX _ctx; +#endif +} isc_hmacmd5_t; + +#elif PKCS11CRYPTO +#include <pk11/pk11.h> + +typedef pk11_context_t isc_hmacmd5_t; + +#else + +typedef struct { + isc_md5_t md5ctx; + unsigned char key[ISC_HMACMD5_KEYLENGTH]; +} isc_hmacmd5_t; +#endif + +ISC_LANG_BEGINDECLS + +void +isc_hmacmd5_init(isc_hmacmd5_t *ctx, const unsigned char *key, + unsigned int len); + +void +isc_hmacmd5_invalidate(isc_hmacmd5_t *ctx); + +void +isc_hmacmd5_update(isc_hmacmd5_t *ctx, const unsigned char *buf, + unsigned int len); + +void +isc_hmacmd5_sign(isc_hmacmd5_t *ctx, unsigned char *digest); + +bool +isc_hmacmd5_verify(isc_hmacmd5_t *ctx, unsigned char *digest); + +bool +isc_hmacmd5_verify2(isc_hmacmd5_t *ctx, unsigned char *digest, size_t len); + +bool +isc_hmacmd5_check(int testing); + +ISC_LANG_ENDDECLS + +#endif /* !PK11_MD5_DISABLE */ + +#endif /* ISC_HMACMD5_H */ diff --git a/lib/isc/include/isc/hmacsha.h b/lib/isc/include/isc/hmacsha.h new file mode 100644 index 0000000..3c80fba --- /dev/null +++ b/lib/isc/include/isc/hmacsha.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file isc/hmacsha.h + * This is the header file for the HMAC-SHA1, HMAC-SHA224, HMAC-SHA256, + * HMAC-SHA334 and HMAC-SHA512 hash algorithm described in RFC 2104. + */ + +#ifndef ISC_HMACSHA_H +#define ISC_HMACSHA_H 1 + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/platform.h> +#include <isc/sha1.h> +#include <isc/sha2.h> +#include <isc/types.h> + +#define ISC_HMACSHA1_KEYLENGTH ISC_SHA1_BLOCK_LENGTH +#define ISC_HMACSHA224_KEYLENGTH ISC_SHA224_BLOCK_LENGTH +#define ISC_HMACSHA256_KEYLENGTH ISC_SHA256_BLOCK_LENGTH +#define ISC_HMACSHA384_KEYLENGTH ISC_SHA384_BLOCK_LENGTH +#define ISC_HMACSHA512_KEYLENGTH ISC_SHA512_BLOCK_LENGTH + +#ifdef ISC_PLATFORM_OPENSSLHASH +#include <openssl/opensslv.h> +#include <openssl/hmac.h> + +typedef struct { + HMAC_CTX *ctx; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + HMAC_CTX _ctx; +#endif +} isc_hmacsha_t; + +typedef isc_hmacsha_t isc_hmacsha1_t; +typedef isc_hmacsha_t isc_hmacsha224_t; +typedef isc_hmacsha_t isc_hmacsha256_t; +typedef isc_hmacsha_t isc_hmacsha384_t; +typedef isc_hmacsha_t isc_hmacsha512_t; + +#elif PKCS11CRYPTO +#include <pk11/pk11.h> + +typedef pk11_context_t isc_hmacsha1_t; +typedef pk11_context_t isc_hmacsha224_t; +typedef pk11_context_t isc_hmacsha256_t; +typedef pk11_context_t isc_hmacsha384_t; +typedef pk11_context_t isc_hmacsha512_t; + +#else + +typedef struct { + isc_sha1_t sha1ctx; + unsigned char key[ISC_HMACSHA1_KEYLENGTH]; +} isc_hmacsha1_t; + +typedef struct { + isc_sha224_t sha224ctx; + unsigned char key[ISC_HMACSHA224_KEYLENGTH]; +} isc_hmacsha224_t; + +typedef struct { + isc_sha256_t sha256ctx; + unsigned char key[ISC_HMACSHA256_KEYLENGTH]; +} isc_hmacsha256_t; + +typedef struct { + isc_sha384_t sha384ctx; + unsigned char key[ISC_HMACSHA384_KEYLENGTH]; +} isc_hmacsha384_t; + +typedef struct { + isc_sha512_t sha512ctx; + unsigned char key[ISC_HMACSHA512_KEYLENGTH]; +} isc_hmacsha512_t; +#endif + +ISC_LANG_BEGINDECLS + +void +isc_hmacsha1_init(isc_hmacsha1_t *ctx, const unsigned char *key, + unsigned int len); + +void +isc_hmacsha1_invalidate(isc_hmacsha1_t *ctx); + +void +isc_hmacsha1_update(isc_hmacsha1_t *ctx, const unsigned char *buf, + unsigned int len); + +void +isc_hmacsha1_sign(isc_hmacsha1_t *ctx, unsigned char *digest, size_t len); + +bool +isc_hmacsha1_verify(isc_hmacsha1_t *ctx, unsigned char *digest, size_t len); + +bool +isc_hmacsha1_check(int testing); + + +void +isc_hmacsha224_init(isc_hmacsha224_t *ctx, const unsigned char *key, + unsigned int len); + +void +isc_hmacsha224_invalidate(isc_hmacsha224_t *ctx); + +void +isc_hmacsha224_update(isc_hmacsha224_t *ctx, const unsigned char *buf, + unsigned int len); + +void +isc_hmacsha224_sign(isc_hmacsha224_t *ctx, unsigned char *digest, size_t len); + +bool +isc_hmacsha224_verify(isc_hmacsha224_t *ctx, unsigned char *digest, size_t len); + + +void +isc_hmacsha256_init(isc_hmacsha256_t *ctx, const unsigned char *key, + unsigned int len); + +void +isc_hmacsha256_invalidate(isc_hmacsha256_t *ctx); + +void +isc_hmacsha256_update(isc_hmacsha256_t *ctx, const unsigned char *buf, + unsigned int len); + +void +isc_hmacsha256_sign(isc_hmacsha256_t *ctx, unsigned char *digest, size_t len); + +bool +isc_hmacsha256_verify(isc_hmacsha256_t *ctx, unsigned char *digest, size_t len); + + +void +isc_hmacsha384_init(isc_hmacsha384_t *ctx, const unsigned char *key, + unsigned int len); + +void +isc_hmacsha384_invalidate(isc_hmacsha384_t *ctx); + +void +isc_hmacsha384_update(isc_hmacsha384_t *ctx, const unsigned char *buf, + unsigned int len); + +void +isc_hmacsha384_sign(isc_hmacsha384_t *ctx, unsigned char *digest, size_t len); + +bool +isc_hmacsha384_verify(isc_hmacsha384_t *ctx, unsigned char *digest, size_t len); + + +void +isc_hmacsha512_init(isc_hmacsha512_t *ctx, const unsigned char *key, + unsigned int len); + +void +isc_hmacsha512_invalidate(isc_hmacsha512_t *ctx); + +void +isc_hmacsha512_update(isc_hmacsha512_t *ctx, const unsigned char *buf, + unsigned int len); + +void +isc_hmacsha512_sign(isc_hmacsha512_t *ctx, unsigned char *digest, size_t len); + +bool +isc_hmacsha512_verify(isc_hmacsha512_t *ctx, unsigned char *digest, size_t len); + +ISC_LANG_ENDDECLS + +#endif /* ISC_HMACSHA_H */ diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h new file mode 100644 index 0000000..b36d77f --- /dev/null +++ b/lib/isc/include/isc/ht.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* ! \file */ + +#ifndef ISC_HT_H +#define ISC_HT_H 1 + +#include <inttypes.h> +#include <string.h> + +#include <isc/types.h> +#include <isc/result.h> + +typedef struct isc_ht isc_ht_t; +typedef struct isc_ht_iter isc_ht_iter_t; + +/*% + * Initialize hashtable at *htp, using memory context and size of (1<<bits) + * + * Requires: + *\li htp is not NULL + *\li *htp is NULL + *\li mctx is a valid memory context + *\li bits >=1 && bits <=32 + * + * Returns: + *\li #ISC_R_NOMEMORY -- not enough memory to create pool + *\li #ISC_R_SUCCESS -- all is well. + */ +isc_result_t +isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits); + +/*% + * Destroy hashtable, freeing everything + * + * Requires: + * \li *htp is valid hashtable + */ +void +isc_ht_destroy(isc_ht_t **htp); + +/*% + * Add a node to hashtable, pointed by binary key 'key' of size 'keysize'; + * set its value to 'value' + * + * Requires: + *\li ht is a valid hashtable + * + * Returns: + *\li #ISC_R_NOMEMORY -- not enough memory to create pool + *\li #ISC_R_EXISTS -- node of the same key already exists + *\li #ISC_R_SUCCESS -- all is well. + */ +isc_result_t +isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize, + void *value); + +/*% + * Find a node matching 'key'/'keysize' in hashtable 'ht'; + * if found, set 'value' to its value + * + * Requires: + * \li 'ht' is a valid hashtable + * + * Returns: + * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOTFOUND -- key not found + */ +isc_result_t +isc_ht_find(const isc_ht_t *ht, const unsigned char *key, + uint32_t keysize, void **valuep); + +/*% + * Delete node from hashtable + * Requires: + *\li ht is a valid hashtable + * + * Returns: + *\li #ISC_R_NOTFOUND -- key not found + *\li #ISC_R_SUCCESS -- all is well + */ +isc_result_t +isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize); + +/*% + * Create an iterator for the hashtable; point '*itp' to it. + */ +isc_result_t +isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp); + +/*% + * Destroy the iterator '*itp', set it to NULL + */ +void +isc_ht_iter_destroy(isc_ht_iter_t **itp); + +/*% + * Set an iterator to the first entry. + * + * Returns: + * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOMORE -- no data in the hashtable + */ +isc_result_t +isc_ht_iter_first(isc_ht_iter_t *it); + +/*% + * Set an iterator to the next entry. + * + * Returns: + * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOMORE -- end of hashtable reached + */ +isc_result_t +isc_ht_iter_next(isc_ht_iter_t *it); + +/*% + * Delete current entry and set an iterator to the next entry. + * + * Returns: + * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOMORE -- end of hashtable reached + */ +isc_result_t +isc_ht_iter_delcurrent_next(isc_ht_iter_t *it); + + +/*% + * Set 'value' to the current value under the iterator + */ +void +isc_ht_iter_current(isc_ht_iter_t *it, void **valuep); + +/*% + * Set 'key' and 'keysize to the current key and keysize for the value + * under the iterator + */ +void +isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize); + +/*% + * Returns the number of items in the hashtable. + * + * Requires: + *\li 'ht' is a valid hashtable + */ +unsigned int +isc_ht_count(isc_ht_t *ht); +#endif diff --git a/lib/isc/include/isc/httpd.h b/lib/isc/include/isc/httpd.h new file mode 100644 index 0000000..b02b33e --- /dev/null +++ b/lib/isc/include/isc/httpd.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_HTTPD_H +#define ISC_HTTPD_H 1 + +/*! \file */ + +#include <stdbool.h> + +#include <isc/event.h> +#include <isc/eventclass.h> +#include <isc/types.h> +#include <isc/mutex.h> +#include <isc/task.h> +#include <isc/time.h> + +/*% + * HTTP urls. These are the URLs we manage, and the function to call to + * provide the data for it. We pass in the base url (so the same function + * can handle multiple requests), and a structure to fill in to return a + * result to the client. We also pass in a pointer to be filled in for + * the data cleanup function. + */ +struct isc_httpdurl { + char *url; + isc_httpdaction_t *action; + void *action_arg; + bool isstatic; + isc_time_t loadtime; + ISC_LINK(isc_httpdurl_t) link; +}; + +#define HTTPD_EVENTCLASS ISC_EVENTCLASS(4300) +#define HTTPD_SHUTDOWN (HTTPD_EVENTCLASS + 0x0001) + +#define ISC_HTTPDMGR_FLAGSHUTTINGDOWN 0x00000001 + +/* + * Create a new http daemon which will send, once every time period, + * a http-like header followed by HTTP data. + */ +isc_result_t +isc_httpdmgr_create(isc_mem_t *mctx, isc_socket_t *sock, isc_task_t *task, + isc_httpdclientok_t *client_ok, + isc_httpdondestroy_t *ondestory, void *cb_arg, + isc_timermgr_t *tmgr, isc_httpdmgr_t **httpdp); + +void +isc_httpdmgr_shutdown(isc_httpdmgr_t **httpdp); + +isc_result_t +isc_httpdmgr_addurl(isc_httpdmgr_t *httpdmgr, const char *url, + isc_httpdaction_t *func, void *arg); + +isc_result_t +isc_httpdmgr_addurl2(isc_httpdmgr_t *httpdmgr, const char *url, + bool isstatic, + isc_httpdaction_t *func, void *arg); + +isc_result_t +isc_httpd_response(isc_httpd_t *httpd); + +isc_result_t +isc_httpd_addheader(isc_httpd_t *httpd, const char *name, + const char *val); + +isc_result_t +isc_httpd_addheaderuint(isc_httpd_t *httpd, const char *name, int val); + +isc_result_t isc_httpd_endheaders(isc_httpd_t *httpd); + +void +isc_httpd_setfinishhook(void (*fn)(void)); + +#endif /* ISC_HTTPD_H */ diff --git a/lib/isc/include/isc/int.h b/lib/isc/include/isc/int.h new file mode 100644 index 0000000..9bf3066 --- /dev/null +++ b/lib/isc/include/isc/int.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +/*! \file */ + +#include <inttypes.h> + +typedef int8_t isc_int8_t; +typedef uint8_t isc_uint8_t; +typedef int16_t isc_int16_t; +typedef uint16_t isc_uint16_t; +typedef int32_t isc_int32_t; +typedef uint32_t isc_uint32_t; +typedef int64_t isc_int64_t; +typedef uint64_t isc_uint64_t; + +#define ISC_INT8_MIN INT8_MIN +#define ISC_INT8_MAX INT8_MAX +#define ISC_UINT8_MAX UINT8_MAX + +#define ISC_INT16_MIN INT16_MIN +#define ISC_INT16_MAX INT16_MAX +#define ISC_UINT16_MAX UINT16_MAX + +#define ISC_INT32_MIN INT32_MIN +#define ISC_INT32_MAX INT32_MAX +#define ISC_UINT32_MAX UINT32_MAX + +#define ISC_INT64_MIN INT64_MIN +#define ISC_INT64_MAX INT64_MAX +#define ISC_UINT64_MAX UINT64_MAX diff --git a/lib/isc/include/isc/interfaceiter.h b/lib/isc/include/isc/interfaceiter.h new file mode 100644 index 0000000..2ef82a3 --- /dev/null +++ b/lib/isc/include/isc/interfaceiter.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_INTERFACEITER_H +#define ISC_INTERFACEITER_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/interfaceiter.h + * \brief Iterates over the list of network interfaces. + * + * Interfaces whose address family is not supported are ignored and never + * returned by the iterator. Interfaces whose netmask, interface flags, + * or similar cannot be obtained are also ignored, and the failure is logged. + * + * Standards: + * The API for scanning varies greatly among operating systems. + * This module attempts to hide the differences. + */ + +/*** + *** Imports + ***/ + +#include <inttypes.h> + +#include <isc/lang.h> +#include <isc/netaddr.h> +#include <isc/types.h> + +/*! + * \brief Public structure describing a network interface. + */ + +struct isc_interface { + char name[32]; /*%< Interface name, null-terminated. */ + unsigned int af; /*%< Address family. */ + isc_netaddr_t address; /*%< Local address. */ + isc_netaddr_t netmask; /*%< Network mask. */ + isc_netaddr_t dstaddress; /*%< Destination address (point-to-point only). */ + uint32_t flags; /*%< Flags; see INTERFACE flags. */ +}; + +/*@{*/ +/*! Interface flags. */ + +#define INTERFACE_F_UP 0x00000001U +#define INTERFACE_F_POINTTOPOINT 0x00000002U +#define INTERFACE_F_LOOPBACK 0x00000004U +/*@}*/ + +/*** + *** Functions + ***/ + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp); +/*!< + * \brief Create an iterator for traversing the operating system's list + * of network interfaces. + * + * Returns: + *\li #ISC_R_SUCCESS + * \li #ISC_R_NOMEMORY + *\li Various network-related errors + */ + +isc_result_t +isc_interfaceiter_first(isc_interfaceiter_t *iter); +/*!< + * \brief Position the iterator on the first interface. + * + * Returns: + *\li #ISC_R_SUCCESS Success. + *\li #ISC_R_NOMORE There are no interfaces. + */ + +isc_result_t +isc_interfaceiter_current(isc_interfaceiter_t *iter, + isc_interface_t *ifdata); +/*!< + * \brief Get information about the interface the iterator is currently + * positioned at and store it at *ifdata. + * + * Requires: + *\li The iterator has been successfully positioned using + * isc_interface_iter_first() / isc_interface_iter_next(). + * + * Returns: + *\li #ISC_R_SUCCESS Success. + */ + +isc_result_t +isc_interfaceiter_next(isc_interfaceiter_t *iter); +/*!< + * \brief Position the iterator on the next interface. + * + * Requires: + * \li The iterator has been successfully positioned using + * isc_interface_iter_first() / isc_interface_iter_next(). + * + * Returns: + *\li #ISC_R_SUCCESS Success. + *\li #ISC_R_NOMORE There are no more interfaces. + */ + +void +isc_interfaceiter_destroy(isc_interfaceiter_t **iterp); +/*!< + * \brief Destroy the iterator. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_INTERFACEITER_H */ diff --git a/lib/isc/include/isc/ipv6.h b/lib/isc/include/isc/ipv6.h new file mode 100644 index 0000000..93852d0 --- /dev/null +++ b/lib/isc/include/isc/ipv6.h @@ -0,0 +1,142 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: ipv6.h,v 1.24 2007/06/19 23:47:18 tbox Exp $ */ + +#ifndef ISC_IPV6_H +#define ISC_IPV6_H 1 + +/*! + * Also define LWRES_IPV6_H to keep it from being included if liblwres is + * being used, or redefinition errors will occur. + */ +#define LWRES_IPV6_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/ipv6.h + * \brief IPv6 definitions for systems which do not support IPv6. + * + * \li MP: + * No impact. + * + * \li Reliability: + * No anticipated impact. + * + * \li Resources: + * N/A. + * + * \li Security: + * No anticipated impact. + * + * \li Standards: + * RFC2553. + */ + +/*** + *** Imports. + ***/ + +#include <inttypes.h> +#include <isc/platform.h> + +/*** + *** Types. + ***/ + +struct in6_addr { + union { + uint8_t _S6_u8[16]; + uint16_t _S6_u16[8]; + uint32_t _S6_u32[4]; + } _S6_un; +}; +#define s6_addr _S6_un._S6_u8 +#define s6_addr8 _S6_un._S6_u8 +#define s6_addr16 _S6_un._S6_u16 +#define s6_addr32 _S6_un._S6_u32 + +#define IN6ADDR_ANY_INIT {{{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 }}} +#define IN6ADDR_LOOPBACK_INIT {{{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 }}} + +LIBISC_EXTERNAL_DATA extern const struct in6_addr in6addr_any; +LIBISC_EXTERNAL_DATA extern const struct in6_addr in6addr_loopback; + +struct sockaddr_in6 { +#ifdef ISC_PLATFORM_HAVESALEN + uint8_t sin6_len; + uint8_t sin6_family; +#else + uint16_t sin6_family; +#endif + uint16_t sin6_port; + uint32_t sin6_flowinfo; + struct in6_addr sin6_addr; + uint32_t sin6_scope_id; +}; + +#ifdef ISC_PLATFORM_HAVESALEN +#define SIN6_LEN 1 +#endif + +/*% + * Unspecified + */ +#define IN6_IS_ADDR_UNSPECIFIED(a) \ + (((a)->s6_addr32[0] == 0) && \ + ((a)->s6_addr32[1] == 0) && \ + ((a)->s6_addr32[2] == 0) && \ + ((a)->s6_addr32[3] == 0)) + +/*% + * Loopback + */ +#define IN6_IS_ADDR_LOOPBACK(a) \ + (((a)->s6_addr32[0] == 0) && \ + ((a)->s6_addr32[1] == 0) && \ + ((a)->s6_addr32[2] == 0) && \ + ((a)->s6_addr32[3] == htonl(1))) + +/*% + * IPv4 compatible + */ +#define IN6_IS_ADDR_V4COMPAT(a) \ + (((a)->s6_addr32[0] == 0) && \ + ((a)->s6_addr32[1] == 0) && \ + ((a)->s6_addr32[2] == 0) && \ + ((a)->s6_addr32[3] != 0) && \ + ((a)->s6_addr32[3] != htonl(1))) + +/*% + * Mapped + */ +#define IN6_IS_ADDR_V4MAPPED(a) \ + (((a)->s6_addr32[0] == 0) && \ + ((a)->s6_addr32[1] == 0) && \ + ((a)->s6_addr32[2] == htonl(0x0000ffff))) + +/*% + * Multicast + */ +#define IN6_IS_ADDR_MULTICAST(a) \ + ((a)->s6_addr8[0] == 0xffU) + +/*% + * Unicast link / site local. + */ +#define IN6_IS_ADDR_LINKLOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80)) +#define IN6_IS_ADDR_SITELOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) + +#endif /* ISC_IPV6_H */ diff --git a/lib/isc/include/isc/iterated_hash.h b/lib/isc/include/isc/iterated_hash.h new file mode 100644 index 0000000..a43ae69 --- /dev/null +++ b/lib/isc/include/isc/iterated_hash.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_ITERATED_HASH_H +#define ISC_ITERATED_HASH_H 1 + +#include <isc/lang.h> +#include <isc/sha1.h> + +/* + * The maximal hash length that can be encoded in a name + * using base32hex. floor(255/8)*5 + */ +#define NSEC3_MAX_HASH_LENGTH 155 + +/* + * The maximum has that can be encoded in a single label using + * base32hex. floor(63/8)*5 + */ +#define NSEC3_MAX_LABEL_HASH 35 + +ISC_LANG_BEGINDECLS + +int isc_iterated_hash(unsigned char out[NSEC3_MAX_HASH_LENGTH], + unsigned int hashalg, int iterations, + const unsigned char *salt, int saltlength, + const unsigned char *in, int inlength); + + +ISC_LANG_ENDDECLS + +#endif /* ISC_ITERATED_HASH_H */ diff --git a/lib/isc/include/isc/json.h b/lib/isc/include/isc/json.h new file mode 100644 index 0000000..52295e6 --- /dev/null +++ b/lib/isc/include/isc/json.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_JSON_H +#define ISC_JSON_H 1 + +#ifdef HAVE_JSON +/* + * This file is here mostly to make it easy to add additional libjson header + * files as needed across all the users of this file. Rather than place + * these libjson includes in each file, one include makes it easy to handle + * the ifdef as well as adding the ability to add additional functions + * which may be useful. + */ +#ifdef HAVE_JSON_C +/* + * We don't include <json-c/json.h> as the subsequent includes do not + * prefix the header file names with "json-c/" and using + * -I <prefix>/include/json-c results in too many filename collisions. + */ +#include <json-c/linkhash.h> +#include <json-c/json_util.h> +#include <json-c/json_object.h> +#include <json-c/json_tokener.h> +#include <json-c/json_object_iterator.h> +#include <json-c/json_c_version.h> +#else +#include <json/json.h> +#endif +#endif + +#define ISC_JSON_RENDERCONFIG 0x00000001 /* render config data */ +#define ISC_JSON_RENDERSTATS 0x00000002 /* render stats */ +#define ISC_JSON_RENDERALL 0x000000ff /* render everything */ + +#endif /* ISC_JSON_H */ diff --git a/lib/isc/include/isc/lang.h b/lib/isc/include/isc/lang.h new file mode 100644 index 0000000..bffcbac --- /dev/null +++ b/lib/isc/include/isc/lang.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_LANG_H +#define ISC_LANG_H 1 + +/*! \file isc/lang.h */ + +#ifdef __cplusplus +#define ISC_LANG_BEGINDECLS extern "C" { +#define ISC_LANG_ENDDECLS } +#else +#define ISC_LANG_BEGINDECLS +#define ISC_LANG_ENDDECLS +#endif + +#endif /* ISC_LANG_H */ diff --git a/lib/isc/include/isc/lex.h b/lib/isc/include/isc/lex.h new file mode 100644 index 0000000..d63ecf9 --- /dev/null +++ b/lib/isc/include/isc/lex.h @@ -0,0 +1,442 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_LEX_H +#define ISC_LEX_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/lex.h + * \brief The "lex" module provides a lightweight tokenizer. It can operate + * on files or buffers, and can handle "include". It is designed for + * parsing of DNS master files and the BIND configuration file, but + * should be general enough to tokenize other things, e.g. HTTP. + * + * \li MP: + * No synchronization is provided. Clients must ensure exclusive + * access. + * + * \li Reliability: + * No anticipated impact. + * + * \li Resources: + * TBS + * + * \li Security: + * No anticipated impact. + * + * \li Standards: + * None. + */ + +/*** + *** Imports + ***/ + +#include <stdbool.h> +#include <stdio.h> + +#include <isc/lang.h> +#include <isc/region.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/*** + *** Options + ***/ + +/*@{*/ +/*! + * Various options for isc_lex_gettoken(). + */ + +#define ISC_LEXOPT_EOL 0x01 /*%< Want end-of-line token. */ +#define ISC_LEXOPT_EOF 0x02 /*%< Want end-of-file token. */ +#define ISC_LEXOPT_INITIALWS 0x04 /*%< Want initial whitespace. */ +#define ISC_LEXOPT_NUMBER 0x08 /*%< Recognize numbers. */ +#define ISC_LEXOPT_QSTRING 0x10 /*%< Recognize qstrings. */ +/*@}*/ + +/*@{*/ +/*! + * The ISC_LEXOPT_DNSMULTILINE option handles the processing of '(' and ')' in + * the DNS master file format. If this option is set, then the + * ISC_LEXOPT_INITIALWS and ISC_LEXOPT_EOL options will be ignored when + * the paren count is > 0. To use this option, '(' and ')' must be special + * characters. + */ +#define ISC_LEXOPT_DNSMULTILINE 0x20 /*%< Handle '(' and ')'. */ +#define ISC_LEXOPT_NOMORE 0x40 /*%< Want "no more" token. */ + +#define ISC_LEXOPT_CNUMBER 0x80 /*%< Recognize octal and hex. */ +#define ISC_LEXOPT_ESCAPE 0x100 /*%< Recognize escapes. */ +#define ISC_LEXOPT_QSTRINGMULTILINE 0x200 /*%< Allow multiline "" strings */ +#define ISC_LEXOPT_OCTAL 0x400 /*%< Expect a octal number. */ +#define ISC_LEXOPT_BTEXT 0x800 /*%< Bracketed text. */ +/*@}*/ +/*@{*/ +/*! + * Various commenting styles, which may be changed at any time with + * isc_lex_setcomments(). + */ + +#define ISC_LEXCOMMENT_C 0x01 +#define ISC_LEXCOMMENT_CPLUSPLUS 0x02 +#define ISC_LEXCOMMENT_SHELL 0x04 +#define ISC_LEXCOMMENT_DNSMASTERFILE 0x08 +/*@}*/ + +/*** + *** Types + ***/ + +/*! Lex */ + +typedef char isc_lexspecials_t[256]; + +/* Tokens */ + +typedef enum { + isc_tokentype_unknown = 0, + isc_tokentype_string = 1, + isc_tokentype_number = 2, + isc_tokentype_qstring = 3, + isc_tokentype_eol = 4, + isc_tokentype_eof = 5, + isc_tokentype_initialws = 6, + isc_tokentype_special = 7, + isc_tokentype_nomore = 8, + isc_tokentype_btext = 8 +} isc_tokentype_t; + +typedef union { + char as_char; + unsigned long as_ulong; + isc_region_t as_region; + isc_textregion_t as_textregion; + void * as_pointer; +} isc_tokenvalue_t; + +typedef struct isc_token { + isc_tokentype_t type; + isc_tokenvalue_t value; +} isc_token_t; + +/*** + *** Functions + ***/ + +isc_result_t +isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp); +/*%< + * Create a lexer. + * + * 'max_token' is a hint of the number of bytes in the largest token. + * + * Requires: + *\li '*lexp' is a valid lexer. + * + * Ensures: + *\li On success, *lexp is attached to the newly created lexer. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + */ + +void +isc_lex_destroy(isc_lex_t **lexp); +/*%< + * Destroy the lexer. + * + * Requires: + *\li '*lexp' is a valid lexer. + * + * Ensures: + *\li *lexp == NULL + */ + +unsigned int +isc_lex_getcomments(isc_lex_t *lex); +/*%< + * Return the current lexer commenting styles. + * + * Requires: + *\li 'lex' is a valid lexer. + * + * Returns: + *\li The commenting sytles which are currently allowed. + */ + +void +isc_lex_setcomments(isc_lex_t *lex, unsigned int comments); +/*%< + * Set allowed lexer commenting styles. + * + * Requires: + *\li 'lex' is a valid lexer. + * + *\li 'comments' has meaningful values. + */ + +void +isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials); +/*%< + * Put the current list of specials into 'specials'. + * + * Requires: + *\li 'lex' is a valid lexer. + */ + +void +isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials); +/*!< + * The characters in 'specials' are returned as tokens. Along with + * whitespace, they delimit strings and numbers. + * + * Note: + *\li Comment processing takes precedence over special character + * recognition. + * + * Requires: + *\li 'lex' is a valid lexer. + */ + +isc_result_t +isc_lex_openfile(isc_lex_t *lex, const char *filename); +/*%< + * Open 'filename' and make it the current input source for 'lex'. + * + * Requires: + *\li 'lex' is a valid lexer. + * + *\li filename is a valid C string. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY Out of memory + *\li #ISC_R_NOTFOUND File not found + *\li #ISC_R_NOPERM No permission to open file + *\li #ISC_R_FAILURE Couldn't open file, not sure why + *\li #ISC_R_UNEXPECTED + */ + +isc_result_t +isc_lex_openstream(isc_lex_t *lex, FILE *stream); +/*%< + * Make 'stream' the current input source for 'lex'. + * + * Requires: + *\li 'lex' is a valid lexer. + * + *\li 'stream' is a valid C stream. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY Out of memory + */ + +isc_result_t +isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer); +/*%< + * Make 'buffer' the current input source for 'lex'. + * + * Requires: + *\li 'lex' is a valid lexer. + * + *\li 'buffer' is a valid buffer. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY Out of memory + */ + +isc_result_t +isc_lex_close(isc_lex_t *lex); +/*%< + * Close the most recently opened object (i.e. file or buffer). + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMORE No more input sources + */ + +isc_result_t +isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp); +/*%< + * Get the next token. + * + * Requires: + *\li 'lex' is a valid lexer. + * + *\li 'lex' has an input source. + * + *\li 'options' contains valid options. + * + *\li '*tokenp' is a valid pointer. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_UNEXPECTEDEND + *\li #ISC_R_NOMEMORY + * + * These two results are returned only if their corresponding lexer + * options are not set. + * + *\li #ISC_R_EOF End of input source + *\li #ISC_R_NOMORE No more input sources + */ + +isc_result_t +isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token, + isc_tokentype_t expect, bool eol); +/*%< + * Get the next token from a DNS master file type stream. This is a + * convenience function that sets appropriate options and handles quoted + * strings and end of line correctly for master files. It also ungets + * unexpected tokens. + * + * Requires: + *\li 'lex' is a valid lexer. + * + *\li 'token' is a valid pointer + * + * Returns: + * + * \li any return code from isc_lex_gettoken(). + */ + +isc_result_t +isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol); +/*%< + * Get the next token from a DNS master file type stream. This is a + * convenience function that sets appropriate options and handles end + * of line correctly for master files. It also ungets unexpected tokens. + * + * Requires: + *\li 'lex' is a valid lexer. + * + *\li 'token' is a valid pointer + * + * Returns: + * + * \li any return code from isc_lex_gettoken(). + */ + +void +isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp); +/*%< + * Unget the current token. + * + * Requires: + *\li 'lex' is a valid lexer. + * + *\li 'lex' has an input source. + * + *\li 'tokenp' points to a valid token. + * + *\li There is no ungotten token already. + */ + +void +isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r); +/*%< + * Returns a region containing the text of the last token returned. + * + * Requires: + *\li 'lex' is a valid lexer. + * + *\li 'lex' has an input source. + * + *\li 'tokenp' points to a valid token. + * + *\li A token has been gotten and not ungotten. + */ + +char * +isc_lex_getsourcename(isc_lex_t *lex); +/*%< + * Return the input source name. + * + * Requires: + *\li 'lex' is a valid lexer. + * + * Returns: + * \li source name or NULL if no current source. + *\li result valid while current input source exists. + */ + + +unsigned long +isc_lex_getsourceline(isc_lex_t *lex); +/*%< + * Return the input source line number. + * + * Requires: + *\li 'lex' is a valid lexer. + * + * Returns: + *\li Current line number or 0 if no current source. + */ + +isc_result_t +isc_lex_setsourcename(isc_lex_t *lex, const char *name); +/*%< + * Assigns a new name to the input source. + * + * Requires: + * + * \li 'lex' is a valid lexer. + * + * Returns: + * \li #ISC_R_SUCCESS + * \li #ISC_R_NOMEMORY + * \li #ISC_R_NOTFOUND - there are no sources. + */ + +isc_result_t +isc_lex_setsourceline(isc_lex_t *lex, unsigned long line); +/*%< + * Assigns a new line number to the input source. This can be used + * when parsing a buffer that's been excerpted from the middle a file, + * allowing logged messages to display the correct line number, + * rather than the line number within the buffer. + * + * Requires: + * + * \li 'lex' is a valid lexer. + * + * Returns: + * \li #ISC_R_SUCCESS + * \li #ISC_R_NOTFOUND - there are no sources. + */ + +bool +isc_lex_isfile(isc_lex_t *lex); +/*%< + * Return whether the current input source is a file. + * + * Requires: + *\li 'lex' is a valid lexer. + * + * Returns: + * \li #true if the current input is a file, + *\li #false otherwise. + */ + + +ISC_LANG_ENDDECLS + +#endif /* ISC_LEX_H */ diff --git a/lib/isc/include/isc/lfsr.h b/lib/isc/include/isc/lfsr.h new file mode 100644 index 0000000..3f2cfb4 --- /dev/null +++ b/lib/isc/include/isc/lfsr.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_LFSR_H +#define ISC_LFSR_H 1 + +/*! \file isc/lfsr.h */ + +#include <inttypes.h> + +#include <isc/lang.h> +#include <isc/types.h> + +typedef struct isc_lfsr isc_lfsr_t; + +/*% + * This function is called when reseeding is needed. It is allowed to + * modify any state in the LFSR in any way it sees fit OTHER THAN "bits". + * + * It MUST set "count" to a new value or the lfsr will never reseed again. + * + * Also, a reseed will never occur in the middle of an extraction. This + * is purely an optimization, and is probably what one would want. + */ +typedef void (*isc_lfsrreseed_t)(isc_lfsr_t *, void *); + +/*% + * The members of this structure can be used by the application, but care + * needs to be taken to not change state once the lfsr is in operation. + */ +struct isc_lfsr { + uint32_t state; /*%< previous state */ + unsigned int bits; /*%< length */ + uint32_t tap; /*%< bit taps */ + unsigned int count; /*%< reseed count (in BITS!) */ + isc_lfsrreseed_t reseed; /*%< reseed function */ + void *arg; /*%< reseed function argument */ +}; + +ISC_LANG_BEGINDECLS + + +void +isc_lfsr_init(isc_lfsr_t *lfsr, uint32_t state, unsigned int bits, + uint32_t tap, unsigned int count, + isc_lfsrreseed_t reseed, void *arg); +/*%< + * Initialize an LFSR. + * + * Note: + * + *\li Putting untrusted values into this function will cause the LFSR to + * generate (perhaps) non-maximal length sequences. + * + * Requires: + * + *\li lfsr != NULL + * + *\li 8 <= bits <= 32 + * + *\li tap != 0 + */ + +void +isc_lfsr_generate(isc_lfsr_t *lfsr, void *data, unsigned int count); +/*%< + * Returns "count" bytes of data from the LFSR. + * + * Requires: + * + *\li lfsr be valid. + * + *\li data != NULL. + * + *\li count > 0. + */ + +void +isc_lfsr_skip(isc_lfsr_t *lfsr, unsigned int skip); +/*%< + * Skip "skip" states. + * + * Requires: + * + *\li lfsr be valid. + */ + +uint32_t +isc_lfsr_generate32(isc_lfsr_t *lfsr1, isc_lfsr_t *lfsr2); +/*%< + * Given two LFSRs, use the current state from each to skip entries in the + * other. The next states are then xor'd together and returned. + * + * WARNING: + * + *\li This function is used only for very, very low security data, such + * as DNS message IDs where it is desired to have an unpredictable + * stream of bytes that are harder to predict than a simple flooding + * attack. + * + * Notes: + * + *\li Since the current state from each of the LFSRs is used to skip + * state in the other, it is important that no state be leaked + * from either LFSR. + * + * Requires: + * + *\li lfsr1 and lfsr2 be valid. + * + *\li 1 <= skipbits <= 31 + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_LFSR_H */ diff --git a/lib/isc/include/isc/lib.h b/lib/isc/include/isc/lib.h new file mode 100644 index 0000000..55ef20a --- /dev/null +++ b/lib/isc/include/isc/lib.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_LIB_H +#define ISC_LIB_H 1 + +/*! \file isc/lib.h */ + +#include <isc/types.h> +#include <isc/lang.h> + +ISC_LANG_BEGINDECLS + +LIBISC_EXTERNAL_DATA extern isc_msgcat_t *isc_msgcat; + +void +isc_lib_initmsgcat(void); +/*!< + * \brief Initialize the ISC library's message catalog, isc_msgcat, if it + * has not already been initialized. + */ + +void +isc_lib_register(void); +/*!< + * \brief Register the ISC library implementations for some base services + * such as memory or event management and handling socket or timer events. + * An external application that wants to use the ISC library must call this + * function very early in main(). + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_LIB_H */ diff --git a/lib/isc/include/isc/likely.h b/lib/isc/include/isc/likely.h new file mode 100644 index 0000000..6c77957 --- /dev/null +++ b/lib/isc/include/isc/likely.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_LIKELY_H +#define ISC_LIKELY_H 1 + +/*% + * Performance + */ +#ifdef HAVE_BUILTIN_EXPECT +#define ISC_LIKELY(x) __builtin_expect((x), 1) +#define ISC_UNLIKELY(x) __builtin_expect((x), 0) +#else +#define ISC_LIKELY(x) (x) +#define ISC_UNLIKELY(x) (x) +#endif + +#endif /* ISC_LIKELY_H */ diff --git a/lib/isc/include/isc/list.h b/lib/isc/include/isc/list.h new file mode 100644 index 0000000..ab476ca --- /dev/null +++ b/lib/isc/include/isc/list.h @@ -0,0 +1,191 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_LIST_H +#define ISC_LIST_H 1 +#include <isc/assertions.h> + +#ifdef ISC_LIST_CHECKINIT +#define ISC_LINK_INSIST(x) ISC_INSIST(x) +#else +#define ISC_LINK_INSIST(x) +#endif + +#define ISC_LIST(type) struct { type *head, *tail; } +#define ISC_LIST_INIT(list) \ + do { (list).head = NULL; (list).tail = NULL; } while (0) + +#define ISC_LINK(type) struct { type *prev, *next; } +#define ISC_LINK_INIT_TYPE(elt, link, type) \ + do { \ + (elt)->link.prev = (type *)(-1); \ + (elt)->link.next = (type *)(-1); \ + } while (0) +#define ISC_LINK_INIT(elt, link) \ + ISC_LINK_INIT_TYPE(elt, link, void) +#define ISC_LINK_LINKED(elt, link) ((void *)((elt)->link.prev) != (void *)(-1)) + +#define ISC_LIST_HEAD(list) ((list).head) +#define ISC_LIST_TAIL(list) ((list).tail) +#define ISC_LIST_EMPTY(list) ((list).head == NULL) + +#define __ISC_LIST_PREPENDUNSAFE(list, elt, link) \ + do { \ + if ((list).head != NULL) \ + (list).head->link.prev = (elt); \ + else \ + (list).tail = (elt); \ + (elt)->link.prev = NULL; \ + (elt)->link.next = (list).head; \ + (list).head = (elt); \ + } while (0) + +#define ISC_LIST_PREPEND(list, elt, link) \ + do { \ + ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \ + __ISC_LIST_PREPENDUNSAFE(list, elt, link); \ + } while (0) + +#define ISC_LIST_INITANDPREPEND(list, elt, link) \ + __ISC_LIST_PREPENDUNSAFE(list, elt, link) + +#define __ISC_LIST_APPENDUNSAFE(list, elt, link) \ + do { \ + if ((list).tail != NULL) \ + (list).tail->link.next = (elt); \ + else \ + (list).head = (elt); \ + (elt)->link.prev = (list).tail; \ + (elt)->link.next = NULL; \ + (list).tail = (elt); \ + } while (0) + +#define ISC_LIST_APPEND(list, elt, link) \ + do { \ + ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \ + __ISC_LIST_APPENDUNSAFE(list, elt, link); \ + } while (0) + +#define ISC_LIST_INITANDAPPEND(list, elt, link) \ + __ISC_LIST_APPENDUNSAFE(list, elt, link) + +#define __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type) \ + do { \ + if ((elt)->link.next != NULL) \ + (elt)->link.next->link.prev = (elt)->link.prev; \ + else { \ + ISC_INSIST((list).tail == (elt)); \ + (list).tail = (elt)->link.prev; \ + } \ + if ((elt)->link.prev != NULL) \ + (elt)->link.prev->link.next = (elt)->link.next; \ + else { \ + ISC_INSIST((list).head == (elt)); \ + (list).head = (elt)->link.next; \ + } \ + (elt)->link.prev = (type *)(-1); \ + (elt)->link.next = (type *)(-1); \ + ISC_INSIST((list).head != (elt)); \ + ISC_INSIST((list).tail != (elt)); \ + } while (0) + +#define __ISC_LIST_UNLINKUNSAFE(list, elt, link) \ + __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, void) + +#define ISC_LIST_UNLINK_TYPE(list, elt, link, type) \ + do { \ + ISC_LINK_INSIST(ISC_LINK_LINKED(elt, link)); \ + __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type); \ + } while (0) +#define ISC_LIST_UNLINK(list, elt, link) \ + ISC_LIST_UNLINK_TYPE(list, elt, link, void) + +#define ISC_LIST_PREV(elt, link) ((elt)->link.prev) +#define ISC_LIST_NEXT(elt, link) ((elt)->link.next) + +#define __ISC_LIST_INSERTBEFOREUNSAFE(list, before, elt, link) \ + do { \ + if ((before)->link.prev == NULL) \ + ISC_LIST_PREPEND(list, elt, link); \ + else { \ + (elt)->link.prev = (before)->link.prev; \ + (before)->link.prev = (elt); \ + (elt)->link.prev->link.next = (elt); \ + (elt)->link.next = (before); \ + } \ + } while (0) + +#define ISC_LIST_INSERTBEFORE(list, before, elt, link) \ + do { \ + ISC_LINK_INSIST(ISC_LINK_LINKED(before, link)); \ + ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \ + __ISC_LIST_INSERTBEFOREUNSAFE(list, before, elt, link); \ + } while (0) + +#define __ISC_LIST_INSERTAFTERUNSAFE(list, after, elt, link) \ + do { \ + if ((after)->link.next == NULL) \ + ISC_LIST_APPEND(list, elt, link); \ + else { \ + (elt)->link.next = (after)->link.next; \ + (after)->link.next = (elt); \ + (elt)->link.next->link.prev = (elt); \ + (elt)->link.prev = (after); \ + } \ + } while (0) + +#define ISC_LIST_INSERTAFTER(list, after, elt, link) \ + do { \ + ISC_LINK_INSIST(ISC_LINK_LINKED(after, link)); \ + ISC_LINK_INSIST(!ISC_LINK_LINKED(elt, link)); \ + __ISC_LIST_INSERTAFTERUNSAFE(list, after, elt, link); \ + } while (0) + +#define ISC_LIST_APPENDLIST(list1, list2, link) \ + do { \ + if (ISC_LIST_EMPTY(list1)) \ + (list1) = (list2); \ + else if (!ISC_LIST_EMPTY(list2)) { \ + (list1).tail->link.next = (list2).head; \ + (list2).head->link.prev = (list1).tail; \ + (list1).tail = (list2).tail; \ + } \ + (list2).head = NULL; \ + (list2).tail = NULL; \ + } while (0) + +#define ISC_LIST_PREPENDLIST(list1, list2, link) \ + do { \ + if (ISC_LIST_EMPTY(list1)) \ + (list1) = (list2); \ + else if (!ISC_LIST_EMPTY(list2)) { \ + (list2).tail->link.next = (list1).head; \ + (list1).head->link.prev = (list2).tail; \ + (list1).head = (list2).head; \ + } \ + (list2).head = NULL; \ + (list2).tail = NULL; \ + } while (0) + +#define ISC_LIST_ENQUEUE(list, elt, link) ISC_LIST_APPEND(list, elt, link) +#define __ISC_LIST_ENQUEUEUNSAFE(list, elt, link) \ + __ISC_LIST_APPENDUNSAFE(list, elt, link) +#define ISC_LIST_DEQUEUE(list, elt, link) \ + ISC_LIST_UNLINK_TYPE(list, elt, link, void) +#define ISC_LIST_DEQUEUE_TYPE(list, elt, link, type) \ + ISC_LIST_UNLINK_TYPE(list, elt, link, type) +#define __ISC_LIST_DEQUEUEUNSAFE(list, elt, link) \ + __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, void) +#define __ISC_LIST_DEQUEUEUNSAFE_TYPE(list, elt, link, type) \ + __ISC_LIST_UNLINKUNSAFE_TYPE(list, elt, link, type) + +#endif /* ISC_LIST_H */ diff --git a/lib/isc/include/isc/log.h b/lib/isc/include/isc/log.h new file mode 100644 index 0000000..fd0443f --- /dev/null +++ b/lib/isc/include/isc/log.h @@ -0,0 +1,920 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_LOG_H +#define ISC_LOG_H 1 + +/*! \file isc/log.h */ + +#include <stdbool.h> +#include <stdio.h> +#include <stdarg.h> +#include <syslog.h> /* XXXDCL NT */ + +#include <isc/formatcheck.h> +#include <isc/lang.h> +#include <isc/platform.h> +#include <isc/types.h> + +/*@{*/ +/*! + * \brief Severity levels, patterned after Unix's syslog levels. + * + */ +#define ISC_LOG_DEBUG(level) (level) +/*! + * #ISC_LOG_DYNAMIC can only be used for defining channels with + * isc_log_createchannel(), not to specify a level in isc_log_write(). + */ +#define ISC_LOG_DYNAMIC 0 +#define ISC_LOG_INFO (-1) +#define ISC_LOG_NOTICE (-2) +#define ISC_LOG_WARNING (-3) +#define ISC_LOG_ERROR (-4) +#define ISC_LOG_CRITICAL (-5) +/*@}*/ + +/*@{*/ +/*! + * \brief Destinations. + */ +#define ISC_LOG_TONULL 1 +#define ISC_LOG_TOSYSLOG 2 +#define ISC_LOG_TOFILE 3 +#define ISC_LOG_TOFILEDESC 4 +/*@}*/ + +/*@{*/ +/*% + * Channel flags. + */ +#define ISC_LOG_PRINTTIME 0x0001 +#define ISC_LOG_PRINTLEVEL 0x0002 +#define ISC_LOG_PRINTCATEGORY 0x0004 +#define ISC_LOG_PRINTMODULE 0x0008 +#define ISC_LOG_PRINTTAG 0x0010 /* tag and ":" */ +#define ISC_LOG_PRINTPREFIX 0x0020 /* tag only, no colon */ +#define ISC_LOG_PRINTALL 0x003F +#define ISC_LOG_BUFFERED 0x0040 +#define ISC_LOG_DEBUGONLY 0x1000 +#define ISC_LOG_OPENERR 0x8000 /* internal */ +/*@}*/ + +/*@{*/ +/*! + * \brief Other options. + * + * XXXDCL INFINITE doesn't yet work. Arguably it isn't needed, but + * since I am intend to make large number of versions work efficiently, + * INFINITE is going to be trivial to add to that. + */ +#define ISC_LOG_ROLLINFINITE (-1) +#define ISC_LOG_ROLLNEVER (-2) +/*@}*/ + +/*! + * \brief Used to name the categories used by a library. + * + * An array of isc_logcategory + * structures names each category, and the id value is initialized by calling + * isc_log_registercategories. + */ +struct isc_logcategory { + const char *name; + unsigned int id; +}; + +/*% + * Similar to isc_logcategory, but for all the modules a library defines. + */ +struct isc_logmodule { + const char *name; + unsigned int id; +}; + +/*% + * The isc_logfile structure is initialized as part of an isc_logdestination + * before calling isc_log_createchannel(). + * + * When defining an #ISC_LOG_TOFILE + * channel the name, versions and maximum_size should be set before calling + * isc_log_createchannel(). To define an #ISC_LOG_TOFILEDESC channel set only + * the stream before the call. + * + * Setting maximum_size to zero implies no maximum. + */ +typedef struct isc_logfile { + FILE *stream; /*%< Initialized to NULL for #ISC_LOG_TOFILE. */ + const char *name; /*%< NULL for #ISC_LOG_TOFILEDESC. */ + int versions; /* >= 0, #ISC_LOG_ROLLNEVER, #ISC_LOG_ROLLINFINITE. */ + /*% + * stdio's ftell is standardized to return a long, which may well not + * be big enough for the largest file supportable by the operating + * system (though it is _probably_ big enough for the largest log + * anyone would want). st_size returned by fstat should be typedef'd + * to a size large enough for the largest possible file on a system. + */ + isc_offset_t maximum_size; + bool maximum_reached; /*%< Private. */ +} isc_logfile_t; + +/*% + * Passed to isc_log_createchannel to define the attributes of either + * a stdio or a syslog log. + */ +typedef union isc_logdestination { + isc_logfile_t file; + int facility; /* XXXDCL NT */ +} isc_logdestination_t; + +/*@{*/ +/*% + * The built-in categories of libisc. + * + * Each library registering categories should provide library_LOGCATEGORY_name + * definitions with indexes into its isc_logcategory structure corresponding to + * the order of the names. + */ +LIBISC_EXTERNAL_DATA extern isc_logcategory_t isc_categories[]; +LIBISC_EXTERNAL_DATA extern isc_log_t *isc_lctx; +LIBISC_EXTERNAL_DATA extern isc_logmodule_t isc_modules[]; +/*@}*/ + +/*@{*/ +/*% + * Do not log directly to DEFAULT. Use another category. When in doubt, + * use GENERAL. + */ +#define ISC_LOGCATEGORY_DEFAULT (&isc_categories[0]) +#define ISC_LOGCATEGORY_GENERAL (&isc_categories[1]) +/*@}*/ + +#define ISC_LOGMODULE_SOCKET (&isc_modules[0]) +#define ISC_LOGMODULE_TIME (&isc_modules[1]) +#define ISC_LOGMODULE_INTERFACE (&isc_modules[2]) +#define ISC_LOGMODULE_TIMER (&isc_modules[3]) +#define ISC_LOGMODULE_FILE (&isc_modules[4]) +#define ISC_LOGMODULE_OTHER (&isc_modules[5]) + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp); +/*%< + * Establish a new logging context, with default channels. + * + * Notes: + *\li isc_log_create() calls isc_logconfig_create(), so see its comment + * below for more information. + * + * Requires: + *\li mctx is a valid memory context. + *\li lctxp is not null and *lctxp is null. + *\li lcfgp is null or lcfgp is not null and *lcfgp is null. + * + * Ensures: + *\li *lctxp will point to a valid logging context if all of the necessary + * memory was allocated, or NULL otherwise. + *\li *lcfgp will point to a valid logging configuration if all of the + * necessary memory was allocated, or NULL otherwise. + *\li On failure, no additional memory is allocated. + * + * Returns: + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOMEMORY Resource limit: Out of memory + */ + +isc_result_t +isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp); +/*%< + * Create the data structure that holds all of the configurable information + * about where messages are actually supposed to be sent -- the information + * that could changed based on some configuration file, as opposed to the + * the category/module specification of isc_log_[v]write[1] that is compiled + * into a program, or the debug_level which is dynamic state information. + * + * Notes: + *\li It is necessary to specify the logging context the configuration + * will be used with because the number of categories and modules + * needs to be known in order to set the configuration. However, + * the configuration is not used by the logging context until the + * isc_logconfig_use function is called. + * + *\li The memory context used for operations that allocate memory for + * the configuration is that of the logging context, as specified + * in the isc_log_create call. + * + *\li Four default channels are established: + *\verbatim + * default_syslog + * - log to syslog's daemon facility #ISC_LOG_INFO or higher + * default_stderr + * - log to stderr #ISC_LOG_INFO or higher + * default_debug + * - log to stderr #ISC_LOG_DEBUG dynamically + * null + * - log nothing + *\endverbatim + * + * Requires: + *\li lctx is a valid logging context. + *\li lcftp is not null and *lcfgp is null. + * + * Ensures: + *\li *lcfgp will point to a valid logging context if all of the necessary + * memory was allocated, or NULL otherwise. + *\li On failure, no additional memory is allocated. + * + * Returns: + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOMEMORY Resource limit: Out of memory + */ + +isc_logconfig_t * +isc_logconfig_get(isc_log_t *lctx); +/*%< + * Returns a pointer to the configuration currently in use by the log context. + * + * Requires: + *\li lctx is a valid context. + * + * Ensures: + *\li The configuration pointer is non-null. + * + * Returns: + *\li The configuration pointer. + */ + +isc_result_t +isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg); +/*%< + * Associate a new configuration with a logging context. + * + * Notes: + *\li This is thread safe. The logging context will lock a mutex + * before attempting to swap in the new configuration, and isc_log_doit + * (the internal function used by all of isc_log_[v]write[1]) locks + * the same lock for the duration of its use of the configuration. + * + * Requires: + *\li lctx is a valid logging context. + *\li lcfg is a valid logging configuration. + *\li lctx is the same configuration given to isc_logconfig_create + * when the configuration was created. + * + * Ensures: + *\li Future calls to isc_log_write will use the new configuration. + * + * Returns: + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOMEMORY Resource limit: Out of memory + */ + +void +isc_log_destroy(isc_log_t **lctxp); +/*%< + * Deallocate the memory associated with a logging context. + * + * Requires: + *\li *lctx is a valid logging context. + * + * Ensures: + *\li All of the memory associated with the logging context is returned + * to the free memory pool. + * + *\li Any open files are closed. + * + *\li The logging context is marked as invalid. + */ + +void +isc_logconfig_destroy(isc_logconfig_t **lcfgp); +/*%< + * Destroy a logging configuration. + * + * Notes: + *\li This function cannot be used directly with the return value of + * isc_logconfig_get, because a logging context must always have + * a valid configuration associated with it. + * + * Requires: + *\li lcfgp is not null and *lcfgp is a valid logging configuration. + *\li The logging configuration is not in use by an existing logging context. + * + * Ensures: + *\li All memory allocated for the configuration is freed. + * + *\li The configuration is marked as invalid. + */ + +void +isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]); +/*%< + * Identify logging categories a library will use. + * + * Notes: + *\li A category should only be registered once, but no mechanism enforces + * this rule. + * + *\li The end of the categories array is identified by a NULL name. + * + *\li Because the name is used by #ISC_LOG_PRINTCATEGORY, it should not + * be altered or destroyed after isc_log_registercategories(). + * + *\li Because each element of the categories array is used by + * isc_log_categorybyname, it should not be altered or destroyed + * after registration. + * + *\li The value of the id integer in each structure is overwritten + * by this function, and so id need not be initialized to any particular + * value prior to the function call. + * + *\li A subsequent call to isc_log_registercategories with the same + * logging context (but new categories) will cause the last + * element of the categories array from the prior call to have + * its "name" member changed from NULL to point to the new + * categories array, and its "id" member set to UINT_MAX. + * + * Requires: + *\li lctx is a valid logging context. + *\li categories != NULL. + *\li categories[0].name != NULL. + * + * Ensures: + * \li There are references to each category in the logging context, + * so they can be used with isc_log_usechannel() and isc_log_write(). + */ + +void +isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]); +/*%< + * Identify logging categories a library will use. + * + * Notes: + *\li A module should only be registered once, but no mechanism enforces + * this rule. + * + *\li The end of the modules array is identified by a NULL name. + * + *\li Because the name is used by #ISC_LOG_PRINTMODULE, it should not + * be altered or destroyed after isc_log_registermodules(). + * + *\li Because each element of the modules array is used by + * isc_log_modulebyname, it should not be altered or destroyed + * after registration. + * + *\li The value of the id integer in each structure is overwritten + * by this function, and so id need not be initialized to any particular + * value prior to the function call. + * + *\li A subsequent call to isc_log_registermodules with the same + * logging context (but new modules) will cause the last + * element of the modules array from the prior call to have + * its "name" member changed from NULL to point to the new + * modules array, and its "id" member set to UINT_MAX. + * + * Requires: + *\li lctx is a valid logging context. + *\li modules != NULL. + *\li modules[0].name != NULL; + * + * Ensures: + *\li Each module has a reference in the logging context, so they can be + * used with isc_log_usechannel() and isc_log_write(). + */ + +isc_result_t +isc_log_createchannel(isc_logconfig_t *lcfg, const char *name, + unsigned int type, int level, + const isc_logdestination_t *destination, + unsigned int flags); +/*%< + * Specify the parameters of a logging channel. + * + * Notes: + *\li The name argument is copied to memory in the logging context, so + * it can be altered or destroyed after isc_log_createchannel(). + * + *\li Defining a very large number of channels will have a performance + * impact on isc_log_usechannel(), since the names are searched + * linearly until a match is made. This same issue does not affect + * isc_log_write, however. + * + *\li Channel names can be redefined; this is primarily useful for programs + * that want their own definition of default_syslog, default_debug + * and default_stderr. + * + *\li Any channel that is redefined will not affect logging that was + * already directed to its original definition, _except_ for the + * default_stderr channel. This case is handled specially so that + * the default logging category can be changed by redefining + * default_stderr. (XXXDCL Though now that I think of it, the default + * logging category can be changed with only one additional function + * call by defining a new channel and then calling isc_log_usechannel() + * for #ISC_LOGCATEGORY_DEFAULT.) + * + *\li Specifying #ISC_LOG_PRINTTIME or #ISC_LOG_PRINTTAG for syslog is + * allowed, but probably not what you wanted to do. + * + * #ISC_LOG_DEBUGONLY will mark the channel as usable only when the + * debug level of the logging context (see isc_log_setdebuglevel) + * is non-zero. + * + * Requires: + *\li lcfg is a valid logging configuration. + * + *\li name is not NULL. + * + *\li type is #ISC_LOG_TOSYSLOG, #ISC_LOG_TOFILE, #ISC_LOG_TOFILEDESC or + * #ISC_LOG_TONULL. + * + *\li destination is not NULL unless type is #ISC_LOG_TONULL. + * + *\li level is >= #ISC_LOG_CRITICAL (the most negative logging level). + * + *\li flags does not include any bits aside from the ISC_LOG_PRINT* bits, + * #ISC_LOG_DEBUGONLY or #ISC_LOG_BUFFERED. + * + * Ensures: + *\li #ISC_R_SUCCESS + * A channel with the given name is usable with + * isc_log_usechannel(). + * + *\li #ISC_R_NOMEMORY or #ISC_R_UNEXPECTED + * No additional memory is being used by the logging context. + * Any channel that previously existed with the given name + * is not redefined. + * + * Returns: + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOMEMORY Resource limit: Out of memory + *\li #ISC_R_UNEXPECTED type was out of range and REQUIRE() + * was disabled. + */ + +isc_result_t +isc_log_usechannel(isc_logconfig_t *lcfg, const char *name, + const isc_logcategory_t *category, + const isc_logmodule_t *module); +/*%< + * Associate a named logging channel with a category and module that + * will use it. + * + * Notes: + *\li The name is searched for linearly in the set of known channel names + * until a match is found. (Note the performance impact of a very large + * number of named channels.) When multiple channels of the same + * name are defined, the most recent definition is found. + * + *\li Specifying a very large number of channels for a category will have + * a moderate impact on performance in isc_log_write(), as each + * call looks up the category for the start of a linked list, which + * it follows all the way to the end to find matching modules. The + * test for matching modules is integral, though. + * + *\li If category is NULL, then the channel is associated with the indicated + * module for all known categories (including the "default" category). + * + *\li If module is NULL, then the channel is associated with every module + * that uses that category. + * + *\li Passing both category and module as NULL would make every log message + * use the indicated channel. + * + * \li Specifying a channel that is #ISC_LOG_TONULL for a category/module pair + * has no effect on any other channels associated with that pair, + * regardless of ordering. Thus you cannot use it to "mask out" one + * category/module pair when you have specified some other channel that + * is also used by that category/module pair. + * + * Requires: + *\li lcfg is a valid logging configuration. + * + *\li category is NULL or has an id that is in the range of known ids. + * + * module is NULL or has an id that is in the range of known ids. + * + * Ensures: + *\li #ISC_R_SUCCESS + * The channel will be used by the indicated category/module + * arguments. + * + *\li #ISC_R_NOMEMORY + * If assignment for a specific category has been requested, + * the channel has not been associated with the indicated + * category/module arguments and no additional memory is + * used by the logging context. + * If assignment for all categories has been requested + * then _some_ may have succeeded (starting with category + * "default" and progressing through the order of categories + * passed to isc_log_registercategories()) and additional memory + * is being used by whatever assignments succeeded. + * + * Returns: + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOMEMORY Resource limit: Out of memory + */ + +/* Attention: next four comments PRECEED code */ +/*! + * \brief + * Write a message to the log channels. + * + * Notes: + *\li Log messages containing natural language text should be logged with + * isc_log_iwrite() to allow for localization. + * + *\li lctx can be NULL; this is allowed so that programs which use + * libraries that use the ISC logging system are not required to + * also use it. + * + *\li The format argument is a printf(3) string, with additional arguments + * as necessary. + * + * Requires: + *\li lctx is a valid logging context. + * + *\li The category and module arguments must have ids that are in the + * range of known ids, as established by isc_log_registercategories() + * and isc_log_registermodules(). + * + *\li level != #ISC_LOG_DYNAMIC. ISC_LOG_DYNAMIC is used only to define + * channels, and explicit debugging level must be identified for + * isc_log_write() via ISC_LOG_DEBUG(level). + * + *\li format != NULL. + * + * Ensures: + *\li The log message is written to every channel associated with the + * indicated category/module pair. + * + * Returns: + *\li Nothing. Failure to log a message is not construed as a + * meaningful error. + */ +void +isc_log_write(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + const char *format, ...) + +ISC_FORMAT_PRINTF(5, 6); + +/*% + * Write a message to the log channels. + * + * Notes: + *\li lctx can be NULL; this is allowed so that programs which use + * libraries that use the ISC logging system are not required to + * also use it. + * + *\li The format argument is a printf(3) string, with additional arguments + * as necessary. + * + * Requires: + *\li lctx is a valid logging context. + * + *\li The category and module arguments must have ids that are in the + * range of known ids, as established by isc_log_registercategories() + * and isc_log_registermodules(). + * + *\li level != #ISC_LOG_DYNAMIC. ISC_LOG_DYNAMIC is used only to define + * channels, and explicit debugging level must be identified for + * isc_log_write() via ISC_LOG_DEBUG(level). + * + *\li format != NULL. + * + * Ensures: + *\li The log message is written to every channel associated with the + * indicated category/module pair. + * + * Returns: + *\li Nothing. Failure to log a message is not construed as a + * meaningful error. + */ +void +isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + const char *format, va_list args) + +ISC_FORMAT_PRINTF(5, 0); + +/*% + * Write a message to the log channels, pruning duplicates that occur within + * a configurable amount of seconds (see isc_log_[sg]etduplicateinterval). + * This function is otherwise identical to isc_log_write(). + */ +void +isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, const char *format, ...) + +ISC_FORMAT_PRINTF(5, 6); + +/*% + * Write a message to the log channels, pruning duplicates that occur within + * a configurable amount of seconds (see isc_log_[sg]etduplicateinterval). + * This function is otherwise identical to isc_log_vwrite(). + */ +void +isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, const char *format, + va_list args) + +ISC_FORMAT_PRINTF(5, 0); + +/*% + * These are four internationalized versions of the isc_log_[v]write[1] + * functions. + * + * The only difference is that they take arguments for a message + * catalog, message set, and message number, all immediately preceding the + * format argument. The format argument becomes the default text, a la + * isc_msgcat_get. If the message catalog is NULL, no lookup is attempted + * for a message -- which makes the message set and message number irrelevant, + * and the non-internationalized call should have probably been used instead. + * + * Yes, that means there are now *eight* interfaces to logging a message. + * Sheesh. Make the madness stop! + */ +/*@{*/ +void +isc_log_iwrite(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int message, + const char *format, ...) +ISC_FORMAT_PRINTF(8, 9); + +void +isc_log_ivwrite(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int message, + const char *format, va_list args) +ISC_FORMAT_PRINTF(8, 0); + +void +isc_log_iwrite1(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int message, + const char *format, ...) +ISC_FORMAT_PRINTF(8, 9); + +void +isc_log_ivwrite1(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int message, + const char *format, va_list args) +ISC_FORMAT_PRINTF(8, 0); +/*@}*/ + +void +isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level); +/*%< + * Set the debugging level used for logging. + * + * Notes: + *\li Setting the debugging level to 0 disables debugging log messages. + * + * Requires: + *\li lctx is a valid logging context. + * + * Ensures: + *\li The debugging level is set to the requested value. + */ + +unsigned int +isc_log_getdebuglevel(isc_log_t *lctx); +/*%< + * Get the current debugging level. + * + * Notes: + *\li This is provided so that a program can have a notion of + * "increment debugging level" or "decrement debugging level" + * without needing to keep track of what the current level is. + * + *\li A return value of 0 indicates that debugging messages are disabled. + * + * Requires: + *\li lctx is a valid logging context. + * + * Ensures: + *\li The current logging debugging level is returned. + */ + +bool +isc_log_wouldlog(isc_log_t *lctx, int level); +/*%< + * Determine whether logging something to 'lctx' at 'level' would + * actually cause something to be logged somewhere. + * + * If #false is returned, it is guaranteed that nothing would + * be logged, allowing the caller to omit unnecessary + * isc_log_write() calls and possible message preformatting. + */ + +void +isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval); +/*%< + * Set the interval over which duplicate log messages will be ignored + * by isc_log_[v]write1(), in seconds. + * + * Notes: + *\li Increasing the duplicate interval from X to Y will not necessarily + * filter out duplicates of messages logged in Y - X seconds since the + * increase. (Example: Message1 is logged at midnight. Message2 + * is logged at 00:01:00, when the interval is only 30 seconds, causing + * Message1 to be expired from the log message history. Then the interval + * is increased to 3000 (five minutes) and at 00:04:00 Message1 is logged + * again. It will appear the second time even though less than five + * passed since the first occurrence. + * + * Requires: + *\li lctx is a valid logging context. + */ + +unsigned int +isc_log_getduplicateinterval(isc_logconfig_t *lcfg); +/*%< + * Get the current duplicate filtering interval. + * + * Requires: + *\li lctx is a valid logging context. + * + * Returns: + *\li The current duplicate filtering interval. + */ + +isc_result_t +isc_log_settag(isc_logconfig_t *lcfg, const char *tag); +/*%< + * Set the program name or other identifier for #ISC_LOG_PRINTTAG. + * + * Requires: + *\li lcfg is a valid logging configuration. + * + * Notes: + *\li If this function has not set the tag to a non-NULL, non-empty value, + * then the #ISC_LOG_PRINTTAG channel flag will not print anything. + * Unlike some implementations of syslog on Unix systems, you *must* set + * the tag in order to get it logged. It is not implicitly derived from + * the program name (which is pretty impossible to infer portably). + * + *\li Setting the tag to NULL or the empty string will also cause the + * #ISC_LOG_PRINTTAG channel flag to not print anything. If tag equals the + * empty string, calls to isc_log_gettag will return NULL. + * + * Returns: + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOMEMORY Resource Limit: Out of memory + * + * XXXDCL when creating a new isc_logconfig_t, it might be nice if the tag + * of the currently active isc_logconfig_t was inherited. this does not + * currently happen. + */ + +char * +isc_log_gettag(isc_logconfig_t *lcfg); +/*%< + * Get the current identifier printed with #ISC_LOG_PRINTTAG. + * + * Requires: + *\li lcfg is a valid logging configuration. + * + * Notes: + *\li Since isc_log_settag() will not associate a zero-length string + * with the logging configuration, attempts to do so will cause + * this function to return NULL. However, a determined programmer + * will observe that (currently) a tag of length greater than zero + * could be set, and then modified to be zero length. + * + * Returns: + *\li A pointer to the current identifier, or NULL if none has been set. + */ + +void +isc_log_opensyslog(const char *tag, int options, int facility); +/*%< + * Initialize syslog logging. + * + * Notes: + *\li XXXDCL NT + * This is currently equivalent to openlog(), but is not going to remain + * that way. In the meantime, the arguments are all identical to + * those used by openlog(3), as follows: + * + * \code + * tag: The string to use in the position of the program + * name in syslog messages. Most (all?) syslogs + * will use basename(argv[0]) if tag is NULL. + * + * options: LOG_CONS, LOG_PID, LOG_NDELAY ... whatever your + * syslog supports. + * + * facility: The default syslog facility. This is irrelevant + * since isc_log_write will ALWAYS use the channel's + * declared facility. + * \endcode + * + *\li Zero effort has been made (yet) to accommodate systems with openlog() + * that only takes two arguments, or to identify valid syslog + * facilities or options for any given architecture. + * + *\li It is necessary to call isc_log_opensyslog() to initialize + * syslogging on machines which do not support network connections to + * syslogd because they require a Unix domain socket to be used. Since + * this is a chore to determine at run-time, it is suggested that it + * always be called by programs using the ISC logging system. + * + * Requires: + *\li Nothing. + * + * Ensures: + *\li openlog() is called to initialize the syslog system. + */ + +void +isc_log_closefilelogs(isc_log_t *lctx); +/*%< + * Close all open files used by #ISC_LOG_TOFILE channels. + * + * Notes: + *\li This function is provided for programs that want to use their own + * log rolling mechanism rather than the one provided internally. + * For example, a program that wanted to keep daily logs would define + * a channel which used #ISC_LOG_ROLLNEVER, then once a day would + * rename the log file and call isc_log_closefilelogs(). + * + *\li #ISC_LOG_TOFILEDESC channels are unaffected. + * + * Requires: + *\li lctx is a valid context. + * + * Ensures: + *\li The open files are closed and will be reopened when they are + * next needed. + */ + +isc_logcategory_t * +isc_log_categorybyname(isc_log_t *lctx, const char *name); +/*%< + * Find a category by its name. + * + * Notes: + *\li The string name of a category is not required to be unique. + * + * Requires: + *\li lctx is a valid context. + *\li name is not NULL. + * + * Returns: + *\li A pointer to the _first_ isc_logcategory_t structure used by "name". + * + *\li NULL if no category exists by that name. + */ + +isc_logmodule_t * +isc_log_modulebyname(isc_log_t *lctx, const char *name); +/*%< + * Find a module by its name. + * + * Notes: + *\li The string name of a module is not required to be unique. + * + * Requires: + *\li lctx is a valid context. + *\li name is not NULL. + * + * Returns: + *\li A pointer to the _first_ isc_logmodule_t structure used by "name". + * + *\li NULL if no module exists by that name. + */ + +void +isc_log_setcontext(isc_log_t *lctx); +/*%< + * Sets the context used by the libisc for logging. + * + * Requires: + *\li lctx be a valid context. + */ + +isc_result_t +isc_logfile_roll(isc_logfile_t *file); +/*%< + * Roll a logfile. + * + * Requires: + *\li file is not NULL. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_LOG_H */ diff --git a/lib/isc/include/isc/magic.h b/lib/isc/include/isc/magic.h new file mode 100644 index 0000000..c342d0c --- /dev/null +++ b/lib/isc/include/isc/magic.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_MAGIC_H +#define ISC_MAGIC_H 1 + +#include <isc/likely.h> + +/*! \file isc/magic.h */ + +typedef struct { + unsigned int magic; +} isc__magic_t; + + +/*% + * To use this macro the magic number MUST be the first thing in the + * structure, and MUST be of type "unsigned int". + * The intent of this is to allow magic numbers to be checked even though + * the object is otherwise opaque. + */ +#define ISC_MAGIC_VALID(a,b) (ISC_LIKELY((a) != NULL) && \ + ISC_LIKELY(((const isc__magic_t *)(a))->magic == (b))) + +#define ISC_MAGIC(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) + +#endif /* ISC_MAGIC_H */ diff --git a/lib/isc/include/isc/md5.h b/lib/isc/include/isc/md5.h new file mode 100644 index 0000000..4d29398 --- /dev/null +++ b/lib/isc/include/isc/md5.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file isc/md5.h + * \brief This is the header file for the MD5 message-digest algorithm. + * + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson <ijackson@nyx.cs.du.edu>. + * Still in the public domain. + */ + +#ifndef ISC_MD5_H +#define ISC_MD5_H 1 + +#include <pk11/site.h> + +#ifndef PK11_MD5_DISABLE + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/platform.h> +#include <isc/types.h> + +#define ISC_MD5_DIGESTLENGTH 16U +#define ISC_MD5_BLOCK_LENGTH 64U + +#ifdef ISC_PLATFORM_OPENSSLHASH +#include <openssl/opensslv.h> +#include <openssl/evp.h> + +typedef struct { + EVP_MD_CTX *ctx; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + EVP_MD_CTX _ctx; +#endif +} isc_md5_t; + +#elif PKCS11CRYPTO +#include <pk11/pk11.h> + +typedef pk11_context_t isc_md5_t; + +#else + +typedef struct { + uint32_t buf[4]; + uint32_t bytes[2]; + uint32_t in[16]; +} isc_md5_t; +#endif + +ISC_LANG_BEGINDECLS + +void +isc_md5_init(isc_md5_t *ctx); + +void +isc_md5_invalidate(isc_md5_t *ctx); + +void +isc_md5_update(isc_md5_t *ctx, const unsigned char *buf, unsigned int len); + +void +isc_md5_final(isc_md5_t *ctx, unsigned char *digest); + +bool +isc_md5_check(bool testing); + +ISC_LANG_ENDDECLS + +#endif /* !PK11_MD5_DISABLE */ + +#endif /* ISC_MD5_H */ diff --git a/lib/isc/include/isc/mem.h b/lib/isc/include/isc/mem.h new file mode 100644 index 0000000..8f01856 --- /dev/null +++ b/lib/isc/include/isc/mem.h @@ -0,0 +1,755 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_MEM_H +#define ISC_MEM_H 1 + +/*! \file isc/mem.h */ + +#include <stdbool.h> +#include <stdio.h> + +#include <isc/json.h> +#include <isc/lang.h> +#include <isc/mutex.h> +#include <isc/platform.h> +#include <isc/types.h> +#include <isc/xml.h> + +ISC_LANG_BEGINDECLS + +#define ISC_MEM_LOWATER 0 +#define ISC_MEM_HIWATER 1 +typedef void (*isc_mem_water_t)(void *, int); + +typedef void * (*isc_memalloc_t)(void *, size_t); +typedef void (*isc_memfree_t)(void *, void *); + +/*% + * Define ISC_MEM_TRACKLINES=1 to turn on detailed tracing of memory + * allocation and freeing by file and line number. + */ +#ifndef ISC_MEM_TRACKLINES +#define ISC_MEM_TRACKLINES 1 +#endif + +/*% + * Define ISC_MEM_CHECKOVERRUN=1 to turn on checks for using memory outside + * the requested space. This will increase the size of each allocation. + * + * If we are performing a Coverity static analysis then ISC_MEM_CHECKOVERRUN + * can hide bugs that would otherwise discovered so force to zero. + */ +#ifdef __COVERITY__ +#undef ISC_MEM_CHECKOVERRUN +#define ISC_MEM_CHECKOVERRUN 0 +#endif +#ifndef ISC_MEM_CHECKOVERRUN +#define ISC_MEM_CHECKOVERRUN 1 +#endif + +/*% + * Define ISC_MEM_FILL=1 to fill each block of memory returned to the system + * with the byte string '0xbe'. This helps track down uninitialized pointers + * and the like. On freeing memory, the space is filled with '0xde' for + * the same reasons. + * + * If we are performing a Coverity static analysis then ISC_MEM_FILL + * can hide bugs that would otherwise discovered so force to zero. + */ +#ifdef __COVERITY__ +#undef ISC_MEM_FILL +#define ISC_MEM_FILL 0 +#endif +#ifndef ISC_MEM_FILL +#define ISC_MEM_FILL 1 +#endif + +/*% + * Define ISC_MEMPOOL_NAMES=1 to make memory pools store a symbolic + * name so that the leaking pool can be more readily identified in + * case of a memory leak. + */ +#ifndef ISC_MEMPOOL_NAMES +#define ISC_MEMPOOL_NAMES 1 +#endif + +LIBISC_EXTERNAL_DATA extern unsigned int isc_mem_debugging; +LIBISC_EXTERNAL_DATA extern unsigned int isc_mem_defaultflags; + +/*@{*/ +#define ISC_MEM_DEBUGTRACE 0x00000001U +#define ISC_MEM_DEBUGRECORD 0x00000002U +#define ISC_MEM_DEBUGUSAGE 0x00000004U +#define ISC_MEM_DEBUGSIZE 0x00000008U +#define ISC_MEM_DEBUGCTX 0x00000010U +#define ISC_MEM_DEBUGALL 0x0000001FU +/*!< + * The variable isc_mem_debugging holds a set of flags for + * turning certain memory debugging options on or off at + * runtime. It is initialized to the value ISC_MEM_DEGBUGGING, + * which is 0 by default but may be overridden at compile time. + * The following flags can be specified: + * + * \li #ISC_MEM_DEBUGTRACE + * Log each allocation and free to isc_lctx. + * + * \li #ISC_MEM_DEBUGRECORD + * Remember each allocation, and match them up on free. + * Crash if a free doesn't match an allocation. + * + * \li #ISC_MEM_DEBUGUSAGE + * If a hi_water mark is set, print the maximum inuse memory + * every time it is raised once it exceeds the hi_water mark. + * + * \li #ISC_MEM_DEBUGSIZE + * Check the size argument being passed to isc_mem_put() matches + * that passed to isc_mem_get(). + * + * \li #ISC_MEM_DEBUGCTX + * Check the mctx argument being passed to isc_mem_put() matches + * that passed to isc_mem_get(). + */ +/*@}*/ + +#if ISC_MEM_TRACKLINES +#define _ISC_MEM_FILELINE , __FILE__, __LINE__ +#define _ISC_MEM_FLARG , const char *, unsigned int +#else +#define _ISC_MEM_FILELINE +#define _ISC_MEM_FLARG +#endif + +/*! + * Define ISC_MEM_USE_INTERNAL_MALLOC=1 to use the internal malloc() + * implementation in preference to the system one. The internal malloc() + * is very space-efficient, and quite fast on uniprocessor systems. It + * performs poorly on multiprocessor machines. + * JT: we can overcome the performance issue on multiprocessor machines + * by carefully separating memory contexts. + */ + +#ifndef ISC_MEM_USE_INTERNAL_MALLOC +#define ISC_MEM_USE_INTERNAL_MALLOC 1 +#endif + +/* + * Flags for isc_mem_create2()calls. + */ +#define ISC_MEMFLAG_NOLOCK 0x00000001 /* no lock is necessary */ +#define ISC_MEMFLAG_INTERNAL 0x00000002 /* use internal malloc */ +#if ISC_MEM_USE_INTERNAL_MALLOC +#define ISC_MEMFLAG_DEFAULT ISC_MEMFLAG_INTERNAL +#else +#define ISC_MEMFLAG_DEFAULT 0 +#endif + + +/*%< + * We use either isc___mem (three underscores) or isc__mem (two) depending on + * whether it's for BIND9's internal purpose (with -DBIND9) or generic export + * library. + */ +#define ISCMEMFUNC(sfx) isc__mem_ ## sfx +#define ISCMEMPOOLFUNC(sfx) isc__mempool_ ## sfx + +#define isc_mem_get(c, s) ISCMEMFUNC(get)((c), (s) _ISC_MEM_FILELINE) +#define isc_mem_allocate(c, s) ISCMEMFUNC(allocate)((c), (s) _ISC_MEM_FILELINE) +#define isc_mem_reallocate(c, p, s) ISCMEMFUNC(reallocate)((c), (p), (s) _ISC_MEM_FILELINE) +#define isc_mem_strdup(c, p) ISCMEMFUNC(strdup)((c), (p) _ISC_MEM_FILELINE) +#define isc_mempool_get(c) ISCMEMPOOLFUNC(get)((c) _ISC_MEM_FILELINE) + +/*% + * isc_mem_putanddetach() is a convenience function for use where you + * have a structure with an attached memory context. + * + * Given: + * + * \code + * struct { + * ... + * isc_mem_t *mctx; + * ... + * } *ptr; + * + * isc_mem_t *mctx; + * + * isc_mem_putanddetach(&ptr->mctx, ptr, sizeof(*ptr)); + * \endcode + * + * is the equivalent of: + * + * \code + * mctx = NULL; + * isc_mem_attach(ptr->mctx, &mctx); + * isc_mem_detach(&ptr->mctx); + * isc_mem_put(mctx, ptr, sizeof(*ptr)); + * isc_mem_detach(&mctx); + * \endcode + */ + +/*% memory and memory pool methods */ +typedef struct isc_memmethods { + void (*attach)(isc_mem_t *source, isc_mem_t **targetp); + void (*detach)(isc_mem_t **mctxp); + void (*destroy)(isc_mem_t **mctxp); + void *(*memget)(isc_mem_t *mctx, size_t size _ISC_MEM_FLARG); + void (*memput)(isc_mem_t *mctx, void *ptr, size_t size _ISC_MEM_FLARG); + void (*memputanddetach)(isc_mem_t **mctxp, void *ptr, + size_t size _ISC_MEM_FLARG); + void *(*memallocate)(isc_mem_t *mctx, size_t size _ISC_MEM_FLARG); + void *(*memreallocate)(isc_mem_t *mctx, void *ptr, + size_t size _ISC_MEM_FLARG); + char *(*memstrdup)(isc_mem_t *mctx, const char *s _ISC_MEM_FLARG); + void (*memfree)(isc_mem_t *mctx, void *ptr _ISC_MEM_FLARG); + void (*setdestroycheck)(isc_mem_t *mctx, bool flag); + void (*setwater)(isc_mem_t *ctx, isc_mem_water_t water, + void *water_arg, size_t hiwater, size_t lowater); + void (*waterack)(isc_mem_t *ctx, int flag); + size_t (*inuse)(isc_mem_t *mctx); + size_t (*maxinuse)(isc_mem_t *mctx); + size_t (*total)(isc_mem_t *mctx); + bool (*isovermem)(isc_mem_t *mctx); + isc_result_t (*mpcreate)(isc_mem_t *mctx, size_t size, + isc_mempool_t **mpctxp); +} isc_memmethods_t; + +typedef struct isc_mempoolmethods { + void (*destroy)(isc_mempool_t **mpctxp); + void *(*get)(isc_mempool_t *mpctx _ISC_MEM_FLARG); + void (*put)(isc_mempool_t *mpctx, void *mem _ISC_MEM_FLARG); + unsigned int (*getallocated)(isc_mempool_t *mpctx); + void (*setmaxalloc)(isc_mempool_t *mpctx, unsigned int limit); + void (*setfreemax)(isc_mempool_t *mpctx, unsigned int limit); + void (*setname)(isc_mempool_t *mpctx, const char *name); + void (*associatelock)(isc_mempool_t *mpctx, isc_mutex_t *lock); + void (*setfillcount)(isc_mempool_t *mpctx, unsigned int limit); +} isc_mempoolmethods_t; + +/*% + * This structure is actually just the common prefix of a memory context + * implementation's version of an isc_mem_t. + * \brief + * Direct use of this structure by clients is forbidden. mctx implementations + * may change the structure. 'magic' must be ISCAPI_MCTX_MAGIC for any of the + * isc_mem_ routines to work. mctx implementations must maintain all mctx + * invariants. + */ +struct isc_mem { + unsigned int impmagic; + unsigned int magic; + isc_memmethods_t *methods; +}; + +#define ISCAPI_MCTX_MAGIC ISC_MAGIC('A','m','c','x') +#define ISCAPI_MCTX_VALID(m) ((m) != NULL && \ + (m)->magic == ISCAPI_MCTX_MAGIC) + +/*% + * This is the common prefix of a memory pool context. The same note as + * that for the mem structure applies. + */ +struct isc_mempool { + unsigned int impmagic; + unsigned int magic; + isc_mempoolmethods_t *methods; +}; + +#define ISCAPI_MPOOL_MAGIC ISC_MAGIC('A','m','p','l') +#define ISCAPI_MPOOL_VALID(mp) ((mp) != NULL && \ + (mp)->magic == ISCAPI_MPOOL_MAGIC) + +#define isc_mem_put(c, p, s) \ + do { \ + ISCMEMFUNC(put)((c), (p), (s) _ISC_MEM_FILELINE); \ + (p) = NULL; \ + } while (0) +#define isc_mem_putanddetach(c, p, s) \ + do { \ + ISCMEMFUNC(putanddetach)((c), (p), (s) _ISC_MEM_FILELINE); \ + (p) = NULL; \ + } while (0) +#define isc_mem_free(c, p) \ + do { \ + ISCMEMFUNC(free)((c), (p) _ISC_MEM_FILELINE); \ + (p) = NULL; \ + } while (0) +#define isc_mempool_put(c, p) \ + do { \ + ISCMEMPOOLFUNC(put)((c), (p) _ISC_MEM_FILELINE); \ + (p) = NULL; \ + } while (0) + +/*@{*/ +isc_result_t +isc_mem_create(size_t max_size, size_t target_size, + isc_mem_t **mctxp); + +isc_result_t +isc_mem_create2(size_t max_size, size_t target_size, + isc_mem_t **mctxp, unsigned int flags); + +isc_result_t +isc_mem_createx(size_t max_size, size_t target_size, + isc_memalloc_t memalloc, isc_memfree_t memfree, + void *arg, isc_mem_t **mctxp); + +isc_result_t +isc_mem_createx2(size_t max_size, size_t target_size, + isc_memalloc_t memalloc, isc_memfree_t memfree, + void *arg, isc_mem_t **mctxp, unsigned int flags); + +/*!< + * \brief Create a memory context. + * + * 'max_size' and 'target_size' are tuning parameters. When + * ISC_MEMFLAG_INTERNAL is set, allocations smaller than 'max_size' + * will be satisfied by getting blocks of size 'target_size' from the + * system allocator and breaking them up into pieces; larger allocations + * will use the system allocator directly. If 'max_size' and/or + * 'target_size' are zero, default values will be * used. When + * ISC_MEMFLAG_INTERNAL is not set, 'target_size' is ignored. + * + * 'max_size' is also used to size the statistics arrays and the array + * used to record active memory when ISC_MEM_DEBUGRECORD is set. Setting + * 'max_size' too low can have detrimental effects on performance. + * + * A memory context created using isc_mem_createx() will obtain + * memory from the system by calling 'memalloc' and 'memfree', + * passing them the argument 'arg'. A memory context created + * using isc_mem_create() will use the standard library malloc() + * and free(). + * + * If ISC_MEMFLAG_NOLOCK is set in 'flags', the corresponding memory context + * will be accessed without locking. The user who creates the context must + * ensure there be no race. Since this can be a source of bug, it is generally + * inadvisable to use this flag unless the user is very sure about the race + * condition and the access to the object is highly performance sensitive. + * + * Requires: + * mctxp != NULL && *mctxp == NULL */ +/*@}*/ + +/*@{*/ +void +isc_mem_attach(isc_mem_t *, isc_mem_t **); +void +isc_mem_detach(isc_mem_t **); +/*!< + * \brief Attach to / detach from a memory context. + * + * This is intended for applications that use multiple memory contexts + * in such a way that it is not obvious when the last allocations from + * a given context has been freed and destroying the context is safe. + * + * Most applications do not need to call these functions as they can + * simply create a single memory context at the beginning of main() + * and destroy it at the end of main(), thereby guaranteeing that it + * is not destroyed while there are outstanding allocations. + */ +/*@}*/ + +void +isc_mem_destroy(isc_mem_t **); +/*%< + * Destroy a memory context. + */ + +isc_result_t +isc_mem_ondestroy(isc_mem_t *ctx, + isc_task_t *task, + isc_event_t **event); +/*%< + * Request to be notified with an event when a memory context has + * been successfully destroyed. + */ + +void +isc_mem_stats(isc_mem_t *mctx, FILE *out); +/*%< + * Print memory usage statistics for 'mctx' on the stream 'out'. + */ + +void +isc_mem_setdestroycheck(isc_mem_t *mctx, + bool on); +/*%< + * If 'on' is true, 'mctx' will check for memory leaks when + * destroyed and abort the program if any are present. + */ + +/*@{*/ +void +isc_mem_setquota(isc_mem_t *, size_t); +size_t +isc_mem_getquota(isc_mem_t *); +/*%< + * Set/get the memory quota of 'mctx'. This is a hard limit + * on the amount of memory that may be allocated from mctx; + * if it is exceeded, allocations will fail. + */ +/*@}*/ + +size_t +isc_mem_inuse(isc_mem_t *mctx); +/*%< + * Get an estimate of the amount of memory in use in 'mctx', in bytes. + * This includes quantization overhead, but does not include memory + * allocated from the system but not yet used. + */ + +size_t +isc_mem_maxinuse(isc_mem_t *mctx); +/*%< + * Get an estimate of the largest amount of memory that has been in + * use in 'mctx' at any time. + */ + +size_t +isc_mem_total(isc_mem_t *mctx); +/*%< + * Get the total amount of memory in 'mctx', in bytes, including memory + * not yet used. + */ + +bool +isc_mem_isovermem(isc_mem_t *mctx); +/*%< + * Return true iff the memory context is in "over memory" state, i.e., + * a hiwater mark has been set and the used amount of memory has exceeds + * the mark. + */ + +void +isc_mem_setwater(isc_mem_t *mctx, isc_mem_water_t water, void *water_arg, + size_t hiwater, size_t lowater); +/*%< + * Set high and low water marks for this memory context. + * + * When the memory usage of 'mctx' exceeds 'hiwater', + * '(water)(water_arg, #ISC_MEM_HIWATER)' will be called. 'water' needs to + * call isc_mem_waterack() with #ISC_MEM_HIWATER to acknowledge the state + * change. 'water' may be called multiple times. + * + * When the usage drops below 'lowater', 'water' will again be called, this + * time with #ISC_MEM_LOWATER. 'water' need to calls isc_mem_waterack() with + * #ISC_MEM_LOWATER to acknowledge the change. + * + * static void + * water(void *arg, int mark) { + * struct foo *foo = arg; + * + * LOCK(&foo->marklock); + * if (foo->mark != mark) { + * foo->mark = mark; + * .... + * isc_mem_waterack(foo->mctx, mark); + * } + * UNLOCK(&foo->marklock); + * } + * + * If 'water' is NULL then 'water_arg', 'hi_water' and 'lo_water' are + * ignored and the state is reset. + * + * Requires: + * + * 'water' is not NULL. + * hi_water >= lo_water + */ + +void +isc_mem_waterack(isc_mem_t *ctx, int mark); +/*%< + * Called to acknowledge changes in signaled by calls to 'water'. + */ + +void +isc_mem_printactive(isc_mem_t *mctx, FILE *file); +/*%< + * Print to 'file' all active memory in 'mctx'. + * + * Requires ISC_MEM_DEBUGRECORD to have been set. + */ + +void +isc_mem_printallactive(FILE *file); +/*%< + * Print to 'file' all active memory in all contexts. + * + * Requires ISC_MEM_DEBUGRECORD to have been set. + */ + +void +isc_mem_checkdestroyed(FILE *file); +/*%< + * Check that all memory contexts have been destroyed. + * Prints out those that have not been. + * Fatally fails if there are still active contexts. + */ + +unsigned int +isc_mem_references(isc_mem_t *ctx); +/*%< + * Return the current reference count. + */ + +void +isc_mem_setname(isc_mem_t *ctx, const char *name, void *tag); +/*%< + * Name 'ctx'. + * + * Notes: + * + *\li Only the first 15 characters of 'name' will be copied. + * + *\li 'tag' is for debugging purposes only. + * + * Requires: + * + *\li 'ctx' is a valid ctx. + */ + +const char * +isc_mem_getname(isc_mem_t *ctx); +/*%< + * Get the name of 'ctx', as previously set using isc_mem_setname(). + * + * Requires: + *\li 'ctx' is a valid ctx. + * + * Returns: + *\li A non-NULL pointer to a null-terminated string. + * If the ctx has not been named, the string is + * empty. + */ + +void * +isc_mem_gettag(isc_mem_t *ctx); +/*%< + * Get the tag value for 'task', as previously set using isc_mem_setname(). + * + * Requires: + *\li 'ctx' is a valid ctx. + * + * Notes: + *\li This function is for debugging purposes only. + * + * Requires: + *\li 'ctx' is a valid task. + */ + +#ifdef HAVE_LIBXML2 +int +isc_mem_renderxml(xmlTextWriterPtr writer); +/*%< + * Render all contexts' statistics and status in XML for writer. + */ +#endif /* HAVE_LIBXML2 */ + +#ifdef HAVE_JSON +isc_result_t +isc_mem_renderjson(json_object *memobj); +/*%< + * Render all contexts' statistics and status in JSON. + */ +#endif /* HAVE_JSON */ + + +/* + * Memory pools + */ + +isc_result_t +isc_mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp); +/*%< + * Create a memory pool. + * + * Requires: + *\li mctx is a valid memory context. + *\li size > 0 + *\li mpctxp != NULL and *mpctxp == NULL + * + * Defaults: + *\li maxalloc = UINT_MAX + *\li freemax = 1 + *\li fillcount = 1 + * + * Returns: + *\li #ISC_R_NOMEMORY -- not enough memory to create pool + *\li #ISC_R_SUCCESS -- all is well. + */ + +void +isc_mempool_destroy(isc_mempool_t **mpctxp); +/*%< + * Destroy a memory pool. + * + * Requires: + *\li mpctxp != NULL && *mpctxp is a valid pool. + *\li The pool has no un"put" allocations outstanding + */ + +void +isc_mempool_setname(isc_mempool_t *mpctx, const char *name); +/*%< + * Associate a name with a memory pool. At most 15 characters may be used. + * + * Requires: + *\li mpctx is a valid pool. + *\li name != NULL; + */ + +void +isc_mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock); +/*%< + * Associate a lock with this memory pool. + * + * This lock is used when getting or putting items using this memory pool, + * and it is also used to set or get internal state via the isc_mempool_get*() + * and isc_mempool_set*() set of functions. + * + * Multiple pools can each share a single lock. For instance, if "manager" + * type object contained pools for various sizes of events, and each of + * these pools used a common lock. Note that this lock must NEVER be used + * by other than mempool routines once it is given to a pool, since that can + * easily cause double locking. + * + * Requires: + * + *\li mpctpx is a valid pool. + * + *\li lock != NULL. + * + *\li No previous lock is assigned to this pool. + * + *\li The lock is initialized before calling this function via the normal + * means of doing that. + */ + +/* + * The following functions get/set various parameters. Note that due to + * the unlocked nature of pools these are potentially random values unless + * the imposed externally provided locking protocols are followed. + * + * Also note that the quota limits will not always take immediate effect. + * For instance, setting "maxalloc" to a number smaller than the currently + * allocated count is permitted. New allocations will be refused until + * the count drops below this threshold. + * + * All functions require (in addition to other requirements): + * mpctx is a valid memory pool + */ + +unsigned int +isc_mempool_getfreemax(isc_mempool_t *mpctx); +/*%< + * Returns the maximum allowed size of the free list. + */ + +void +isc_mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit); +/*%< + * Sets the maximum allowed size of the free list. + */ + +unsigned int +isc_mempool_getfreecount(isc_mempool_t *mpctx); +/*%< + * Returns current size of the free list. + */ + +unsigned int +isc_mempool_getmaxalloc(isc_mempool_t *mpctx); +/*!< + * Returns the maximum allowed number of allocations. + */ + +void +isc_mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit); +/*%< + * Sets the maximum allowed number of allocations. + * + * Additional requirements: + *\li limit > 0 + */ + +unsigned int +isc_mempool_getallocated(isc_mempool_t *mpctx); +/*%< + * Returns the number of items allocated from this pool. + */ + +unsigned int +isc_mempool_getfillcount(isc_mempool_t *mpctx); +/*%< + * Returns the number of items allocated as a block from the parent memory + * context when the free list is empty. + */ + +void +isc_mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit); +/*%< + * Sets the fillcount. + * + * Additional requirements: + *\li limit > 0 + */ + + +/* + * Pseudo-private functions for use via macros. Do not call directly. + */ +void * +ISCMEMFUNC(get)(isc_mem_t *, size_t _ISC_MEM_FLARG); +void +ISCMEMFUNC(putanddetach)(isc_mem_t **, void *, size_t _ISC_MEM_FLARG); +void +ISCMEMFUNC(put)(isc_mem_t *, void *, size_t _ISC_MEM_FLARG); +void * +ISCMEMFUNC(allocate)(isc_mem_t *, size_t _ISC_MEM_FLARG); +void * +ISCMEMFUNC(reallocate)(isc_mem_t *, void *, size_t _ISC_MEM_FLARG); +void +ISCMEMFUNC(free)(isc_mem_t *, void * _ISC_MEM_FLARG); +char * +ISCMEMFUNC(strdup)(isc_mem_t *, const char *_ISC_MEM_FLARG); +void * +ISCMEMPOOLFUNC(get)(isc_mempool_t * _ISC_MEM_FLARG); +void +ISCMEMPOOLFUNC(put)(isc_mempool_t *, void * _ISC_MEM_FLARG); + +/*%< + * See isc_mem_create2() above. + */ +typedef isc_result_t +(*isc_memcreatefunc_t)(size_t init_max_size, size_t target_size, + isc_mem_t **ctxp, unsigned int flags); + +isc_result_t +isc_mem_register(isc_memcreatefunc_t createfunc); +/*%< + * Register a new memory management implementation and add it to the list of + * supported implementations. This function must be called when a different + * memory management library is used than the one contained in the ISC library. + */ + +isc_result_t +isc__mem_register(void); +/*%< + * A short cut function that specifies the memory management module in the ISC + * library for isc_mem_register(). An application that uses the ISC library + * usually do not have to care about this function: it would call + * isc_lib_register(), which internally calls this function. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_MEM_H */ diff --git a/lib/isc/include/isc/meminfo.h b/lib/isc/include/isc/meminfo.h new file mode 100644 index 0000000..c2c7e57 --- /dev/null +++ b/lib/isc/include/isc/meminfo.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_MEMINFO_H +#define ISC_MEMINFO_H 1 + +#include <inttypes.h> + +#include <isc/types.h> + +#include <isc/lang.h> + +ISC_LANG_BEGINDECLS + +uint64_t +isc_meminfo_totalphys(void); +/*%< + * Return total available physical memory in bytes, or 0 if this cannot + * be determined +*/ + +ISC_LANG_ENDDECLS + +#endif /* ISC_MEMINFO_H */ diff --git a/lib/isc/include/isc/msgcat.h b/lib/isc/include/isc/msgcat.h new file mode 100644 index 0000000..3b533d6 --- /dev/null +++ b/lib/isc/include/isc/msgcat.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_MSGCAT_H +#define ISC_MSGCAT_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/msgcat.h + * \brief The ISC Message Catalog + * aids internationalization of applications by allowing + * messages to be retrieved from locale-specific files instead of + * hardwiring them into the application. This allows translations of + * messages appropriate to the locale to be supplied without recompiling + * the application. + * + * Notes: + *\li It's very important that message catalogs work, even if only the + * default_text can be used. + * + * MP: + *\li The caller must ensure appropriate synchronization of + * isc_msgcat_open() and isc_msgcat_close(). isc_msgcat_get() + * ensures appropriate synchronization. + * + * Reliability: + *\li No anticipated impact. + * + * Resources: + *\li TBS + * + * \li Security: + * No anticipated impact. + * + * \li Standards: + * None. + */ + +/***** + ***** Imports + *****/ + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/***** + ***** Methods + *****/ + +void +isc_msgcat_open(const char *name, isc_msgcat_t **msgcatp); +/*%< + * Open a message catalog. + * + * Notes: + * + *\li If memory cannot be allocated or other failures occur, *msgcatp + * will be set to NULL. If a NULL msgcat is given to isc_msgcat_get(), + * the default_text will be returned, ensuring that some message text + * will be available, no matter what's going wrong. + * + * Requires: + * + *\li 'name' is a valid string. + * + *\li msgcatp != NULL && *msgcatp == NULL + */ + +void +isc_msgcat_close(isc_msgcat_t **msgcatp); +/*%< + * Close a message catalog. + * + * Notes: + * + *\li Any string pointers returned by prior calls to isc_msgcat_get() are + * invalid after isc_msgcat_close() has been called and must not be + * used. + * + * Requires: + * + *\li *msgcatp is a valid message catalog or is NULL. + * + * Ensures: + * + *\li All resources associated with the message catalog are released. + * + *\li *msgcatp == NULL + */ + +const char * +isc_msgcat_get(isc_msgcat_t *msgcat, int set, int message, + const char *default_text); +/*%< + * Get message 'message' from message set 'set' in 'msgcat'. If it + * is not available, use 'default_text'. + * + * Requires: + * + *\li 'msgcat' is a valid message catalog or is NULL. + * + *\li set > 0 + * + *\li message > 0 + * + *\li 'default_text' is a valid string. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_MSGCAT_H */ diff --git a/lib/isc/include/isc/msgs.h b/lib/isc/include/isc/msgs.h new file mode 100644 index 0000000..01e3b48 --- /dev/null +++ b/lib/isc/include/isc/msgs.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_MSGS_H +#define ISC_MSGS_H 1 + +/*! \file isc/msgs.h */ + +#include <isc/lib.h> /* Provide isc_msgcat global variable. */ +#include <isc/msgcat.h> /* Provide isc_msgcat_*() functions. */ + +/*@{*/ +/*! + * \brief Message sets, named per source file, excepting "GENERAL". + * + * IMPORTANT: The original list is alphabetical, but any new sets must + * be added to the end. + */ +#define ISC_MSGSET_GENERAL 1 +/* ISC_RESULT_RESULTSET 2 */ /* XXX */ +/* ISC_RESULT_UNAVAILABLESET 3 */ /* XXX */ +#define ISC_MSGSET_APP 4 +#define ISC_MSGSET_COMMANDLINE 5 +#define ISC_MSGSET_ENTROPY 6 +#define ISC_MSGSET_IFITERIOCTL 7 +#define ISC_MSGSET_IFITERSYSCTL 8 +#define ISC_MSGSET_LEX 9 +#define ISC_MSGSET_LOG 10 +#define ISC_MSGSET_MEM 11 +#define ISC_MSGSET_NETADDR 12 +#define ISC_MSGSET_PRINT 13 +#define ISC_MSGSET_RESULT 14 +#define ISC_MSGSET_RWLOCK 15 +#define ISC_MSGSET_SOCKADDR 16 +#define ISC_MSGSET_SOCKET 17 +#define ISC_MSGSET_TASK 18 +#define ISC_MSGSET_TIMER 19 +#define ISC_MSGSET_UTIL 20 +#define ISC_MSGSET_IFITERGETIFADDRS 21 +/*@}*/ + +/*@{*/ +/*! + * Message numbers + * are only required to be unique per message set, + * but are unique throughout the entire catalog to not be as confusing when + * debugging. + * + * The initial numbering was done by multiply by 100 the set number the + * message appears in then adding the incremental message number. + */ +#define ISC_MSG_FAILED 101 /*%< "failed" */ +#define ISC_MSG_SUCCEEDED 102 /*%< Compatible with "failed" */ +#define ISC_MSG_SUCCESS 103 /*%< More usual way to say "success" */ +#define ISC_MSG_STARTING 104 /*%< As in "daemon: starting" */ +#define ISC_MSG_STOPING 105 /*%< As in "daemon: stopping" */ +#define ISC_MSG_ENTERING 106 /*%< As in "some_subr: entering" */ +#define ISC_MSG_EXITING 107 /*%< As in "some_subr: exiting" */ +#define ISC_MSG_CALLING 108 /*%< As in "calling some_subr()" */ +#define ISC_MSG_RETURNED 109 /*%< As in "some_subr: returned <foo>" */ +#define ISC_MSG_FATALERROR 110 /*%< "fatal error" */ +#define ISC_MSG_SHUTTINGDOWN 111 /*%< "shutting down" */ +#define ISC_MSG_RUNNING 112 /*%< "running" */ +#define ISC_MSG_WAIT 113 /*%< "wait" */ +#define ISC_MSG_WAITUNTIL 114 /*%< "waituntil" */ + +#define ISC_MSG_SIGNALSETUP 201 /*%< "handle_signal() %d setup: %s" */ + +#define ISC_MSG_ILLEGALOPT 301 /*%< "illegal option" */ +#define ISC_MSG_OPTNEEDARG 302 /*%< "option requires an argument" */ + +#define ISC_MSG_ENTROPYSTATS 401 /*%< "Entropy pool %p: refcnt %u ..." */ + +#define ISC_MSG_MAKESCANSOCKET 501 /*%< "making interface scan socket: %s" */ +#define ISC_MSG_GETIFCONFIG 502 /*%< "get interface configuration: %s" */ +#define ISC_MSG_BUFFERMAX 503 /*%< "... maximum buffer size exceeded" */ +#define ISC_MSG_GETDESTADDR 504 /*%< "%s: getting destination address: %s" */ +#define ISC_MSG_GETNETMASK 505 /*%< "%s: getting netmask: %s" */ + +#define ISC_MSG_GETIFLISTSIZE 601 /*%< "getting interface list size: ..." */ +#define ISC_MSG_GETIFLIST 602 /*%< "getting interface list: ..." */ +#define ISC_MSG_UNEXPECTEDTYPE 603 /*%< "... unexpected ... message type" */ + +#define ISC_MSG_UNEXPECTEDSTATE 701 /*%< "Unexpected state %d" */ + +#define ISC_MSG_BADTIME 801 /*%< "Bad 00 99:99:99.999 " */ +#define ISC_MSG_LEVEL 802 /*%< "level %d: " */ + +#define ISC_MSG_ADDTRACE 901 /*%< "add %p size %u " */ +#define ISC_MSG_DELTRACE 902 /*%< "del %p size %u " */ +#define ISC_MSG_POOLSTATS 903 /*%< "[Pool statistics]\n" */ +#define ISC_MSG_POOLNAME 904 /*%< "name" */ +#define ISC_MSG_POOLSIZE 905 /*%< "size" */ +#define ISC_MSG_POOLMAXALLOC 906 /*%< "maxalloc" */ +#define ISC_MSG_POOLALLOCATED 907 /*%< "allocated" */ +#define ISC_MSG_POOLFREECOUNT 908 /*%< "freecount" */ +#define ISC_MSG_POOLFREEMAX 909 /*%< "freemax" */ +#define ISC_MSG_POOLFILLCOUNT 910 /*%< "fillcount" */ +#define ISC_MSG_POOLGETS 911 /*%< "gets" */ +#define ISC_MSG_DUMPALLOC 912 /*%< "DUMP OF ALL OUTSTANDING MEMORY ..." */ +#define ISC_MSG_NONE 913 /*%< "\tNone.\n" */ +#define ISC_MSG_PTRFILELINE 914 /*%< "\tptr %p file %s line %u\n" */ + +#define ISC_MSG_UNKNOWNADDR 1001 /*%< "<unknown address, family %u>" */ + +#define ISC_MSG_NOLONGDBL 1104 /*%< "long doubles are not supported" */ + +#define ISC_MSG_PRINTLOCK 1201 /*%< "rwlock %p thread %lu ..." */ +#define ISC_MSG_READ 1202 /*%< "read" */ +#define ISC_MSG_WRITE 1203 /*%< "write" */ +#define ISC_MSG_READING 1204 /*%< "reading" */ +#define ISC_MSG_WRITING 1205 /*%< "writing" */ +#define ISC_MSG_PRELOCK 1206 /*%< "prelock" */ +#define ISC_MSG_POSTLOCK 1207 /*%< "postlock" */ +#define ISC_MSG_PREUNLOCK 1208 /*%< "preunlock" */ +#define ISC_MSG_POSTUNLOCK 1209 /*%< "postunlock" */ +#define ISC_MSG_PRINTLOCK2 1210 /*%< "rwlock %p thread %lu ..." w/ atomic */ + +#define ISC_MSG_UNKNOWNFAMILY 1301 /*%< "unknown address family: %d" */ + +#define ISC_MSG_WRITEFAILED 1401 /*%< "write() failed during watcher ..." */ +#define ISC_MSG_READFAILED 1402 /*%< "read() failed during watcher ... " */ +#define ISC_MSG_PROCESSCMSG 1403 /*%< "processing cmsg %p" */ +#define ISC_MSG_IFRECEIVED 1404 /*%< "interface received on ifindex %u" */ +#define ISC_MSG_SENDTODATA 1405 /*%< "sendto pktinfo data, ifindex %u" */ +#define ISC_MSG_DOIORECV 1406 /*%< "doio_recv: recvmsg(%d) %d bytes ..." */ +#define ISC_MSG_PKTRECV 1407 /*%< "packet received correctly" */ +#define ISC_MSG_DESTROYING 1408 /*%< "destroying" */ +#define ISC_MSG_CREATED 1409 /*%< "created" */ +#define ISC_MSG_ACCEPTLOCK 1410 /*%< "internal_accept called, locked ..." */ +#define ISC_MSG_ACCEPTEDCXN 1411 /*%< "accepted connection, new socket %p" */ +#define ISC_MSG_INTERNALRECV 1412 /*%< "internal_recv: task %p got event %p" */ +#define ISC_MSG_INTERNALSEND 1413 /*%< "internal_send: task %p got event %p" */ +#define ISC_MSG_WATCHERMSG 1414 /*%< "watcher got message %d" */ +#define ISC_MSG_SOCKETSREMAIN 1415 /*%< "sockets exist" */ +#define ISC_MSG_PKTINFOPROVIDED 1416 /*%< "pktinfo structure provided, ..." */ +#define ISC_MSG_BOUND 1417 /*%< "bound" */ +#define ISC_MSG_ACCEPTRETURNED 1418 /*%< accept() returned %d/%s */ +#define ISC_MSG_TOOMANYFDS 1419 /*%< %s: too many open file descriptors */ +#define ISC_MSG_ZEROPORT 1420 /*%< dropping source port zero packet */ +#define ISC_MSG_FILTER 1421 /*%< setsockopt(SO_ACCEPTFILTER): %s */ + +#define ISC_MSG_TOOMANYHANDLES 1422 /*%< %s: too many open WSA event handles: %s */ +#define ISC_MSG_POKED 1423 /*%< "poked flags: %d" */ + +#define ISC_MSG_AWAKE 1502 /*%< "awake" */ +#define ISC_MSG_WORKING 1503 /*%< "working" */ +#define ISC_MSG_EXECUTE 1504 /*%< "execute action" */ +#define ISC_MSG_EMPTY 1505 /*%< "empty" */ +#define ISC_MSG_DONE 1506 /*%< "done" */ +#define ISC_MSG_QUANTUM 1507 /*%< "quantum" */ + +#define ISC_MSG_SCHEDULE 1601 /*%< "schedule" */ +#define ISC_MSG_SIGNALSCHED 1602 /*%< "signal (schedule)" */ +#define ISC_MSG_SIGNALDESCHED 1603 /*%< "signal (deschedule)" */ +#define ISC_MSG_SIGNALDESTROY 1604 /*%< "signal (destroy)" */ +#define ISC_MSG_IDLERESCHED 1605 /*%< "idle reschedule" */ +#define ISC_MSG_EVENTNOTALLOC 1606 /*%< "couldn't allocate event" */ +#define ISC_MSG_SCHEDFAIL 1607 /*%< "couldn't schedule timer: %u" */ +#define ISC_MSG_POSTING 1608 /*%< "posting" */ +#define ISC_MSG_WAKEUP 1609 /*%< "wakeup" */ + +#define ISC_MSG_LOCK 1701 /*%< "LOCK" */ +#define ISC_MSG_LOCKING 1702 /*%< "LOCKING" */ +#define ISC_MSG_LOCKED 1703 /*%< "LOCKED" */ +#define ISC_MSG_UNLOCKED 1704 /*%< "UNLOCKED" */ +#define ISC_MSG_RWLOCK 1705 /*%< "RWLOCK" */ +#define ISC_MSG_RWLOCKED 1706 /*%< "RWLOCKED" */ +#define ISC_MSG_RWUNLOCK 1707 /*%< "RWUNLOCK" */ +#define ISC_MSG_BROADCAST 1708 /*%< "BROADCAST" */ +#define ISC_MSG_SIGNAL 1709 /*%< "SIGNAL" */ +#define ISC_MSG_UTILWAIT 1710 /*%< "WAIT" */ +#define ISC_MSG_WAITED 1711 /*%< "WAITED" */ + +#define ISC_MSG_GETIFADDRS 1801 /*%< "getting interface addresses: ..." */ + +/*@}*/ + +#endif /* ISC_MSGS_H */ diff --git a/lib/isc/include/isc/mutexblock.h b/lib/isc/include/isc/mutexblock.h new file mode 100644 index 0000000..f00088d --- /dev/null +++ b/lib/isc/include/isc/mutexblock.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_MUTEXBLOCK_H +#define ISC_MUTEXBLOCK_H 1 + +/*! \file isc/mutexblock.h */ + +#include <isc/lang.h> +#include <isc/mutex.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_mutexblock_init(isc_mutex_t *block, unsigned int count); +/*%< + * Initialize a block of locks. If an error occurs all initialized locks + * will be destroyed, if possible. + * + * Requires: + * + *\li block != NULL + * + *\li count > 0 + * + * Returns: + * + *\li Any code isc_mutex_init() can return is a valid return for this + * function. + */ + +isc_result_t +isc_mutexblock_destroy(isc_mutex_t *block, unsigned int count); +/*%< + * Destroy a block of locks. + * + * Requires: + * + *\li block != NULL + * + *\li count > 0 + * + *\li Each lock in the block be initialized via isc_mutex_init() or + * the whole block was initialized via isc_mutex_initblock(). + * + * Returns: + * + *\li Any code isc_mutex_init() can return is a valid return for this + * function. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_MUTEXBLOCK_H */ diff --git a/lib/isc/include/isc/netaddr.h b/lib/isc/include/isc/netaddr.h new file mode 100644 index 0000000..2d6b12b --- /dev/null +++ b/lib/isc/include/isc/netaddr.h @@ -0,0 +1,188 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_NETADDR_H +#define ISC_NETADDR_H 1 + +/*! \file isc/netaddr.h */ + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/net.h> +#include <isc/types.h> + +#ifdef ISC_PLATFORM_HAVESYSUNH +#include <sys/types.h> +#include <sys/un.h> +#endif + +ISC_LANG_BEGINDECLS + +struct isc_netaddr { + unsigned int family; + union { + struct in_addr in; + struct in6_addr in6; +#ifdef ISC_PLATFORM_HAVESYSUNH + char un[sizeof(((struct sockaddr_un *)0)->sun_path)]; +#endif + } type; + uint32_t zone; +}; + +bool +isc_netaddr_equal(const isc_netaddr_t *a, const isc_netaddr_t *b); + +/*%< + * Compare network addresses 'a' and 'b'. Return #true if + * they are equal, #false if not. + */ + +bool +isc_netaddr_eqprefix(const isc_netaddr_t *a, const isc_netaddr_t *b, + unsigned int prefixlen); +/*%< + * Compare the 'prefixlen' most significant bits of the network + * addresses 'a' and 'b'. If 'b''s scope is zero then 'a''s scope is + * ignored. Return #true if they are equal, #false if not. + */ + +isc_result_t +isc_netaddr_masktoprefixlen(const isc_netaddr_t *s, unsigned int *lenp); +/*%< + * Convert a netmask in 's' into a prefix length in '*lenp'. + * The mask should consist of zero or more '1' bits in the + * most significant part of the address, followed by '0' bits. + * If this is not the case, #ISC_R_MASKNONCONTIG is returned. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_MASKNONCONTIG + */ + +isc_result_t +isc_netaddr_totext(const isc_netaddr_t *netaddr, isc_buffer_t *target); +/*%< + * Append a text representation of 'sockaddr' to the buffer 'target'. + * The text is NOT null terminated. Handles IPv4 and IPv6 addresses. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOSPACE The text or the null termination did not fit. + *\li #ISC_R_FAILURE Unspecified failure + */ + +void +isc_netaddr_format(const isc_netaddr_t *na, char *array, unsigned int size); +/*%< + * Format a human-readable representation of the network address '*na' + * into the character array 'array', which is of size 'size'. + * The resulting string is guaranteed to be null-terminated. + */ + +#define ISC_NETADDR_FORMATSIZE \ + sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX%SSSSSSSSSS") +/*%< + * Minimum size of array to pass to isc_netaddr_format(). + */ + +void +isc_netaddr_fromsockaddr(isc_netaddr_t *netaddr, const isc_sockaddr_t *source); + +void +isc_netaddr_fromin(isc_netaddr_t *netaddr, const struct in_addr *ina); + +void +isc_netaddr_fromin6(isc_netaddr_t *netaddr, const struct in6_addr *ina6); + +isc_result_t +isc_netaddr_frompath(isc_netaddr_t *netaddr, const char *path); + +void +isc_netaddr_setzone(isc_netaddr_t *netaddr, uint32_t zone); + +uint32_t +isc_netaddr_getzone(const isc_netaddr_t *netaddr); + +void +isc_netaddr_any(isc_netaddr_t *netaddr); +/*%< + * Return the IPv4 wildcard address. + */ + +void +isc_netaddr_any6(isc_netaddr_t *netaddr); +/*%< + * Return the IPv6 wildcard address. + */ + +bool +isc_netaddr_ismulticast(isc_netaddr_t *na); +/*%< + * Returns true if the address is a multicast address. + */ + +bool +isc_netaddr_isexperimental(isc_netaddr_t *na); +/*%< + * Returns true if the address is a experimental (CLASS E) address. + */ + +bool +isc_netaddr_islinklocal(isc_netaddr_t *na); +/*%< + * Returns #true if the address is a link local address. + */ + +bool +isc_netaddr_issitelocal(isc_netaddr_t *na); +/*%< + * Returns #true if the address is a site local address. + */ + +bool +isc_netaddr_isnetzero(isc_netaddr_t *na); +/*%< + * Returns #true if the address is in net zero. + */ + +void +isc_netaddr_fromv4mapped(isc_netaddr_t *t, const isc_netaddr_t *s); +/*%< + * Convert an IPv6 v4mapped address into an IPv4 address. + */ + +isc_result_t +isc_netaddr_prefixok(const isc_netaddr_t *na, unsigned int prefixlen); +/* + * Test whether the netaddr 'na' and 'prefixlen' are consistant. + * e.g. prefixlen within range. + * na does not have bits set which are not covered by the prefixlen. + * + * Returns: + * ISC_R_SUCCESS + * ISC_R_RANGE prefixlen out of range + * ISC_R_NOTIMPLEMENTED unsupported family + * ISC_R_FAILURE extra bits. + */ + +bool +isc_netaddr_isloopback(const isc_netaddr_t *na); +/* + * Test whether the netaddr 'na' is a loopback IPv4 or IPv6 address (in + * 127.0.0.0/8 or ::1). + */ +ISC_LANG_ENDDECLS + +#endif /* ISC_NETADDR_H */ diff --git a/lib/isc/include/isc/netscope.h b/lib/isc/include/isc/netscope.h new file mode 100644 index 0000000..22224a1 --- /dev/null +++ b/lib/isc/include/isc/netscope.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_NETSCOPE_H +#define ISC_NETSCOPE_H 1 + +/*! \file isc/netscope.h */ + +#include <inttypes.h> + +ISC_LANG_BEGINDECLS + +/*% + * Convert a string of an IPv6 scope zone to zone index. If the conversion + * succeeds, 'zoneid' will store the index value. + * + * XXXJT: when a standard interface for this purpose is defined, + * we should use it. + * + * Returns: + * \li ISC_R_SUCCESS: conversion succeeds + * \li ISC_R_FAILURE: conversion fails + */ +isc_result_t +isc_netscope_pton(int af, char *scopename, void *addr, uint32_t *zoneid); + +ISC_LANG_ENDDECLS + +#endif /* ISC_NETSCOPE_H */ diff --git a/lib/isc/include/isc/ondestroy.h b/lib/isc/include/isc/ondestroy.h new file mode 100644 index 0000000..ad1aa61 --- /dev/null +++ b/lib/isc/include/isc/ondestroy.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: ondestroy.h,v 1.14 2007/06/19 23:47:18 tbox Exp $ */ + +#ifndef ISC_ONDESTROY_H +#define ISC_ONDESTROY_H 1 + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/*! \file isc/ondestroy.h + * ondestroy handling. + * + * Any class ``X'' of objects that wants to send out notifications + * on its destruction should declare a field of type isc_ondestroy_t + * (call it 'ondest'). + * + * \code + * typedef struct { + * ... + * isc_ondestroy_t ondest; + * ... + * } X; + * \endcode + * + * When an object ``A'' of type X is created + * it must initialize the field ondest with a call to + * + * \code + * isc_ondestroy_init(&A->ondest). + * \endcode + * + * X should also provide a registration function for third-party + * objects to call to register their interest in being told about + * the destruction of a particular instance of X. + * + * \code + * isc_result_t + * X_ondestroy(X *instance, isc_task_t *task, + * isc_event_t **eventp) { + * return(isc_ondestroy_register(&instance->ondest, task,eventp)); + * } + * \endcode + * + * Note: locking of the ondestory structure embedded inside of X, is + * X's responsibility. + * + * When an instance of X is destroyed, a call to isc_ondestroy_notify() + * sends the notifications: + * + * \code + * X *instance; + * isc_ondestroy_t ondest = instance->ondest; + * + * ... completely cleanup 'instance' here... + * + * isc_ondestroy_notify(&ondest, instance); + * \endcode + * + * + * see lib/dns/zone.c for an ifdef'd-out example. + */ + +struct isc_ondestroy { + unsigned int magic; + isc_eventlist_t events; +}; + +void +isc_ondestroy_init(isc_ondestroy_t *ondest); +/*%< + * Initialize the on ondest structure. *must* be called before first call + * to isc_ondestroy_register(). + */ + +isc_result_t +isc_ondestroy_register(isc_ondestroy_t *ondest, isc_task_t *task, + isc_event_t **eventp); + +/*%< + * Stores task and *eventp away inside *ondest. Ownership of **event is + * taken from the caller (and *eventp is set to NULL). The task is attached + * to. + */ + +void +isc_ondestroy_notify(isc_ondestroy_t *ondest, void *sender); +/*%< + * Dispatches the event(s) to the task(s) that were given in + * isc_ondestroy_register call(s) (done via calls to + * isc_task_sendanddetach()). Before dispatch, the sender value of each + * event structure is set to the value of the sender paramater. The + * internal structures of the ondest parameter are cleaned out, so no other + * cleanup is needed. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_ONDESTROY_H */ diff --git a/lib/isc/include/isc/os.h b/lib/isc/include/isc/os.h new file mode 100644 index 0000000..eb8223e --- /dev/null +++ b/lib/isc/include/isc/os.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_OS_H +#define ISC_OS_H 1 + +/*! \file isc/os.h */ + +#include <isc/lang.h> + +ISC_LANG_BEGINDECLS + +unsigned int +isc_os_ncpus(void); +/*%< + * Return the number of CPUs available on the system, or 1 if this cannot + * be determined. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_OS_H */ diff --git a/lib/isc/include/isc/parseint.h b/lib/isc/include/isc/parseint.h new file mode 100644 index 0000000..41e8a6c --- /dev/null +++ b/lib/isc/include/isc/parseint.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_PARSEINT_H +#define ISC_PARSEINT_H 1 + +#include <inttypes.h> + +#include <isc/lang.h> +#include <isc/types.h> + +/*! \file isc/parseint.h + * \brief Parse integers, in a saner way than atoi() or strtoul() do. + */ + +/*** + *** Functions + ***/ + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_parse_uint32(uint32_t *uip, const char *string, int base); + +isc_result_t +isc_parse_uint16(uint16_t *uip, const char *string, int base); + +isc_result_t +isc_parse_uint8(uint8_t *uip, const char *string, int base); +/*%< + * Parse the null-terminated string 'string' containing a base 'base' + * integer, storing the result in '*uip'. + * The base is interpreted + * as in strtoul(). Unlike strtoul(), leading whitespace, minus or + * plus signs are not accepted, and all errors (including overflow) + * are reported uniformly through the return value. + * + * Requires: + *\li 'string' points to a null-terminated string + *\li 0 <= 'base' <= 36 + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_BADNUMBER The string is not numeric (in the given base) + *\li #ISC_R_RANGE The number is not representable as the requested type. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_PARSEINT_H */ diff --git a/lib/isc/include/isc/platform.h.in b/lib/isc/include/isc/platform.h.in new file mode 100644 index 0000000..c902d46 --- /dev/null +++ b/lib/isc/include/isc/platform.h.in @@ -0,0 +1,412 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +/***** + ***** Platform-dependent defines. + *****/ + +/*** + *** Network. + ***/ + +/*! \brief + * Define if this system needs the <netinet/in6.h> header file included + * for full IPv6 support (pretty much only UnixWare). + */ +@ISC_PLATFORM_NEEDNETINETIN6H@ + +/*! \brief + * Define if this system needs the <netinet6/in6.h> header file included + * to support in6_pkinfo (pretty much only BSD/OS). + */ +@ISC_PLATFORM_NEEDNETINET6IN6H@ + +/*! \brief + * If sockaddrs on this system have an sa_len field, ISC_PLATFORM_HAVESALEN + * will be defined. + */ +@ISC_PLATFORM_HAVESALEN@ + +/*! \brief + * If this system has the IPv6 structure definitions, ISC_PLATFORM_HAVEIPV6 + * will be defined. + */ +@ISC_PLATFORM_HAVEIPV6@ + +/*! \brief + * If this system is missing in6addr_any, ISC_PLATFORM_NEEDIN6ADDRANY will + * be defined. + */ +@ISC_PLATFORM_NEEDIN6ADDRANY@ + +/*! \brief + * If this system is missing in6addr_loopback, ISC_PLATFORM_NEEDIN6ADDRLOOPBACK + * will be defined. + */ +@ISC_PLATFORM_NEEDIN6ADDRLOOPBACK@ + +/*! \brief + * If this system has in6_pktinfo, ISC_PLATFORM_HAVEIN6PKTINFO will be + * defined. + */ +@ISC_PLATFORM_HAVEIN6PKTINFO@ + +/*! \brief + * If this system has in_addr6, rather than in6_addr, ISC_PLATFORM_HAVEINADDR6 + * will be defined. + */ +@ISC_PLATFORM_HAVEINADDR6@ + +/*! \brief + * If this system has sin6_scope_id, ISC_PLATFORM_HAVESCOPEID will be defined. + */ +@ISC_PLATFORM_HAVESCOPEID@ + +/*! \brief + * If this system needs inet_ntop(), ISC_PLATFORM_NEEDNTOP will be defined. + */ +@ISC_PLATFORM_NEEDNTOP@ + +/*! \brief + * If this system needs inet_pton(), ISC_PLATFORM_NEEDPTON will be defined. + */ +@ISC_PLATFORM_NEEDPTON@ + +/*! \brief + * If this system needs in_port_t, ISC_PLATFORM_NEEDPORTT will be defined. + */ +@ISC_PLATFORM_NEEDPORTT@ + +/*! \brief + * Define if the system has struct lifconf which is a extended struct ifconf + * for IPv6. + */ +@ISC_PLATFORM_HAVELIFCONF@ + +/*! \brief + * Define if the system has struct if_laddrconf which is a extended struct + * ifconf for IPv6. + */ +@ISC_PLATFORM_HAVEIF_LADDRCONF@ + +/*! \brief + * Define if the system has struct if_laddrreq. + */ +@ISC_PLATFORM_HAVEIF_LADDRREQ@ + +/*! \brief + * Define either ISC_PLATFORM_BSD44MSGHDR or ISC_PLATFORM_BSD43MSGHDR. + */ +@ISC_PLATFORM_MSGHDRFLAVOR@ + +/*! \brief + * Define if the system supports if_nametoindex. + */ +@ISC_PLATFORM_HAVEIFNAMETOINDEX@ + +/*! \brief + * Define on some UnixWare systems to fix erroneous definitions of various + * IN6_IS_ADDR_* macros. + */ +@ISC_PLATFORM_FIXIN6ISADDR@ + +/*! \brief + * Define if the system has struct sockaddr_storage. + */ +@ISC_PLATFORM_HAVESOCKADDRSTORAGE@ + +/*! \brief + * Define if the system has TCP_FASTOPEN socket option. + */ +@ISC_PLATFORM_HAVETFO@ + +/*! \brief + * Define if the system supports kqueue multiplexing + */ +@ISC_PLATFORM_HAVEKQUEUE@ + +/*! \brief + * Define if the system supports epoll multiplexing + */ +@ISC_PLATFORM_HAVEEPOLL@ + +/*! \brief + * Define if the system supports /dev/poll multiplexing + */ +@ISC_PLATFORM_HAVEDEVPOLL@ + +/*! \brief + * Define if we want to log backtrace + */ +@ISC_PLATFORM_USEBACKTRACE@ + +/* + *** Printing. + ***/ + +/*! \brief + * If this system needs vsnprintf() and snprintf(), ISC_PLATFORM_NEEDVSNPRINTF + * will be defined. + */ +@ISC_PLATFORM_NEEDVSNPRINTF@ + +/*! \brief + * If this system need a modern sprintf() that returns (int) not (char*). + */ +@ISC_PLATFORM_NEEDSPRINTF@ + +/*! \brief + * If this system need a modern printf() that format size %z (size_t). + */ +@ISC_PLATFORM_NEEDPRINTF@ + +/*! \brief + * If this system need a modern fprintf() that format size %z (size_t). + */ +@ISC_PLATFORM_NEEDFPRINTF@ + +/*** + *** String functions. + ***/ +/* + * If the system needs strsep(), ISC_PLATFORM_NEEDSTRSEP will be defined. + */ +@ISC_PLATFORM_NEEDSTRSEP@ + +/* + * If the system needs strlcpy(), ISC_PLATFORM_NEEDSTRLCPY will be defined. + */ +@ISC_PLATFORM_NEEDSTRLCPY@ + +/* + * If the system needs strlcat(), ISC_PLATFORM_NEEDSTRLCAT will be defined. + */ +@ISC_PLATFORM_NEEDSTRLCAT@ + +/* + * Define if this system needs strtoul. + */ +@ISC_PLATFORM_NEEDSTRTOUL@ + +/* + * Define if this system needs memmove. + */ +@ISC_PLATFORM_NEEDMEMMOVE@ + +/* + * Define if this system needs strcasestr. + */ +@ISC_PLATFORM_NEEDSTRCASESTR@ + +/*** + *** Miscellaneous. + ***/ + +/* + * Defined if we are using threads. + */ +@ISC_PLATFORM_USETHREADS@ + +/* + * Defined if unistd.h does not cause fd_set to be delared. + */ +@ISC_PLATFORM_NEEDSYSSELECTH@ + +/* + * Defined to <gssapi.h> or <gssapi/gssapi.h> for how to include + * the GSSAPI header. + */ +@ISC_PLATFORM_GSSAPIHEADER@ + +/* + * Defined to <gssapi_krb5.h> or <gssapi/gssapi_krb5.h> for how to + * include the GSSAPI KRB5 header. + */ +@ISC_PLATFORM_GSSAPI_KRB5_HEADER@ + +/* + * Defined to <krb5.h> or <krb5/krb5.h> for how to include + * the KRB5 header. + */ +@ISC_PLATFORM_KRB5HEADER@ + +/* + * Define if the system has nanosecond-level accuracy in file stats. + */ +@ISC_PLATFORM_HAVESTATNSEC@ + +/* + * Type used for resource limits. + */ +@ISC_PLATFORM_RLIMITTYPE@ + +/* + * Define if your compiler supports "long long int". + */ +@ISC_PLATFORM_HAVELONGLONG@ + +/* + * Define if PTHREAD_ONCE_INIT should be surrounded by braces to + * prevent compiler warnings (such as with gcc on Solaris 2.8). + */ +@ISC_PLATFORM_BRACEPTHREADONCEINIT@ + +/* + * Used to control how extern data is linked; needed for Win32 platforms. + */ +@ISC_PLATFORM_USEDECLSPEC@ + +/* + * Define if the platform has <sys/un.h>. + */ +@ISC_PLATFORM_HAVESYSUNH@ + +/* + * If the "xadd" operation is available on this architecture, + * ISC_PLATFORM_HAVEXADD will be defined. + */ +@ISC_PLATFORM_HAVEXADD@ + +/* + * If the "xaddq" operation (64bit xadd) is available on this architecture, + * ISC_PLATFORM_HAVEXADDQ will be defined. + */ +@ISC_PLATFORM_HAVEXADDQ@ + +/* + * If the 32-bit "atomic swap" operation is available on this + * architecture, ISC_PLATFORM_HAVEATOMICSTORE" will be defined. + */ +@ISC_PLATFORM_HAVEATOMICSTORE@ + +/* + * If the 64-bit "atomic swap" operation is available on this + * architecture, ISC_PLATFORM_HAVEATOMICSTORE" will be defined. + */ +@ISC_PLATFORM_HAVEATOMICSTOREQ@ + +/* + * If the "compare-and-exchange" operation is available on this architecture, + * ISC_PLATFORM_HAVECMPXCHG will be defined. + */ +@ISC_PLATFORM_HAVECMPXCHG@ + +/* + * If <stdatomic.h> is available on this architecture, + * ISC_PLATFORM_HAVESTDATOMIC will be defined. + */ +@ISC_PLATFORM_HAVESTDATOMIC@ + +/* + * Define if gcc ASM extension is available + */ +@ISC_PLATFORM_USEGCCASM@ + +/* + * Define if Tru64 style ASM syntax must be used. + */ +@ISC_PLATFORM_USEOSFASM@ + +/* + * Define if the standard __asm function must be used. + */ +@ISC_PLATFORM_USESTDASM@ + +/* + * Define with the busy wait nop asm or function call. + */ +@ISC_PLATFORM_BUSYWAITNOP@ + +/* + * Define if the platform has <strings.h>. + */ +@ISC_PLATFORM_HAVESTRINGSH@ + +/* + * Define if the hash functions must be provided by OpenSSL. + */ +@ISC_PLATFORM_OPENSSLHASH@ + +/* + * Define if AES support is wanted + */ +@ISC_PLATFORM_WANTAES@ + +/* + * Defines for the noreturn attribute. + */ +@ISC_PLATFORM_NORETURN_PRE@ +@ISC_PLATFORM_NORETURN_POST@ + +/*** + *** Windows dll support. + ***/ + +/* + * Define if MacOS style of PPC assembly must be used. + * e.g. "r6", not "6", for register six. + */ +@ISC_PLATFORM_USEMACASM@ + +#ifndef ISC_PLATFORM_USEDECLSPEC +#define LIBISC_EXTERNAL_DATA +#define LIBDNS_EXTERNAL_DATA +#define LIBISCCC_EXTERNAL_DATA +#define LIBISCCFG_EXTERNAL_DATA +#define LIBBIND9_EXTERNAL_DATA +#define LIBTESTS_EXTERNAL_DATA +#else /*! \brief ISC_PLATFORM_USEDECLSPEC */ +#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 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 /*! \brief ISC_PLATFORM_USEDECLSPEC */ + +/* + * Tell emacs to use C mode for this file. + * + * Local Variables: + * mode: c + * End: + */ + +#endif /* ISC_PLATFORM_H */ diff --git a/lib/isc/include/isc/pool.h b/lib/isc/include/isc/pool.h new file mode 100644 index 0000000..a648312 --- /dev/null +++ b/lib/isc/include/isc/pool.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_OBJPOOL_H +#define ISC_OBJPOOL_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/pool.h + * \brief An object pool is a mechanism for sharing a small pool of + * fungible objects among a large number of objects that depend on them. + * + * This is useful, for example, when it causes performance problems for + * large number of zones to share a single memory context or task object, + * but it would create a different set of problems for them each to have an + * independent task or memory context. + */ + + +/*** + *** Imports. + ***/ + +#include <isc/lang.h> +#include <isc/mem.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/***** + ***** Types. + *****/ + +typedef void +(*isc_pooldeallocator_t)(void **object); + +typedef isc_result_t +(*isc_poolinitializer_t)(void **target, void *arg); + +typedef struct isc_pool isc_pool_t; + +/***** + ***** Functions. + *****/ + +isc_result_t +isc_pool_create(isc_mem_t *mctx, unsigned int count, + isc_pooldeallocator_t free, + isc_poolinitializer_t init, void *initarg, + isc_pool_t **poolp); +/*%< + * Create a pool of "count" object pointers. If 'free' is not NULL, + * it points to a function that will detach the objects. 'init' + * points to a function that will initialize the arguments, and + * 'arg' to an argument to be passed into that function (for example, + * a relevant manager or context object). + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li init != NULL + * + *\li poolp != NULL && *poolp == NULL + * + * Ensures: + * + *\li On success, '*poolp' points to the new object pool. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_UNEXPECTED + */ + +void * +isc_pool_get(isc_pool_t *pool); +/*%< + * Returns a pointer to an object from the pool. Currently the object + * is chosen from the pool at random. (This may be changed in the future + * to something that guaratees balance.) + */ + +int +isc_pool_count(isc_pool_t *pool); +/*%< + * Returns the number of objcts in the pool 'pool'. + */ + +isc_result_t +isc_pool_expand(isc_pool_t **sourcep, unsigned int count, isc_pool_t **targetp); + +/*%< + * If 'size' is larger than the number of objects in the pool pointed to by + * 'sourcep', then a new pool of size 'count' is allocated, the existing + * objects are copied into it, additional ones created to bring the + * total number up to 'count', and the resulting pool is attached to + * 'targetp'. + * + * If 'count' is less than or equal to the number of objects in 'source', then + * 'sourcep' is attached to 'targetp' without any other action being taken. + * + * In either case, 'sourcep' is detached. + * + * Requires: + * + * \li 'sourcep' is not NULL and '*source' is not NULL + * \li 'targetp' is not NULL and '*source' is NULL + * + * Ensures: + * + * \li On success, '*targetp' points to a valid task pool. + * \li On success, '*sourcep' points to NULL. + * + * Returns: + * + * \li #ISC_R_SUCCESS + * \li #ISC_R_NOMEMORY + */ + +void +isc_pool_destroy(isc_pool_t **poolp); +/*%< + * Destroy a task pool. The tasks in the pool are detached but not + * shut down. + * + * Requires: + * \li '*poolp' is a valid task pool. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_OBJPOOL_H */ diff --git a/lib/isc/include/isc/portset.h b/lib/isc/include/isc/portset.h new file mode 100644 index 0000000..1f0d928 --- /dev/null +++ b/lib/isc/include/isc/portset.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file isc/portset.h + * \brief Transport Protocol Port Manipulation Module + * + * This module provides simple utilities to handle a set of transport protocol + * (UDP or TCP) port numbers, e.g., for creating an ACL list. An isc_portset_t + * object is an opaque instance of a port set, for which the user can add or + * remove a specific port or a range of consecutive ports. This object is + * expected to be used as a temporary work space only, and does not protect + * simultaneous access from multiple threads. Therefore it must not be stored + * in a place that can be accessed from multiple threads. + */ + +#ifndef ISC_PORTSET_H +#define ISC_PORTSET_H 1 + +/*** + *** Imports + ***/ + +#include <stdbool.h> + +#include <isc/net.h> + +/*** + *** Functions + ***/ + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_portset_create(isc_mem_t *mctx, isc_portset_t **portsetp); +/*%< + * Create a port set and initialize it as an empty set. + * + * Requires: + *\li 'mctx' to be valid. + *\li 'portsetp' to be non NULL and '*portsetp' to be NULL; + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + */ + +void +isc_portset_destroy(isc_mem_t *mctx, isc_portset_t **portsetp); +/*%< + * Destroy a port set. + * + * Requires: + *\li 'mctx' to be valid and must be the same context given when the port set + * was created. + *\li '*portsetp' to be a valid set. + */ + +bool +isc_portset_isset(isc_portset_t *portset, in_port_t port); +/*%< + * Test whether the given port is stored in the portset. + * + * Requires: + *\li 'portset' to be a valid set. + * + * Returns + * \li #true if the port is found, false otherwise. + */ + +unsigned int +isc_portset_nports(isc_portset_t *portset); +/*%< + * Provides the number of ports stored in the given portset. + * + * Requires: + *\li 'portset' to be a valid set. + * + * Returns + * \li the number of ports stored in portset. + */ + +void +isc_portset_add(isc_portset_t *portset, in_port_t port); +/*%< + * Add the given port to the portset. The port may or may not be stored in + * the portset. + * + * Requires: + *\li 'portlist' to be valid. + */ + +void +isc_portset_remove(isc_portset_t *portset, in_port_t port); +/*%< + * Remove the given port to the portset. The port may or may not be stored in + * the portset. + * + * Requires: + *\li 'portlist' to be valid. + */ + +void +isc_portset_addrange(isc_portset_t *portset, in_port_t port_lo, + in_port_t port_hi); +/*%< + * Add a subset of [port_lo, port_hi] (inclusive) to the portset. Ports in the + * subset may or may not be stored in portset. + * + * Requires: + *\li 'portlist' to be valid. + *\li port_lo <= port_hi + */ + +void +isc_portset_removerange(isc_portset_t *portset, in_port_t port_lo, + in_port_t port_hi); +/*%< + * Subtract a subset of [port_lo, port_hi] (inclusive) from the portset. Ports + * in the subset may or may not be stored in portset. + * + * Requires: + *\li 'portlist' to be valid. + *\li port_lo <= port_hi + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_PORTSET_H */ diff --git a/lib/isc/include/isc/print.h b/lib/isc/include/isc/print.h new file mode 100644 index 0000000..e4fa76c --- /dev/null +++ b/lib/isc/include/isc/print.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_PRINT_H +#define ISC_PRINT_H 1 + +/*! \file isc/print.h */ + +/*** + *** Imports + ***/ + +#include <isc/formatcheck.h> /* Required for ISC_FORMAT_PRINTF() macro. */ +#include <isc/lang.h> +#include <isc/platform.h> + +/*! + * This block allows lib/isc/print.c to be cleanly compiled even if + * the platform does not need it. The standard Makefile will still + * not compile print.c or archive print.o, so this is just to make test + * compilation ("make print.o") easier. + */ +#if !defined(ISC_PLATFORM_NEEDVSNPRINTF) && defined(ISC__PRINT_SOURCE) +#define ISC_PLATFORM_NEEDVSNPRINTF +#undef snprintf +#undef vsnprintf +#endif + +#if !defined(ISC_PLATFORM_NEEDSPRINTF) && defined(ISC__PRINT_SOURCE) +#define ISC_PLATFORM_NEEDSPRINTF +#undef sprintf +#endif + +#if !defined(ISC_PLATFORM_NEEDFPRINTF) && defined(ISC__PRINT_SOURCE) +#define ISC_PLATFORM_NEEDFPRINTF +#undef fprintf +#endif + +#if !defined(ISC_PLATFORM_NEEDPRINTF) && defined(ISC__PRINT_SOURCE) +#define ISC_PLATFORM_NEEDPRINTF +#undef printf +#endif + +/*** + *** Functions + ***/ + +#ifdef ISC_PLATFORM_NEEDVSNPRINTF +#include <stdarg.h> +#include <stddef.h> +#endif + +#include <stdio.h> + +ISC_LANG_BEGINDECLS + +#ifdef ISC_PLATFORM_NEEDVSNPRINTF +int +isc_print_vsnprintf(char *str, size_t size, const char *format, va_list ap) + ISC_FORMAT_PRINTF(3, 0); +#undef vsnprintf +#define vsnprintf isc_print_vsnprintf + +int +isc_print_snprintf(char *str, size_t size, const char *format, ...) + ISC_FORMAT_PRINTF(3, 4); +#undef snprintf +#define snprintf isc_print_snprintf +#endif /* ISC_PLATFORM_NEEDVSNPRINTF */ + +#ifdef ISC_PLATFORM_NEEDSPRINTF +int +isc_print_sprintf(char *str, const char *format, ...) ISC_FORMAT_PRINTF(2, 3); +#undef sprintf +#define sprintf isc_print_sprintf +#endif + +#ifdef ISC_PLATFORM_NEEDPRINTF +int +isc_print_printf(const char *format, ...) ISC_FORMAT_PRINTF(1, 2); +#undef printf +#define printf isc_print_printf +#endif + +#ifdef ISC_PLATFORM_NEEDFPRINTF +int +isc_print_fprintf(FILE * fp, const char *format, ...) ISC_FORMAT_PRINTF(2, 3); +#undef fprintf +#define fprintf isc_print_fprintf +#endif + +ISC_LANG_ENDDECLS + +#endif /* ISC_PRINT_H */ diff --git a/lib/isc/include/isc/queue.h b/lib/isc/include/isc/queue.h new file mode 100644 index 0000000..210f302 --- /dev/null +++ b/lib/isc/include/isc/queue.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/* + * This is a generic implementation of a two-lock concurrent queue. + * There are built-in mutex locks for the head and tail of the queue, + * allowing elements to be safely added and removed at the same time. + * + * NULL is "end of list" + * -1 is "not linked" + */ + +#ifndef ISC_QUEUE_H +#define ISC_QUEUE_H 1 + +#include <stdbool.h> + +#include <isc/assertions.h> +#include <isc/mutex.h> + +#ifdef ISC_QUEUE_CHECKINIT +#define ISC_QLINK_INSIST(x) ISC_INSIST(x) +#else +#define ISC_QLINK_INSIST(x) (void)0 +#endif + +#define ISC_QLINK(type) struct { type *prev, *next; } + +#define ISC_QLINK_INIT(elt, link) \ + do { \ + (elt)->link.next = (elt)->link.prev = (void *)(-1); \ + } while(0) + +#define ISC_QLINK_LINKED(elt, link) ((void*)(elt)->link.next != (void*)(-1)) + +#define ISC_QUEUE(type) struct { \ + type *head, *tail; \ + isc_mutex_t headlock, taillock; \ +} + +#define ISC_QUEUE_INIT(queue, link) \ + do { \ + (void) isc_mutex_init(&(queue).taillock); \ + (void) isc_mutex_init(&(queue).headlock); \ + (queue).tail = (queue).head = NULL; \ + } while (0) + +#define ISC_QUEUE_EMPTY(queue) ((queue).head == NULL) + +#define ISC_QUEUE_DESTROY(queue) \ + do { \ + ISC_QLINK_INSIST(ISC_QUEUE_EMPTY(queue)); \ + (void) isc_mutex_destroy(&(queue).taillock); \ + (void) isc_mutex_destroy(&(queue).headlock); \ + } while (0) + +/* + * queues are meant to separate the locks at either end. For best effect, that + * means keeping the ends separate - i.e. non-empty queues work best. + * + * a push to an empty queue has to take the pop lock to update + * the pop side of the queue. + * Popping the last entry has to take the push lock to update + * the push side of the queue. + * + * The order is (pop, push), because a pop is presumably in the + * latency path and a push is when we're done. + * + * We do an MT hot test in push to see if we need both locks, so we can + * acquire them in order. Hopefully that makes the case where we get + * the push lock and find we need the pop lock (and have to release it) rare. + * + * > 1 entry - no collision, push works on one end, pop on the other + * 0 entry - headlock race + * pop wins - return(NULL), push adds new as both head/tail + * push wins - updates head/tail, becomes 1 entry case. + * 1 entry - taillock race + * pop wins - return(pop) sets head/tail NULL, becomes 0 entry case + * push wins - updates {head,tail}->link.next, pop updates head + * with new ->link.next and doesn't update tail + * + */ +#define ISC_QUEUE_PUSH(queue, elt, link) \ + do { \ + bool headlocked = false; \ + ISC_QLINK_INSIST(!ISC_QLINK_LINKED(elt, link)); \ + if ((queue).head == NULL) { \ + LOCK(&(queue).headlock); \ + headlocked = true; \ + } \ + LOCK(&(queue).taillock); \ + if ((queue).tail == NULL && !headlocked) { \ + UNLOCK(&(queue).taillock); \ + LOCK(&(queue).headlock); \ + LOCK(&(queue).taillock); \ + headlocked = true; \ + } \ + (elt)->link.prev = (queue).tail; \ + (elt)->link.next = NULL; \ + if ((queue).tail != NULL) \ + (queue).tail->link.next = (elt); \ + (queue).tail = (elt); \ + UNLOCK(&(queue).taillock); \ + if (headlocked) { \ + if ((queue).head == NULL) \ + (queue).head = (elt); \ + UNLOCK(&(queue).headlock); \ + } \ + } while (0) + +#define ISC_QUEUE_POP(queue, link, ret) \ + do { \ + LOCK(&(queue).headlock); \ + ret = (queue).head; \ + while (ret != NULL) { \ + if (ret->link.next == NULL) { \ + LOCK(&(queue).taillock); \ + if (ret->link.next == NULL) { \ + (queue).head = (queue).tail = NULL; \ + UNLOCK(&(queue).taillock); \ + break; \ + }\ + UNLOCK(&(queue).taillock); \ + } \ + (queue).head = ret->link.next; \ + (queue).head->link.prev = NULL; \ + break; \ + } \ + UNLOCK(&(queue).headlock); \ + if (ret != NULL) \ + (ret)->link.next = (ret)->link.prev = (void *)(-1); \ + } while(0) + +#define ISC_QUEUE_UNLINK(queue, elt, link) \ + do { \ + ISC_QLINK_INSIST(ISC_QLINK_LINKED(elt, link)); \ + LOCK(&(queue).headlock); \ + LOCK(&(queue).taillock); \ + if ((elt)->link.prev == NULL) \ + (queue).head = (elt)->link.next; \ + else \ + (elt)->link.prev->link.next = (elt)->link.next; \ + if ((elt)->link.next == NULL) \ + (queue).tail = (elt)->link.prev; \ + else \ + (elt)->link.next->link.prev = (elt)->link.prev; \ + UNLOCK(&(queue).taillock); \ + UNLOCK(&(queue).headlock); \ + (elt)->link.next = (elt)->link.prev = (void *)(-1); \ + } while(0) + +#endif /* ISC_QUEUE_H */ diff --git a/lib/isc/include/isc/quota.h b/lib/isc/include/isc/quota.h new file mode 100644 index 0000000..b9bf598 --- /dev/null +++ b/lib/isc/include/isc/quota.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_QUOTA_H +#define ISC_QUOTA_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/quota.h + * + * \brief The isc_quota_t object is a simple helper object for implementing + * quotas on things like the number of simultaneous connections to + * a server. It keeps track of the amount of quota in use, and + * encapsulates the locking necessary to allow multiple tasks to + * share a quota. + */ + +/*** + *** Imports. + ***/ + +#include <isc/lang.h> +#include <isc/mutex.h> +#include <isc/types.h> + +/***** + ***** Types. + *****/ + +ISC_LANG_BEGINDECLS + +/*% isc_quota structure */ +struct isc_quota { + isc_mutex_t lock; /*%< Locked by lock. */ + int max; + int used; + int soft; +}; + +isc_result_t +isc_quota_init(isc_quota_t *quota, int max); +/*%< + * Initialize a quota object. + * + * Returns: + * ISC_R_SUCCESS + * Other error Lock creation failed. + */ + +void +isc_quota_destroy(isc_quota_t *quota); +/*%< + * Destroy a quota object. + */ + +void +isc_quota_soft(isc_quota_t *quota, int soft); +/*%< + * Set a soft quota. + */ + +void +isc_quota_max(isc_quota_t *quota, int max); +/*%< + * Re-set a maximum quota. + */ + +isc_result_t +isc_quota_reserve(isc_quota_t *quota); +/*%< + * Attempt to reserve one unit of 'quota'. + * + * Returns: + * \li #ISC_R_SUCCESS Success + * \li #ISC_R_SOFTQUOTA Success soft quota reached + * \li #ISC_R_QUOTA Quota is full + */ + +void +isc_quota_release(isc_quota_t *quota); +/*%< + * Release one unit of quota. + */ + +isc_result_t +isc_quota_attach(isc_quota_t *quota, isc_quota_t **p); +/*%< + * Like isc_quota_reserve, and also attaches '*p' to the + * quota if successful (ISC_R_SUCCESS or ISC_R_SOFTQUOTA). + */ + +void +isc_quota_detach(isc_quota_t **p); +/*%< + * Like isc_quota_release, and also detaches '*p' from the + * quota. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_QUOTA_H */ diff --git a/lib/isc/include/isc/radix.h b/lib/isc/include/isc/radix.h new file mode 100644 index 0000000..135552a --- /dev/null +++ b/lib/isc/include/isc/radix.h @@ -0,0 +1,232 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <stdbool.h> + +#include <isc/magic.h> +#include <isc/types.h> +#include <isc/mutex.h> +#include <isc/net.h> +#include <isc/refcount.h> + +#include <string.h> + +#ifndef _RADIX_H +#define _RADIX_H + +#define NETADDR_TO_PREFIX_T(na,pt,bits,is_ecs) \ + do { \ + const void *p = na; \ + memset(&(pt), 0, sizeof(pt)); \ + if (p != NULL) { \ + (pt).family = (na)->family; \ + (pt).bitlen = (bits); \ + if ((pt).family == AF_INET6) { \ + memmove(&(pt).add.sin6, &(na)->type.in6, \ + ((bits)+7)/8); \ + } else \ + memmove(&(pt).add.sin, &(na)->type.in, \ + ((bits)+7)/8); \ + } else { \ + (pt).family = AF_UNSPEC; \ + (pt).bitlen = 0; \ + } \ + (pt).ecs = is_ecs; \ + isc_refcount_init(&(pt).refcount, 0); \ + } while(0) + +typedef struct isc_prefix { + isc_mem_t *mctx; + unsigned int family; /* AF_INET | AF_INET6, or AF_UNSPEC for "any" */ + unsigned int bitlen; /* 0 for "any" */ + bool ecs; /* true for an EDNS client subnet address */ + isc_refcount_t refcount; + union { + struct in_addr sin; + struct in6_addr sin6; + } add; +} isc_prefix_t; + +typedef void (*isc_radix_destroyfunc_t)(void *); +typedef void (*isc_radix_processfunc_t)(isc_prefix_t *, void **); + +#define isc_prefix_tochar(prefix) ((char *)&(prefix)->add.sin) +#define isc_prefix_touchar(prefix) ((u_char *)&(prefix)->add.sin) + +#define BIT_TEST(f, b) ((f) & (b)) + +/* + * We need "first match" when we search the radix tree to preserve + * compatibility with the existing ACL implementation. Radix trees + * naturally lend themselves to "best match". In order to get "first match" + * behavior, we keep track of the order in which entries are added to the + * tree--and when a search is made, we find all matching entries, and + * return the one that was added first. + * + * An IPv4 prefix and an IPv6 prefix may share a radix tree node if they + * have the same length and bit pattern (e.g., 127/8 and 7f::/8). Also, + * a node that matches a client address may also match an EDNS client + * subnet address. To disambiguate between these, node_num and data + * are four-element arrays; + * + * - node_num[0] and data[0] are used for IPv4 client addresses + * - node_num[1] and data[1] for IPv4 client subnet addresses + * - node_num[2] and data[2] are used for IPv6 client addresses + * - node_num[3] and data[3] for IPv6 client subnet addresses + * + * A prefix of 0/0 (aka "any" or "none"), is always stored as IPv4, + * but matches IPv6 addresses too, as well as all client subnet + * addresses. + */ + +#define RADIX_NOECS 0 +#define RADIX_ECS 2 +#define RADIX_V4 0 +#define RADIX_V6 1 +#define RADIX_V4_ECS 2 +#define RADIX_V6_ECS 3 +#define RADIX_FAMILIES 4 + +#define ISC_RADIX_FAMILY(p) \ + ((((p)->family == AF_INET6) ? RADIX_V6 : RADIX_V4) + \ + ((p)->ecs ? RADIX_ECS : RADIX_NOECS)) + +typedef struct isc_radix_node { + isc_mem_t *mctx; + uint32_t bit; /* bit length of the prefix */ + isc_prefix_t *prefix; /* who we are in radix tree */ + struct isc_radix_node *l, *r; /* left and right children */ + struct isc_radix_node *parent; /* may be used */ + void *data[RADIX_FAMILIES]; /* pointers to IPv4 and IPV6 data */ + int node_num[RADIX_FAMILIES]; /* which node this was in the tree, + or -1 for glue nodes */ +} isc_radix_node_t; + +#define RADIX_TREE_MAGIC ISC_MAGIC('R','d','x','T'); +#define RADIX_TREE_VALID(a) ISC_MAGIC_VALID(a, RADIX_TREE_MAGIC); + +typedef struct isc_radix_tree { + unsigned int magic; + isc_mem_t *mctx; + isc_radix_node_t *head; + uint32_t maxbits; /* for IP, 32 bit addresses */ + int num_active_node; /* for debugging purposes */ + int num_added_node; /* total number of nodes */ +} isc_radix_tree_t; + +isc_result_t +isc_radix_search(isc_radix_tree_t *radix, isc_radix_node_t **target, + isc_prefix_t *prefix); +/*%< + * Search 'radix' for the best match to 'prefix'. + * Return the node found in '*target'. + * + * Requires: + * \li 'radix' to be valid. + * \li 'target' is not NULL and "*target" is NULL. + * \li 'prefix' to be valid. + * + * Returns: + * \li ISC_R_NOTFOUND + * \li ISC_R_SUCCESS + */ + +isc_result_t +isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target, + isc_radix_node_t *source, isc_prefix_t *prefix); +/*%< + * Insert 'source' or 'prefix' into the radix tree 'radix'. + * Return the node added in 'target'. + * + * Requires: + * \li 'radix' to be valid. + * \li 'target' is not NULL and "*target" is NULL. + * \li 'prefix' to be valid or 'source' to be non NULL and contain + * a valid prefix. + * + * Returns: + * \li ISC_R_NOMEMORY + * \li ISC_R_SUCCESS + */ + +void +isc_radix_remove(isc_radix_tree_t *radix, isc_radix_node_t *node); +/*%< + * Remove the node 'node' from the radix tree 'radix'. + * + * Requires: + * \li 'radix' to be valid. + * \li 'node' to be valid. + */ + +isc_result_t +isc_radix_create(isc_mem_t *mctx, isc_radix_tree_t **target, int maxbits); +/*%< + * Create a radix tree with a maximum depth of 'maxbits'; + * + * Requires: + * \li 'mctx' to be valid. + * \li 'target' to be non NULL and '*target' to be NULL. + * \li 'maxbits' to be less than or equal to RADIX_MAXBITS. + * + * Returns: + * \li ISC_R_NOMEMORY + * \li ISC_R_SUCCESS + */ + +void +isc_radix_destroy(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func); +/*%< + * Destroy a radix tree optionally calling 'func' to clean up node data. + * + * Requires: + * \li 'radix' to be valid. + */ + +void +isc_radix_process(isc_radix_tree_t *radix, isc_radix_processfunc_t func); +/*%< + * Walk a radix tree calling 'func' to process node data. + * + * Requires: + * \li 'radix' to be valid. + * \li 'func' to point to a function. + */ + +#define RADIX_MAXBITS 128 +#define RADIX_NBIT(x) (0x80 >> ((x) & 0x7f)) +#define RADIX_NBYTE(x) ((x) >> 3) + +#define RADIX_WALK(Xhead, Xnode) \ + do { \ + isc_radix_node_t *Xstack[RADIX_MAXBITS+1]; \ + isc_radix_node_t **Xsp = Xstack; \ + isc_radix_node_t *Xrn = (Xhead); \ + while ((Xnode = Xrn)) { \ + if (Xnode->prefix) + +#define RADIX_WALK_END \ + if (Xrn->l) { \ + if (Xrn->r) { \ + *Xsp++ = Xrn->r; \ + } \ + Xrn = Xrn->l; \ + } else if (Xrn->r) { \ + Xrn = Xrn->r; \ + } else if (Xsp != Xstack) { \ + Xrn = *(--Xsp); \ + } else { \ + Xrn = (isc_radix_node_t *) 0; \ + } \ + } \ + } while (0) + +#endif /* _RADIX_H */ diff --git a/lib/isc/include/isc/random.h b/lib/isc/include/isc/random.h new file mode 100644 index 0000000..f8aed34 --- /dev/null +++ b/lib/isc/include/isc/random.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: random.h,v 1.20 2009/01/17 23:47:43 tbox Exp $ */ + +#ifndef ISC_RANDOM_H +#define ISC_RANDOM_H 1 + +#include <isc/lang.h> +#include <isc/types.h> +#include <isc/entropy.h> +#include <isc/mem.h> +#include <isc/mutex.h> + +/*! \file isc/random.h + * \brief Implements a random state pool which will let the caller return a + * series of possibly non-reproducible random values. + * + * Note that the + * strength of these numbers is not all that high, and should not be + * used in cryptography functions. It is useful for jittering values + * a bit here and there, such as timeouts, etc. + */ + +ISC_LANG_BEGINDECLS + +typedef struct isc_rng isc_rng_t; +/*%< + * Opaque type + */ + +void +isc_random_seed(uint32_t seed); +/*%< + * Set the initial seed of the random state. + */ + +void +isc_random_get(uint32_t *val); +/*%< + * Get a random value. + * + * Requires: + * val != NULL. + */ + +uint32_t +isc_random_jitter(uint32_t max, uint32_t jitter); +/*%< + * Get a random value between (max - jitter) and (max). + * This is useful for jittering timer values. + */ + +isc_result_t +isc_rng_create(isc_mem_t *mctx, isc_entropy_t *entropy, isc_rng_t **rngp); +/*%< + * Creates and initializes a pseudo random number generator. The + * returned RNG can be used to generate pseudo random numbers. + * + * The reference count of the returned RNG is set to 1. + * + * Requires: + * \li mctx is a pointer to a valid memory context. + * \li entropy is an optional entopy source (can be NULL) + * \li rngp != NULL && *rngp == NULL is where a pointer to the RNG is + * returned. + * + * Ensures: + *\li If result is ISC_R_SUCCESS: + * *rngp points to a valid RNG. + * + *\li If result is failure: + * *rngp does not point to a valid RNG. + * + * Returns: + *\li #ISC_R_SUCCESS Success + *\li #ISC_R_NOMEMORY Resource limit: Out of Memory + */ + +void +isc_rng_attach(isc_rng_t *source, isc_rng_t **targetp); +/*%< + * Increments a reference count on the passed RNG. + * + * Requires: + * \li source the RNG struct to attach to (is refcount is incremented) + * \li targetp != NULL && *targetp == NULL where a pointer to the + * reference incremented RNG is returned. + */ + +void +isc_rng_detach(isc_rng_t **rngp); +/*%< + * Decrements a reference count on the passed RNG. If the reference + * count reaches 0, the RNG is destroyed. + * + * Requires: + * \li rngp != NULL the RNG struct to decrement reference for + */ + +uint16_t +isc_rng_random(isc_rng_t *rngctx); +/*%< + * Returns a pseudo random 16-bit unsigned integer. + */ + +uint16_t +isc_rng_uniformrandom(isc_rng_t *rngctx, uint16_t upper_bound); +/*%< + * Returns a uniformly distributed pseudo random 16-bit unsigned + * integer. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_RANDOM_H */ diff --git a/lib/isc/include/isc/ratelimiter.h b/lib/isc/include/isc/ratelimiter.h new file mode 100644 index 0000000..f6320b2 --- /dev/null +++ b/lib/isc/include/isc/ratelimiter.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_RATELIMITER_H +#define ISC_RATELIMITER_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/ratelimiter.h + * \brief A rate limiter is a mechanism for dispatching events at a limited + * rate. This is intended to be used when sending zone maintenance + * SOA queries, NOTIFY messages, etc. + */ + +/*** + *** Imports. + ***/ + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/***** + ***** Functions. + *****/ + +isc_result_t +isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, + isc_task_t *task, isc_ratelimiter_t **ratelimiterp); +/*%< + * Create a rate limiter. The execution interval is initially undefined. + */ + +isc_result_t +isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval); +/*!< + * Set the minimum interval between event executions. + * The interval value is copied, so the caller need not preserve it. + * + * Requires: + * '*interval' is a nonzero interval. + */ + +void +isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, uint32_t perint); +/*%< + * Set the number of events processed per interval timer tick. + * If 'perint' is zero it is treated as 1. + */ + +void +isc_ratelimiter_setpushpop(isc_ratelimiter_t *rl, bool pushpop); +/*%< + * Set / clear the ratelimiter to from push pop mode rather + * first in - first out mode (default). + */ + +isc_result_t +isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task, + isc_event_t **eventp); +/*%< + * Queue an event for rate-limited execution. + * + * This is similar + * to doing an isc_task_send() to the 'task', except that the + * execution may be delayed to achieve the desired rate of + * execution. + * + * '(*eventp)->ev_sender' is used to hold the task. The caller + * must ensure that the task exists until the event is delivered. + * + * Requires: + *\li An interval has been set by calling + * isc_ratelimiter_setinterval(). + * + *\li 'task' to be non NULL. + *\li '(*eventp)->ev_sender' to be NULL. + */ + +isc_result_t +isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event); +/* + * Dequeue a event off the ratelimiter queue. + * + * Returns: + * \li ISC_R_NOTFOUND if the event is no longer linked to the rate limiter. + * \li ISC_R_SUCCESS + */ + +void +isc_ratelimiter_shutdown(isc_ratelimiter_t *ratelimiter); +/*%< + * Shut down a rate limiter. + * + * Ensures: + *\li All events that have not yet been + * dispatched to the task are dispatched immediately with + * the #ISC_EVENTATTR_CANCELED bit set in ev_attributes. + * + *\li Further attempts to enqueue events will fail with + * #ISC_R_SHUTTINGDOWN. + * + *\li The rate limiter is no longer attached to its task. + */ + +void +isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target); +/*%< + * Attach to a rate limiter. + */ + +void +isc_ratelimiter_detach(isc_ratelimiter_t **ratelimiterp); +/*%< + * Detach from a rate limiter. + */ + +isc_result_t +isc_ratelimiter_stall(isc_ratelimiter_t *rl); +/*%< + * Stall event processing. + */ + +isc_result_t +isc_ratelimiter_release(isc_ratelimiter_t *rl); +/*%< + * Release a stalled rate limiter. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_RATELIMITER_H */ diff --git a/lib/isc/include/isc/refcount.h b/lib/isc/include/isc/refcount.h new file mode 100644 index 0000000..c911e5b --- /dev/null +++ b/lib/isc/include/isc/refcount.h @@ -0,0 +1,304 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_REFCOUNT_H +#define ISC_REFCOUNT_H 1 + +#include <inttypes.h> + +#include <isc/assertions.h> +#include <isc/atomic.h> +#include <isc/error.h> +#include <isc/lang.h> +#include <isc/mutex.h> +#include <isc/platform.h> +#include <isc/types.h> + +#if defined(ISC_PLATFORM_HAVESTDATOMIC) +#if defined (__cplusplus) +#include <isc/stdatomic.h> +#else +#include <stdatomic.h> +#endif +#endif + +/*! \file isc/refcount.h + * \brief Implements a locked reference counter. + * + * These functions may actually be + * implemented using macros, and implementations of these macros are below. + * The isc_refcount_t type should not be accessed directly, as its contents + * depend on the implementation. + */ + +ISC_LANG_BEGINDECLS + +/* + * Function prototypes + */ + +/* + * isc_result_t + * isc_refcount_init(isc_refcount_t *ref, unsigned int n); + * + * Initialize the reference counter. There will be 'n' initial references. + * + * Requires: + * ref != NULL + */ + +/* + * void + * isc_refcount_destroy(isc_refcount_t *ref); + * + * Destroys a reference counter. + * + * Requires: + * ref != NULL + * The number of references is 0. + */ + +/* + * void + * isc_refcount_increment(isc_refcount_t *ref, unsigned int *targetp); + * isc_refcount_increment0(isc_refcount_t *ref, unsigned int *targetp); + * + * Increments the reference count, returning the new value in targetp if it's + * not NULL. The reference counter typically begins with the initial counter + * of 1, and will be destroyed once the counter reaches 0. Thus, + * isc_refcount_increment() additionally requires the previous counter be + * larger than 0 so that an error which violates the usage can be easily + * caught. isc_refcount_increment0() does not have this restriction. + * + * Requires: + * ref != NULL. + */ + +/* + * void + * isc_refcount_decrement(isc_refcount_t *ref, unsigned int *targetp); + * + * Decrements the reference count, returning the new value in targetp if it's + * not NULL. + * + * Requires: + * ref != NULL. + */ + + +/* + * Sample implementations + */ +#ifdef ISC_PLATFORM_USETHREADS +#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) || defined(ISC_PLATFORM_HAVEXADD) +#define ISC_REFCOUNT_HAVEATOMIC 1 +#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) +#define ISC_REFCOUNT_HAVESTDATOMIC 1 +#endif + +typedef struct isc_refcount { +#if defined(ISC_REFCOUNT_HAVESTDATOMIC) + atomic_int_fast32_t refs; +#else + int32_t refs; +#endif +} isc_refcount_t; + +#if defined(ISC_REFCOUNT_HAVESTDATOMIC) + +#define isc_refcount_current(rp) \ + ((unsigned int)(atomic_load_explicit(&(rp)->refs, \ + memory_order_relaxed))) +#define isc_refcount_destroy(rp) ISC_REQUIRE(isc_refcount_current(rp) == 0) + +#define isc_refcount_increment0(rp, tp) \ + do { \ + unsigned int *_tmp = (unsigned int *)(tp); \ + int32_t prev; \ + prev = atomic_fetch_add_explicit \ + (&(rp)->refs, 1, memory_order_relaxed); \ + if (_tmp != NULL) \ + *_tmp = prev + 1; \ + } while (0) + +#define isc_refcount_increment(rp, tp) \ + do { \ + unsigned int *_tmp = (unsigned int *)(tp); \ + int32_t prev; \ + prev = atomic_fetch_add_explicit \ + (&(rp)->refs, 1, memory_order_relaxed); \ + ISC_REQUIRE(prev > 0); \ + if (_tmp != NULL) \ + *_tmp = prev + 1; \ + } while (0) + +#define isc_refcount_decrement(rp, tp) \ + do { \ + unsigned int *_tmp = (unsigned int *)(tp); \ + int32_t prev; \ + prev = atomic_fetch_sub_explicit \ + (&(rp)->refs, 1, memory_order_relaxed); \ + ISC_REQUIRE(prev > 0); \ + if (_tmp != NULL) \ + *_tmp = prev - 1; \ + } while (0) + +#else /* ISC_REFCOUNT_HAVESTDATOMIC */ + +#define isc_refcount_current(rp) \ + ((unsigned int)(isc_atomic_xadd(&(rp)->refs, 0))) +#define isc_refcount_destroy(rp) ISC_REQUIRE(isc_refcount_current(rp) == 0) + +#define isc_refcount_increment0(rp, tp) \ + do { \ + unsigned int *_tmp = (unsigned int *)(tp); \ + int32_t prev; \ + prev = isc_atomic_xadd(&(rp)->refs, 1); \ + if (_tmp != NULL) \ + *_tmp = prev + 1; \ + } while (0) + +#define isc_refcount_increment(rp, tp) \ + do { \ + unsigned int *_tmp = (unsigned int *)(tp); \ + int32_t prev; \ + prev = isc_atomic_xadd(&(rp)->refs, 1); \ + ISC_REQUIRE(prev > 0); \ + if (_tmp != NULL) \ + *_tmp = prev + 1; \ + } while (0) + +#define isc_refcount_decrement(rp, tp) \ + do { \ + unsigned int *_tmp = (unsigned int *)(tp); \ + int32_t prev; \ + prev = isc_atomic_xadd(&(rp)->refs, -1); \ + ISC_REQUIRE(prev > 0); \ + if (_tmp != NULL) \ + *_tmp = prev - 1; \ + } while (0) + +#endif /* ISC_REFCOUNT_HAVESTDATOMIC */ + +#else /* ISC_PLATFORM_HAVEXADD */ + +typedef struct isc_refcount { + int refs; + isc_mutex_t lock; +} isc_refcount_t; + +/*% Destroys a reference counter. */ +#define isc_refcount_destroy(rp) \ + do { \ + isc_result_t _result; \ + ISC_REQUIRE((rp)->refs == 0); \ + _result = isc_mutex_destroy(&(rp)->lock); \ + ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \ + } while (0) + +#define isc_refcount_current(rp) ((unsigned int)((rp)->refs)) + +/*% + * Increments the reference count, returning the new value in + * 'tp' if it's not NULL. + */ +#define isc_refcount_increment0(rp, tp) \ + do { \ + isc_result_t _result; \ + unsigned int *_tmp = (unsigned int *)(tp); \ + _result = isc_mutex_lock(&(rp)->lock); \ + ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \ + ++((rp)->refs); \ + if (_tmp != NULL) \ + *_tmp = ((rp)->refs); \ + _result = isc_mutex_unlock(&(rp)->lock); \ + ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \ + } while (0) + +#define isc_refcount_increment(rp, tp) \ + do { \ + isc_result_t _result; \ + unsigned int *_tmp = (unsigned int *)(tp); \ + _result = isc_mutex_lock(&(rp)->lock); \ + ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \ + ISC_REQUIRE((rp)->refs > 0); \ + ++((rp)->refs); \ + if (_tmp != NULL) \ + *_tmp = ((rp)->refs); \ + _result = isc_mutex_unlock(&(rp)->lock); \ + ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \ + } while (0) + +/*% + * Decrements the reference count, returning the new value in 'tp' + * if it's not NULL. + */ +#define isc_refcount_decrement(rp, tp) \ + do { \ + isc_result_t _result; \ + unsigned int *_tmp = (unsigned int *)(tp); \ + _result = isc_mutex_lock(&(rp)->lock); \ + ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \ + ISC_REQUIRE((rp)->refs > 0); \ + --((rp)->refs); \ + if (_tmp != NULL) \ + *_tmp = ((rp)->refs); \ + _result = isc_mutex_unlock(&(rp)->lock); \ + ISC_ERROR_RUNTIMECHECK(_result == ISC_R_SUCCESS); \ + } while (0) + +#endif /* (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) || defined(ISC_PLATFORM_HAVEXADD) */ +#else /* ISC_PLATFORM_USETHREADS */ + +typedef struct isc_refcount { + int refs; +} isc_refcount_t; + +#define isc_refcount_destroy(rp) ISC_REQUIRE((rp)->refs == 0) +#define isc_refcount_current(rp) ((unsigned int)((rp)->refs)) + +#define isc_refcount_increment0(rp, tp) \ + do { \ + unsigned int *_tmp = (unsigned int *)(tp); \ + int _n = ++(rp)->refs; \ + if (_tmp != NULL) \ + *_tmp = _n; \ + } while (0) + +#define isc_refcount_increment(rp, tp) \ + do { \ + unsigned int *_tmp = (unsigned int *)(tp); \ + int _n; \ + ISC_REQUIRE((rp)->refs > 0); \ + _n = ++(rp)->refs; \ + if (_tmp != NULL) \ + *_tmp = _n; \ + } while (0) + +#define isc_refcount_decrement(rp, tp) \ + do { \ + unsigned int *_tmp = (unsigned int *)(tp); \ + int _n; \ + ISC_REQUIRE((rp)->refs > 0); \ + _n = --(rp)->refs; \ + if (_tmp != NULL) \ + *_tmp = _n; \ + } while (0) + +#endif /* ISC_PLATFORM_USETHREADS */ + +isc_result_t +isc_refcount_init(isc_refcount_t *ref, unsigned int n); + +ISC_LANG_ENDDECLS + +#endif /* ISC_REFCOUNT_H */ diff --git a/lib/isc/include/isc/regex.h b/lib/isc/include/isc/regex.h new file mode 100644 index 0000000..cab10c1 --- /dev/null +++ b/lib/isc/include/isc/regex.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_REGEX_H +#define ISC_REGEX_H 1 + +/*! \file isc/regex.h */ + +#include <isc/types.h> +#include <isc/lang.h> + +ISC_LANG_BEGINDECLS + +int +isc_regex_validate(const char *expression); +/*%< + * Check a regular expression for syntactic correctness. + * + * Returns: + *\li -1 on error. + *\li the number of groups in the expression. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_REGEX_H */ diff --git a/lib/isc/include/isc/region.h b/lib/isc/include/isc/region.h new file mode 100644 index 0000000..ee01354 --- /dev/null +++ b/lib/isc/include/isc/region.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_REGION_H +#define ISC_REGION_H 1 + +/*! \file isc/region.h */ + +#include <isc/types.h> +#include <isc/lang.h> + +struct isc_region { + unsigned char * base; + unsigned int length; +}; + +struct isc_textregion { + char * base; + unsigned int length; +}; + +/* XXXDCL questionable ... bears discussion. we have been putting off + * discussing the region api. + */ +struct isc_constregion { + const void * base; + unsigned int length; +}; + +struct isc_consttextregion { + const char * base; + unsigned int length; +}; + +/*@{*/ +/*! + * The region structure is not opaque, and is usually directly manipulated. + * Some macros are defined below for convenience. + */ + +#define isc_region_consume(r,l) \ + do { \ + isc_region_t *_r = (r); \ + unsigned int _l = (l); \ + INSIST(_r->length >= _l); \ + _r->base += _l; \ + _r->length -= _l; \ + } while (0) + +#define isc_textregion_consume(r,l) \ + do { \ + isc_textregion_t *_r = (r); \ + unsigned int _l = (l); \ + INSIST(_r->length >= _l); \ + _r->base += _l; \ + _r->length -= _l; \ + } while (0) + +#define isc_constregion_consume(r,l) \ + do { \ + isc_constregion_t *_r = (r); \ + unsigned int _l = (l); \ + INSIST(_r->length >= _l); \ + _r->base += _l; \ + _r->length -= _l; \ + } while (0) +/*@}*/ + +ISC_LANG_BEGINDECLS + +int +isc_region_compare(isc_region_t *r1, isc_region_t *r2); +/*%< + * Compares the contents of two regions + * + * Requires: + *\li 'r1' is a valid region + *\li 'r2' is a valid region + * + * Returns: + *\li < 0 if r1 is lexicographically less than r2 + *\li = 0 if r1 is lexicographically identical to r2 + *\li > 0 if r1 is lexicographically greater than r2 + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_REGION_H */ diff --git a/lib/isc/include/isc/resource.h b/lib/isc/include/isc/resource.h new file mode 100644 index 0000000..0179234 --- /dev/null +++ b/lib/isc/include/isc/resource.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_RESOURCE_H +#define ISC_RESOURCE_H 1 + +/*! \file isc/resource.h */ + +#include <isc/lang.h> +#include <isc/types.h> + +#define ISC_RESOURCE_UNLIMITED ((isc_resourcevalue_t)UINT64_MAX) + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_resource_setlimit(isc_resource_t resource, isc_resourcevalue_t value); +/*%< + * Set the maximum limit for a system resource. + * + * Notes: + *\li If 'value' exceeds the maximum possible on the operating system, + * it is silently limited to that maximum -- or to "infinity", if + * the operating system has that concept. #ISC_RESOURCE_UNLIMITED + * can be used to explicitly ask for the maximum. + * + * Requires: + *\li 'resource' is a valid member of the isc_resource_t enumeration. + * + * Returns: + *\li #ISC_R_SUCCESS Success. + *\li #ISC_R_NOTIMPLEMENTED 'resource' is not a type known by the OS. + *\li #ISC_R_NOPERM The calling process did not have adequate permission + * to change the resource limit. + */ + +isc_result_t +isc_resource_getlimit(isc_resource_t resource, isc_resourcevalue_t *value); +/*%< + * Get the maximum limit for a system resource. + * + * Notes: + *\li 'value' is set to the maximum limit. + * + *\li #ISC_RESOURCE_UNLIMITED is the maximum value of isc_resourcevalue_t. + * + *\li On many (all?) Unix systems, RLIM_INFINITY is a valid value that is + * significantly less than #ISC_RESOURCE_UNLIMITED, but which in practice + * behaves the same. + * + *\li The current ISC libdns configuration file parser assigns a value + * of UINT32_MAX for a size_spec of "unlimited" and ISC_UNIT32_MAX - 1 + * for "default", the latter of which is supposed to represent "the + * limit that was in force when the server started". Since these are + * valid values in the middle of the range of isc_resourcevalue_t, + * there is the possibility for confusion over what exactly those + * particular values are supposed to represent in a particular context -- + * discrete integral values or generalized concepts. + * + * Requires: + *\li 'resource' is a valid member of the isc_resource_t enumeration. + * + * Returns: + *\li #ISC_R_SUCCESS Success. + *\li #ISC_R_NOTIMPLEMENTED 'resource' is not a type known by the OS. + */ + +isc_result_t +isc_resource_getcurlimit(isc_resource_t resource, isc_resourcevalue_t *value); +/*%< + * Same as isc_resource_getlimit(), but returns the current (soft) limit. + * + * Returns: + *\li #ISC_R_SUCCESS Success. + *\li #ISC_R_NOTIMPLEMENTED 'resource' is not a type known by the OS. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_RESOURCE_H */ + diff --git a/lib/isc/include/isc/result.h b/lib/isc/include/isc/result.h new file mode 100644 index 0000000..246aefb --- /dev/null +++ b/lib/isc/include/isc/result.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_RESULT_H +#define ISC_RESULT_H 1 + +/*! \file isc/result.h */ + +#include <isc/lang.h> +#include <isc/types.h> + +#define ISC_R_SUCCESS 0 /*%< success */ +#define ISC_R_NOMEMORY 1 /*%< out of memory */ +#define ISC_R_TIMEDOUT 2 /*%< timed out */ +#define ISC_R_NOTHREADS 3 /*%< no available threads */ +#define ISC_R_ADDRNOTAVAIL 4 /*%< address not available */ +#define ISC_R_ADDRINUSE 5 /*%< address in use */ +#define ISC_R_NOPERM 6 /*%< permission denied */ +#define ISC_R_NOCONN 7 /*%< no pending connections */ +#define ISC_R_NETUNREACH 8 /*%< network unreachable */ +#define ISC_R_HOSTUNREACH 9 /*%< host unreachable */ +#define ISC_R_NETDOWN 10 /*%< network down */ +#define ISC_R_HOSTDOWN 11 /*%< host down */ +#define ISC_R_CONNREFUSED 12 /*%< connection refused */ +#define ISC_R_NORESOURCES 13 /*%< not enough free resources */ +#define ISC_R_EOF 14 /*%< end of file */ +#define ISC_R_BOUND 15 /*%< socket already bound */ +#define ISC_R_RELOAD 16 /*%< reload */ +#define ISC_R_SUSPEND ISC_R_RELOAD /*%< alias of 'reload' */ +#define ISC_R_LOCKBUSY 17 /*%< lock busy */ +#define ISC_R_EXISTS 18 /*%< already exists */ +#define ISC_R_NOSPACE 19 /*%< ran out of space */ +#define ISC_R_CANCELED 20 /*%< operation canceled */ +#define ISC_R_NOTBOUND 21 /*%< socket is not bound */ +#define ISC_R_SHUTTINGDOWN 22 /*%< shutting down */ +#define ISC_R_NOTFOUND 23 /*%< not found */ +#define ISC_R_UNEXPECTEDEND 24 /*%< unexpected end of input */ +#define ISC_R_FAILURE 25 /*%< generic failure */ +#define ISC_R_IOERROR 26 /*%< I/O error */ +#define ISC_R_NOTIMPLEMENTED 27 /*%< not implemented */ +#define ISC_R_UNBALANCED 28 /*%< unbalanced parentheses */ +#define ISC_R_NOMORE 29 /*%< no more */ +#define ISC_R_INVALIDFILE 30 /*%< invalid file */ +#define ISC_R_BADBASE64 31 /*%< bad base64 encoding */ +#define ISC_R_UNEXPECTEDTOKEN 32 /*%< unexpected token */ +#define ISC_R_QUOTA 33 /*%< quota reached */ +#define ISC_R_UNEXPECTED 34 /*%< unexpected error */ +#define ISC_R_ALREADYRUNNING 35 /*%< already running */ +#define ISC_R_IGNORE 36 /*%< ignore */ +#define ISC_R_MASKNONCONTIG 37 /*%< addr mask not contiguous */ +#define ISC_R_FILENOTFOUND 38 /*%< file not found */ +#define ISC_R_FILEEXISTS 39 /*%< file already exists */ +#define ISC_R_NOTCONNECTED 40 /*%< socket is not connected */ +#define ISC_R_RANGE 41 /*%< out of range */ +#define ISC_R_NOENTROPY 42 /*%< out of entropy */ +#define ISC_R_MULTICAST 43 /*%< invalid use of multicast */ +#define ISC_R_NOTFILE 44 /*%< not a file */ +#define ISC_R_NOTDIRECTORY 45 /*%< not a directory */ +#define ISC_R_QUEUEFULL 46 /*%< queue is full */ +#define ISC_R_FAMILYMISMATCH 47 /*%< address family mismatch */ +#define ISC_R_FAMILYNOSUPPORT 48 /*%< AF not supported */ +#define ISC_R_BADHEX 49 /*%< bad hex encoding */ +#define ISC_R_TOOMANYOPENFILES 50 /*%< too many open files */ +#define ISC_R_NOTBLOCKING 51 /*%< not blocking */ +#define ISC_R_UNBALANCEDQUOTES 52 /*%< unbalanced quotes */ +#define ISC_R_INPROGRESS 53 /*%< operation in progress */ +#define ISC_R_CONNECTIONRESET 54 /*%< connection reset */ +#define ISC_R_SOFTQUOTA 55 /*%< soft quota reached */ +#define ISC_R_BADNUMBER 56 /*%< not a valid number */ +#define ISC_R_DISABLED 57 /*%< disabled */ +#define ISC_R_MAXSIZE 58 /*%< max size */ +#define ISC_R_BADADDRESSFORM 59 /*%< invalid address format */ +#define ISC_R_BADBASE32 60 /*%< bad base32 encoding */ +#define ISC_R_UNSET 61 /*%< unset */ +#define ISC_R_MULTIPLE 62 /*%< multiple */ +#define ISC_R_WOULDBLOCK 63 /*%< would block */ + +/*% Not a result code: the number of results. */ +#define ISC_R_NRESULTS 64 + +ISC_LANG_BEGINDECLS + +const char * +isc_result_totext(isc_result_t); +/*%< + * Convert an isc_result_t into a string message describing the result. + */ + +const char * +isc_result_toid(isc_result_t); +/*%< + * Convert an isc_result_t into a string identifier such as + * "ISC_R_SUCCESS". + */ + +isc_result_t +isc_result_register(unsigned int base, unsigned int nresults, + const char **text, isc_msgcat_t *msgcat, int set); + +isc_result_t +isc_result_registerids(unsigned int base, unsigned int nresults, + const char **ids, isc_msgcat_t *msgcat, int set); + +ISC_LANG_ENDDECLS + +#endif /* ISC_RESULT_H */ diff --git a/lib/isc/include/isc/resultclass.h b/lib/isc/include/isc/resultclass.h new file mode 100644 index 0000000..08135f4 --- /dev/null +++ b/lib/isc/include/isc/resultclass.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_RESULTCLASS_H +#define ISC_RESULTCLASS_H 1 + + +/*! \file isc/resultclass.h + * \brief Registry of Predefined Result Type Classes + * + * A result class number is an unsigned 16 bit number. Each class may + * contain up to 65536 results. A result code is formed by adding the + * result number within the class to the class number multiplied by 65536. + * + * Classes < 1024 are reserved for ISC use. + * Result classes >= 1024 and <= 65535 are reserved for application use. + */ + +#define ISC_RESULTCLASS_FROMNUM(num) ((num) << 16) +#define ISC_RESULTCLASS_TONUM(rclass) ((rclass) >> 16) +#define ISC_RESULTCLASS_SIZE 65536 +#define ISC_RESULTCLASS_INCLASS(rclass, result) \ + ((rclass) == ((result) & 0xFFFF0000)) + + +#define ISC_RESULTCLASS_ISC ISC_RESULTCLASS_FROMNUM(0) +#define ISC_RESULTCLASS_DNS ISC_RESULTCLASS_FROMNUM(1) +#define ISC_RESULTCLASS_DST ISC_RESULTCLASS_FROMNUM(2) +#define ISC_RESULTCLASS_DNSRCODE ISC_RESULTCLASS_FROMNUM(3) +#define ISC_RESULTCLASS_OMAPI ISC_RESULTCLASS_FROMNUM(4) +#define ISC_RESULTCLASS_ISCCC ISC_RESULTCLASS_FROMNUM(5) +#define ISC_RESULTCLASS_DHCP ISC_RESULTCLASS_FROMNUM(6) +#define ISC_RESULTCLASS_PK11 ISC_RESULTCLASS_FROMNUM(7) + +#endif /* ISC_RESULTCLASS_H */ diff --git a/lib/isc/include/isc/rwlock.h b/lib/isc/include/isc/rwlock.h new file mode 100644 index 0000000..b957509 --- /dev/null +++ b/lib/isc/include/isc/rwlock.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_RWLOCK_H +#define ISC_RWLOCK_H 1 + +#include <inttypes.h> + +/*! \file isc/rwlock.h */ + +#include <isc/condition.h> +#include <isc/lang.h> +#include <isc/platform.h> +#include <isc/types.h> + +#if defined(ISC_PLATFORM_HAVESTDATOMIC) +#if defined(__cplusplus) +#include <isc/stdatomic.h> +#else +#include <stdatomic.h> +#endif +#endif + +ISC_LANG_BEGINDECLS + +typedef enum { + isc_rwlocktype_none = 0, + isc_rwlocktype_read, + isc_rwlocktype_write +} isc_rwlocktype_t; + +#ifdef ISC_PLATFORM_USETHREADS +#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) || (defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)) +#define ISC_RWLOCK_USEATOMIC 1 +#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) +#define ISC_RWLOCK_USESTDATOMIC 1 +#endif +#endif + +struct isc_rwlock { + /* Unlocked. */ + unsigned int magic; + isc_mutex_t lock; + int32_t spins; + +#if defined(ISC_RWLOCK_USEATOMIC) + /* + * When some atomic instructions with hardware assistance are + * available, rwlock will use those so that concurrent readers do not + * interfere with each other through mutex as long as no writers + * appear, massively reducing the lock overhead in the typical case. + * + * The basic algorithm of this approach is the "simple + * writer-preference lock" shown in the following URL: + * http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/rw.html + * but our implementation does not rely on the spin lock unlike the + * original algorithm to be more portable as a user space application. + */ + + /* Read or modified atomically. */ +#if defined(ISC_RWLOCK_USESTDATOMIC) + atomic_int_fast32_t write_requests; + atomic_int_fast32_t write_completions; + atomic_int_fast32_t cnt_and_flag; +#else + int32_t write_requests; + int32_t write_completions; + int32_t cnt_and_flag; +#endif + + /* Locked by lock. */ + isc_condition_t readable; + isc_condition_t writeable; + unsigned int readers_waiting; + + /* Locked by rwlock itself. */ + unsigned int write_granted; + + /* Unlocked. */ + unsigned int write_quota; + +#else /* ISC_RWLOCK_USEATOMIC */ + + /*%< Locked by lock. */ + isc_condition_t readable; + isc_condition_t writeable; + isc_rwlocktype_t type; + + /*% The number of threads that have the lock. */ + unsigned int active; + + /*% + * The number of lock grants made since the lock was last switched + * from reading to writing or vice versa; used in determining + * when the quota is reached and it is time to switch. + */ + unsigned int granted; + + unsigned int readers_waiting; + unsigned int writers_waiting; + unsigned int read_quota; + unsigned int write_quota; + isc_rwlocktype_t original; +#endif /* ISC_RWLOCK_USEATOMIC */ +}; +#else /* ISC_PLATFORM_USETHREADS */ +struct isc_rwlock { + unsigned int magic; + isc_rwlocktype_t type; + unsigned int active; +}; +#endif /* ISC_PLATFORM_USETHREADS */ + + +isc_result_t +isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota, + unsigned int write_quota); + +isc_result_t +isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type); + +isc_result_t +isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type); + +isc_result_t +isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type); + +isc_result_t +isc_rwlock_tryupgrade(isc_rwlock_t *rwl); + +void +isc_rwlock_downgrade(isc_rwlock_t *rwl); + +void +isc_rwlock_destroy(isc_rwlock_t *rwl); + +ISC_LANG_ENDDECLS + +#endif /* ISC_RWLOCK_H */ diff --git a/lib/isc/include/isc/safe.h b/lib/isc/include/isc/safe.h new file mode 100644 index 0000000..66ed08b --- /dev/null +++ b/lib/isc/include/isc/safe.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_SAFE_H +#define ISC_SAFE_H 1 + +/*! \file isc/safe.h */ + +#include <stdbool.h> + +#include <isc/types.h> +#include <stdlib.h> + +ISC_LANG_BEGINDECLS + +bool +isc_safe_memequal(const void *s1, const void *s2, size_t n); +/*%< + * Returns true iff. two blocks of memory are equal, otherwise + * false. + * + */ + +int +isc_safe_memcompare(const void *b1, const void *b2, size_t len); +/*%< + * Clone of libc memcmp() which is safe to differential timing attacks. + */ + +void +isc_safe_memwipe(void *ptr, size_t len); +/*%< + * Clear the memory of length `len` pointed to by `ptr`. + * + * Some crypto code calls memset() on stack allocated buffers just + * before return so that they are wiped. Such memset() calls can be + * optimized away by the compiler. We provide this external non-inline C + * function to perform the memset operation so that the compiler cannot + * infer about what the function does and optimize the call away. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_SAFE_H */ diff --git a/lib/isc/include/isc/serial.h b/lib/isc/include/isc/serial.h new file mode 100644 index 0000000..2c6923f --- /dev/null +++ b/lib/isc/include/isc/serial.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_SERIAL_H +#define ISC_SERIAL_H 1 + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/types.h> + +/*! \file isc/serial.h + * \brief Implement 32 bit serial space arithmetic comparison functions. + * Note: Undefined results are returned as false. + */ + +/*** + *** Functions + ***/ + +ISC_LANG_BEGINDECLS + +bool +isc_serial_lt(uint32_t a, uint32_t b); +/*%< + * Return true if 'a' < 'b' otherwise false. + */ + +bool +isc_serial_gt(uint32_t a, uint32_t b); +/*%< + * Return true if 'a' > 'b' otherwise false. + */ + +bool +isc_serial_le(uint32_t a, uint32_t b); +/*%< + * Return true if 'a' <= 'b' otherwise false. + */ + +bool +isc_serial_ge(uint32_t a, uint32_t b); +/*%< + * Return true if 'a' >= 'b' otherwise false. + */ + +bool +isc_serial_eq(uint32_t a, uint32_t b); +/*%< + * Return true if 'a' == 'b' otherwise false. + */ + +bool +isc_serial_ne(uint32_t a, uint32_t b); +/*%< + * Return true if 'a' != 'b' otherwise false. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_SERIAL_H */ diff --git a/lib/isc/include/isc/sha1.h b/lib/isc/include/isc/sha1.h new file mode 100644 index 0000000..02bca12 --- /dev/null +++ b/lib/isc/include/isc/sha1.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_SHA1_H +#define ISC_SHA1_H 1 + +/* $NetBSD: sha1.h,v 1.2 1998/05/29 22:55:44 thorpej Exp $ */ + +/*! \file isc/sha1.h + * \brief SHA-1 in C + * \author By Steve Reid <steve@edmweb.com> + * \note 100% Public Domain + */ + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/platform.h> +#include <isc/types.h> + +#define ISC_SHA1_DIGESTLENGTH 20U +#define ISC_SHA1_BLOCK_LENGTH 64U + +#ifdef ISC_PLATFORM_OPENSSLHASH +#include <openssl/opensslv.h> +#include <openssl/evp.h> + +typedef struct { + EVP_MD_CTX *ctx; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + EVP_MD_CTX _ctx; +#endif +} isc_sha1_t; + +#elif PKCS11CRYPTO +#include <pk11/pk11.h> + +typedef pk11_context_t isc_sha1_t; + +#else + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[ISC_SHA1_BLOCK_LENGTH]; +} isc_sha1_t; +#endif + +ISC_LANG_BEGINDECLS + +void +isc_sha1_init(isc_sha1_t *ctx); + +void +isc_sha1_invalidate(isc_sha1_t *ctx); + +void +isc_sha1_update(isc_sha1_t *ctx, const unsigned char *data, unsigned int len); + +void +isc_sha1_final(isc_sha1_t *ctx, unsigned char *digest); + +bool +isc_sha1_check(bool testing); + +ISC_LANG_ENDDECLS + +#endif /* ISC_SHA1_H */ diff --git a/lib/isc/include/isc/sha2.h b/lib/isc/include/isc/sha2.h new file mode 100644 index 0000000..f9e1be3 --- /dev/null +++ b/lib/isc/include/isc/sha2.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/* $FreeBSD: src/sys/crypto/sha2/sha2.h,v 1.1.2.1 2001/07/03 11:01:36 ume Exp $ */ +/* $KAME: sha2.h,v 1.3 2001/03/12 08:27:48 itojun Exp $ */ + +/* + * sha2.h + * + * Version 1.0.0beta1 + * + * Written by Aaron D. Gifford <me@aarongifford.com> + * + * Copyright 2000 Aaron D. Gifford. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#ifndef ISC_SHA2_H +#define ISC_SHA2_H + +#include <inttypes.h> + +#include <isc/lang.h> +#include <isc/platform.h> +#include <isc/types.h> + +/*** SHA-224/256/384/512 Various Length Definitions ***********************/ + +#define ISC_SHA224_BLOCK_LENGTH 64U +#define ISC_SHA224_DIGESTLENGTH 28U +#define ISC_SHA224_DIGESTSTRINGLENGTH (ISC_SHA224_DIGESTLENGTH * 2 + 1) +#define ISC_SHA256_BLOCK_LENGTH 64U +#define ISC_SHA256_DIGESTLENGTH 32U +#define ISC_SHA256_DIGESTSTRINGLENGTH (ISC_SHA256_DIGESTLENGTH * 2 + 1) +#define ISC_SHA384_BLOCK_LENGTH 128 +#define ISC_SHA384_DIGESTLENGTH 48U +#define ISC_SHA384_DIGESTSTRINGLENGTH (ISC_SHA384_DIGESTLENGTH * 2 + 1) +#define ISC_SHA512_BLOCK_LENGTH 128U +#define ISC_SHA512_DIGESTLENGTH 64U +#define ISC_SHA512_DIGESTSTRINGLENGTH (ISC_SHA512_DIGESTLENGTH * 2 + 1) + +/*** SHA-256/384/512 Context Structures *******************************/ + +#if defined(ISC_PLATFORM_OPENSSLHASH) +#include <openssl/opensslv.h> +#include <openssl/evp.h> +#endif + +#if defined(ISC_PLATFORM_OPENSSLHASH) && !defined(LIBRESSL_VERSION_NUMBER) + + +typedef struct { + EVP_MD_CTX *ctx; +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) + EVP_MD_CTX _ctx; +#endif +} isc_sha2_t; + +typedef isc_sha2_t isc_sha256_t; +typedef isc_sha2_t isc_sha512_t; + +#elif PKCS11CRYPTO +#include <pk11/pk11.h> + +typedef pk11_context_t isc_sha256_t; +typedef pk11_context_t isc_sha512_t; + +#else + +/* + * Keep buffer immediately after bitcount to preserve alignment. + */ +typedef struct { + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[ISC_SHA256_BLOCK_LENGTH]; +} isc_sha256_t; + +/* + * Keep buffer immediately after bitcount to preserve alignment. + */ +typedef struct { + uint64_t state[8]; + uint64_t bitcount[2]; + uint8_t buffer[ISC_SHA512_BLOCK_LENGTH]; +} isc_sha512_t; +#endif + +typedef isc_sha256_t isc_sha224_t; +typedef isc_sha512_t isc_sha384_t; + +ISC_LANG_BEGINDECLS + +/*** SHA-224/256/384/512 Function Prototypes ******************************/ + +void isc_sha224_init (isc_sha224_t *); +void isc_sha224_invalidate (isc_sha224_t *); +void isc_sha224_update (isc_sha224_t *, const uint8_t *, size_t); +void isc_sha224_final (uint8_t[ISC_SHA224_DIGESTLENGTH], isc_sha224_t *); +char *isc_sha224_end (isc_sha224_t *, char[ISC_SHA224_DIGESTSTRINGLENGTH]); +char *isc_sha224_data (const uint8_t *, size_t, char[ISC_SHA224_DIGESTSTRINGLENGTH]); + +void isc_sha256_init (isc_sha256_t *); +void isc_sha256_invalidate (isc_sha256_t *); +void isc_sha256_update (isc_sha256_t *, const uint8_t *, size_t); +void isc_sha256_final (uint8_t[ISC_SHA256_DIGESTLENGTH], isc_sha256_t *); +char *isc_sha256_end (isc_sha256_t *, char[ISC_SHA256_DIGESTSTRINGLENGTH]); +char *isc_sha256_data (const uint8_t *, size_t, char[ISC_SHA256_DIGESTSTRINGLENGTH]); + +void isc_sha384_init (isc_sha384_t *); +void isc_sha384_invalidate (isc_sha384_t *); +void isc_sha384_update (isc_sha384_t *, const uint8_t *, size_t); +void isc_sha384_final (uint8_t[ISC_SHA384_DIGESTLENGTH], isc_sha384_t *); +char *isc_sha384_end (isc_sha384_t *, char[ISC_SHA384_DIGESTSTRINGLENGTH]); +char *isc_sha384_data (const uint8_t *, size_t, char[ISC_SHA384_DIGESTSTRINGLENGTH]); + +void isc_sha512_init (isc_sha512_t *); +void isc_sha512_invalidate (isc_sha512_t *); +void isc_sha512_update (isc_sha512_t *, const uint8_t *, size_t); +void isc_sha512_final (uint8_t[ISC_SHA512_DIGESTLENGTH], isc_sha512_t *); +char *isc_sha512_end (isc_sha512_t *, char[ISC_SHA512_DIGESTSTRINGLENGTH]); +char *isc_sha512_data (const uint8_t *, size_t, char[ISC_SHA512_DIGESTSTRINGLENGTH]); + +ISC_LANG_ENDDECLS + +#endif /* ISC_SHA2_H */ diff --git a/lib/isc/include/isc/sockaddr.h b/lib/isc/include/isc/sockaddr.h new file mode 100644 index 0000000..478e77c --- /dev/null +++ b/lib/isc/include/isc/sockaddr.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_SOCKADDR_H +#define ISC_SOCKADDR_H 1 + +/*! \file isc/sockaddr.h */ + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/net.h> +#include <isc/types.h> +#ifdef ISC_PLATFORM_HAVESYSUNH +#include <sys/un.h> +#endif + +struct isc_sockaddr { + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + struct sockaddr_storage ss; +#ifdef ISC_PLATFORM_HAVESYSUNH + struct sockaddr_un sunix; +#endif + } type; + unsigned int length; /* XXXRTH beginning? */ + ISC_LINK(struct isc_sockaddr) link; +}; + +#define ISC_SOCKADDR_CMPADDR 0x0001 /*%< compare the address + * sin_addr/sin6_addr */ +#define ISC_SOCKADDR_CMPPORT 0x0002 /*%< compare the port + * sin_port/sin6_port */ +#define ISC_SOCKADDR_CMPSCOPE 0x0004 /*%< compare the scope + * sin6_scope */ +#define ISC_SOCKADDR_CMPSCOPEZERO 0x0008 /*%< when comparing scopes + * zero scopes always match */ + +ISC_LANG_BEGINDECLS + +bool +isc_sockaddr_compare(const isc_sockaddr_t *a, const isc_sockaddr_t *b, + unsigned int flags); +/*%< + * Compare the elements of the two address ('a' and 'b') as specified + * by 'flags' and report if they are equal or not. + * + * 'flags' is set from ISC_SOCKADDR_CMP*. + */ + +bool +isc_sockaddr_equal(const isc_sockaddr_t *a, const isc_sockaddr_t *b); +/*%< + * Return true iff the socket addresses 'a' and 'b' are equal. + */ + +bool +isc_sockaddr_eqaddr(const isc_sockaddr_t *a, const isc_sockaddr_t *b); +/*%< + * Return true iff the address parts of the socket addresses + * 'a' and 'b' are equal, ignoring the ports. + */ + +bool +isc_sockaddr_eqaddrprefix(const isc_sockaddr_t *a, const isc_sockaddr_t *b, + unsigned int prefixlen); +/*%< + * Return true iff the most significant 'prefixlen' bits of the + * socket addresses 'a' and 'b' are equal, ignoring the ports. + * If 'b''s scope is zero then 'a''s scope will be ignored. + */ + +unsigned int +isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, bool address_only); +/*%< + * Return a hash value for the socket address 'sockaddr'. If 'address_only' + * is true, the hash value will not depend on the port. + * + * IPv6 addresses containing mapped IPv4 addresses generate the same hash + * value as the equivalent IPv4 address. + */ + +void +isc_sockaddr_any(isc_sockaddr_t *sockaddr); +/*%< + * Return the IPv4 wildcard address. + */ + +void +isc_sockaddr_any6(isc_sockaddr_t *sockaddr); +/*%< + * Return the IPv6 wildcard address. + */ + +void +isc_sockaddr_anyofpf(isc_sockaddr_t *sockaddr, int family); +/*%< + * Set '*sockaddr' to the wildcard address of protocol family + * 'family'. + * + * Requires: + * \li 'family' is AF_INET or AF_INET6. + */ + +void +isc_sockaddr_fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina, + in_port_t port); +/*%< + * Construct an isc_sockaddr_t from an IPv4 address and port. + */ + +void +isc_sockaddr_fromin6(isc_sockaddr_t *sockaddr, const struct in6_addr *ina6, + in_port_t port); +/*%< + * Construct an isc_sockaddr_t from an IPv6 address and port. + */ + +void +isc_sockaddr_v6fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina, + in_port_t port); +/*%< + * Construct an IPv6 isc_sockaddr_t representing a mapped IPv4 address. + */ + +void +isc_sockaddr_fromnetaddr(isc_sockaddr_t *sockaddr, const isc_netaddr_t *na, + in_port_t port); +/*%< + * Construct an isc_sockaddr_t from an isc_netaddr_t and port. + */ + +int +isc_sockaddr_pf(const isc_sockaddr_t *sockaddr); +/*%< + * Get the protocol family of 'sockaddr'. + * + * Requires: + * + *\li 'sockaddr' is a valid sockaddr with an address family of AF_INET + * or AF_INET6. + * + * Returns: + * + *\li The protocol family of 'sockaddr', e.g. PF_INET or PF_INET6. + */ + +void +isc_sockaddr_setport(isc_sockaddr_t *sockaddr, in_port_t port); +/*%< + * Set the port of 'sockaddr' to 'port'. + */ + +in_port_t +isc_sockaddr_getport(const isc_sockaddr_t *sockaddr); +/*%< + * Get the port stored in 'sockaddr'. + */ + +isc_result_t +isc_sockaddr_totext(const isc_sockaddr_t *sockaddr, isc_buffer_t *target); +/*%< + * Append a text representation of 'sockaddr' to the buffer 'target'. + * The text will include both the IP address (v4 or v6) and the port. + * The text is null terminated, but the terminating null is not + * part of the buffer's used region. + * + * Returns: + * \li ISC_R_SUCCESS + * \li ISC_R_NOSPACE The text or the null termination did not fit. + */ + +void +isc_sockaddr_format(const isc_sockaddr_t *sa, char *array, unsigned int size); +/*%< + * Format a human-readable representation of the socket address '*sa' + * into the character array 'array', which is of size 'size'. + * The resulting string is guaranteed to be null-terminated. + */ + +bool +isc_sockaddr_ismulticast(const isc_sockaddr_t *sa); +/*%< + * Returns #true if the address is a multicast address. + */ + +bool +isc_sockaddr_isexperimental(const isc_sockaddr_t *sa); +/* + * Returns true if the address is a experimental (CLASS E) address. + */ + +bool +isc_sockaddr_islinklocal(const isc_sockaddr_t *sa); +/*%< + * Returns true if the address is a link local address. + */ + +bool +isc_sockaddr_issitelocal(const isc_sockaddr_t *sa); +/*%< + * Returns true if the address is a sitelocal address. + */ + +bool +isc_sockaddr_isnetzero(const isc_sockaddr_t *sa); +/*%< + * Returns true if the address is in net zero. + */ + +isc_result_t +isc_sockaddr_frompath(isc_sockaddr_t *sockaddr, const char *path); +/* + * Create a UNIX domain sockaddr that refers to path. + * + * Returns: + * \li ISC_R_NOSPACE + * \li ISC_R_NOTIMPLEMENTED + * \li ISC_R_SUCCESS + */ + +#define ISC_SOCKADDR_FORMATSIZE \ + sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX%SSSSSSSSSS#YYYYY") +/*%< + * Minimum size of array to pass to isc_sockaddr_format(). + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_SOCKADDR_H */ diff --git a/lib/isc/include/isc/socket.h b/lib/isc/include/isc/socket.h new file mode 100644 index 0000000..574de2a --- /dev/null +++ b/lib/isc/include/isc/socket.h @@ -0,0 +1,1272 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_SOCKET_H +#define ISC_SOCKET_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/socket.h + * \brief Provides TCP and UDP sockets for network I/O. The sockets are event + * sources in the task system. + * + * When I/O completes, a completion event for the socket is posted to the + * event queue of the task which requested the I/O. + * + * \li MP: + * The module ensures appropriate synchronization of data structures it + * creates and manipulates. + * Clients of this module must not be holding a socket's task's lock when + * making a call that affects that socket. Failure to follow this rule + * can result in deadlock. + * The caller must ensure that isc_socketmgr_destroy() is called only + * once for a given manager. + * + * \li Reliability: + * No anticipated impact. + * + * \li Resources: + * TBS + * + * \li Security: + * No anticipated impact. + * + * \li Standards: + * None. + */ + +/*** + *** Imports + ***/ + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/event.h> +#include <isc/eventclass.h> +#include <isc/lang.h> +#include <isc/json.h> +#include <isc/region.h> +#include <isc/sockaddr.h> +#include <isc/time.h> +#include <isc/types.h> +#include <isc/xml.h> + +#ifdef WIN32 + +/* from the old namespace.h */ + +#define isc_socket_create isc__socket_create +#define isc_socket_dup isc__socket_dup +#define isc_socket_attach isc__socket_attach +#define isc_socket_detach isc__socket_detach +#define isc_socketmgr_create isc__socketmgr_create +#define isc_socketmgr_create2 isc__socketmgr_create2 +#define isc_socketmgr_destroy isc__socketmgr_destroy +#define isc_socket_open isc__socket_open +#define isc_socket_close isc__socket_close +#define isc_socket_recvv isc__socket_recvv +#define isc_socket_recv isc__socket_recv +#define isc_socket_recv2 isc__socket_recv2 +#define isc_socket_send isc__socket_send +#define isc_socket_sendto isc__socket_sendto +#define isc_socket_sendv isc__socket_sendv +#define isc_socket_sendtov isc__socket_sendtov +#define isc_socket_sendtov2 isc__socket_sendtov2 +#define isc_socket_sendto2 isc__socket_sendto2 +#define isc_socket_cleanunix isc__socket_cleanunix +#define isc_socket_permunix isc__socket_permunix +#define isc_socket_bind isc__socket_bind +#define isc_socket_filter isc__socket_filter +#define isc_socket_listen isc__socket_listen +#define isc_socket_accept isc__socket_accept +#define isc_socket_connect isc__socket_connect +#define isc_socket_getfd isc__socket_getfd +#define isc_socket_getname isc__socket_getname +#define isc_socket_gettag isc__socket_gettag +#define isc_socket_getpeername isc__socket_getpeername +#define isc_socket_getsockname isc__socket_getsockname +#define isc_socket_cancel isc__socket_cancel +#define isc_socket_gettype isc__socket_gettype +#define isc_socket_isbound isc__socket_isbound +#define isc_socket_ipv6only isc__socket_ipv6only +#define isc_socket_setname isc__socket_setname +#define isc_socketmgr_getmaxsockets isc__socketmgr_getmaxsockets +#define isc_socketmgr_setstats isc__socketmgr_setstats +#define isc_socketmgr_setreserved isc__socketmgr_setreserved +#define isc__socketmgr_maxudp isc___socketmgr_maxudp +#define isc_socket_fdwatchcreate isc__socket_fdwatchcreate +#define isc_socket_fdwatchpoke isc__socket_fdwatchpoke +#define isc_socket_dscp isc__socket_dscp + +#endif + +ISC_LANG_BEGINDECLS + +/*** + *** Constants + ***/ + +/*% + * Maximum number of buffers in a scatter/gather read/write. The operating + * system in use must support at least this number (plus one on some.) + */ +#define ISC_SOCKET_MAXSCATTERGATHER 8 + +/*% + * In isc_socket_bind() set socket option SO_REUSEADDR prior to calling + * bind() if a non zero port is specified (AF_INET and AF_INET6). + */ +#define ISC_SOCKET_REUSEADDRESS 0x01U + +/*% + * Statistics counters. Used as isc_statscounter_t values. + */ +enum { + isc_sockstatscounter_udp4open = 0, + isc_sockstatscounter_udp6open = 1, + isc_sockstatscounter_tcp4open = 2, + isc_sockstatscounter_tcp6open = 3, + isc_sockstatscounter_unixopen = 4, + + isc_sockstatscounter_udp4openfail = 5, + isc_sockstatscounter_udp6openfail = 6, + isc_sockstatscounter_tcp4openfail = 7, + isc_sockstatscounter_tcp6openfail = 8, + isc_sockstatscounter_unixopenfail = 9, + + isc_sockstatscounter_udp4close = 10, + isc_sockstatscounter_udp6close = 11, + isc_sockstatscounter_tcp4close = 12, + isc_sockstatscounter_tcp6close = 13, + isc_sockstatscounter_unixclose = 14, + isc_sockstatscounter_fdwatchclose = 15, + + isc_sockstatscounter_udp4bindfail = 16, + isc_sockstatscounter_udp6bindfail = 17, + isc_sockstatscounter_tcp4bindfail = 18, + isc_sockstatscounter_tcp6bindfail = 19, + isc_sockstatscounter_unixbindfail = 20, + isc_sockstatscounter_fdwatchbindfail = 21, + + isc_sockstatscounter_udp4connect = 22, + isc_sockstatscounter_udp6connect = 23, + isc_sockstatscounter_tcp4connect = 24, + isc_sockstatscounter_tcp6connect = 25, + isc_sockstatscounter_unixconnect = 26, + isc_sockstatscounter_fdwatchconnect = 27, + + isc_sockstatscounter_udp4connectfail = 28, + isc_sockstatscounter_udp6connectfail = 29, + isc_sockstatscounter_tcp4connectfail = 30, + isc_sockstatscounter_tcp6connectfail = 31, + isc_sockstatscounter_unixconnectfail = 32, + isc_sockstatscounter_fdwatchconnectfail = 33, + + isc_sockstatscounter_tcp4accept = 34, + isc_sockstatscounter_tcp6accept = 35, + isc_sockstatscounter_unixaccept = 36, + + isc_sockstatscounter_tcp4acceptfail = 37, + isc_sockstatscounter_tcp6acceptfail = 38, + isc_sockstatscounter_unixacceptfail = 39, + + isc_sockstatscounter_udp4sendfail = 40, + isc_sockstatscounter_udp6sendfail = 41, + isc_sockstatscounter_tcp4sendfail = 42, + isc_sockstatscounter_tcp6sendfail = 43, + isc_sockstatscounter_unixsendfail = 44, + isc_sockstatscounter_fdwatchsendfail = 45, + + isc_sockstatscounter_udp4recvfail = 46, + isc_sockstatscounter_udp6recvfail = 47, + isc_sockstatscounter_tcp4recvfail = 48, + isc_sockstatscounter_tcp6recvfail = 49, + isc_sockstatscounter_unixrecvfail = 50, + isc_sockstatscounter_fdwatchrecvfail = 51, + + isc_sockstatscounter_udp4active = 52, + isc_sockstatscounter_udp6active = 53, + isc_sockstatscounter_tcp4active = 54, + isc_sockstatscounter_tcp6active = 55, + isc_sockstatscounter_unixactive = 56, + + isc_sockstatscounter_rawopen = 57, + isc_sockstatscounter_rawopenfail = 58, + isc_sockstatscounter_rawclose = 59, + isc_sockstatscounter_rawrecvfail = 60, + isc_sockstatscounter_rawactive = 61, + + isc_sockstatscounter_max = 62 +}; + +/*** + *** Types + ***/ + +struct isc_socketevent { + ISC_EVENT_COMMON(isc_socketevent_t); + isc_result_t result; /*%< OK, EOF, whatever else */ + unsigned int minimum; /*%< minimum i/o for event */ + unsigned int n; /*%< bytes read or written */ + unsigned int offset; /*%< offset into buffer list */ + isc_region_t region; /*%< for single-buffer i/o */ + isc_bufferlist_t bufferlist; /*%< list of buffers */ + isc_sockaddr_t address; /*%< source address */ + isc_time_t timestamp; /*%< timestamp of packet recv */ + struct in6_pktinfo pktinfo; /*%< ipv6 pktinfo */ + uint32_t attributes; /*%< see below */ + isc_eventdestructor_t destroy; /*%< original destructor */ + unsigned int dscp; /*%< UDP dscp value */ +}; + +typedef struct isc_socket_newconnev isc_socket_newconnev_t; +struct isc_socket_newconnev { + ISC_EVENT_COMMON(isc_socket_newconnev_t); + isc_socket_t * newsocket; + isc_result_t result; /*%< OK, EOF, whatever else */ + isc_sockaddr_t address; /*%< source address */ +}; + +typedef struct isc_socket_connev isc_socket_connev_t; +struct isc_socket_connev { + ISC_EVENT_COMMON(isc_socket_connev_t); + isc_result_t result; /*%< OK, EOF, whatever else */ +}; + +/*@{*/ +/*! + * _ATTACHED: Internal use only. + * _TRUNC: Packet was truncated on receive. + * _CTRUNC: Packet control information was truncated. This can + * indicate that the packet is not complete, even though + * all the data is valid. + * _TIMESTAMP: The timestamp member is valid. + * _PKTINFO: The pktinfo member is valid. + * _MULTICAST: The UDP packet was received via a multicast transmission. + * _DSCP: The UDP DSCP value is valid. + * _USEMINMTU: Set the per packet IPV6_USE_MIN_MTU flag. + */ +#define ISC_SOCKEVENTATTR_ATTACHED 0x10000000U /* internal */ +#define ISC_SOCKEVENTATTR_TRUNC 0x00800000U /* public */ +#define ISC_SOCKEVENTATTR_CTRUNC 0x00400000U /* public */ +#define ISC_SOCKEVENTATTR_TIMESTAMP 0x00200000U /* public */ +#define ISC_SOCKEVENTATTR_PKTINFO 0x00100000U /* public */ +#define ISC_SOCKEVENTATTR_MULTICAST 0x00080000U /* public */ +#define ISC_SOCKEVENTATTR_DSCP 0x00040000U /* public */ +#define ISC_SOCKEVENTATTR_USEMINMTU 0x00020000U /* public */ +/*@}*/ + +#define ISC_SOCKEVENT_ANYEVENT (0) +#define ISC_SOCKEVENT_RECVDONE (ISC_EVENTCLASS_SOCKET + 1) +#define ISC_SOCKEVENT_SENDDONE (ISC_EVENTCLASS_SOCKET + 2) +#define ISC_SOCKEVENT_NEWCONN (ISC_EVENTCLASS_SOCKET + 3) +#define ISC_SOCKEVENT_CONNECT (ISC_EVENTCLASS_SOCKET + 4) + +/* + * Internal events. + */ +#define ISC_SOCKEVENT_INTR (ISC_EVENTCLASS_SOCKET + 256) +#define ISC_SOCKEVENT_INTW (ISC_EVENTCLASS_SOCKET + 257) + +typedef enum { + isc_sockettype_udp = 1, + isc_sockettype_tcp = 2, + isc_sockettype_unix = 3, + isc_sockettype_fdwatch = 4, + isc_sockettype_raw = 5 +} isc_sockettype_t; + +/*@{*/ +/*! + * How a socket should be shutdown in isc_socket_shutdown() calls. + */ +#define ISC_SOCKSHUT_RECV 0x00000001 /*%< close read side */ +#define ISC_SOCKSHUT_SEND 0x00000002 /*%< close write side */ +#define ISC_SOCKSHUT_ALL 0x00000003 /*%< close them all */ +/*@}*/ + +/*@{*/ +/*! + * What I/O events to cancel in isc_socket_cancel() calls. + */ +#define ISC_SOCKCANCEL_RECV 0x00000001 /*%< cancel recv */ +#define ISC_SOCKCANCEL_SEND 0x00000002 /*%< cancel send */ +#define ISC_SOCKCANCEL_ACCEPT 0x00000004 /*%< cancel accept */ +#define ISC_SOCKCANCEL_CONNECT 0x00000008 /*%< cancel connect */ +#define ISC_SOCKCANCEL_ALL 0x0000000f /*%< cancel everything */ +/*@}*/ + +/*@{*/ +/*! + * Flags for isc_socket_send() and isc_socket_recv() calls. + */ +#define ISC_SOCKFLAG_IMMEDIATE 0x00000001 /*%< send event only if needed */ +#define ISC_SOCKFLAG_NORETRY 0x00000002 /*%< drop failed UDP sends */ +/*@}*/ + +/*@{*/ +/*! + * Flags for fdwatchcreate. + */ +#define ISC_SOCKFDWATCH_READ 0x00000001 /*%< watch for readable */ +#define ISC_SOCKFDWATCH_WRITE 0x00000002 /*%< watch for writable */ +/*@}*/ + +/*% Socket and socket manager methods */ +typedef struct isc_socketmgrmethods { + void (*destroy)(isc_socketmgr_t **managerp); + isc_result_t (*socketcreate)(isc_socketmgr_t *manager, int pf, + isc_sockettype_t type, + isc_socket_t **socketp); + isc_result_t (*fdwatchcreate)(isc_socketmgr_t *manager, int fd, + int flags, + isc_sockfdwatch_t callback, + void *cbarg, isc_task_t *task, + isc_socket_t **socketp); +} isc_socketmgrmethods_t; + +typedef struct isc_socketmethods { + void (*attach)(isc_socket_t *socket, + isc_socket_t **socketp); + void (*detach)(isc_socket_t **socketp); + isc_result_t (*bind)(isc_socket_t *sock, isc_sockaddr_t *sockaddr, + unsigned int options); + isc_result_t (*sendto)(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, isc_taskaction_t action, + void *arg, isc_sockaddr_t *address, + struct in6_pktinfo *pktinfo); + isc_result_t (*sendto2)(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, isc_sockaddr_t *address, + struct in6_pktinfo *pktinfo, + isc_socketevent_t *event, + unsigned int flags); + isc_result_t (*connect)(isc_socket_t *sock, isc_sockaddr_t *addr, + isc_task_t *task, isc_taskaction_t action, + void *arg); + isc_result_t (*recv)(isc_socket_t *sock, isc_region_t *region, + unsigned int minimum, isc_task_t *task, + isc_taskaction_t action, void *arg); + isc_result_t (*recv2)(isc_socket_t *sock, isc_region_t *region, + unsigned int minimum, isc_task_t *task, + isc_socketevent_t *event, unsigned int flags); + void (*cancel)(isc_socket_t *sock, isc_task_t *task, + unsigned int how); + isc_result_t (*getsockname)(isc_socket_t *sock, + isc_sockaddr_t *addressp); + isc_sockettype_t (*gettype)(isc_socket_t *sock); + void (*ipv6only)(isc_socket_t *sock, bool yes); + isc_result_t (*fdwatchpoke)(isc_socket_t *sock, int flags); + isc_result_t (*dup)(isc_socket_t *socket, + isc_socket_t **socketp); + int (*getfd)(isc_socket_t *socket); + void (*dscp)(isc_socket_t *socket, isc_dscp_t dscp); +} isc_socketmethods_t; + +/*% + * This structure is actually just the common prefix of a socket manager + * object implementation's version of an isc_socketmgr_t. + * \brief + * Direct use of this structure by clients is forbidden. socket implementations + * may change the structure. 'magic' must be ISCAPI_SOCKETMGR_MAGIC for any + * of the isc_socket_ routines to work. socket implementations must maintain + * all socket invariants. + * In effect, this definition is used only for non-BIND9 version ("export") + * of the library, and the export version does not work for win32. So, to avoid + * the definition conflict with win32/socket.c, we enable this definition only + * for non-Win32 (i.e. Unix) platforms. + */ +#ifndef WIN32 +struct isc_socketmgr { + unsigned int impmagic; + unsigned int magic; + isc_socketmgrmethods_t *methods; +}; +#endif + +#define ISCAPI_SOCKETMGR_MAGIC ISC_MAGIC('A','s','m','g') +#define ISCAPI_SOCKETMGR_VALID(m) ((m) != NULL && \ + (m)->magic == ISCAPI_SOCKETMGR_MAGIC) + +/*% + * This is the common prefix of a socket object. The same note as + * that for the socketmgr structure applies. + */ +#ifndef WIN32 +struct isc_socket { + unsigned int impmagic; + unsigned int magic; + isc_socketmethods_t *methods; +}; +#endif + +#define ISCAPI_SOCKET_MAGIC ISC_MAGIC('A','s','c','t') +#define ISCAPI_SOCKET_VALID(s) ((s) != NULL && \ + (s)->magic == ISCAPI_SOCKET_MAGIC) + +/*** + *** Socket and Socket Manager Functions + *** + *** Note: all Ensures conditions apply only if the result is success for + *** those functions which return an isc_result. + ***/ + +isc_result_t +isc_socket_fdwatchcreate(isc_socketmgr_t *manager, + int fd, + int flags, + isc_sockfdwatch_t callback, + void *cbarg, + isc_task_t *task, + isc_socket_t **socketp); +/*%< + * Create a new file descriptor watch socket managed by 'manager'. + * + * Note: + * + *\li 'fd' is the already-opened file descriptor (must be less + * than maxsockets). + *\li This function is not available on Windows. + *\li The callback function is called "in-line" - this means the function + * needs to return as fast as possible, as all other I/O will be suspended + * until the callback completes. + * + * Requires: + * + *\li 'manager' is a valid manager + * + *\li 'socketp' is a valid pointer, and *socketp == NULL + * + *\li 'fd' be opened. + * + * Ensures: + * + * '*socketp' is attached to the newly created fdwatch socket + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_NORESOURCES + *\li #ISC_R_UNEXPECTED + *\li #ISC_R_RANGE + */ + +isc_result_t +isc_socket_fdwatchpoke(isc_socket_t *sock, + int flags); +/*%< + * Poke a file descriptor watch socket informing the manager that it + * should restart watching the socket + * + * Note: + * + *\li 'sock' is the socket returned by isc_socket_fdwatchcreate + * + *\li 'flags' indicates what the manager should watch for on the socket + * in addition to what it may already be watching. It can be one or + * both of ISC_SOCKFDWATCH_READ and ISC_SOCKFDWATCH_WRITE. To + * temporarily disable watching on a socket the value indicating + * no more data should be returned from the call back routine. + * + *\li This function is not available on Windows. + * + * Requires: + * + *\li 'sock' is a valid isc socket + * + * + * Returns: + * + *\li #ISC_R_SUCCESS + */ + +isc_result_t +isc_socket_create(isc_socketmgr_t *manager, + int pf, + isc_sockettype_t type, + isc_socket_t **socketp); +/*%< + * Create a new 'type' socket managed by 'manager'. + * + * For isc_sockettype_fdwatch sockets you should use isc_socket_fdwatchcreate() + * rather than isc_socket_create(). + * + * Note: + * + *\li 'pf' is the desired protocol family, e.g. PF_INET or PF_INET6. + * + * Requires: + * + *\li 'manager' is a valid manager + * + *\li 'socketp' is a valid pointer, and *socketp == NULL + * + *\li 'type' is not isc_sockettype_fdwatch + * + * Ensures: + * + * '*socketp' is attached to the newly created socket + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_NORESOURCES + *\li #ISC_R_UNEXPECTED + */ + +isc_result_t +isc_socket_dup(isc_socket_t *sock0, isc_socket_t **socketp); +/*%< + * Duplicate an existing socket, reusing its file descriptor. + */ + +void +isc_socket_cancel(isc_socket_t *sock, isc_task_t *task, + unsigned int how); +/*%< + * Cancel pending I/O of the type specified by "how". + * + * Note: if "task" is NULL, then the cancel applies to all tasks using the + * socket. + * + * Requires: + * + * \li "socket" is a valid socket + * + * \li "task" is NULL or a valid task + * + * "how" is a bitmask describing the type of cancelation to perform. + * The type ISC_SOCKCANCEL_ALL will cancel all pending I/O on this + * socket. + * + * \li ISC_SOCKCANCEL_RECV: + * Cancel pending isc_socket_recv() calls. + * + * \li ISC_SOCKCANCEL_SEND: + * Cancel pending isc_socket_send() and isc_socket_sendto() calls. + * + * \li ISC_SOCKCANCEL_ACCEPT: + * Cancel pending isc_socket_accept() calls. + * + * \li ISC_SOCKCANCEL_CONNECT: + * Cancel pending isc_socket_connect() call. + */ + +void +isc_socket_shutdown(isc_socket_t *sock, unsigned int how); +/*%< + * Shutdown 'socket' according to 'how'. + * + * Requires: + * + * \li 'socket' is a valid socket. + * + * \li 'task' is NULL or is a valid task. + * + * \li If 'how' is 'ISC_SOCKSHUT_RECV' or 'ISC_SOCKSHUT_ALL' then + * + * The read queue must be empty. + * + * No further read requests may be made. + * + * \li If 'how' is 'ISC_SOCKSHUT_SEND' or 'ISC_SOCKSHUT_ALL' then + * + * The write queue must be empty. + * + * No further write requests may be made. + */ + +void +isc_socket_attach(isc_socket_t *sock, isc_socket_t **socketp); +/*%< + * Attach *socketp to socket. + * + * Requires: + * + * \li 'socket' is a valid socket. + * + * \li 'socketp' points to a NULL socket. + * + * Ensures: + * + * \li *socketp is attached to socket. + */ + +void +isc_socket_detach(isc_socket_t **socketp); +/*%< + * Detach *socketp from its socket. + * + * Requires: + * + * \li 'socketp' points to a valid socket. + * + * \li If '*socketp' is the last reference to the socket, + * then: + * + * There must be no pending I/O requests. + * + * Ensures: + * + * \li *socketp is NULL. + * + * \li If '*socketp' is the last reference to the socket, + * then: + * + * The socket will be shutdown (both reading and writing) + * for all tasks. + * + * All resources used by the socket have been freed + */ + +isc_result_t +isc_socket_open(isc_socket_t *sock); +/*%< + * Open a new socket file descriptor of the given socket structure. It simply + * opens a new descriptor; all of the other parameters including the socket + * type are inherited from the existing socket. This function is provided to + * avoid overhead of destroying and creating sockets when many short-lived + * sockets are frequently opened and closed. When the efficiency is not an + * issue, it should be safer to detach the unused socket and re-create a new + * one. This optimization may not be available for some systems, in which + * case this function will return ISC_R_NOTIMPLEMENTED and must not be used. + * + * isc_socket_open() should not be called on sockets created by + * isc_socket_fdwatchcreate(). + * + * Requires: + * + * \li there must be no other reference to this socket. + * + * \li 'socket' is a valid and previously closed by isc_socket_close() + * + * \li 'sock->type' is not isc_sockettype_fdwatch + * + * Returns: + * Same as isc_socket_create(). + * \li ISC_R_NOTIMPLEMENTED + */ + +isc_result_t +isc_socket_close(isc_socket_t *sock); +/*%< + * Close a socket file descriptor of the given socket structure. This function + * is provided as an alternative to destroying an unused socket when overhead + * destroying/re-creating sockets can be significant, and is expected to be + * used with isc_socket_open(). This optimization may not be available for some + * systems, in which case this function will return ISC_R_NOTIMPLEMENTED and + * must not be used. + * + * isc_socket_close() should not be called on sockets created by + * isc_socket_fdwatchcreate(). + * + * Requires: + * + * \li The socket must have a valid descriptor. + * + * \li There must be no other reference to this socket. + * + * \li There must be no pending I/O requests. + * + * \li 'sock->type' is not isc_sockettype_fdwatch + * + * Returns: + * \li #ISC_R_NOTIMPLEMENTED + */ + +isc_result_t +isc_socket_bind(isc_socket_t *sock, isc_sockaddr_t *addressp, + unsigned int options); +/*%< + * Bind 'socket' to '*addressp'. + * + * Requires: + * + * \li 'socket' is a valid socket + * + * \li 'addressp' points to a valid isc_sockaddr. + * + * Returns: + * + * \li ISC_R_SUCCESS + * \li ISC_R_NOPERM + * \li ISC_R_ADDRNOTAVAIL + * \li ISC_R_ADDRINUSE + * \li ISC_R_BOUND + * \li ISC_R_UNEXPECTED + */ + +isc_result_t +isc_socket_filter(isc_socket_t *sock, const char *filter); +/*%< + * Inform the kernel that it should perform accept filtering. + * If filter is NULL the current filter will be removed.:w + */ + +isc_result_t +isc_socket_listen(isc_socket_t *sock, unsigned int backlog); +/*%< + * Set listen mode on the socket. After this call, the only function that + * can be used (other than attach and detach) is isc_socket_accept(). + * + * Notes: + * + * \li 'backlog' is as in the UNIX system call listen() and may be + * ignored by non-UNIX implementations. + * + * \li If 'backlog' is zero, a reasonable system default is used, usually + * SOMAXCONN. + * + * Requires: + * + * \li 'socket' is a valid, bound TCP socket or a valid, bound UNIX socket. + * + * Returns: + * + * \li ISC_R_SUCCESS + * \li ISC_R_UNEXPECTED + */ + +isc_result_t +isc_socket_accept(isc_socket_t *sock, + isc_task_t *task, isc_taskaction_t action, void *arg); +/*%< + * Queue accept event. When a new connection is received, the task will + * get an ISC_SOCKEVENT_NEWCONN event with the sender set to the listen + * socket. The new socket structure is sent inside the isc_socket_newconnev_t + * event type, and is attached to the task 'task'. + * + * REQUIRES: + * \li 'socket' is a valid TCP socket that isc_socket_listen() was called + * on. + * + * \li 'task' is a valid task + * + * \li 'action' is a valid action + * + * RETURNS: + * \li ISC_R_SUCCESS + * \li ISC_R_NOMEMORY + * \li ISC_R_UNEXPECTED + */ + +isc_result_t +isc_socket_connect(isc_socket_t *sock, isc_sockaddr_t *addressp, + isc_task_t *task, isc_taskaction_t action, + void *arg); +/*%< + * Connect 'socket' to peer with address *saddr. When the connection + * succeeds, or when an error occurs, a CONNECT event with action 'action' + * and arg 'arg' will be posted to the event queue for 'task'. + * + * Requires: + * + * \li 'socket' is a valid TCP socket + * + * \li 'addressp' points to a valid isc_sockaddr + * + * \li 'task' is a valid task + * + * \li 'action' is a valid action + * + * Returns: + * + * \li ISC_R_SUCCESS + * \li ISC_R_NOMEMORY + * \li ISC_R_UNEXPECTED + * + * Posted event's result code: + * + * \li ISC_R_SUCCESS + * \li ISC_R_TIMEDOUT + * \li ISC_R_CONNREFUSED + * \li ISC_R_NETUNREACH + * \li ISC_R_UNEXPECTED + */ + +isc_result_t +isc_socket_getpeername(isc_socket_t *sock, isc_sockaddr_t *addressp); +/*%< + * Get the name of the peer connected to 'socket'. + * + * Requires: + * + * \li 'socket' is a valid TCP socket. + * + * Returns: + * + * \li ISC_R_SUCCESS + * \li ISC_R_TOOSMALL + * \li ISC_R_UNEXPECTED + */ + +isc_result_t +isc_socket_getsockname(isc_socket_t *sock, isc_sockaddr_t *addressp); +/*%< + * Get the name of 'socket'. + * + * Requires: + * + * \li 'socket' is a valid socket. + * + * Returns: + * + * \li ISC_R_SUCCESS + * \li ISC_R_TOOSMALL + * \li ISC_R_UNEXPECTED + */ + +/*@{*/ +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_result_t +isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, + unsigned int minimum, + isc_task_t *task, isc_taskaction_t action, void *arg); + +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); + +/*! + * Receive from 'socket', storing the results in region. + * + * Notes: + * + *\li Let 'length' refer to the length of 'region' or to the sum of all + * available regions in the list of buffers '*buflist'. + * + *\li If 'minimum' is non-zero and at least that many bytes are read, + * the completion event will be posted to the task 'task.' If minimum + * is zero, the exact number of bytes requested in the region must + * be read for an event to be posted. This only makes sense for TCP + * connections, and is always set to 1 byte for UDP. + * + *\li The read will complete when the desired number of bytes have been + * read, if end-of-input occurs, or if an error occurs. A read done + * event with the given 'action' and 'arg' will be posted to the + * event queue of 'task'. + * + *\li The caller may not modify 'region', the buffers which are passed + * into this function, or any data they refer to until the completion + * event is received. + * + *\li For isc_socket_recvv(): + * On successful completion, '*buflist' will be empty, and the list of + * all buffers will be returned in the done event's 'bufferlist' + * member. On error return, '*buflist' will be unchanged. + * + *\li For isc_socket_recv2(): + * 'event' is not NULL, and the non-socket specific fields are + * expected to be initialized. + * + *\li For isc_socket_recv2(): + * The only defined value for 'flags' is ISC_SOCKFLAG_IMMEDIATE. If + * set and the operation completes, the return value will be + * ISC_R_SUCCESS and the event will be filled in and not sent. If the + * operation does not complete, the return value will be + * ISC_R_INPROGRESS and the event will be sent when the operation + * completes. + * + * Requires: + * + *\li 'socket' is a valid, bound socket. + * + *\li For isc_socket_recv(): + * 'region' is a valid region + * + *\li For isc_socket_recvv(): + * 'buflist' is non-NULL, and '*buflist' contain at least one buffer. + * + *\li 'task' is a valid task + * + *\li For isc_socket_recv() and isc_socket_recvv(): + * action != NULL and is a valid action + * + *\li For isc_socket_recv2(): + * event != NULL + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_INPROGRESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_UNEXPECTED + * + * Event results: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_UNEXPECTED + *\li XXX needs other net-type errors + */ +/*@}*/ + +/*@{*/ +isc_result_t +isc_socket_send(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, isc_taskaction_t action, void *arg); +isc_result_t +isc_socket_sendto(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo); +isc_result_t +isc_socket_sendv(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg); +isc_result_t +isc_socket_sendtov(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo); +isc_result_t +isc_socket_sendtov2(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + unsigned int flags); +isc_result_t +isc_socket_sendto2(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + isc_socketevent_t *event, unsigned int flags); + +/*! + * Send the contents of 'region' to the socket's peer. + * + * Notes: + * + *\li Shutting down the requestor's task *may* result in any + * still pending writes being dropped or completed, depending on the + * underlying OS implementation. + * + *\li If 'action' is NULL, then no completion event will be posted. + * + *\li The caller may not modify 'region', the buffers which are passed + * into this function, or any data they refer to until the completion + * event is received. + * + *\li For isc_socket_sendv() and isc_socket_sendtov(): + * On successful completion, '*buflist' will be empty, and the list of + * all buffers will be returned in the done event's 'bufferlist' + * member. On error return, '*buflist' will be unchanged. + * + *\li For isc_socket_sendto2(): + * 'event' is not NULL, and the non-socket specific fields are + * expected to be initialized. + * + *\li For isc_socket_sendto2(): + * The only defined values for 'flags' are ISC_SOCKFLAG_IMMEDIATE + * and ISC_SOCKFLAG_NORETRY. + * + *\li If ISC_SOCKFLAG_IMMEDIATE is set and the operation completes, the + * return value will be ISC_R_SUCCESS and the event will be filled + * in and not sent. If the operation does not complete, the return + * value will be ISC_R_INPROGRESS and the event will be sent when + * the operation completes. + * + *\li ISC_SOCKFLAG_NORETRY can only be set for UDP sockets. If set + * and the send operation fails due to a transient error, the send + * will not be retried and the error will be indicated in the event. + * Using this option along with ISC_SOCKFLAG_IMMEDIATE allows the caller + * to specify a region that is allocated on the stack. + * + * Requires: + * + *\li 'socket' is a valid, bound socket. + * + *\li For isc_socket_send(): + * 'region' is a valid region + * + *\li For isc_socket_sendv() and isc_socket_sendtov(): + * 'buflist' is non-NULL, and '*buflist' contain at least one buffer. + * + *\li 'task' is a valid task + * + *\li For isc_socket_sendv(), isc_socket_sendtov(), isc_socket_send(), and + * isc_socket_sendto(): + * action == NULL or is a valid action + * + *\li For isc_socket_sendto2(): + * event != NULL + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_INPROGRESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_UNEXPECTED + * + * Event results: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_UNEXPECTED + *\li XXX needs other net-type errors + */ +/*@}*/ + +isc_result_t +isc_socketmgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, + isc_socketmgr_t **managerp); + +isc_result_t +isc_socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp); + +isc_result_t +isc_socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp, + unsigned int maxsocks); +/*%< + * Create a socket manager. If "maxsocks" is non-zero, it specifies the + * maximum number of sockets that the created manager should handle. + * isc_socketmgr_create() is equivalent of isc_socketmgr_create2() with + * "maxsocks" being zero. + * isc_socketmgr_createinctx() also associates the new manager with the + * specified application context. + * + * Notes: + * + *\li All memory will be allocated in memory context 'mctx'. + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li 'managerp' points to a NULL isc_socketmgr_t. + * + *\li 'actx' is a valid application context (for createinctx()). + * + * Ensures: + * + *\li '*managerp' is a valid isc_socketmgr_t. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_UNEXPECTED + *\li #ISC_R_NOTIMPLEMENTED + */ + +isc_result_t +isc_socketmgr_getmaxsockets(isc_socketmgr_t *manager, unsigned int *nsockp); +/*%< + * Returns in "*nsockp" the maximum number of sockets this manager may open. + * + * Requires: + * + *\li '*manager' is a valid isc_socketmgr_t. + *\li 'nsockp' is not NULL. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOTIMPLEMENTED + */ + +void +isc_socketmgr_setstats(isc_socketmgr_t *manager, isc_stats_t *stats); +/*%< + * Set a general socket statistics counter set 'stats' for 'manager'. + * + * Requires: + * \li 'manager' is valid, hasn't opened any socket, and doesn't have + * stats already set. + * + *\li stats is a valid statistics supporting socket statistics counters + * (see above). + */ + +void +isc_socketmgr_destroy(isc_socketmgr_t **managerp); +/*%< + * Destroy a socket manager. + * + * Notes: + * + *\li This routine blocks until there are no sockets left in the manager, + * so if the caller holds any socket references using the manager, it + * must detach them before calling isc_socketmgr_destroy() or it will + * block forever. + * + * Requires: + * + *\li '*managerp' is a valid isc_socketmgr_t. + * + *\li All sockets managed by this manager are fully detached. + * + * Ensures: + * + *\li *managerp == NULL + * + *\li All resources used by the manager have been freed. + */ + +isc_sockettype_t +isc_socket_gettype(isc_socket_t *sock); +/*%< + * Returns the socket type for "sock." + * + * Requires: + * + *\li "sock" is a valid socket. + */ + +/*@{*/ +bool +isc__socket_isbound(isc_socket_t *sock); +/*% + * Intended for internal use in BIND9 only + */ + +void +isc_socket_ipv6only(isc_socket_t *sock, bool yes); +/*%< + * If the socket is an IPv6 socket set/clear the IPV6_IPV6ONLY socket + * option if the host OS supports this option. + * + * Requires: + *\li 'sock' is a valid socket. + */ +/*@}*/ + +void +isc_socket_dscp(isc_socket_t *sock, isc_dscp_t dscp); +/*%< + * Sets the Differentiated Services Code Point (DSCP) field for packets + * transmitted on this socket. If 'dscp' is -1, return immediately. + * + * Requires: + *\li 'sock' is a valid socket. + */ + +isc_socketevent_t * +isc_socket_socketevent(isc_mem_t *mctx, void *sender, + isc_eventtype_t eventtype, isc_taskaction_t action, + void *arg); +/*%< + * Get a isc_socketevent_t to be used with isc_socket_sendto2(), etc. + */ + +void +isc_socket_cleanunix(isc_sockaddr_t *addr, bool active); + +/*%< + * Cleanup UNIX domain sockets in the file-system. If 'active' is true + * then just unlink the socket. If 'active' is false try to determine + * if there is a listener of the socket or not. If no listener is found + * then unlink socket. + * + * Prior to unlinking the path is tested to see if it a socket. + * + * Note: there are a number of race conditions which cannot be avoided + * both in the filesystem and any application using UNIX domain + * sockets (e.g. socket is tested between bind() and listen(), + * the socket is deleted and replaced in the file-system between + * stat() and unlink()). + */ + +isc_result_t +isc_socket_permunix(isc_sockaddr_t *sockaddr, uint32_t perm, + uint32_t owner, uint32_t group); +/*%< + * Set ownership and file permissions on the UNIX domain socket. + * + * Note: On Solaris and SunOS this secures the directory containing + * the socket as Solaris and SunOS do not honour the filesystem + * permissions on the socket. + * + * Requires: + * \li 'sockaddr' to be a valid UNIX domain sockaddr. + * + * Returns: + * \li #ISC_R_SUCCESS + * \li #ISC_R_FAILURE + */ + +void isc_socket_setname(isc_socket_t *socket, const char *name, void *tag); +/*%< + * Set the name and optional tag for a socket. This allows tracking of the + * owner or purpose for this socket, and is useful for tracing and statistics + * reporting. + */ + +const char *isc_socket_getname(isc_socket_t *socket); +/*%< + * Get the name associated with a socket, if any. + */ + +void *isc_socket_gettag(isc_socket_t *socket); +/*%< + * Get the tag associated with a socket, if any. + */ + +int isc_socket_getfd(isc_socket_t *socket); +/*%< + * Get the file descriptor associated with a socket + */ + +void +isc__socketmgr_setreserved(isc_socketmgr_t *mgr, uint32_t); +/*%< + * Temporary. For use by named only. + */ + +void +isc__socketmgr_maxudp(isc_socketmgr_t *mgr, int maxudp); +/*%< + * Test interface. Drop UDP packet > 'maxudp'. + */ + +#ifdef HAVE_LIBXML2 +int +isc_socketmgr_renderxml(isc_socketmgr_t *mgr, xmlTextWriterPtr writer); +/*%< + * Render internal statistics and other state into the XML document. + */ +#endif /* HAVE_LIBXML2 */ + +#ifdef HAVE_JSON +isc_result_t +isc_socketmgr_renderjson(isc_socketmgr_t *mgr, json_object *stats); +/*%< + * Render internal statistics and other state into JSON format. + */ +#endif /* HAVE_JSON */ + +/*%< + * See isc_socketmgr_create() above. + */ +typedef isc_result_t +(*isc_socketmgrcreatefunc_t)(isc_mem_t *mctx, isc_socketmgr_t **managerp); + +isc_result_t +isc_socket_register(isc_socketmgrcreatefunc_t createfunc); +/*%< + * Register a new socket I/O implementation and add it to the list of + * supported implementations. This function must be called when a different + * event library is used than the one contained in the ISC library. + */ + +isc_result_t +isc__socket_register(void); +/*%< + * A short cut function that specifies the socket I/O module in the ISC + * library for isc_socket_register(). An application that uses the ISC library + * usually do not have to care about this function: it would call + * isc_lib_register(), which internally calls this function. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_SOCKET_H */ diff --git a/lib/isc/include/isc/stats.h b/lib/isc/include/isc/stats.h new file mode 100644 index 0000000..8f41bb9 --- /dev/null +++ b/lib/isc/include/isc/stats.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_STATS_H +#define ISC_STATS_H 1 + +/*! \file isc/stats.h */ + +#include <inttypes.h> + +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +/*%< + * Flag(s) for isc_stats_dump(). + */ +#define ISC_STATSDUMP_VERBOSE 0x00000001 /*%< dump 0-value counters */ + +/*%< + * Dump callback type. + */ +typedef void (*isc_stats_dumper_t)(isc_statscounter_t, uint64_t, void *); + +isc_result_t +isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters); +/*%< + * Create a statistics counter structure of general type. It counts a general + * set of counters indexed by an ID between 0 and ncounters -1. + * + * Requires: + *\li 'mctx' must be a valid memory context. + * + *\li 'statsp' != NULL && '*statsp' == NULL. + * + * Returns: + *\li ISC_R_SUCCESS -- all ok + * + *\li anything else -- failure + */ + +void +isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp); +/*%< + * Attach to a statistics set. + * + * Requires: + *\li 'stats' is a valid isc_stats_t. + * + *\li 'statsp' != NULL && '*statsp' == NULL + */ + +void +isc_stats_detach(isc_stats_t **statsp); +/*%< + * Detaches from the statistics set. + * + * Requires: + *\li 'statsp' != NULL and '*statsp' is a valid isc_stats_t. + */ + +int +isc_stats_ncounters(isc_stats_t *stats); +/*%< + * Returns the number of counters contained in stats. + * + * Requires: + *\li 'stats' is a valid isc_stats_t. + * + */ + +void +isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter); +/*%< + * Increment the counter-th counter of stats. + * + * Requires: + *\li 'stats' is a valid isc_stats_t. + * + *\li counter is less than the maximum available ID for the stats specified + * on creation. + */ + +void +isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter); +/*%< + * Decrement the counter-th counter of stats. + * + * Requires: + *\li 'stats' is a valid isc_stats_t. + */ + +void +isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn, void *arg, + unsigned int options); +/*%< + * Dump the current statistics counters in a specified way. For each counter + * in stats, dump_fn is called with its current value and the given argument + * arg. By default counters that have a value of 0 is skipped; if options has + * the ISC_STATSDUMP_VERBOSE flag, even such counters are dumped. + * + * Requires: + *\li 'stats' is a valid isc_stats_t. + */ + +void +isc_stats_set(isc_stats_t *stats, uint64_t val, + isc_statscounter_t counter); +/*%< + * Set the given counter to the specfied value. + * + * Requires: + *\li 'stats' is a valid isc_stats_t. + */ + +void +isc_stats_set(isc_stats_t *stats, uint64_t val, + isc_statscounter_t counter); +/*%< + * Set the given counter to the specfied value. + * + * Requires: + *\li 'stats' is a valid isc_stats_t. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_STATS_H */ diff --git a/lib/isc/include/isc/stdatomic.h b/lib/isc/include/isc/stdatomic.h new file mode 100644 index 0000000..723ed1e --- /dev/null +++ b/lib/isc/include/isc/stdatomic.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#pragma once + +#if !defined(__has_feature) +#define __has_feature(x) 0 +#endif + +#if !defined(__has_extension) +#define __has_extension(x) __has_feature(x) +#endif + +#if !defined(__GNUC_PREREQ__) +#if defined(__GNUC__) && defined(__GNUC_MINOR__) +#define __GNUC_PREREQ__(maj, min) \ + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) +#else +#define __GNUC_PREREQ__(maj, min) 0 +#endif +#endif + +#if !defined(__CLANG_ATOMICS) && !defined(__GNUC_ATOMICS) +#if __has_extension(c_atomic) || __has_extension(cxx_atomic) +#define __CLANG_ATOMICS +#elif __GNUC_PREREQ__(4, 7) +#define __GNUC_ATOMICS +#elif !defined(__GNUC__) +#error "isc/stdatomic.h does not support your compiler" +#endif +#endif + +#ifndef __ATOMIC_RELAXED +#define __ATOMIC_RELAXED 0 +#endif +#ifndef __ATOMIC_CONSUME +#define __ATOMIC_CONSUME 1 +#endif +#ifndef __ATOMIC_ACQUIRE +#define __ATOMIC_ACQUIRE 2 +#endif +#ifndef __ATOMIC_RELEASE +#define __ATOMIC_RELEASE 3 +#endif +#ifndef __ATOMIC_ACQ_REL +#define __ATOMIC_ACQ_REL 4 +#endif +#ifndef __ATOMIC_SEQ_CST +#define __ATOMIC_SEQ_CST 5 +#endif + + +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; + +typedef int_fast32_t atomic_int_fast32_t; +typedef uint_fast32_t atomic_uint_fast32_t; +typedef int_fast64_t atomic_int_fast64_t; +typedef uint_fast64_t atomic_uint_fast64_t; + +#if defined(__CLANG_ATOMICS) /* __c11_atomic builtins */ +#define atomic_init(obj, desired) \ + __c11_atomic_init(obj, desired) +#define atomic_load_explicit(obj, order) \ + __c11_atomic_load(obj, order) +#define atomic_store_explicit(obj, desired, order) \ + __c11_atomic_store(obj, desired, order) +#define atomic_fetch_add_explicit(obj, arg, order) \ + __c11_atomic_fetch_add(obj, arg, order) +#define atomic_fetch_sub_explicit(obj, arg, order) \ + __c11_atomic_fetch_sub(obj, arg, order) +#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) \ + __c11_atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) +#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) \ + __c11_atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) +#elif defined(__GNUC_ATOMICS) /* __atomic builtins */ +#define atomic_init(obj, desired) \ + (*obj = desired) +#define atomic_load_explicit(obj, order) \ + __atomic_load_n(obj, order) +#define atomic_store_explicit(obj, desired, order) \ + __atomic_store_n(obj, desired, order) +#define atomic_fetch_add_explicit(obj, arg, order) \ + __atomic_fetch_add(obj, arg, order) +#define atomic_fetch_sub_explicit(obj, arg, order) \ + __atomic_fetch_sub(obj, arg, order) +#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) \ + __atomic_compare_exchange_n(obj, expected, desired, 0, succ, fail) +#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) \ + __atomic_compare_exchange_n(obj, expected, desired, 1, succ, fail) +#else /* __sync builtins */ +#define atomic_init(obj, desired) \ + (*obj = desired) +#define atomic_load_explicit(obj, order) \ + __sync_fetch_and_add(obj, 0) +#define atomic_store_explicit(obj, desired, order) \ + do { \ + __sync_synchronize(); \ + *obj = desired; \ + __sync_synchronize(); \ + } while (0); +#define atomic_fetch_add_explicit(obj, arg, order) \ + __sync_fetch_and_add(obj, arg) +#define atomic_fetch_sub_explicit(obj, arg, order) \ + __sync_fetch_and_sub(obj, arg, order) +#define atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) \ + ({ \ + __typeof__(obj) __v; \ + _Bool __r; \ + __v = __sync_val_compare_and_swap(obj, *(expected), desired); \ + __r = *(expected) == __v; \ + *(expected) = __v; \ + __r; \ + }) +#define atomic_compare_exchange_weak_explicit(obj, expected, desired, succ, fail) \ + atomic_compare_exchange_strong_explicit(obj, expected, desired, succ, fail) +#endif + +#define atomic_load(obj) \ + atomic_load_explicit(obj, memory_order_seq_cst) +#define atomic_store(obj) \ + atomic_store_explicit(obj, memory_order_seq_cst) +#define atomic_fetch_add(obj) \ + atomic_fetch_add_explicit(obj, arg, memory_order_seq_cst) +#define atomic_fetch_sub(obj) \ + atomic_fetch_sub_explicit(obj, arg, memory_order_seq_cst) +#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(obj, expected, desired) \ + atomic_compare_exchange_weak_explicit(obj, expected, desired, memory_order_seq_cst, memory_order_seq_cst) diff --git a/lib/isc/include/isc/stdio.h b/lib/isc/include/isc/stdio.h new file mode 100644 index 0000000..1f44b5a --- /dev/null +++ b/lib/isc/include/isc/stdio.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_STDIO_H +#define ISC_STDIO_H 1 + +/*! \file isc/stdio.h */ + +/*% + * These functions are wrappers around the corresponding stdio functions. + * + * They return a detailed error code in the form of an an isc_result_t. ANSI C + * does not guarantee that stdio functions set errno, hence these functions + * must use platform dependent methods (e.g., the POSIX errno) to construct the + * error code. + */ + +#include <stdio.h> + +#include <isc/lang.h> +#include <isc/result.h> + +ISC_LANG_BEGINDECLS + +/*% Open */ +isc_result_t +isc_stdio_open(const char *filename, const char *mode, FILE **fp); + +/*% Close */ +isc_result_t +isc_stdio_close(FILE *f); + +/*% Seek */ +isc_result_t +isc_stdio_seek(FILE *f, off_t offset, int whence); + +/*% Tell */ +isc_result_t +isc_stdio_tell(FILE *f, off_t *offsetp); + +/*% Read */ +isc_result_t +isc_stdio_read(void *ptr, size_t size, size_t nmemb, FILE *f, + size_t *nret); + +/*% Write */ +isc_result_t +isc_stdio_write(const void *ptr, size_t size, size_t nmemb, FILE *f, + size_t *nret); + +/*% Flush */ +isc_result_t +isc_stdio_flush(FILE *f); + +isc_result_t +isc_stdio_sync(FILE *f); +/*%< + * Invoke fsync() on the file descriptor underlying an stdio stream, or an + * equivalent system-dependent operation. Note that this function has no + * direct counterpart in the stdio library. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_STDIO_H */ diff --git a/lib/isc/include/isc/stdlib.h b/lib/isc/include/isc/stdlib.h new file mode 100644 index 0000000..c44b0ea --- /dev/null +++ b/lib/isc/include/isc/stdlib.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_STDLIB_H +#define ISC_STDLIB_H 1 + +/*! \file isc/stdlib.h */ + +#include <stdlib.h> + +#include <isc/lang.h> +#include <isc/platform.h> + +#ifdef ISC_PLATFORM_NEEDSTRTOUL +#define strtoul isc_strtoul +#endif + +ISC_LANG_BEGINDECLS + +unsigned long isc_strtoul(const char *, char **, int); + +ISC_LANG_ENDDECLS + +#endif diff --git a/lib/isc/include/isc/string.h b/lib/isc/include/isc/string.h new file mode 100644 index 0000000..5323aa7 --- /dev/null +++ b/lib/isc/include/isc/string.h @@ -0,0 +1,233 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: string.h,v 1.23 2007/09/13 04:48:16 each Exp $ */ + +#ifndef ISC_STRING_H +#define ISC_STRING_H 1 + +/*! \file isc/string.h */ + +#include <inttypes.h> + +#include <isc/formatcheck.h> +#include <isc/lang.h> +#include <isc/platform.h> +#include <isc/types.h> + +#include <string.h> + +#ifdef ISC_PLATFORM_HAVESTRINGSH +#include <strings.h> +#endif + +#define ISC_STRING_MAGIC 0x5e + +ISC_LANG_BEGINDECLS + +uint64_t +isc_string_touint64(char *source, char **endp, int base); +/*%< + * Convert the string pointed to by 'source' to uint64_t. + * + * On successful conversion 'endp' points to the first character + * after conversion is complete. + * + * 'base': 0 or 2..36 + * + * If base is 0 the base is computed from the string type. + * + * On error 'endp' points to 'source'. + */ + +isc_result_t +isc_string_copy(char *target, size_t size, const char *source); +/* + * Copy the string pointed to by 'source' to 'target' which is a + * pointer to a string of at least 'size' bytes. + * + * Requires: + * 'target' is a pointer to a char[] of at least 'size' bytes. + * 'size' an integer > 0. + * 'source' == NULL or points to a NUL terminated string. + * + * Ensures: + * If result == ISC_R_SUCCESS + * 'target' will be a NUL terminated string of no more + * than 'size' bytes (including NUL). + * + * If result == ISC_R_NOSPACE + * 'target' is undefined. + * + * Returns: + * ISC_R_SUCCESS -- 'source' was successfully copied to 'target'. + * ISC_R_NOSPACE -- 'source' could not be copied since 'target' + * is too small. + */ + +void +isc_string_copy_truncate(char *target, size_t size, const char *source); +/* + * Copy the string pointed to by 'source' to 'target' which is a + * pointer to a string of at least 'size' bytes. + * + * Requires: + * 'target' is a pointer to a char[] of at least 'size' bytes. + * 'size' an integer > 0. + * 'source' == NULL or points to a NUL terminated string. + * + * Ensures: + * 'target' will be a NUL terminated string of no more + * than 'size' bytes (including NUL). + */ + +isc_result_t +isc_string_append(char *target, size_t size, const char *source); +/* + * Append the string pointed to by 'source' to 'target' which is a + * pointer to a NUL terminated string of at least 'size' bytes. + * + * Requires: + * 'target' is a pointer to a NUL terminated char[] of at + * least 'size' bytes. + * 'size' an integer > 0. + * 'source' == NULL or points to a NUL terminated string. + * + * Ensures: + * If result == ISC_R_SUCCESS + * 'target' will be a NUL terminated string of no more + * than 'size' bytes (including NUL). + * + * If result == ISC_R_NOSPACE + * 'target' is undefined. + * + * Returns: + * ISC_R_SUCCESS -- 'source' was successfully appended to 'target'. + * ISC_R_NOSPACE -- 'source' could not be appended since 'target' + * is too small. + */ + +void +isc_string_append_truncate(char *target, size_t size, const char *source); +/* + * Append the string pointed to by 'source' to 'target' which is a + * pointer to a NUL terminated string of at least 'size' bytes. + * + * Requires: + * 'target' is a pointer to a NUL terminated char[] of at + * least 'size' bytes. + * 'size' an integer > 0. + * 'source' == NULL or points to a NUL terminated string. + * + * Ensures: + * 'target' will be a NUL terminated string of no more + * than 'size' bytes (including NUL). + */ + +isc_result_t +isc_string_printf(char *target, size_t size, const char *format, ...) + ISC_FORMAT_PRINTF(3, 4); +/* + * Print 'format' to 'target' which is a pointer to a string of at least + * 'size' bytes. + * + * Requires: + * 'target' is a pointer to a char[] of at least 'size' bytes. + * 'size' an integer > 0. + * 'format' == NULL or points to a NUL terminated string. + * + * Ensures: + * If result == ISC_R_SUCCESS + * 'target' will be a NUL terminated string of no more + * than 'size' bytes (including NUL). + * + * If result == ISC_R_NOSPACE + * 'target' is undefined. + * + * Returns: + * ISC_R_SUCCESS -- 'format' was successfully printed to 'target'. + * ISC_R_NOSPACE -- 'format' could not be printed to 'target' since it + * is too small. + */ + +void +isc_string_printf_truncate(char *target, size_t size, const char *format, ...) + ISC_FORMAT_PRINTF(3, 4); +/* + * Print 'format' to 'target' which is a pointer to a string of at least + * 'size' bytes. + * + * Requires: + * 'target' is a pointer to a char[] of at least 'size' bytes. + * 'size' an integer > 0. + * 'format' == NULL or points to a NUL terminated string. + * + * Ensures: + * 'target' will be a NUL terminated string of no more + * than 'size' bytes (including NUL). + */ + + +char * +isc_string_regiondup(isc_mem_t *mctx, const isc_region_t *source); +/* + * Copy the region pointed to by r to a NUL terminated string + * allocated from the memory context pointed to by mctx. + * + * The result should be deallocated using isc_mem_free() + * + * Requires: + * 'mctx' is a point to a valid memory context. + * 'source' is a pointer to a valid region. + * + * Returns: + * a pointer to a NUL terminated string or + * NULL if memory for the copy could not be allocated + * + */ + +char * +isc_string_separate(char **stringp, const char *delim); + +#ifdef ISC_PLATFORM_NEEDSTRSEP +#define strsep isc_string_separate +#endif + +#ifdef ISC_PLATFORM_NEEDMEMMOVE +#define memmove(a,b,c) bcopy(b,a,c) +#endif + +size_t +isc_string_strlcpy(char *dst, const char *src, size_t size); + + +#ifdef ISC_PLATFORM_NEEDSTRLCPY +#define strlcpy isc_string_strlcpy +#endif + + +size_t +isc_string_strlcat(char *dst, const char *src, size_t size); + +#ifdef ISC_PLATFORM_NEEDSTRLCAT +#define strlcat isc_string_strlcat +#endif + +char * +isc_string_strcasestr(const char *big, const char *little); + +#ifdef ISC_PLATFORM_NEEDSTRCASESTR +#define strcasestr isc_string_strcasestr +#endif + +ISC_LANG_ENDDECLS + +#endif /* ISC_STRING_H */ diff --git a/lib/isc/include/isc/symtab.h b/lib/isc/include/isc/symtab.h new file mode 100644 index 0000000..0f53b5e --- /dev/null +++ b/lib/isc/include/isc/symtab.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_SYMTAB_H +#define ISC_SYMTAB_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/symtab.h + * \brief Provides a simple memory-based symbol table. + * + * Keys are C strings, and key comparisons are case-insensitive. A type may + * be specified when looking up, defining, or undefining. A type value of + * 0 means "match any type"; any other value will only match the given + * type. + * + * It's possible that a client will attempt to define a <key, type, value> + * tuple when a tuple with the given key and type already exists in the table. + * What to do in this case is specified by the client. Possible policies are: + * + *\li #isc_symexists_reject Disallow the define, returning #ISC_R_EXISTS + *\li #isc_symexists_replace Replace the old value with the new. The + * undefine action (if provided) will be called + * with the old <key, type, value> tuple. + *\li #isc_symexists_add Add the new tuple, leaving the old tuple in + * the table. Subsequent lookups will retrieve + * the most-recently-defined tuple. + * + * A lookup of a key using type 0 will return the most-recently defined + * symbol with that key. An undefine of a key using type 0 will undefine the + * most-recently defined symbol with that key. Trying to define a key with + * type 0 is illegal. + * + * The symbol table library does not make a copy the key field, so the + * caller must ensure that any key it passes to isc_symtab_define() will not + * change until it calls isc_symtab_undefine() or isc_symtab_destroy(). + * + * A user-specified action will be called (if provided) when a symbol is + * undefined. It can be used to free memory associated with keys and/or + * values. + * + * A symbol table is implemented as a hash table of lists; the size of the + * hash table is set by the 'size' parameter to isc_symtbl_create(). When + * the number of entries in the symbol table reaches three quarters of this + * value, the hash table is reallocated with size doubled, in order to + * optimize lookup performance. This has a negative effect on insertion + * performance, which can be mitigated by sizing the table appropriately + * when creating it. + * + * \li MP: + * The callers of this module must ensure any required synchronization. + * + * \li Reliability: + * No anticipated impact. + * + * \li Resources: + * TBS + * + * \li Security: + * No anticipated impact. + * + * \li Standards: + * None. + */ + +/*** + *** Imports. + ***/ + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/types.h> + +/* + *** Symbol Tables. + ***/ +/*% Symbol table value. */ +typedef union isc_symvalue { + void * as_pointer; + const void * as_cpointer; + int as_integer; + unsigned int as_uinteger; +} isc_symvalue_t; + +typedef void (*isc_symtabaction_t)(char *key, unsigned int type, + isc_symvalue_t value, void *userarg); +/*% Symbol table exists. */ +typedef enum { + isc_symexists_reject = 0, /*%< Disallow the define */ + isc_symexists_replace = 1, /*%< Replace the old value with the new */ + isc_symexists_add = 2 /*%< Add the new tuple */ +} isc_symexists_t; + +ISC_LANG_BEGINDECLS + +/*% Create a symbol table. */ +isc_result_t +isc_symtab_create(isc_mem_t *mctx, unsigned int size, + isc_symtabaction_t undefine_action, void *undefine_arg, + bool case_sensitive, isc_symtab_t **symtabp); + +/*% Destroy a symbol table. */ +void +isc_symtab_destroy(isc_symtab_t **symtabp); + +/*% Lookup a symbol table. */ +isc_result_t +isc_symtab_lookup(isc_symtab_t *symtab, const char *key, unsigned int type, + isc_symvalue_t *value); + +/*% Define a symbol table. */ +isc_result_t +isc_symtab_define(isc_symtab_t *symtab, const char *key, unsigned int type, + isc_symvalue_t value, isc_symexists_t exists_policy); + +/*% Undefine a symbol table. */ +isc_result_t +isc_symtab_undefine(isc_symtab_t *symtab, const char *key, unsigned int type); + +/*% Return the number of items in a symbol table. */ +unsigned int +isc_symtab_count(isc_symtab_t *symtab); +ISC_LANG_ENDDECLS + +#endif /* ISC_SYMTAB_H */ diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h new file mode 100644 index 0000000..6920395 --- /dev/null +++ b/lib/isc/include/isc/task.h @@ -0,0 +1,827 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_TASK_H +#define ISC_TASK_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/task.h + * \brief The task system provides a lightweight execution context, which is + * basically an event queue. + + * When a task's event queue is non-empty, the + * task is runnable. A small work crew of threads, typically one per CPU, + * execute runnable tasks by dispatching the events on the tasks' event + * queues. Context switching between tasks is fast. + * + * \li MP: + * The module ensures appropriate synchronization of data structures it + * creates and manipulates. + * The caller must ensure that isc_taskmgr_destroy() is called only + * once for a given manager. + * + * \li Reliability: + * No anticipated impact. + * + * \li Resources: + * TBS + * + * \li Security: + * No anticipated impact. + * + * \li Standards: + * None. + * + * \section purge Purging and Unsending + * + * Events which have been queued for a task but not delivered may be removed + * from the task's event queue by purging or unsending. + * + * With both types, the caller specifies a matching pattern that selects + * events based upon their sender, type, and tag. + * + * Purging calls isc_event_free() on the matching events. + * + * Unsending returns a list of events that matched the pattern. + * The caller is then responsible for them. + * + * Consumers of events should purge, not unsend. + * + * Producers of events often want to remove events when the caller indicates + * it is no longer interested in the object, e.g. by canceling a timer. + * Sometimes this can be done by purging, but for some event types, the + * calls to isc_event_free() cause deadlock because the event free routine + * wants to acquire a lock the caller is already holding. Unsending instead + * of purging solves this problem. As a general rule, producers should only + * unsend events which they have sent. + */ + + +/*** + *** Imports. + ***/ + +#include <stdbool.h> + +#include <isc/eventclass.h> +#include <isc/json.h> +#include <isc/lang.h> +#include <isc/stdtime.h> +#include <isc/types.h> +#include <isc/xml.h> + +#define ISC_TASKEVENT_FIRSTEVENT (ISC_EVENTCLASS_TASK + 0) +#define ISC_TASKEVENT_SHUTDOWN (ISC_EVENTCLASS_TASK + 1) +#define ISC_TASKEVENT_TEST (ISC_EVENTCLASS_TASK + 1) +#define ISC_TASKEVENT_LASTEVENT (ISC_EVENTCLASS_TASK + 65535) + +/***** + ***** Tasks. + *****/ + +ISC_LANG_BEGINDECLS + +/*** + *** Types + ***/ + +typedef enum { + isc_taskmgrmode_normal = 0, + isc_taskmgrmode_privileged +} isc_taskmgrmode_t; + +/*% Task and task manager methods */ +typedef struct isc_taskmgrmethods { + void (*destroy)(isc_taskmgr_t **managerp); + void (*setmode)(isc_taskmgr_t *manager, + isc_taskmgrmode_t mode); + isc_taskmgrmode_t (*mode)(isc_taskmgr_t *manager); + isc_result_t (*taskcreate)(isc_taskmgr_t *manager, + unsigned int quantum, + isc_task_t **taskp); + void (*setexcltask)(isc_taskmgr_t *mgr, isc_task_t *task); + isc_result_t (*excltask)(isc_taskmgr_t *mgr, isc_task_t **taskp); +} isc_taskmgrmethods_t; + +typedef struct isc_taskmethods { + void (*attach)(isc_task_t *source, isc_task_t **targetp); + void (*detach)(isc_task_t **taskp); + void (*destroy)(isc_task_t **taskp); + void (*send)(isc_task_t *task, isc_event_t **eventp); + void (*sendanddetach)(isc_task_t **taskp, isc_event_t **eventp); + unsigned int (*unsend)(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag, isc_eventlist_t *events); + isc_result_t (*onshutdown)(isc_task_t *task, isc_taskaction_t action, + void *arg); + void (*shutdown)(isc_task_t *task); + void (*setname)(isc_task_t *task, const char *name, void *tag); + unsigned int (*purgeevents)(isc_task_t *task, void *sender, + isc_eventtype_t type, void *tag); + unsigned int (*purgerange)(isc_task_t *task, void *sender, + isc_eventtype_t first, isc_eventtype_t last, + void *tag); + isc_result_t (*beginexclusive)(isc_task_t *task); + void (*endexclusive)(isc_task_t *task); + void (*setprivilege)(isc_task_t *task, bool priv); + bool (*privilege)(isc_task_t *task); +} isc_taskmethods_t; + +/*% + * This structure is actually just the common prefix of a task manager + * object implementation's version of an isc_taskmgr_t. + * \brief + * Direct use of this structure by clients is forbidden. task implementations + * may change the structure. 'magic' must be ISCAPI_TASKMGR_MAGIC for any + * of the isc_task_ routines to work. task implementations must maintain + * all task invariants. + */ +struct isc_taskmgr { + unsigned int impmagic; + unsigned int magic; + isc_taskmgrmethods_t *methods; +}; + +#define ISCAPI_TASKMGR_MAGIC ISC_MAGIC('A','t','m','g') +#define ISCAPI_TASKMGR_VALID(m) ((m) != NULL && \ + (m)->magic == ISCAPI_TASKMGR_MAGIC) + +/*% + * This is the common prefix of a task object. The same note as + * that for the taskmgr structure applies. + */ +struct isc_task { + unsigned int impmagic; + unsigned int magic; + isc_taskmethods_t *methods; +}; + +#define ISCAPI_TASK_MAGIC ISC_MAGIC('A','t','s','t') +#define ISCAPI_TASK_VALID(s) ((s) != NULL && \ + (s)->magic == ISCAPI_TASK_MAGIC) + +isc_result_t +isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, + isc_task_t **taskp); +/*%< + * Create a task. + * + * Notes: + * + *\li If 'quantum' is non-zero, then only that many events can be dispatched + * before the task must yield to other tasks waiting to execute. If + * quantum is zero, then the default quantum of the task manager will + * be used. + * + *\li The 'quantum' option may be removed from isc_task_create() in the + * future. If this happens, isc_task_getquantum() and + * isc_task_setquantum() will be provided. + * + * Requires: + * + *\li 'manager' is a valid task manager. + * + *\li taskp != NULL && *taskp == NULL + * + * Ensures: + * + *\li On success, '*taskp' is bound to the new task. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_UNEXPECTED + *\li #ISC_R_SHUTTINGDOWN + */ + +void +isc_task_attach(isc_task_t *source, isc_task_t **targetp); +/*%< + * Attach *targetp to source. + * + * Requires: + * + *\li 'source' is a valid task. + * + *\li 'targetp' points to a NULL isc_task_t *. + * + * Ensures: + * + *\li *targetp is attached to source. + */ + +void +isc_task_detach(isc_task_t **taskp); +/*%< + * Detach *taskp from its task. + * + * Requires: + * + *\li '*taskp' is a valid task. + * + * Ensures: + * + *\li *taskp is NULL. + * + *\li If '*taskp' is the last reference to the task, the task is idle (has + * an empty event queue), and has not been shutdown, the task will be + * shutdown. + * + *\li If '*taskp' is the last reference to the task and + * the task has been shutdown, + * all resources used by the task will be freed. + */ + +void +isc_task_send(isc_task_t *task, isc_event_t **eventp); +/*%< + * Send '*event' to 'task'. + * + * Requires: + * + *\li 'task' is a valid task. + *\li eventp != NULL && *eventp != NULL. + * + * Ensures: + * + *\li *eventp == NULL. + */ + +void +isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp); +/*%< + * Send '*event' to '*taskp' and then detach '*taskp' from its + * task. + * + * Requires: + * + *\li '*taskp' is a valid task. + *\li eventp != NULL && *eventp != NULL. + * + * Ensures: + * + *\li *eventp == NULL. + * + *\li *taskp == NULL. + * + *\li If '*taskp' is the last reference to the task, the task is + * idle (has an empty event queue), and has not been shutdown, + * the task will be shutdown. + * + *\li If '*taskp' is the last reference to the task and + * the task has been shutdown, + * all resources used by the task will be freed. + */ + + +unsigned int +isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag); +/*%< + * Purge events from a task's event queue. + * + * Requires: + * + *\li 'task' is a valid task. + * + *\li last >= first + * + * Ensures: + * + *\li Events in the event queue of 'task' whose sender is 'sender', whose + * type is >= first and <= last, and whose tag is 'tag' will be purged, + * unless they are marked as unpurgable. + * + *\li A sender of NULL will match any sender. A NULL tag matches any + * tag. + * + * Returns: + * + *\li The number of events purged. + */ + +unsigned int +isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag); +/*%< + * Purge events from a task's event queue. + * + * Notes: + * + *\li This function is equivalent to + * + *\code + * isc_task_purgerange(task, sender, type, type, tag); + *\endcode + * + * Requires: + * + *\li 'task' is a valid task. + * + * Ensures: + * + *\li Events in the event queue of 'task' whose sender is 'sender', whose + * type is 'type', and whose tag is 'tag' will be purged, unless they + * are marked as unpurgable. + * + *\li A sender of NULL will match any sender. A NULL tag matches any + * tag. + * + * Returns: + * + *\li The number of events purged. + */ + +bool +isc_task_purgeevent(isc_task_t *task, isc_event_t *event); +/*%< + * Purge 'event' from a task's event queue. + * + * XXXRTH: WARNING: This method may be removed before beta. + * + * Notes: + * + *\li If 'event' is on the task's event queue, it will be purged, + * unless it is marked as unpurgeable. 'event' does not have to be + * on the task's event queue; in fact, it can even be an invalid + * pointer. Purging only occurs if the event is actually on the task's + * event queue. + * + * \li Purging never changes the state of the task. + * + * Requires: + * + *\li 'task' is a valid task. + * + * Ensures: + * + *\li 'event' is not in the event queue for 'task'. + * + * Returns: + * + *\li #true The event was purged. + *\li #false The event was not in the event queue, + * or was marked unpurgeable. + */ + +unsigned int +isc_task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag, isc_eventlist_t *events); +/*%< + * Remove events from a task's event queue. + * + * Requires: + * + *\li 'task' is a valid task. + * + *\li last >= first. + * + *\li *events is a valid list. + * + * Ensures: + * + *\li Events in the event queue of 'task' whose sender is 'sender', whose + * type is >= first and <= last, and whose tag is 'tag' will be dequeued + * and appended to *events. + * + *\li A sender of NULL will match any sender. A NULL tag matches any + * tag. + * + * Returns: + * + *\li The number of events unsent. + */ + +unsigned int +isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag, isc_eventlist_t *events); +/*%< + * Remove events from a task's event queue. + * + * Notes: + * + *\li This function is equivalent to + * + *\code + * isc_task_unsendrange(task, sender, type, type, tag, events); + *\endcode + * + * Requires: + * + *\li 'task' is a valid task. + * + *\li *events is a valid list. + * + * Ensures: + * + *\li Events in the event queue of 'task' whose sender is 'sender', whose + * type is 'type', and whose tag is 'tag' will be dequeued and appended + * to *events. + * + * Returns: + * + *\li The number of events unsent. + */ + +isc_result_t +isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, + void *arg); +/*%< + * Send a shutdown event with action 'action' and argument 'arg' when + * 'task' is shutdown. + * + * Notes: + * + *\li Shutdown events are posted in LIFO order. + * + * Requires: + * + *\li 'task' is a valid task. + * + *\li 'action' is a valid task action. + * + * Ensures: + * + *\li When the task is shutdown, shutdown events requested with + * isc_task_onshutdown() will be appended to the task's event queue. + * + + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_SHUTTINGDOWN Task is shutting down. + */ + +void +isc_task_shutdown(isc_task_t *task); +/*%< + * Shutdown 'task'. + * + * Notes: + * + *\li Shutting down a task causes any shutdown events requested with + * isc_task_onshutdown() to be posted (in LIFO order). The task + * moves into a "shutting down" mode which prevents further calls + * to isc_task_onshutdown(). + * + *\li Trying to shutdown a task that has already been shutdown has no + * effect. + * + * Requires: + * + *\li 'task' is a valid task. + * + * Ensures: + * + *\li Any shutdown events requested with isc_task_onshutdown() have been + * posted (in LIFO order). + */ + +void +isc_task_destroy(isc_task_t **taskp); +/*%< + * Destroy '*taskp'. + * + * Notes: + * + *\li This call is equivalent to: + * + *\code + * isc_task_shutdown(*taskp); + * isc_task_detach(taskp); + *\endcode + * + * Requires: + * + * '*taskp' is a valid task. + * + * Ensures: + * + *\li Any shutdown events requested with isc_task_onshutdown() have been + * posted (in LIFO order). + * + *\li *taskp == NULL + * + *\li If '*taskp' is the last reference to the task, + * all resources used by the task will be freed. + */ + +void +isc_task_setname(isc_task_t *task, const char *name, void *tag); +/*%< + * Name 'task'. + * + * Notes: + * + *\li Only the first 15 characters of 'name' will be copied. + * + *\li Naming a task is currently only useful for debugging purposes. + * + * Requires: + * + *\li 'task' is a valid task. + */ + +const char * +isc_task_getname(isc_task_t *task); +/*%< + * Get the name of 'task', as previously set using isc_task_setname(). + * + * Notes: + *\li This function is for debugging purposes only. + * + * Requires: + *\li 'task' is a valid task. + * + * Returns: + *\li A non-NULL pointer to a null-terminated string. + * If the task has not been named, the string is + * empty. + * + */ + +void * +isc_task_gettag(isc_task_t *task); +/*%< + * Get the tag value for 'task', as previously set using isc_task_settag(). + * + * Notes: + *\li This function is for debugging purposes only. + * + * Requires: + *\li 'task' is a valid task. + */ + +isc_result_t +isc_task_beginexclusive(isc_task_t *task); +/*%< + * Request exclusive access for 'task', which must be the calling + * task. Waits for any other concurrently executing tasks to finish their + * current event, and prevents any new events from executing in any of the + * tasks sharing a task manager with 'task'. + * + * The exclusive access must be relinquished by calling + * isc_task_endexclusive() before returning from the current event handler. + * + * Requires: + *\li 'task' is the calling task. + * + * Returns: + *\li #ISC_R_SUCCESS The current task now has exclusive access. + *\li #ISC_R_LOCKBUSY Another task has already requested exclusive + * access. + */ + +void +isc_task_endexclusive(isc_task_t *task); +/*%< + * Relinquish the exclusive access obtained by isc_task_beginexclusive(), + * allowing other tasks to execute. + * + * Requires: + *\li 'task' is the calling task, and has obtained + * exclusive access by calling isc_task_spl(). + */ + +void +isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t); +void +isc_task_getcurrenttimex(isc_task_t *task, isc_time_t *t); +/*%< + * Provide the most recent timestamp on the task. The timestamp is considered + * as the "current time". + * + * isc_task_getcurrentime() returns the time in one-second granularity; + * isc_task_getcurrentimex() returns it in nanosecond granularity. + * + * Requires: + *\li 'task' is a valid task. + *\li 't' is a valid non NULL pointer. + * + * Ensures: + *\li '*t' has the "current time". + */ + +bool +isc_task_exiting(isc_task_t *t); +/*%< + * Returns true if the task is in the process of shutting down, + * false otherwise. + * + * Requires: + *\li 'task' is a valid task. + */ + +void +isc_task_setprivilege(isc_task_t *task, bool priv); +/*%< + * Set or unset the task's "privileged" flag depending on the value of + * 'priv'. + * + * Under normal circumstances this flag has no effect on the task behavior, + * but when the task manager has been set to privileged execution mode via + * isc_taskmgr_setmode(), only tasks with the flag set will be executed, + * and all other tasks will wait until they're done. Once all privileged + * tasks have finished executing, the task manager will automatically + * return to normal execution mode and nonprivileged task can resume. + * + * Requires: + *\li 'task' is a valid task. + */ + +bool +isc_task_privilege(isc_task_t *task); +/*%< + * Returns the current value of the task's privilege flag. + * + * Requires: + *\li 'task' is a valid task. + */ + +/***** + ***** Task Manager. + *****/ + +isc_result_t +isc_taskmgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, + unsigned int workers, unsigned int default_quantum, + isc_taskmgr_t **managerp); +isc_result_t +isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, + unsigned int default_quantum, isc_taskmgr_t **managerp); +/*%< + * Create a new task manager. isc_taskmgr_createinctx() also associates + * the new manager with the specified application context. + * + * Notes: + * + *\li 'workers' in the number of worker threads to create. In general, + * the value should be close to the number of processors in the system. + * The 'workers' value is advisory only. An attempt will be made to + * create 'workers' threads, but if at least one thread creation + * succeeds, isc_taskmgr_create() may return ISC_R_SUCCESS. + * + *\li If 'default_quantum' is non-zero, then it will be used as the default + * quantum value when tasks are created. If zero, then an implementation + * defined default quantum will be used. + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li workers > 0 + * + *\li managerp != NULL && *managerp == NULL + * + *\li 'actx' is a valid application context (for createinctx()). + * + * Ensures: + * + *\li On success, '*managerp' will be attached to the newly created task + * manager. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_NOTHREADS No threads could be created. + *\li #ISC_R_UNEXPECTED An unexpected error occurred. + *\li #ISC_R_SHUTTINGDOWN The non-threaded, shared, task + * manager shutting down. + */ + +void +isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode); + +isc_taskmgrmode_t +isc_taskmgr_mode(isc_taskmgr_t *manager); +/*%< + * Set/get the current operating mode of the task manager. Valid modes are: + * + *\li isc_taskmgrmode_normal + *\li isc_taskmgrmode_privileged + * + * In privileged execution mode, only tasks that have had the "privilege" + * flag set via isc_task_setprivilege() can be executed. When all such + * tasks are complete, the manager automatically returns to normal mode + * and proceeds with running non-privileged ready tasks. This means it is + * necessary to have at least one privileged task waiting on the ready + * queue *before* setting the manager into privileged execution mode, + * which in turn means the task which calls this function should be in + * task-exclusive mode when it does so. + * + * Requires: + * + *\li 'manager' is a valid task manager. + */ + +void +isc_taskmgr_destroy(isc_taskmgr_t **managerp); +/*%< + * Destroy '*managerp'. + * + * Notes: + * + *\li Calling isc_taskmgr_destroy() will shutdown all tasks managed by + * *managerp that haven't already been shutdown. The call will block + * until all tasks have entered the done state. + * + *\li isc_taskmgr_destroy() must not be called by a task event action, + * because it would block forever waiting for the event action to + * complete. An event action that wants to cause task manager shutdown + * should request some non-event action thread of execution to do the + * shutdown, e.g. by signaling a condition variable or using + * isc_app_shutdown(). + * + *\li Task manager references are not reference counted, so the caller + * must ensure that no attempt will be made to use the manager after + * isc_taskmgr_destroy() returns. + * + * Requires: + * + *\li '*managerp' is a valid task manager. + * + *\li isc_taskmgr_destroy() has not be called previously on '*managerp'. + * + * Ensures: + * + *\li All resources used by the task manager, and any tasks it managed, + * have been freed. + */ + +void +isc_taskmgr_setexcltask(isc_taskmgr_t *mgr, isc_task_t *task); +/*%< + * Set a task which will be used for all task-exclusive operations. + * + * Requires: + *\li 'manager' is a valid task manager. + * + *\li 'task' is a valid task. + */ + +isc_result_t +isc_taskmgr_excltask(isc_taskmgr_t *mgr, isc_task_t **taskp); +/*%< + * Attach '*taskp' to the task set by isc_taskmgr_getexcltask(). + * This task should be used whenever running in task-exclusive mode, + * so as to prevent deadlock between two exclusive tasks. + * + * Requires: + *\li 'manager' is a valid task manager. + + *\li taskp != NULL && *taskp == NULL + */ + + +#ifdef HAVE_LIBXML2 +int +isc_taskmgr_renderxml(isc_taskmgr_t *mgr, xmlTextWriterPtr writer); +#endif + +#ifdef HAVE_JSON +isc_result_t +isc_taskmgr_renderjson(isc_taskmgr_t *mgr, json_object *tasksobj); +#endif + +/*%< + * See isc_taskmgr_create() above. + */ +typedef isc_result_t +(*isc_taskmgrcreatefunc_t)(isc_mem_t *mctx, unsigned int workers, + unsigned int default_quantum, + isc_taskmgr_t **managerp); + +isc_result_t +isc_task_register(isc_taskmgrcreatefunc_t createfunc); +/*%< + * Register a new task management implementation and add it to the list of + * supported implementations. This function must be called when a different + * event library is used than the one contained in the ISC library. + */ + +isc_result_t +isc__task_register(void); +/*%< + * A short cut function that specifies the task management module in the ISC + * library for isc_task_register(). An application that uses the ISC library + * usually do not have to care about this function: it would call + * isc_lib_register(), which internally calls this function. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_TASK_H */ diff --git a/lib/isc/include/isc/taskpool.h b/lib/isc/include/isc/taskpool.h new file mode 100644 index 0000000..32ca594 --- /dev/null +++ b/lib/isc/include/isc/taskpool.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_TASKPOOL_H +#define ISC_TASKPOOL_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/taskpool.h + * \brief A task pool is a mechanism for sharing a small number of tasks + * among a large number of objects such that each object is + * assigned a unique task, but each task may be shared by several + * objects. + * + * Task pools are used to let objects that can exist in large + * numbers (e.g., zones) use tasks for synchronization without + * the memory overhead and unfair scheduling competition that + * could result from creating a separate task for each object. + */ + + +/*** + *** Imports. + ***/ + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/task.h> + +ISC_LANG_BEGINDECLS + +/***** + ***** Types. + *****/ + +typedef struct isc_taskpool isc_taskpool_t; + +/***** + ***** Functions. + *****/ + +isc_result_t +isc_taskpool_create(isc_taskmgr_t *tmgr, isc_mem_t *mctx, + unsigned int ntasks, unsigned int quantum, + isc_taskpool_t **poolp); +/*%< + * Create a task pool of "ntasks" tasks, each with quantum + * "quantum". + * + * Requires: + * + *\li 'tmgr' is a valid task manager. + * + *\li 'mctx' is a valid memory context. + * + *\li poolp != NULL && *poolp == NULL + * + * Ensures: + * + *\li On success, '*taskp' points to the new task pool. + * + * Returns: + * + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li #ISC_R_UNEXPECTED + */ + +void +isc_taskpool_gettask(isc_taskpool_t *pool, isc_task_t **targetp); +/*%< + * Attach to a task from the pool. Currently the next task is chosen + * from the pool at random. (This may be changed in the future to + * something that guaratees balance.) + */ + +int +isc_taskpool_size(isc_taskpool_t *pool); +/*%< + * Returns the number of tasks in the task pool 'pool'. + */ + +isc_result_t +isc_taskpool_expand(isc_taskpool_t **sourcep, unsigned int size, + isc_taskpool_t **targetp); + +/*%< + * If 'size' is larger than the number of tasks in the pool pointed to by + * 'sourcep', then a new taskpool of size 'size' is allocated, the existing + * tasks from are moved into it, additional tasks are created to bring the + * total number up to 'size', and the resulting pool is attached to + * 'targetp'. + * + * If 'size' is less than or equal to the tasks in pool 'source', then + * 'sourcep' is attached to 'targetp' without any other action being taken. + * + * In either case, 'sourcep' is detached. + * + * Requires: + * + * \li 'sourcep' is not NULL and '*source' is not NULL + * \li 'targetp' is not NULL and '*source' is NULL + * + * Ensures: + * + * \li On success, '*targetp' points to a valid task pool. + * \li On success, '*sourcep' points to NULL. + * + * Returns: + * + * \li #ISC_R_SUCCESS + * \li #ISC_R_NOMEMORY + */ + +void +isc_taskpool_destroy(isc_taskpool_t **poolp); +/*%< + * Destroy a task pool. The tasks in the pool are detached but not + * shut down. + * + * Requires: + * \li '*poolp' is a valid task pool. + */ + +void +isc_taskpool_setprivilege(isc_taskpool_t *pool, bool priv); +/*%< + * Set the privilege flag on all tasks in 'pool' to 'priv'. If 'priv' is + * true, then when the task manager is set into privileged mode, only + * tasks wihin this pool will be able to execute. (Note: It is important + * to turn the pool tasks' privilege back off before the last task finishes + * executing.) + * + * Requires: + * \li 'pool' is a valid task pool. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_TASKPOOL_H */ diff --git a/lib/isc/include/isc/timer.h b/lib/isc/include/isc/timer.h new file mode 100644 index 0000000..40aa5a6 --- /dev/null +++ b/lib/isc/include/isc/timer.h @@ -0,0 +1,426 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_TIMER_H +#define ISC_TIMER_H 1 + +/***** + ***** Module Info + *****/ + +/*! \file isc/timer.h + * \brief Provides timers which are event sources in the task system. + * + * Three types of timers are supported: + * + *\li 'ticker' timers generate a periodic tick event. + * + *\li 'once' timers generate an idle timeout event if they are idle for too + * long, and generate a life timeout event if their lifetime expires. + * They are used to implement both (possibly expiring) idle timers and + * 'one-shot' timers. + * + *\li 'limited' timers generate a periodic tick event until they reach + * their lifetime when they generate a life timeout event. + * + *\li 'inactive' timers generate no events. + * + * Timers can change type. It is typical to create a timer as + * an 'inactive' timer and then change it into a 'ticker' or + * 'once' timer. + * + *\li MP: + * The module ensures appropriate synchronization of data structures it + * creates and manipulates. + * Clients of this module must not be holding a timer's task's lock when + * making a call that affects that timer. Failure to follow this rule + * can result in deadlock. + * The caller must ensure that isc_timermgr_destroy() is called only + * once for a given manager. + * + * \li Reliability: + * No anticipated impact. + * + * \li Resources: + * TBS + * + * \li Security: + * No anticipated impact. + * + * \li Standards: + * None. + */ + + +/*** + *** Imports + ***/ + +#include <stdbool.h> + +#include <isc/types.h> +#include <isc/event.h> +#include <isc/eventclass.h> +#include <isc/lang.h> +#include <isc/time.h> + +ISC_LANG_BEGINDECLS + +/*** + *** Types + ***/ + +/*% Timer Type */ +typedef enum { + isc_timertype_undefined = -1, /*%< Undefined */ + isc_timertype_ticker = 0, /*%< Ticker */ + isc_timertype_once = 1, /*%< Once */ + isc_timertype_limited = 2, /*%< Limited */ + isc_timertype_inactive = 3 /*%< Inactive */ +} isc_timertype_t; + +typedef struct isc_timerevent { + struct isc_event common; + isc_time_t due; +} isc_timerevent_t; + +#define ISC_TIMEREVENT_FIRSTEVENT (ISC_EVENTCLASS_TIMER + 0) +#define ISC_TIMEREVENT_TICK (ISC_EVENTCLASS_TIMER + 1) +#define ISC_TIMEREVENT_IDLE (ISC_EVENTCLASS_TIMER + 2) +#define ISC_TIMEREVENT_LIFE (ISC_EVENTCLASS_TIMER + 3) +#define ISC_TIMEREVENT_LASTEVENT (ISC_EVENTCLASS_TIMER + 65535) + +/*% Timer and timer manager methods */ +typedef struct { + void (*destroy)(isc_timermgr_t **managerp); + isc_result_t (*timercreate)(isc_timermgr_t *manager, + isc_timertype_t type, + const isc_time_t *expires, + const isc_interval_t *interval, + isc_task_t *task, + isc_taskaction_t action, + void *arg, + isc_timer_t **timerp); +} isc_timermgrmethods_t; + +typedef struct { + void (*attach)(isc_timer_t *timer, isc_timer_t **timerp); + void (*detach)(isc_timer_t **timerp); + isc_result_t (*reset)(isc_timer_t *timer, isc_timertype_t type, + const isc_time_t *expires, + const isc_interval_t *interval, + bool purge); + isc_result_t (*touch)(isc_timer_t *timer); +} isc_timermethods_t; + +/*% + * This structure is actually just the common prefix of a timer manager + * object implementation's version of an isc_timermgr_t. + * \brief + * Direct use of this structure by clients is forbidden. timer implementations + * may change the structure. 'magic' must be ISCAPI_TIMERMGR_MAGIC for any + * of the isc_timer_ routines to work. timer implementations must maintain + * all timer invariants. + */ +struct isc_timermgr { + unsigned int impmagic; + unsigned int magic; + isc_timermgrmethods_t *methods; +}; + +#define ISCAPI_TIMERMGR_MAGIC ISC_MAGIC('A','t','m','g') +#define ISCAPI_TIMERMGR_VALID(m) ((m) != NULL && \ + (m)->magic == ISCAPI_TIMERMGR_MAGIC) + +/*% + * This is the common prefix of a timer object. The same note as + * that for the timermgr structure applies. + */ +struct isc_timer { + unsigned int impmagic; + unsigned int magic; + isc_timermethods_t *methods; +}; + +#define ISCAPI_TIMER_MAGIC ISC_MAGIC('A','t','m','r') +#define ISCAPI_TIMER_VALID(s) ((s) != NULL && \ + (s)->magic == ISCAPI_TIMER_MAGIC) + +/*** + *** Timer and Timer Manager Functions + *** + *** Note: all Ensures conditions apply only if the result is success for + *** those functions which return an isc_result_t. + ***/ + +isc_result_t +isc_timer_create(isc_timermgr_t *manager, + isc_timertype_t type, + const isc_time_t *expires, + const isc_interval_t *interval, + isc_task_t *task, + isc_taskaction_t action, + void *arg, + isc_timer_t **timerp); +/*%< + * Create a new 'type' timer managed by 'manager'. The timers parameters + * are specified by 'expires' and 'interval'. Events will be posted to + * 'task' and when dispatched 'action' will be called with 'arg' as the + * arg value. The new timer is returned in 'timerp'. + * + * Notes: + * + *\li For ticker timers, the timer will generate a 'tick' event every + * 'interval' seconds. The value of 'expires' is ignored. + * + *\li For once timers, 'expires' specifies the time when a life timeout + * event should be generated. If 'expires' is 0 (the epoch), then no life + * timeout will be generated. 'interval' specifies how long the timer + * can be idle before it generates an idle timeout. If 0, then no + * idle timeout will be generated. + * + *\li If 'expires' is NULL, the epoch will be used. + * + * If 'interval' is NULL, the zero interval will be used. + * + * Requires: + * + *\li 'manager' is a valid manager + * + *\li 'task' is a valid task + * + *\li 'action' is a valid action + * + *\li 'expires' points to a valid time, or is NULL. + * + *\li 'interval' points to a valid interval, or is NULL. + * + *\li type == isc_timertype_inactive || + * ('expires' and 'interval' are not both 0) + * + *\li 'timerp' is a valid pointer, and *timerp == NULL + * + * Ensures: + * + *\li '*timerp' is attached to the newly created timer + * + *\li The timer is attached to the task + * + *\li An idle timeout will not be generated until at least Now + the + * timer's interval if 'timer' is a once timer with a non-zero + * interval. + * + * Returns: + * + *\li Success + *\li No memory + *\li Unexpected error + */ + +isc_result_t +isc_timer_reset(isc_timer_t *timer, + isc_timertype_t type, + const isc_time_t *expires, + const isc_interval_t *interval, + bool purge); +/*%< + * Change the timer's type, expires, and interval values to the given + * values. If 'purge' is TRUE, any pending events from this timer + * are purged from its task's event queue. + * + * Notes: + * + *\li If 'expires' is NULL, the epoch will be used. + * + *\li If 'interval' is NULL, the zero interval will be used. + * + * Requires: + * + *\li 'timer' is a valid timer + * + *\li The same requirements that isc_timer_create() imposes on 'type', + * 'expires' and 'interval' apply. + * + * Ensures: + * + *\li An idle timeout will not be generated until at least Now + the + * timer's interval if 'timer' is a once timer with a non-zero + * interval. + * + * Returns: + * + *\li Success + *\li No memory + *\li Unexpected error + */ + +isc_result_t +isc_timer_touch(isc_timer_t *timer); +/*%< + * Set the last-touched time of 'timer' to the current time. + * + * Requires: + * + *\li 'timer' is a valid once timer. + * + * Ensures: + * + *\li An idle timeout will not be generated until at least Now + the + * timer's interval if 'timer' is a once timer with a non-zero + * interval. + * + * Returns: + * + *\li Success + *\li Unexpected error + */ + +void +isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp); +/*%< + * Attach *timerp to timer. + * + * Requires: + * + *\li 'timer' is a valid timer. + * + *\li 'timerp' points to a NULL timer. + * + * Ensures: + * + *\li *timerp is attached to timer. + */ + +void +isc_timer_detach(isc_timer_t **timerp); +/*%< + * Detach *timerp from its timer. + * + * Requires: + * + *\li 'timerp' points to a valid timer. + * + * Ensures: + * + *\li *timerp is NULL. + * + *\li If '*timerp' is the last reference to the timer, + * then: + * + *\code + * The timer will be shutdown + * + * The timer will detach from its task + * + * All resources used by the timer have been freed + * + * Any events already posted by the timer will be purged. + * Therefore, if isc_timer_detach() is called in the context + * of the timer's task, it is guaranteed that no more + * timer event callbacks will run after the call. + *\endcode + */ + +isc_timertype_t +isc_timer_gettype(isc_timer_t *timer); +/*%< + * Return the timer type. + * + * Requires: + * + *\li 'timer' to be a valid timer. + */ + +isc_result_t +isc_timermgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, + isc_timermgr_t **managerp); + +isc_result_t +isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp); +/*%< + * Create a timer manager. isc_timermgr_createinctx() also associates + * the new manager with the specified application context. + * + * Notes: + * + *\li All memory will be allocated in memory context 'mctx'. + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li 'managerp' points to a NULL isc_timermgr_t. + * + *\li 'actx' is a valid application context (for createinctx()). + * + * Ensures: + * + *\li '*managerp' is a valid isc_timermgr_t. + * + * Returns: + * + *\li Success + *\li No memory + *\li Unexpected error + */ + +void +isc_timermgr_destroy(isc_timermgr_t **managerp); +/*%< + * Destroy a timer manager. + * + * Notes: + * + *\li This routine blocks until there are no timers left in the manager, + * so if the caller holds any timer references using the manager, it + * must detach them before calling isc_timermgr_destroy() or it will + * block forever. + * + * Requires: + * + *\li '*managerp' is a valid isc_timermgr_t. + * + * Ensures: + * + *\li *managerp == NULL + * + *\li All resources used by the manager have been freed. + */ + +void isc_timermgr_poke(isc_timermgr_t *m); + +/*%< + * See isc_timermgr_create() above. + */ +typedef isc_result_t +(*isc_timermgrcreatefunc_t)(isc_mem_t *mctx, isc_timermgr_t **managerp); + +isc_result_t +isc__timer_register(void); +/*%< + * Register a new timer management implementation and add it to the list of + * supported implementations. This function must be called when a different + * event library is used than the one contained in the ISC library. + */ + +isc_result_t +isc_timer_register(isc_timermgrcreatefunc_t createfunc); +/*%< + * A short cut function that specifies the timer management module in the ISC + * library for isc_timer_register(). An application that uses the ISC library + * usually do not have to care about this function: it would call + * isc_lib_register(), which internally calls this function. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_TIMER_H */ diff --git a/lib/isc/include/isc/tm.h b/lib/isc/include/isc/tm.h new file mode 100644 index 0000000..b6f520c --- /dev/null +++ b/lib/isc/include/isc/tm.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_TM_H +#define ISC_TM_H 1 + +/*! \file isc/tm.h + * Provides portable conversion routines for struct tm. + */ +#include <time.h> + +#include <isc/lang.h> +#include <isc/types.h> + + +ISC_LANG_BEGINDECLS + +time_t +isc_tm_timegm(struct tm *tm); +/* + * Convert a tm structure to time_t, using UTC rather than the local + * time zone. + */ + +char * +isc_tm_strptime(const char *buf, const char *fmt, struct tm *tm); +/* + * Parse a formatted date string into struct tm. + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_TIMER_H */ diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h new file mode 100644 index 0000000..42ff7e0 --- /dev/null +++ b/lib/isc/include/isc/types.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id$ */ + +#ifndef ISC_TYPES_H +#define ISC_TYPES_H 1 + +#include <stdbool.h> + +#include <isc/bind9.h> + +/*! \file isc/types.h + * \brief + * OS-specific types, from the OS-specific include directories. + */ +#include <inttypes.h> +#include <isc/offset.h> + +/* + * XXXDCL should bool be moved here, requiring an explicit include + */ +/* + * XXXDCL This is just for ISC_LIST and ISC_LINK, but gets all of the other + * list macros too. + */ +#include <isc/list.h> + +/* Core Types. Alphabetized by defined type. */ + +typedef struct isc_appctx isc_appctx_t; /*%< Application context */ +typedef struct isc_backtrace_symmap isc_backtrace_symmap_t; /*%< Symbol Table Entry */ +typedef struct isc_buffer isc_buffer_t; /*%< Buffer */ +typedef ISC_LIST(isc_buffer_t) isc_bufferlist_t; /*%< Buffer List */ +typedef struct isc_constregion isc_constregion_t; /*%< Const region */ +typedef struct isc_consttextregion isc_consttextregion_t; /*%< Const Text Region */ +typedef struct isc_counter isc_counter_t; /*%< Counter */ +typedef int16_t isc_dscp_t; /*%< Diffserv code point */ +typedef struct isc_entropy isc_entropy_t; /*%< Entropy */ +typedef struct isc_entropysource isc_entropysource_t; /*%< Entropy Source */ +typedef struct isc_event isc_event_t; /*%< Event */ +typedef ISC_LIST(isc_event_t) isc_eventlist_t; /*%< Event List */ +typedef unsigned int isc_eventtype_t; /*%< Event Type */ +typedef uint32_t isc_fsaccess_t; /*%< FS Access */ +typedef struct isc_hash isc_hash_t; /*%< Hash */ +typedef struct isc_httpd isc_httpd_t; /*%< HTTP client */ +typedef void (isc_httpdfree_t)(isc_buffer_t *, void *); /*%< HTTP free function */ +typedef struct isc_httpdmgr isc_httpdmgr_t; /*%< HTTP manager */ +typedef struct isc_httpdurl isc_httpdurl_t; /*%< HTTP URL */ +typedef void (isc_httpdondestroy_t)(void *); /*%< Callback on destroying httpd */ +typedef struct isc_interface isc_interface_t; /*%< Interface */ +typedef struct isc_interfaceiter isc_interfaceiter_t; /*%< Interface Iterator */ +typedef struct isc_interval isc_interval_t; /*%< Interval */ +typedef struct isc_lex isc_lex_t; /*%< Lex */ +typedef struct isc_log isc_log_t; /*%< Log */ +typedef struct isc_logcategory isc_logcategory_t; /*%< Log Category */ +typedef struct isc_logconfig isc_logconfig_t; /*%< Log Configuration */ +typedef struct isc_logmodule isc_logmodule_t; /*%< Log Module */ +typedef struct isc_mem isc_mem_t; /*%< Memory */ +typedef struct isc_mempool isc_mempool_t; /*%< Memory Pool */ +typedef struct isc_msgcat isc_msgcat_t; /*%< Message Catalog */ +typedef struct isc_ondestroy isc_ondestroy_t; /*%< On Destroy */ +typedef struct isc_netaddr isc_netaddr_t; /*%< Net Address */ +typedef struct isc_portset isc_portset_t; /*%< Port Set */ +typedef struct isc_quota isc_quota_t; /*%< Quota */ +typedef struct isc_random isc_random_t; /*%< Random */ +typedef struct isc_ratelimiter isc_ratelimiter_t; /*%< Rate Limiter */ +typedef struct isc_region isc_region_t; /*%< Region */ +typedef uint64_t isc_resourcevalue_t; /*%< Resource Value */ +typedef unsigned int isc_result_t; /*%< Result */ +typedef struct isc_rwlock isc_rwlock_t; /*%< Read Write Lock */ +typedef struct isc_sockaddr isc_sockaddr_t; /*%< Socket Address */ +typedef ISC_LIST(isc_sockaddr_t) isc_sockaddrlist_t; /*%< Socket Address List */ +typedef struct isc_socket isc_socket_t; /*%< Socket */ +typedef struct isc_socketevent isc_socketevent_t; /*%< Socket Event */ +typedef struct isc_socketmgr isc_socketmgr_t; /*%< Socket Manager */ +typedef struct isc_stats isc_stats_t; /*%< Statistics */ +typedef int isc_statscounter_t; /*%< Statistics Counter */ +typedef struct isc_symtab isc_symtab_t; /*%< Symbol Table */ +typedef struct isc_task isc_task_t; /*%< Task */ +typedef ISC_LIST(isc_task_t) isc_tasklist_t; /*%< Task List */ +typedef struct isc_taskmgr isc_taskmgr_t; /*%< Task Manager */ +typedef struct isc_textregion isc_textregion_t; /*%< Text Region */ +typedef struct isc_time isc_time_t; /*%< Time */ +typedef struct isc_timer isc_timer_t; /*%< Timer */ +typedef struct isc_timermgr isc_timermgr_t; /*%< Timer Manager */ + +typedef void (*isc_taskaction_t)(isc_task_t *, isc_event_t *); +typedef int (*isc_sockfdwatch_t)(isc_task_t *, isc_socket_t *, void *, int); + +/* The following cannot be listed alphabetically due to forward reference */ +typedef isc_result_t (isc_httpdaction_t)(const char *url, + isc_httpdurl_t *urlinfo, + const char *querystring, + const char *headers, + void *arg, + unsigned int *retcode, + const char **retmsg, + const char **mimetype, + isc_buffer_t *body, + isc_httpdfree_t **freecb, + void **freecb_args); +typedef bool (isc_httpdclientok_t)(const isc_sockaddr_t *, void *); + +/*% Resource */ +typedef enum { + isc_resource_coresize = 1, + isc_resource_cputime, + isc_resource_datasize, + isc_resource_filesize, + isc_resource_lockedmemory, + isc_resource_openfiles, + isc_resource_processes, + isc_resource_residentsize, + isc_resource_stacksize +} isc_resource_t; + +/*% Statistics formats (text file or XML) */ +typedef enum { + isc_statsformat_file, + isc_statsformat_xml, + isc_statsformat_json +} isc_statsformat_t; + +#endif /* ISC_TYPES_H */ diff --git a/lib/isc/include/isc/util.h b/lib/isc/include/isc/util.h new file mode 100644 index 0000000..75a63c1 --- /dev/null +++ b/lib/isc/include/isc/util.h @@ -0,0 +1,247 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISC_UTIL_H +#define ISC_UTIL_H 1 + +/*! \file isc/util.h + * NOTE: + * + * This file is not to be included from any <isc/???.h> (or other) library + * files. + * + * \brief + * Including this file puts several macros in your name space that are + * not protected (as all the other ISC functions/macros do) by prepending + * ISC_ or isc_ to the name. + */ + +/*** + *** General Macros. + ***/ + +/*% + * Use this to hide unused function arguments. + * \code + * int + * foo(char *bar) + * { + * UNUSED(bar); + * } + * \endcode + */ +#define UNUSED(x) (void)(x) + +/*% + * The opposite: silent warnings about stored values which are never read. + */ +#define POST(x) (void)(x) + +#define ISC_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define ISC_MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define ISC_CLAMP(v, x, y) ((v) < (x) ? (x) : ((v) > (y) ? (y) : (v))) + +/*% + * Use this to remove the const qualifier of a variable to assign it to + * a non-const variable or pass it as a non-const function argument ... + * but only when you are sure it won't then be changed! + * This is necessary to sometimes shut up some compilers + * (as with gcc -Wcast-qual) when there is just no other good way to avoid the + * situation. + */ +#define DE_CONST(konst, var) \ + do { \ + union { const void *k; void *v; } _u; \ + _u.k = konst; \ + var = _u.v; \ + } while (0) + +/*% + * Use this in translation units that would otherwise be empty, to + * suppress compiler warnings. + */ +#define EMPTY_TRANSLATION_UNIT static void isc__empty(void) { isc__empty(); } + +/*% + * We use macros instead of calling the routines directly because + * the capital letters make the locking stand out. + * We RUNTIME_CHECK for success since in general there's no way + * for us to continue if they fail. + */ + +#ifdef ISC_UTIL_TRACEON +#define ISC_UTIL_TRACE(a) a +#include <stdio.h> /* Required for fprintf/stderr when tracing. */ +#include <isc/msgs.h> /* Required for isc_msgcat when tracing. */ +#else +#define ISC_UTIL_TRACE(a) +#endif + +#include <isc/result.h> /* Contractual promise. */ + +#define LOCK(lp) do { \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_LOCKING, "LOCKING"), \ + (lp), __FILE__, __LINE__)); \ + RUNTIME_CHECK(isc_mutex_lock((lp)) == ISC_R_SUCCESS); \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_LOCKED, "LOCKED"), \ + (lp), __FILE__, __LINE__)); \ + } while (0) +#define UNLOCK(lp) do { \ + RUNTIME_CHECK(isc_mutex_unlock((lp)) == ISC_R_SUCCESS); \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_UNLOCKED, "UNLOCKED"), \ + (lp), __FILE__, __LINE__)); \ + } while (0) +#define ISLOCKED(lp) (1) +#define DESTROYLOCK(lp) \ + RUNTIME_CHECK(isc_mutex_destroy((lp)) == ISC_R_SUCCESS) + + +#define BROADCAST(cvp) do { \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_BROADCAST, "BROADCAST"),\ + (cvp), __FILE__, __LINE__)); \ + RUNTIME_CHECK(isc_condition_broadcast((cvp)) == ISC_R_SUCCESS); \ + } while (0) +#define SIGNAL(cvp) do { \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_SIGNAL, "SIGNAL"), \ + (cvp), __FILE__, __LINE__)); \ + RUNTIME_CHECK(isc_condition_signal((cvp)) == ISC_R_SUCCESS); \ + } while (0) +#define WAIT(cvp, lp) do { \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p %s %p %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_UTILWAIT, "WAIT"), \ + (cvp), \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_LOCK, "LOCK"), \ + (lp), __FILE__, __LINE__)); \ + RUNTIME_CHECK(isc_condition_wait((cvp), (lp)) == ISC_R_SUCCESS); \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p %s %p %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_WAITED, "WAITED"), \ + (cvp), \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_LOCKED, "LOCKED"), \ + (lp), __FILE__, __LINE__)); \ + } while (0) + +/* + * isc_condition_waituntil can return ISC_R_TIMEDOUT, so we + * don't RUNTIME_CHECK the result. + * + * XXX Also, can't really debug this then... + */ + +#define WAITUNTIL(cvp, lp, tp) \ + isc_condition_waituntil((cvp), (lp), (tp)) + +#define RWLOCK(lp, t) do { \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p, %d %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_RWLOCK, "RWLOCK"), \ + (lp), (t), __FILE__, __LINE__)); \ + RUNTIME_CHECK(isc_rwlock_lock((lp), (t)) == ISC_R_SUCCESS); \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p, %d %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_RWLOCKED, "RWLOCKED"), \ + (lp), (t), __FILE__, __LINE__)); \ + } while (0) +#define RWUNLOCK(lp, t) do { \ + ISC_UTIL_TRACE(fprintf(stderr, "%s %p, %d %s %d\n", \ + isc_msgcat_get(isc_msgcat, ISC_MSGSET_UTIL, \ + ISC_MSG_RWUNLOCK, "RWUNLOCK"), \ + (lp), (t), __FILE__, __LINE__)); \ + RUNTIME_CHECK(isc_rwlock_unlock((lp), (t)) == ISC_R_SUCCESS); \ + } while (0) + +#define DESTROYMUTEXBLOCK(bp, n) \ + RUNTIME_CHECK(isc_mutexblock_destroy((bp), (n)) == ISC_R_SUCCESS) + +/* + * List Macros. + */ +#include <isc/list.h> /* Contractual promise. */ + +#define LIST(type) ISC_LIST(type) +#define INIT_LIST(type) ISC_LIST_INIT(type) +#define LINK(type) ISC_LINK(type) +#define INIT_LINK(elt, link) ISC_LINK_INIT(elt, link) +#define HEAD(list) ISC_LIST_HEAD(list) +#define TAIL(list) ISC_LIST_TAIL(list) +#define EMPTY(list) ISC_LIST_EMPTY(list) +#define PREV(elt, link) ISC_LIST_PREV(elt, link) +#define NEXT(elt, link) ISC_LIST_NEXT(elt, link) +#define APPEND(list, elt, link) ISC_LIST_APPEND(list, elt, link) +#define PREPEND(list, elt, link) ISC_LIST_PREPEND(list, elt, link) +#define UNLINK(list, elt, link) ISC_LIST_UNLINK(list, elt, link) +#define ENQUEUE(list, elt, link) ISC_LIST_APPEND(list, elt, link) +#define DEQUEUE(list, elt, link) ISC_LIST_UNLINK(list, elt, link) +#define INSERTBEFORE(li, b, e, ln) ISC_LIST_INSERTBEFORE(li, b, e, ln) +#define INSERTAFTER(li, a, e, ln) ISC_LIST_INSERTAFTER(li, a, e, ln) +#define APPENDLIST(list1, list2, link) ISC_LIST_APPENDLIST(list1, list2, link) + +/*% + * Performance + */ +#include <isc/likely.h> + +/* + * Assertions + */ +#include <isc/assertions.h> /* Contractual promise. */ + +/*% Require Assertion */ +#define REQUIRE(e) ISC_REQUIRE(e) +/*% Ensure Assertion */ +#define ENSURE(e) ISC_ENSURE(e) +/*% Insist Assertion */ +#define INSIST(e) ISC_INSIST(e) +/*% Invariant Assertion */ +#define INVARIANT(e) ISC_INVARIANT(e) + +/* + * Errors + */ +#include <isc/error.h> /* Contractual promise. */ + +/*% Unexpected Error */ +#define UNEXPECTED_ERROR isc_error_unexpected +/*% Fatal Error */ +#define FATAL_ERROR isc_error_fatal +/*% Runtime Check */ +#define RUNTIME_CHECK(cond) ISC_ERROR_RUNTIMECHECK(cond) + +/*% + * Time + */ +#define TIME_NOW(tp) RUNTIME_CHECK(isc_time_now((tp)) == ISC_R_SUCCESS) + +/*% + * Alignment + */ +#define ISC_ALIGN(x, a) (((x) + (a) - 1) & ~((typeof(x))(a)-1)) + +/*% + * Misc + */ +#include <isc/deprecated.h> + +#endif /* ISC_UTIL_H */ diff --git a/lib/isc/include/isc/version.h b/lib/isc/include/isc/version.h new file mode 100644 index 0000000..d371e0d --- /dev/null +++ b/lib/isc/include/isc/version.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file isc/version.h */ + +#include <isc/platform.h> + +LIBISC_EXTERNAL_DATA extern const char isc_version[]; + +LIBISC_EXTERNAL_DATA extern const unsigned int isc_libinterface; +LIBISC_EXTERNAL_DATA extern const unsigned int isc_librevision; +LIBISC_EXTERNAL_DATA extern const unsigned int isc_libage; diff --git a/lib/isc/include/isc/xml.h b/lib/isc/include/isc/xml.h new file mode 100644 index 0000000..a091d30 --- /dev/null +++ b/lib/isc/include/isc/xml.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_XML_H +#define ISC_XML_H 1 + +/* + * This file is here mostly to make it easy to add additional libxml header + * files as needed across all the users of this file. Rather than place + * these libxml includes in each file, one include makes it easy to handle + * the ifdef as well as adding the ability to add additional functions + * which may be useful. + */ + +#ifdef HAVE_LIBXML2 +#include <libxml/encoding.h> +#include <libxml/xmlwriter.h> +#endif + +#define ISC_XMLCHAR (const xmlChar *) + +#define ISC_XML_RENDERCONFIG 0x00000001 /* render config data */ +#define ISC_XML_RENDERSTATS 0x00000002 /* render stats */ +#define ISC_XML_RENDERALL 0x000000ff /* render everything */ + +#endif /* ISC_XML_H */ diff --git a/lib/isc/include/pk11/Makefile.in b/lib/isc/include/pk11/Makefile.in new file mode 100644 index 0000000..395c924 --- /dev/null +++ b/lib/isc/include/pk11/Makefile.in @@ -0,0 +1,38 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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@ + +# +# Only list headers that are to be installed and are not +# machine generated. The latter are handled specially in the +# install target below. +# +HEADERS = constants.h internal.h pk11.h result.h site.h +SUBDIRS = +TARGETS = + +@BIND9_MAKE_RULES@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/pk11 + +install:: installdirs + for i in ${HEADERS}; do \ + ${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/pk11 || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/pk11/$$i || exit 1; \ + done diff --git a/lib/isc/include/pk11/README.site b/lib/isc/include/pk11/README.site new file mode 100644 index 0000000..6c49891 --- /dev/null +++ b/lib/isc/include/pk11/README.site @@ -0,0 +1,72 @@ +Copyright (C) Internet Systems Consortium, Inc. ("ISC") + +See COPYRIGHT in the source root or http://isc.org/copyright.html for terms. + +How to use site.h for the PKCS#11 provider of your HSM +------------------------------------------------------ + +First run "pkcs11-tokens" (in bin/pkcs11). This tool is built when BIND9 +is configured with the --with-pcks11 flag. It prints the addresses of +selected tokens per algorithm: + + - random number generation + - RSA (sign/verify) + - DSA (sign/verify) + - DH (secret derivation) + - digest (hash) + - EC (ECDSA, sign/verify) + - GOST (Russian hash and sign/verify) + - AES (encrypt/decrypt) + +...and a summary of PKCS#11 tokens that have been found. + +Current well-known HSMs are predefined in site.h according to HSM "flavors": + + - Thales nCipher (default) + - OpenDNSSEC SoftHSMv2 + +...and with experimental status: + + - OpenDNSSEC SoftHSMv1 with SHA224 support added + - Cryptech + - AEP Keyper + +If BIND9 is configured with native PKCS#11 support (--enable-native-pkcs11), +then pkcs11-tokens will raise an error when a mandatory algorithm is not +supported. (The usual error is 0x70, or CKR_MECHANISM_INVALID; 0x0 +indicates that a required flag is not available.) The following steps +may be taken, depending on which algorithms indicate failures: + + - rand or RSA: nothing can be done; native PKCS#11 is not supported + in BIND9 with this HSM. + + - DSA or DH: run pkcs11-tokens with the -v (verbose) flag. If the + parameter generation mechanism is not supported you can make the token + selection to ignore the error. Note DSA and DH are not critical + algorithms; you can use BIND9 in production without them. + + - digest: run pkcs11-tokens with the -v (verbose) flag. If the problem is + with HMAC mechanisms, use the corresponding REPLACE flags in site.h. + If the problem is with MD5, use the corresponding DISABLE flag in + site.h. If the problem is with SHA224, contact the implementor of the + PKCS#11 provider and ask to have this hash algorithm implemented. For + any other problem, nothing can be done; native PKCS#11 is not supported + with this HSM. + + - EC: you may wish to configure BIND9 without ECDSA support by adding + --without-ecdsa to the "configure" arguments. + + - GOST: you SHOULD configure BIND9 without GOST support by adding + --without-gost to the "configure" arguments. + + - AES: you MUST reconfigure bind9 without AES support by adding + --without-aes to configure arguments. + +You can disable some algorithms (e.g. DSA, DH and MD5) using the +"disable-algorithms" option in named.conf, and some other algorithms can be +disabled at compile time (ECDSA, GOST, AES). Note, however, that disabling +algorithms can have unwanted side effects; for instance, disabling DH breaks +TKEY support. + +A final note: the DISABLE flags in site.h work for OpenSSL code too, but +this feature is not officially supported yet and should not be relied on. diff --git a/lib/isc/include/pk11/constants.h b/lib/isc/include/pk11/constants.h new file mode 100644 index 0000000..0abbf7e --- /dev/null +++ b/lib/isc/include/pk11/constants.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef PK11_CONSTANTS_H +#define PK11_CONSTANTS_H 1 + +/*! \file pk11/constants.h */ + +/*% + * Static arrays of data used for key template initalization + */ +#ifdef WANT_ECC_CURVES +static CK_BYTE pk11_ecc_prime256v1[] = { + 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 +}; +static CK_BYTE pk11_ecc_secp384r1[] = { + 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 +}; +static CK_BYTE pk11_ecc_ed25519[] = { + 0x06, 0x03, 0x2b, 0x65, 0x70 +}; +static CK_BYTE pk11_ecc_ed448[] = { + 0x06, 0x03, 0x2b, 0x65, 0x71 +}; +#endif + +#ifdef WANT_DH_PRIMES +static CK_BYTE pk11_dh_bn2[] = { 2 }; +static CK_BYTE pk11_dh_bn768[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, + 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, + 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, + 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, + 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; +static CK_BYTE pk11_dh_bn1024[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, + 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, + 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, + 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, + 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, + 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, + 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe6, 0x53, 0x81, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; +static CK_BYTE pk11_dh_bn1536[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, + 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, + 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, + 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, + 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, + 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, + 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, + 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, + 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, + 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, + 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, + 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, + 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, + 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, + 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, + 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, + 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, + 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, + 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, + 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, + 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, + 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x23, 0x73, 0x27, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; +#endif + +#ifdef WANT_GOST_PARAMS +static CK_BYTE pk11_gost_a_paramset[] = { + 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01 +}; +static CK_BYTE pk11_gost_paramset[] = { + 0x06, 0x07, 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01 +}; +#endif + +#endif /* PK11_CONSTANTS_H */ diff --git a/lib/isc/include/pk11/internal.h b/lib/isc/include/pk11/internal.h new file mode 100644 index 0000000..aa8907a --- /dev/null +++ b/lib/isc/include/pk11/internal.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef PK11_INTERNAL_H +#define PK11_INTERNAL_H 1 + +/*! \file pk11/internal.h */ + +ISC_LANG_BEGINDECLS + +const char *pk11_get_lib_name(void); + +void *pk11_mem_get(size_t size); + +void pk11_mem_put(void *ptr, size_t size); + +CK_SLOT_ID pk11_get_best_token(pk11_optype_t optype); + +unsigned int pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt); + +CK_ATTRIBUTE *pk11_attribute_first(const pk11_object_t *obj); + +CK_ATTRIBUTE *pk11_attribute_next(const pk11_object_t *obj, + CK_ATTRIBUTE *attr); + +CK_ATTRIBUTE *pk11_attribute_bytype(const pk11_object_t *obj, + CK_ATTRIBUTE_TYPE type); + +ISC_LANG_ENDDECLS + +#endif /* PK11_INTERNAL_H */ diff --git a/lib/isc/include/pk11/pk11.h b/lib/isc/include/pk11/pk11.h new file mode 100644 index 0000000..2ff21fb --- /dev/null +++ b/lib/isc/include/pk11/pk11.h @@ -0,0 +1,302 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef PK11_PK11_H +#define PK11_PK11_H 1 + +/*! \file pk11/pk11.h */ + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/magic.h> +#include <isc/types.h> + +#define PK11_FATALCHECK(func, args) \ + ((void) (((rv = (func) args) == CKR_OK) || \ + ((pk11_error_fatalcheck)(__FILE__, __LINE__, #func, rv), 0))) + +#include <pkcs11/cryptoki.h> +#include <pk11/site.h> + +ISC_LANG_BEGINDECLS + +#define SES_MAGIC ISC_MAGIC('P','K','S','S') +#define TOK_MAGIC ISC_MAGIC('P','K','T','K') + +#define VALID_SES(x) ISC_MAGIC_VALID(x, SES_MAGIC) +#define VALID_TOK(x) ISC_MAGIC_VALID(x, TOK_MAGIC) + +typedef struct pk11_context pk11_context_t; + +struct pk11_object { + CK_OBJECT_HANDLE object; + CK_SLOT_ID slot; + CK_BBOOL ontoken; + CK_BBOOL reqlogon; + CK_BYTE attrcnt; + CK_ATTRIBUTE *repr; +}; + +struct pk11_context { + void *handle; + CK_SESSION_HANDLE session; + CK_BBOOL ontoken; + CK_OBJECT_HANDLE object; +#if defined(PK11_MD5_HMAC_REPLACE) || defined(PK11_SHA_1_HMAC_REPLACE) || \ + defined(PK11_SHA224_HMAC_REPLACE) || defined(PK11_SHA256_HMAC_REPLACE) || \ + defined(PK11_SHA384_HMAC_REPLACE) || defined(PK11_SHA512_HMAC_REPLACE) + unsigned char *key; +#endif +}; + +typedef struct pk11_object pk11_object_t; + +typedef enum { + OP_ANY = 0, + OP_RAND = 1, + OP_RSA = 2, + OP_DSA = 3, + OP_DH = 4, + OP_DIGEST = 5, + OP_EC = 6, + OP_GOST = 7, + OP_AES = 8, + OP_MAX = 9 +} pk11_optype_t; + +/*% + * Global flag to make choose_slots() verbose + */ +LIBISC_EXTERNAL_DATA extern bool pk11_verbose_init; + +/*% + * Function prototypes + */ + +void pk11_set_lib_name(const char *lib_name); +/*%< + * Set the PKCS#11 provider (aka library) path/name. + */ + +isc_result_t pk11_initialize(isc_mem_t *mctx, const char *engine); +/*%< + * Initialize PKCS#11 device + * + * mctx: memory context to attach to pk11_mctx. + * engine: PKCS#11 provider (aka library) path/name. + * + * returns: + * ISC_R_SUCCESS + * PK11_R_NOPROVIDER: can't load the provider + * PK11_R_INITFAILED: C_Initialize() failed + * PK11_R_NORANDOMSERVICE: can't find required random service + * PK11_R_NODIGESTSERVICE: can't find required digest service + * PK11_R_NOAESSERVICE: can't find required AES service + */ + +isc_result_t pk11_get_session(pk11_context_t *ctx, + pk11_optype_t optype, + bool need_services, + bool rw, + bool logon, + const char *pin, + CK_SLOT_ID slot); +/*%< + * Initialize PKCS#11 device and acquire a session. + * + * need_services: + * if true, this session requires full PKCS#11 API + * support including random and digest services, and + * the lack of these services will cause the session not + * to be initialized. If false, the function will return + * an error code indicating the missing service, but the + * session will be usable for other purposes. + * rw: if true, session will be read/write (useful for + * generating or destroying keys); otherwise read-only. + * login: indicates whether to log in to the device + * pin: optional PIN, overriding any PIN currently associated + * with the + * slot: device slot ID + */ + +void pk11_return_session(pk11_context_t *ctx); +/*%< + * Release an active PKCS#11 session for reuse. + */ + +isc_result_t pk11_finalize(void); +/*%< + * Shut down PKCS#11 device and free all sessions. + */ + +isc_result_t pk11_rand_bytes(unsigned char *buf, int num); + +void pk11_rand_seed_fromfile(const char *randomfile); + +isc_result_t pk11_parse_uri(pk11_object_t *obj, const char *label, + isc_mem_t *mctx, pk11_optype_t optype); + +ISC_PLATFORM_NORETURN_PRE void +pk11_error_fatalcheck(const char *file, int line, + const char *funcname, CK_RV rv) +ISC_PLATFORM_NORETURN_POST; + +void pk11_dump_tokens(void); + +CK_RV +pkcs_C_Initialize(CK_VOID_PTR pReserved); + +char *pk11_get_load_error_message(void); + +CK_RV +pkcs_C_Finalize(CK_VOID_PTR pReserved); + +CK_RV +pkcs_C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, + CK_ULONG_PTR pulCount); + +CK_RV +pkcs_C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo); + +CK_RV +pkcs_C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, + CK_MECHANISM_INFO_PTR 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); + +CK_RV +pkcs_C_CloseSession(CK_SESSION_HANDLE hSession); + +CK_RV +pkcs_C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, + CK_CHAR_PTR pPin, CK_ULONG usPinLen); + +CK_RV +pkcs_C_Logout(CK_SESSION_HANDLE hSession); + +CK_RV +pkcs_C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG usCount, CK_OBJECT_HANDLE_PTR phObject); + +CK_RV +pkcs_C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject); + +CK_RV +pkcs_C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount); + +CK_RV +pkcs_C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, + CK_ATTRIBUTE_PTR pTemplate, CK_ULONG usCount); + +CK_RV +pkcs_C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, + CK_ULONG usCount); + +CK_RV +pkcs_C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, + CK_ULONG usMaxObjectCount, CK_ULONG_PTR pusObjectCount); + +CK_RV +pkcs_C_FindObjectsFinal(CK_SESSION_HANDLE hSession); + +CK_RV +pkcs_C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE 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); + +CK_RV +pkcs_C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism); + +CK_RV +pkcs_C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen); + +CK_RV +pkcs_C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, + CK_ULONG_PTR pulDigestLen); + +CK_RV +pkcs_C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE 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); + +CK_RV +pkcs_C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen); + +CK_RV +pkcs_C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG_PTR pulSignatureLen); + +CK_RV +pkcs_C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, + CK_OBJECT_HANDLE hKey); + +CK_RV +pkcs_C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, + CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, + CK_ULONG ulSignatureLen); + +CK_RV +pkcs_C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, + CK_ULONG ulPartLen); + +CK_RV +pkcs_C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, + CK_ULONG 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); + +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); + +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); + +CK_RV +pkcs_C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, + CK_ULONG ulSeedLen); + +CK_RV +pkcs_C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR RandomData, + CK_ULONG ulRandomLen); + +ISC_LANG_ENDDECLS + +#endif /* PK11_PK11_H */ diff --git a/lib/isc/include/pk11/result.h b/lib/isc/include/pk11/result.h new file mode 100644 index 0000000..cce9150 --- /dev/null +++ b/lib/isc/include/pk11/result.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef PK11_RESULT_H +#define PK11_RESULT_H 1 + +/*! \file pk11/result.h */ + +#include <isc/lang.h> +#include <isc/resultclass.h> + +/* + * Nothing in this file truly depends on <isc/result.h>, but the + * PK11 result codes are considered to be publicly derived from + * the ISC result codes, so including this file buys you the ISC_R_ + * namespace too. + */ +#include <isc/result.h> /* Contractual promise. */ + +#define PK11_R_INITFAILED (ISC_RESULTCLASS_PK11 + 0) +#define PK11_R_NOPROVIDER (ISC_RESULTCLASS_PK11 + 1) +#define PK11_R_NORANDOMSERVICE (ISC_RESULTCLASS_PK11 + 2) +#define PK11_R_NODIGESTSERVICE (ISC_RESULTCLASS_PK11 + 3) +#define PK11_R_NOAESSERVICE (ISC_RESULTCLASS_PK11 + 4) + +#define PK11_R_NRESULTS 5 /* Number of results */ + +ISC_LANG_BEGINDECLS + +LIBISC_EXTERNAL_DATA extern isc_msgcat_t *pk11_msgcat; + +void +pk11_initmsgcat(void); + +const char * +pk11_result_totext(isc_result_t); + +void +pk11_result_register(void); + +ISC_LANG_ENDDECLS + +#endif /* PK11_RESULT_H */ diff --git a/lib/isc/include/pk11/site.h b/lib/isc/include/pk11/site.h new file mode 100644 index 0000000..1d97dbb --- /dev/null +++ b/lib/isc/include/pk11/site.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* The documentation about this file is in README.site */ + +#ifndef PK11_SITE_H +#define PK11_SITE_H 1 + +/*! \file pk11/site.h */ + +/*\brief Put here specific PKCS#11 tweaks + * + *\li PK11_<mechanism>_SKIP: + * Don't consider the lack of this mechanism as a fatal error. + * + *\li PK11_<mechanism>_REPLACE: + * Same as SKIP, and implement the mechanism using lower-level steps. + * + *\li PK11_<algorithm>_DISABLE: + * Same as SKIP, and disable support for the algorithm. + * + *\li PK11_PAD_HMAC_KEYS: + * Extend HMAC keys shorter than digest length. + */ + +/* current implemented flags are: +PK11_DH_PKCS_PARAMETER_GEN_SKIP +PK11_DSA_PARAMETER_GEN_SKIP +PK11_RSA_PKCS_REPLACE +PK11_MD5_HMAC_REPLACE +PK11_SHA_1_HMAC_REPLACE +PK11_SHA224_HMAC_REPLACE +PK11_SHA256_HMAC_REPLACE +PK11_SHA384_HMAC_REPLACE +PK11_SHA512_HMAC_REPLACE +PK11_MD5_DISABLE +PK11_DSA_DISABLE +PK11_DH_DISABLE +PK11_PAD_HMAC_KEYS +*/ + +/* + * Predefined flavors + */ +/* Thales nCipher */ +#define PK11_THALES_FLAVOR 0 +/* SoftHSMv1 with SHA224 */ +#define PK11_SOFTHSMV1_FLAVOR 1 +/* SoftHSMv2 */ +#define PK11_SOFTHSMV2_FLAVOR 2 +/* Cryptech */ +#define PK11_CRYPTECH_FLAVOR 3 +/* AEP Keyper */ +#define PK11_AEP_FLAVOR 4 + +/* Default is for Thales nCipher */ +#ifndef PK11_FLAVOR +#define PK11_FLAVOR PK11_THALES_FLAVOR +#endif + +#if PK11_FLAVOR == PK11_THALES_FLAVOR +#define PK11_DH_PKCS_PARAMETER_GEN_SKIP +/* doesn't work but supported #define PK11_DSA_PARAMETER_GEN_SKIP */ +#define PK11_MD5_HMAC_REPLACE +#endif + +#if PK11_FLAVOR == PK11_SOFTHSMV1_FLAVOR +#define PK11_PAD_HMAC_KEYS +#endif + +#if PK11_FLAVOR == PK11_SOFTHSMV2_FLAVOR +/* SoftHSMv2 was updated to enforce minimal key sizes... argh! */ +#define PK11_MD5_HMAC_REPLACE +#define PK11_SHA_1_HMAC_REPLACE +#define PK11_SHA224_HMAC_REPLACE +#define PK11_SHA256_HMAC_REPLACE +#define PK11_SHA384_HMAC_REPLACE +#define PK11_SHA512_HMAC_REPLACE +#endif + +#if PK11_FLAVOR == PK11_CRYPTECH_FLAVOR +#define PK11_DH_DISABLE +#define PK11_DSA_DISABLE +#define PK11_MD5_DISABLE +#define PK11_SHA_1_HMAC_REPLACE +#define PK11_SHA224_HMAC_REPLACE +#define PK11_SHA256_HMAC_REPLACE +#define PK11_SHA384_HMAC_REPLACE +#define PK11_SHA512_HMAC_REPLACE +#endif + +#if PK11_FLAVOR == PK11_AEP_FLAVOR +#define PK11_DH_DISABLE +#define PK11_DSA_DISABLE +#define PK11_RSA_PKCS_REPLACE +#define PK11_MD5_HMAC_REPLACE +#define PK11_SHA_1_HMAC_REPLACE +#define PK11_SHA224_HMAC_REPLACE +#define PK11_SHA256_HMAC_REPLACE +#define PK11_SHA384_HMAC_REPLACE +#define PK11_SHA512_HMAC_REPLACE +#endif + +#endif /* PK11_SITE_H */ diff --git a/lib/isc/include/pkcs11/Makefile.in b/lib/isc/include/pkcs11/Makefile.in new file mode 100644 index 0000000..8b442b4 --- /dev/null +++ b/lib/isc/include/pkcs11/Makefile.in @@ -0,0 +1,38 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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@ + +# +# Only list headers that are to be installed and are not +# machine generated. The latter are handled specially in the +# install target below. +# +HEADERS = pkcs11f.h pkcs11.h pkcs11t.h eddsa.h +SUBDIRS = +TARGETS = + +@BIND9_MAKE_RULES@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/pkcs11 + +install:: installdirs + for i in ${HEADERS}; do \ + ${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/pkcs11 || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/pkcs11/$$i || exit 1; \ + done diff --git a/lib/isc/include/pkcs11/eddsa.h b/lib/isc/include/pkcs11/eddsa.h new file mode 100644 index 0000000..c0b2e9c --- /dev/null +++ b/lib/isc/include/pkcs11/eddsa.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef _EDDSA_H_ +#define _EDDSA_H_ 1 + +#ifndef CKK_EDDSA +#ifdef PK11_SOFTHSMV2_FLAVOR +#define CKK_EDDSA 0x00008003UL +#endif +#endif + +#ifndef CKM_EDDSA_KEY_PAIR_GEN +#ifdef PK11_SOFTHSMV2_FLAVOR +#define CKM_EDDSA_KEY_PAIR_GEN 0x00009040UL +#endif +#endif + +#ifndef CKM_EDDSA +#ifdef PK11_SOFTHSMV2_FLAVOR +#define CKM_EDDSA 0x00009041UL +#endif +#endif + +#endif /* _EDDSA_H_ */ diff --git a/lib/isc/include/pkcs11/pkcs11.h b/lib/isc/include/pkcs11/pkcs11.h new file mode 100644 index 0000000..c66b0bc --- /dev/null +++ b/lib/isc/include/pkcs11/pkcs11.h @@ -0,0 +1,264 @@ +/* + * PKCS #11 Cryptographic Token Interface Base Specification Version 2.40 Errata 01 + * Committee Specification Draft 01 / Public Review Draft 01 + * 09 December 2015 + * Copyright (c) OASIS Open 2015. All Rights Reserved. + * Source: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/csprd01/include/pkcs11-v2.40/ + * Latest version of the specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html + * https://www.oasis-open.org/policies-guidelines/ipr + */ + +#ifndef _PKCS11_H_ +#define _PKCS11_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Before including this file (pkcs11.h) (or pkcs11t.h by + * itself), 5 platform-specific macros must be defined. These + * macros are described below, and typical definitions for them + * are also given. Be advised that these definitions can depend + * on both the platform and the compiler used (and possibly also + * on whether a Cryptoki library is linked statically or + * dynamically). + * + * In addition to defining these 5 macros, the packing convention + * for Cryptoki structures should be set. The Cryptoki + * convention on packing is that structures should be 1-byte + * aligned. + * + * If you're using Microsoft Developer Studio 5.0 to produce + * Win32 stuff, this might be done by using the following + * preprocessor directive before including pkcs11.h or pkcs11t.h: + * + * #pragma pack(push, cryptoki, 1) + * + * and using the following preprocessor directive after including + * pkcs11.h or pkcs11t.h: + * + * #pragma pack(pop, cryptoki) + * + * If you're using an earlier version of Microsoft Developer + * Studio to produce Win16 stuff, this might be done by using + * the following preprocessor directive before including + * pkcs11.h or pkcs11t.h: + * + * #pragma pack(1) + * + * In a UNIX environment, you're on your own for this. You might + * not need to do (or be able to do!) anything. + * + * + * Now for the macros: + * + * + * 1. CK_PTR: The indirection string for making a pointer to an + * object. It can be used like this: + * + * typedef CK_BYTE CK_PTR CK_BYTE_PTR; + * + * If you're using Microsoft Developer Studio 5.0 to produce + * Win32 stuff, it might be defined by: + * + * #define CK_PTR * + * + * If you're using an earlier version of Microsoft Developer + * Studio to produce Win16 stuff, it might be defined by: + * + * #define CK_PTR far * + * + * In a typical UNIX environment, it might be defined by: + * + * #define CK_PTR * + * + * + * 2. CK_DECLARE_FUNCTION(returnType, name): A macro which makes + * an importable Cryptoki library function declaration out of a + * return type and a function name. It should be used in the + * following fashion: + * + * extern CK_DECLARE_FUNCTION(CK_RV, C_Initialize)( + * CK_VOID_PTR pReserved + * ); + * + * If you're using Microsoft Developer Studio 5.0 to declare a + * function in a Win32 Cryptoki .dll, it might be defined by: + * + * #define CK_DECLARE_FUNCTION(returnType, name) \ + * returnType __declspec(dllimport) name + * + * If you're using an earlier version of Microsoft Developer + * Studio to declare a function in a Win16 Cryptoki .dll, it + * might be defined by: + * + * #define CK_DECLARE_FUNCTION(returnType, name) \ + * returnType __export _far _pascal name + * + * In a UNIX environment, it might be defined by: + * + * #define CK_DECLARE_FUNCTION(returnType, name) \ + * returnType name + * + * + * 3. CK_DECLARE_FUNCTION_POINTER(returnType, name): A macro + * which makes a Cryptoki API function pointer declaration or + * function pointer type declaration out of a return type and a + * function name. It should be used in the following fashion: + * + * // Define funcPtr to be a pointer to a Cryptoki API function + * // taking arguments args and returning CK_RV. + * CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtr)(args); + * + * or + * + * // Define funcPtrType to be the type of a pointer to a + * // Cryptoki API function taking arguments args and returning + * // CK_RV, and then define funcPtr to be a variable of type + * // funcPtrType. + * typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtrType)(args); + * funcPtrType funcPtr; + * + * If you're using Microsoft Developer Studio 5.0 to access + * functions in a Win32 Cryptoki .dll, in might be defined by: + * + * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ + * returnType __declspec(dllimport) (* name) + * + * If you're using an earlier version of Microsoft Developer + * Studio to access functions in a Win16 Cryptoki .dll, it might + * be defined by: + * + * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ + * returnType __export _far _pascal (* name) + * + * In a UNIX environment, it might be defined by: + * + * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ + * returnType (* name) + * + * + * 4. CK_CALLBACK_FUNCTION(returnType, name): A macro which makes + * a function pointer type for an application callback out of + * a return type for the callback and a name for the callback. + * It should be used in the following fashion: + * + * CK_CALLBACK_FUNCTION(CK_RV, myCallback)(args); + * + * to declare a function pointer, myCallback, to a callback + * which takes arguments args and returns a CK_RV. It can also + * be used like this: + * + * typedef CK_CALLBACK_FUNCTION(CK_RV, myCallbackType)(args); + * myCallbackType myCallback; + * + * If you're using Microsoft Developer Studio 5.0 to do Win32 + * Cryptoki development, it might be defined by: + * + * #define CK_CALLBACK_FUNCTION(returnType, name) \ + * returnType (* name) + * + * If you're using an earlier version of Microsoft Developer + * Studio to do Win16 development, it might be defined by: + * + * #define CK_CALLBACK_FUNCTION(returnType, name) \ + * returnType _far _pascal (* name) + * + * In a UNIX environment, it might be defined by: + * + * #define CK_CALLBACK_FUNCTION(returnType, name) \ + * returnType (* name) + * + * + * 5. NULL_PTR: This macro is the value of a NULL pointer. + * + * In any ANSI/ISO C environment (and in many others as well), + * this should best be defined by + * + * #ifndef NULL_PTR + * #define NULL_PTR 0 + * #endif + */ + + +/* All the various Cryptoki types and #define'd values are in the + * file pkcs11t.h. + */ +#include "pkcs11t.h" + +#define __PASTE(x,y) x##y + + +/* ============================================================== + * Define the "extern" form of all the entry points. + * ============================================================== + */ + +#define CK_NEED_ARG_LIST 1 +#define CK_PKCS11_FUNCTION_INFO(name) \ + extern CK_DECLARE_FUNCTION(CK_RV, name) + +/* pkcs11f.h has all the information about the Cryptoki + * function prototypes. + */ +#include "pkcs11f.h" + +#undef CK_NEED_ARG_LIST +#undef CK_PKCS11_FUNCTION_INFO + + +/* ============================================================== + * Define the typedef form of all the entry points. That is, for + * each Cryptoki function C_XXX, define a type CK_C_XXX which is + * a pointer to that kind of function. + * ============================================================== + */ + +#define CK_NEED_ARG_LIST 1 +#define CK_PKCS11_FUNCTION_INFO(name) \ + typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, __PASTE(CK_,name)) + +/* pkcs11f.h has all the information about the Cryptoki + * function prototypes. + */ +#include "pkcs11f.h" + +#undef CK_NEED_ARG_LIST +#undef CK_PKCS11_FUNCTION_INFO + + +/* ============================================================== + * Define structed vector of entry points. A CK_FUNCTION_LIST + * contains a CK_VERSION indicating a library's Cryptoki version + * and then a whole slew of function pointers to the routines in + * the library. This type was declared, but not defined, in + * pkcs11t.h. + * ============================================================== + */ + +#define CK_PKCS11_FUNCTION_INFO(name) \ + __PASTE(CK_,name) name; + +struct CK_FUNCTION_LIST { + + CK_VERSION version; /* Cryptoki version */ + +/* Pile all the function pointers into the CK_FUNCTION_LIST. */ +/* pkcs11f.h has all the information about the Cryptoki + * function prototypes. + */ +#include "pkcs11f.h" + +}; + +#undef CK_PKCS11_FUNCTION_INFO + + +#undef __PASTE + +#ifdef __cplusplus +} +#endif + +#endif /* _PKCS11_H_ */ + diff --git a/lib/isc/include/pkcs11/pkcs11f.h b/lib/isc/include/pkcs11/pkcs11f.h new file mode 100644 index 0000000..48ba572 --- /dev/null +++ b/lib/isc/include/pkcs11/pkcs11f.h @@ -0,0 +1,938 @@ +/* + * PKCS #11 Cryptographic Token Interface Base Specification Version 2.40 Errata 01 + * Committee Specification Draft 01 / Public Review Draft 01 + * 09 December 2015 + * Copyright (c) OASIS Open 2015. All Rights Reserved. + * Source: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/csprd01/include/pkcs11-v2.40/ + * Latest version of the specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html + * https://www.oasis-open.org/policies-guidelines/ipr + */ + +/* This header file contains pretty much everything about all the + * Cryptoki function prototypes. Because this information is + * used for more than just declaring function prototypes, the + * order of the functions appearing herein is important, and + * should not be altered. + */ + +/* General-purpose */ + +/* C_Initialize initializes the Cryptoki library. */ +CK_PKCS11_FUNCTION_INFO(C_Initialize) +#ifdef CK_NEED_ARG_LIST +( + CK_VOID_PTR pInitArgs /* if this is not NULL_PTR, it gets + * cast to CK_C_INITIALIZE_ARGS_PTR + * and dereferenced + */ +); +#endif + + +/* C_Finalize indicates that an application is done with the + * Cryptoki library. + */ +CK_PKCS11_FUNCTION_INFO(C_Finalize) +#ifdef CK_NEED_ARG_LIST +( + CK_VOID_PTR pReserved /* reserved. Should be NULL_PTR */ +); +#endif + + +/* C_GetInfo returns general information about Cryptoki. */ +CK_PKCS11_FUNCTION_INFO(C_GetInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_INFO_PTR pInfo /* location that receives information */ +); +#endif + + +/* C_GetFunctionList returns the function list. */ +CK_PKCS11_FUNCTION_INFO(C_GetFunctionList) +#ifdef CK_NEED_ARG_LIST +( + CK_FUNCTION_LIST_PTR_PTR ppFunctionList /* receives pointer to + * function list + */ +); +#endif + + + +/* Slot and token management */ + +/* C_GetSlotList obtains a list of slots in the system. */ +CK_PKCS11_FUNCTION_INFO(C_GetSlotList) +#ifdef CK_NEED_ARG_LIST +( + CK_BBOOL tokenPresent, /* only slots with tokens */ + CK_SLOT_ID_PTR pSlotList, /* receives array of slot IDs */ + CK_ULONG_PTR pulCount /* receives number of slots */ +); +#endif + + +/* C_GetSlotInfo obtains information about a particular slot in + * the system. + */ +CK_PKCS11_FUNCTION_INFO(C_GetSlotInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* the ID of the slot */ + CK_SLOT_INFO_PTR pInfo /* receives the slot information */ +); +#endif + + +/* C_GetTokenInfo obtains information about a particular token + * in the system. + */ +CK_PKCS11_FUNCTION_INFO(C_GetTokenInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* ID of the token's slot */ + CK_TOKEN_INFO_PTR pInfo /* receives the token information */ +); +#endif + + +/* C_GetMechanismList obtains a list of mechanism types + * supported by a token. + */ +CK_PKCS11_FUNCTION_INFO(C_GetMechanismList) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* ID of token's slot */ + CK_MECHANISM_TYPE_PTR pMechanismList, /* gets mech. array */ + CK_ULONG_PTR pulCount /* gets # of mechs. */ +); +#endif + + +/* C_GetMechanismInfo obtains information about a particular + * mechanism possibly supported by a token. + */ +CK_PKCS11_FUNCTION_INFO(C_GetMechanismInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* ID of the token's slot */ + CK_MECHANISM_TYPE type, /* type of mechanism */ + CK_MECHANISM_INFO_PTR pInfo /* receives mechanism info */ +); +#endif + + +/* C_InitToken initializes a token. */ +CK_PKCS11_FUNCTION_INFO(C_InitToken) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* ID of the token's slot */ + CK_UTF8CHAR_PTR pPin, /* the SO's initial PIN */ + CK_ULONG ulPinLen, /* length in bytes of the PIN */ + CK_UTF8CHAR_PTR pLabel /* 32-byte token label (blank padded) */ +); +#endif + + +/* C_InitPIN initializes the normal user's PIN. */ +CK_PKCS11_FUNCTION_INFO(C_InitPIN) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_UTF8CHAR_PTR pPin, /* the normal user's PIN */ + CK_ULONG ulPinLen /* length in bytes of the PIN */ +); +#endif + + +/* C_SetPIN modifies the PIN of the user who is logged in. */ +CK_PKCS11_FUNCTION_INFO(C_SetPIN) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_UTF8CHAR_PTR pOldPin, /* the old PIN */ + CK_ULONG ulOldLen, /* length of the old PIN */ + CK_UTF8CHAR_PTR pNewPin, /* the new PIN */ + CK_ULONG ulNewLen /* length of the new PIN */ +); +#endif + + + +/* Session management */ + +/* C_OpenSession opens a session between an application and a + * token. + */ +CK_PKCS11_FUNCTION_INFO(C_OpenSession) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID, /* the slot's ID */ + CK_FLAGS flags, /* from CK_SESSION_INFO */ + CK_VOID_PTR pApplication, /* passed to callback */ + CK_NOTIFY Notify, /* callback function */ + CK_SESSION_HANDLE_PTR phSession /* gets session handle */ +); +#endif + + +/* C_CloseSession closes a session between an application and a + * token. + */ +CK_PKCS11_FUNCTION_INFO(C_CloseSession) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + +/* C_CloseAllSessions closes all sessions with a token. */ +CK_PKCS11_FUNCTION_INFO(C_CloseAllSessions) +#ifdef CK_NEED_ARG_LIST +( + CK_SLOT_ID slotID /* the token's slot */ +); +#endif + + +/* C_GetSessionInfo obtains information about the session. */ +CK_PKCS11_FUNCTION_INFO(C_GetSessionInfo) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_SESSION_INFO_PTR pInfo /* receives session info */ +); +#endif + + +/* C_GetOperationState obtains the state of the cryptographic operation + * in a session. + */ +CK_PKCS11_FUNCTION_INFO(C_GetOperationState) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pOperationState, /* gets state */ + CK_ULONG_PTR pulOperationStateLen /* gets state length */ +); +#endif + + +/* C_SetOperationState restores the state of the cryptographic + * operation in a session. + */ +CK_PKCS11_FUNCTION_INFO(C_SetOperationState) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pOperationState, /* holds state */ + CK_ULONG ulOperationStateLen, /* holds state length */ + CK_OBJECT_HANDLE hEncryptionKey, /* en/decryption key */ + CK_OBJECT_HANDLE hAuthenticationKey /* sign/verify key */ +); +#endif + + +/* C_Login logs a user into a token. */ +CK_PKCS11_FUNCTION_INFO(C_Login) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_USER_TYPE userType, /* the user type */ + CK_UTF8CHAR_PTR pPin, /* the user's PIN */ + CK_ULONG ulPinLen /* the length of the PIN */ +); +#endif + + +/* C_Logout logs a user out from a token. */ +CK_PKCS11_FUNCTION_INFO(C_Logout) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + + +/* Object management */ + +/* C_CreateObject creates a new object. */ +CK_PKCS11_FUNCTION_INFO(C_CreateObject) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* the object's template */ + CK_ULONG ulCount, /* attributes in template */ + CK_OBJECT_HANDLE_PTR phObject /* gets new object's handle. */ +); +#endif + + +/* C_CopyObject copies an object, creating a new object for the + * copy. + */ +CK_PKCS11_FUNCTION_INFO(C_CopyObject) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject, /* the object's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* template for new object */ + CK_ULONG ulCount, /* attributes in template */ + CK_OBJECT_HANDLE_PTR phNewObject /* receives handle of copy */ +); +#endif + + +/* C_DestroyObject destroys an object. */ +CK_PKCS11_FUNCTION_INFO(C_DestroyObject) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject /* the object's handle */ +); +#endif + + +/* C_GetObjectSize gets the size of an object in bytes. */ +CK_PKCS11_FUNCTION_INFO(C_GetObjectSize) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject, /* the object's handle */ + CK_ULONG_PTR pulSize /* receives size of object */ +); +#endif + + +/* C_GetAttributeValue obtains the value of one or more object + * attributes. + */ +CK_PKCS11_FUNCTION_INFO(C_GetAttributeValue) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject, /* the object's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs; gets vals */ + CK_ULONG ulCount /* attributes in template */ +); +#endif + + +/* C_SetAttributeValue modifies the value of one or more object + * attributes. + */ +CK_PKCS11_FUNCTION_INFO(C_SetAttributeValue) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hObject, /* the object's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs and values */ + CK_ULONG ulCount /* attributes in template */ +); +#endif + + +/* C_FindObjectsInit initializes a search for token and session + * objects that match a template. + */ +CK_PKCS11_FUNCTION_INFO(C_FindObjectsInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */ + CK_ULONG ulCount /* attrs in search template */ +); +#endif + + +/* C_FindObjects continues a search for token and session + * objects that match a template, obtaining additional object + * handles. + */ +CK_PKCS11_FUNCTION_INFO(C_FindObjects) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_OBJECT_HANDLE_PTR phObject, /* gets obj. handles */ + CK_ULONG ulMaxObjectCount, /* max handles to get */ + CK_ULONG_PTR pulObjectCount /* actual # returned */ +); +#endif + + +/* C_FindObjectsFinal finishes a search for token and session + * objects. + */ +CK_PKCS11_FUNCTION_INFO(C_FindObjectsFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + + +/* Encryption and decryption */ + +/* C_EncryptInit initializes an encryption operation. */ +CK_PKCS11_FUNCTION_INFO(C_EncryptInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the encryption mechanism */ + CK_OBJECT_HANDLE hKey /* handle of encryption key */ +); +#endif + + +/* C_Encrypt encrypts single-part data. */ +CK_PKCS11_FUNCTION_INFO(C_Encrypt) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pData, /* the plaintext data */ + CK_ULONG ulDataLen, /* bytes of plaintext */ + CK_BYTE_PTR pEncryptedData, /* gets ciphertext */ + CK_ULONG_PTR pulEncryptedDataLen /* gets c-text size */ +); +#endif + + +/* C_EncryptUpdate continues a multiple-part encryption + * operation. + */ +CK_PKCS11_FUNCTION_INFO(C_EncryptUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pPart, /* the plaintext data */ + CK_ULONG ulPartLen, /* plaintext data len */ + CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */ + CK_ULONG_PTR pulEncryptedPartLen /* gets c-text size */ +); +#endif + + +/* C_EncryptFinal finishes a multiple-part encryption + * operation. + */ +CK_PKCS11_FUNCTION_INFO(C_EncryptFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session handle */ + CK_BYTE_PTR pLastEncryptedPart, /* last c-text */ + CK_ULONG_PTR pulLastEncryptedPartLen /* gets last size */ +); +#endif + + +/* C_DecryptInit initializes a decryption operation. */ +CK_PKCS11_FUNCTION_INFO(C_DecryptInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the decryption mechanism */ + CK_OBJECT_HANDLE hKey /* handle of decryption key */ +); +#endif + + +/* C_Decrypt decrypts encrypted data in a single part. */ +CK_PKCS11_FUNCTION_INFO(C_Decrypt) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pEncryptedData, /* ciphertext */ + CK_ULONG ulEncryptedDataLen, /* ciphertext length */ + CK_BYTE_PTR pData, /* gets plaintext */ + CK_ULONG_PTR pulDataLen /* gets p-text size */ +); +#endif + + +/* C_DecryptUpdate continues a multiple-part decryption + * operation. + */ +CK_PKCS11_FUNCTION_INFO(C_DecryptUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pEncryptedPart, /* encrypted data */ + CK_ULONG ulEncryptedPartLen, /* input length */ + CK_BYTE_PTR pPart, /* gets plaintext */ + CK_ULONG_PTR pulPartLen /* p-text size */ +); +#endif + + +/* C_DecryptFinal finishes a multiple-part decryption + * operation. + */ +CK_PKCS11_FUNCTION_INFO(C_DecryptFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pLastPart, /* gets plaintext */ + CK_ULONG_PTR pulLastPartLen /* p-text size */ +); +#endif + + + +/* Message digesting */ + +/* C_DigestInit initializes a message-digesting operation. */ +CK_PKCS11_FUNCTION_INFO(C_DigestInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism /* the digesting mechanism */ +); +#endif + + +/* C_Digest digests data in a single part. */ +CK_PKCS11_FUNCTION_INFO(C_Digest) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pData, /* data to be digested */ + CK_ULONG ulDataLen, /* bytes of data to digest */ + CK_BYTE_PTR pDigest, /* gets the message digest */ + CK_ULONG_PTR pulDigestLen /* gets digest length */ +); +#endif + + +/* C_DigestUpdate continues a multiple-part message-digesting + * operation. + */ +CK_PKCS11_FUNCTION_INFO(C_DigestUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pPart, /* data to be digested */ + CK_ULONG ulPartLen /* bytes of data to be digested */ +); +#endif + + +/* C_DigestKey continues a multi-part message-digesting + * operation, by digesting the value of a secret key as part of + * the data already digested. + */ +CK_PKCS11_FUNCTION_INFO(C_DigestKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_OBJECT_HANDLE hKey /* secret key to digest */ +); +#endif + + +/* C_DigestFinal finishes a multiple-part message-digesting + * operation. + */ +CK_PKCS11_FUNCTION_INFO(C_DigestFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pDigest, /* gets the message digest */ + CK_ULONG_PTR pulDigestLen /* gets byte count of digest */ +); +#endif + + + +/* Signing and MACing */ + +/* C_SignInit initializes a signature (private key encryption) + * operation, where the signature is (will be) an appendix to + * the data, and plaintext cannot be recovered from the + * signature. + */ +CK_PKCS11_FUNCTION_INFO(C_SignInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the signature mechanism */ + CK_OBJECT_HANDLE hKey /* handle of signature key */ +); +#endif + + +/* C_Sign signs (encrypts with private key) data in a single + * part, where the signature is (will be) an appendix to the + * data, and plaintext cannot be recovered from the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_Sign) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pData, /* the data to sign */ + CK_ULONG ulDataLen, /* count of bytes to sign */ + CK_BYTE_PTR pSignature, /* gets the signature */ + CK_ULONG_PTR pulSignatureLen /* gets signature length */ +); +#endif + + +/* C_SignUpdate continues a multiple-part signature operation, + * where the signature is (will be) an appendix to the data, + * and plaintext cannot be recovered from the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_SignUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pPart, /* the data to sign */ + CK_ULONG ulPartLen /* count of bytes to sign */ +); +#endif + + +/* C_SignFinal finishes a multiple-part signature operation, + * returning the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_SignFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pSignature, /* gets the signature */ + CK_ULONG_PTR pulSignatureLen /* gets signature length */ +); +#endif + + +/* C_SignRecoverInit initializes a signature operation, where + * the data can be recovered from the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_SignRecoverInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the signature mechanism */ + CK_OBJECT_HANDLE hKey /* handle of the signature key */ +); +#endif + + +/* C_SignRecover signs data in a single operation, where the + * data can be recovered from the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_SignRecover) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pData, /* the data to sign */ + CK_ULONG ulDataLen, /* count of bytes to sign */ + CK_BYTE_PTR pSignature, /* gets the signature */ + CK_ULONG_PTR pulSignatureLen /* gets signature length */ +); +#endif + + + +/* Verifying signatures and MACs */ + +/* C_VerifyInit initializes a verification operation, where the + * signature is an appendix to the data, and plaintext cannot + * cannot be recovered from the signature (e.g. DSA). + */ +CK_PKCS11_FUNCTION_INFO(C_VerifyInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the verification mechanism */ + CK_OBJECT_HANDLE hKey /* verification key */ +); +#endif + + +/* C_Verify verifies a signature in a single-part operation, + * where the signature is an appendix to the data, and plaintext + * cannot be recovered from the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_Verify) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pData, /* signed data */ + CK_ULONG ulDataLen, /* length of signed data */ + CK_BYTE_PTR pSignature, /* signature */ + CK_ULONG ulSignatureLen /* signature length*/ +); +#endif + + +/* C_VerifyUpdate continues a multiple-part verification + * operation, where the signature is an appendix to the data, + * and plaintext cannot be recovered from the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_VerifyUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pPart, /* signed data */ + CK_ULONG ulPartLen /* length of signed data */ +); +#endif + + +/* C_VerifyFinal finishes a multiple-part verification + * operation, checking the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_VerifyFinal) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pSignature, /* signature to verify */ + CK_ULONG ulSignatureLen /* signature length */ +); +#endif + + +/* C_VerifyRecoverInit initializes a signature verification + * operation, where the data is recovered from the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_VerifyRecoverInit) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the verification mechanism */ + CK_OBJECT_HANDLE hKey /* verification key */ +); +#endif + + +/* C_VerifyRecover verifies a signature in a single-part + * operation, where the data is recovered from the signature. + */ +CK_PKCS11_FUNCTION_INFO(C_VerifyRecover) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pSignature, /* signature to verify */ + CK_ULONG ulSignatureLen, /* signature length */ + CK_BYTE_PTR pData, /* gets signed data */ + CK_ULONG_PTR pulDataLen /* gets signed data len */ +); +#endif + + + +/* Dual-function cryptographic operations */ + +/* C_DigestEncryptUpdate continues a multiple-part digesting + * and encryption operation. + */ +CK_PKCS11_FUNCTION_INFO(C_DigestEncryptUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pPart, /* the plaintext data */ + CK_ULONG ulPartLen, /* plaintext length */ + CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */ + CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */ +); +#endif + + +/* C_DecryptDigestUpdate continues a multiple-part decryption and + * digesting operation. + */ +CK_PKCS11_FUNCTION_INFO(C_DecryptDigestUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pEncryptedPart, /* ciphertext */ + CK_ULONG ulEncryptedPartLen, /* ciphertext length */ + CK_BYTE_PTR pPart, /* gets plaintext */ + CK_ULONG_PTR pulPartLen /* gets plaintext len */ +); +#endif + + +/* C_SignEncryptUpdate continues a multiple-part signing and + * encryption operation. + */ +CK_PKCS11_FUNCTION_INFO(C_SignEncryptUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pPart, /* the plaintext data */ + CK_ULONG ulPartLen, /* plaintext length */ + CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */ + CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */ +); +#endif + + +/* C_DecryptVerifyUpdate continues a multiple-part decryption and + * verify operation. + */ +CK_PKCS11_FUNCTION_INFO(C_DecryptVerifyUpdate) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_BYTE_PTR pEncryptedPart, /* ciphertext */ + CK_ULONG ulEncryptedPartLen, /* ciphertext length */ + CK_BYTE_PTR pPart, /* gets plaintext */ + CK_ULONG_PTR pulPartLen /* gets p-text length */ +); +#endif + + + +/* Key management */ + +/* C_GenerateKey generates a secret key, creating a new key + * object. + */ +CK_PKCS11_FUNCTION_INFO(C_GenerateKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* key generation mech. */ + CK_ATTRIBUTE_PTR pTemplate, /* template for new key */ + CK_ULONG ulCount, /* # of attrs in template */ + CK_OBJECT_HANDLE_PTR phKey /* gets handle of new key */ +); +#endif + + +/* C_GenerateKeyPair generates a public-key/private-key pair, + * creating new key objects. + */ +CK_PKCS11_FUNCTION_INFO(C_GenerateKeyPair) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session handle */ + CK_MECHANISM_PTR pMechanism, /* key-gen mech. */ + CK_ATTRIBUTE_PTR pPublicKeyTemplate, /* template for pub. key */ + CK_ULONG ulPublicKeyAttributeCount, /* # pub. attrs. */ + CK_ATTRIBUTE_PTR pPrivateKeyTemplate, /* template for priv. key */ + CK_ULONG ulPrivateKeyAttributeCount, /* # priv. attrs. */ + CK_OBJECT_HANDLE_PTR phPublicKey, /* gets pub. key handle */ + CK_OBJECT_HANDLE_PTR phPrivateKey /* gets priv. key handle */ +); +#endif + + +/* C_WrapKey wraps (i.e., encrypts) a key. */ +CK_PKCS11_FUNCTION_INFO(C_WrapKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_MECHANISM_PTR pMechanism, /* the wrapping mechanism */ + CK_OBJECT_HANDLE hWrappingKey, /* wrapping key */ + CK_OBJECT_HANDLE hKey, /* key to be wrapped */ + CK_BYTE_PTR pWrappedKey, /* gets wrapped key */ + CK_ULONG_PTR pulWrappedKeyLen /* gets wrapped key size */ +); +#endif + + +/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new + * key object. + */ +CK_PKCS11_FUNCTION_INFO(C_UnwrapKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_MECHANISM_PTR pMechanism, /* unwrapping mech. */ + CK_OBJECT_HANDLE hUnwrappingKey, /* unwrapping key */ + CK_BYTE_PTR pWrappedKey, /* the wrapped key */ + CK_ULONG ulWrappedKeyLen, /* wrapped key len */ + CK_ATTRIBUTE_PTR pTemplate, /* new key template */ + CK_ULONG ulAttributeCount, /* template length */ + CK_OBJECT_HANDLE_PTR phKey /* gets new handle */ +); +#endif + + +/* C_DeriveKey derives a key from a base key, creating a new key + * object. + */ +CK_PKCS11_FUNCTION_INFO(C_DeriveKey) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* session's handle */ + CK_MECHANISM_PTR pMechanism, /* key deriv. mech. */ + CK_OBJECT_HANDLE hBaseKey, /* base key */ + CK_ATTRIBUTE_PTR pTemplate, /* new key template */ + CK_ULONG ulAttributeCount, /* template length */ + CK_OBJECT_HANDLE_PTR phKey /* gets new handle */ +); +#endif + + + +/* Random number generation */ + +/* C_SeedRandom mixes additional seed material into the token's + * random number generator. + */ +CK_PKCS11_FUNCTION_INFO(C_SeedRandom) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR pSeed, /* the seed material */ + CK_ULONG ulSeedLen /* length of seed material */ +); +#endif + + +/* C_GenerateRandom generates random data. */ +CK_PKCS11_FUNCTION_INFO(C_GenerateRandom) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_BYTE_PTR RandomData, /* receives the random data */ + CK_ULONG ulRandomLen /* # of bytes to generate */ +); +#endif + + + +/* Parallel function management */ + +/* C_GetFunctionStatus is a legacy function; it obtains an + * updated status of a function running in parallel with an + * application. + */ +CK_PKCS11_FUNCTION_INFO(C_GetFunctionStatus) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + +/* C_CancelFunction is a legacy function; it cancels a function + * running in parallel. + */ +CK_PKCS11_FUNCTION_INFO(C_CancelFunction) +#ifdef CK_NEED_ARG_LIST +( + CK_SESSION_HANDLE hSession /* the session's handle */ +); +#endif + + +/* C_WaitForSlotEvent waits for a slot event (token insertion, + * removal, etc.) to occur. + */ +CK_PKCS11_FUNCTION_INFO(C_WaitForSlotEvent) +#ifdef CK_NEED_ARG_LIST +( + CK_FLAGS flags, /* blocking/nonblocking flag */ + CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */ + CK_VOID_PTR pRserved /* reserved. Should be NULL_PTR */ +); +#endif + diff --git a/lib/isc/include/pkcs11/pkcs11t.h b/lib/isc/include/pkcs11/pkcs11t.h new file mode 100644 index 0000000..ed83ed3 --- /dev/null +++ b/lib/isc/include/pkcs11/pkcs11t.h @@ -0,0 +1,2006 @@ +/* + * PKCS #11 Cryptographic Token Interface Base Specification Version 2.40 Errata 01 + * Committee Specification Draft 01 / Public Review Draft 01 + * 09 December 2015 + * Copyright (c) OASIS Open 2015. All Rights Reserved. + * Source: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/errata01/csprd01/include/pkcs11-v2.40/ + * Latest version of the specification: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.html + * https://www.oasis-open.org/policies-guidelines/ipr + */ + +/* See top of pkcs11.h for information about the macros that + * must be defined and the structure-packing conventions that + * must be set before including this file. + */ + +#ifndef _PKCS11T_H_ +#define _PKCS11T_H_ 1 + +#define CRYPTOKI_VERSION_MAJOR 2 +#define CRYPTOKI_VERSION_MINOR 40 +#define CRYPTOKI_VERSION_AMENDMENT 0 + +#define CK_TRUE 1 +#define CK_FALSE 0 + +#ifndef CK_DISABLE_TRUE_FALSE +#ifndef FALSE +#define FALSE CK_FALSE +#endif +#ifndef TRUE +#define TRUE CK_TRUE +#endif +#endif + +/* an unsigned 8-bit value */ +typedef unsigned char CK_BYTE; + +/* an unsigned 8-bit character */ +typedef CK_BYTE CK_CHAR; + +/* an 8-bit UTF-8 character */ +typedef CK_BYTE CK_UTF8CHAR; + +/* a BYTE-sized Boolean flag */ +typedef CK_BYTE CK_BBOOL; + +/* an unsigned value, at least 32 bits long */ +typedef unsigned long int CK_ULONG; + +/* a signed value, the same size as a CK_ULONG */ +typedef long int CK_LONG; + +/* at least 32 bits; each bit is a Boolean flag */ +typedef CK_ULONG CK_FLAGS; + + +/* some special values for certain CK_ULONG variables */ +#define CK_UNAVAILABLE_INFORMATION (~0UL) +#define CK_EFFECTIVELY_INFINITE 0UL + + +typedef CK_BYTE CK_PTR CK_BYTE_PTR; +typedef CK_CHAR CK_PTR CK_CHAR_PTR; +typedef CK_UTF8CHAR CK_PTR CK_UTF8CHAR_PTR; +typedef CK_ULONG CK_PTR CK_ULONG_PTR; +typedef void CK_PTR CK_VOID_PTR; + +/* Pointer to a CK_VOID_PTR-- i.e., pointer to pointer to void */ +typedef CK_VOID_PTR CK_PTR CK_VOID_PTR_PTR; + + +/* The following value is always invalid if used as a session + * handle or object handle + */ +#define CK_INVALID_HANDLE 0UL + + +typedef struct CK_VERSION { + CK_BYTE major; /* integer portion of version number */ + CK_BYTE minor; /* 1/100ths portion of version number */ +} CK_VERSION; + +typedef CK_VERSION CK_PTR CK_VERSION_PTR; + + +typedef struct CK_INFO { + CK_VERSION cryptokiVersion; /* Cryptoki interface ver */ + CK_UTF8CHAR manufacturerID[32]; /* blank padded */ + CK_FLAGS flags; /* must be zero */ + CK_UTF8CHAR libraryDescription[32]; /* blank padded */ + CK_VERSION libraryVersion; /* version of library */ +} CK_INFO; + +typedef CK_INFO CK_PTR CK_INFO_PTR; + + +/* CK_NOTIFICATION enumerates the types of notifications that + * Cryptoki provides to an application + */ +typedef CK_ULONG CK_NOTIFICATION; +#define CKN_SURRENDER 0UL +#define CKN_OTP_CHANGED 1UL + +typedef CK_ULONG CK_SLOT_ID; + +typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR; + + +/* CK_SLOT_INFO provides information about a slot */ +typedef struct CK_SLOT_INFO { + CK_UTF8CHAR slotDescription[64]; /* blank padded */ + CK_UTF8CHAR manufacturerID[32]; /* blank padded */ + CK_FLAGS flags; + + CK_VERSION hardwareVersion; /* version of hardware */ + CK_VERSION firmwareVersion; /* version of firmware */ +} CK_SLOT_INFO; + +/* flags: bit flags that provide capabilities of the slot + * Bit Flag Mask Meaning + */ +#define CKF_TOKEN_PRESENT 0x00000001UL /* a token is there */ +#define CKF_REMOVABLE_DEVICE 0x00000002UL /* removable devices*/ +#define CKF_HW_SLOT 0x00000004UL /* hardware slot */ + +typedef CK_SLOT_INFO CK_PTR CK_SLOT_INFO_PTR; + + +/* CK_TOKEN_INFO provides information about a token */ +typedef struct CK_TOKEN_INFO { + CK_UTF8CHAR label[32]; /* blank padded */ + CK_UTF8CHAR manufacturerID[32]; /* blank padded */ + CK_UTF8CHAR model[16]; /* blank padded */ + CK_CHAR serialNumber[16]; /* blank padded */ + CK_FLAGS flags; /* see below */ + + CK_ULONG ulMaxSessionCount; /* max open sessions */ + CK_ULONG ulSessionCount; /* sess. now open */ + CK_ULONG ulMaxRwSessionCount; /* max R/W sessions */ + CK_ULONG ulRwSessionCount; /* R/W sess. now open */ + CK_ULONG ulMaxPinLen; /* in bytes */ + CK_ULONG ulMinPinLen; /* in bytes */ + CK_ULONG ulTotalPublicMemory; /* in bytes */ + CK_ULONG ulFreePublicMemory; /* in bytes */ + CK_ULONG ulTotalPrivateMemory; /* in bytes */ + CK_ULONG ulFreePrivateMemory; /* in bytes */ + CK_VERSION hardwareVersion; /* version of hardware */ + CK_VERSION firmwareVersion; /* version of firmware */ + CK_CHAR utcTime[16]; /* time */ +} CK_TOKEN_INFO; + +/* The flags parameter is defined as follows: + * Bit Flag Mask Meaning + */ +#define CKF_RNG 0x00000001UL /* has random # generator */ +#define CKF_WRITE_PROTECTED 0x00000002UL /* token is write-protected */ +#define CKF_LOGIN_REQUIRED 0x00000004UL /* user must login */ +#define CKF_USER_PIN_INITIALIZED 0x00000008UL /* normal user's PIN is set */ + +/* CKF_RESTORE_KEY_NOT_NEEDED. If it is set, + * that means that *every* time the state of cryptographic + * operations of a session is successfully saved, all keys + * needed to continue those operations are stored in the state + */ +#define CKF_RESTORE_KEY_NOT_NEEDED 0x00000020UL + +/* CKF_CLOCK_ON_TOKEN. If it is set, that means + * that the token has some sort of clock. The time on that + * clock is returned in the token info structure + */ +#define CKF_CLOCK_ON_TOKEN 0x00000040UL + +/* CKF_PROTECTED_AUTHENTICATION_PATH. If it is + * set, that means that there is some way for the user to login + * without sending a PIN through the Cryptoki library itself + */ +#define CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100UL + +/* CKF_DUAL_CRYPTO_OPERATIONS. If it is true, + * that means that a single session with the token can perform + * dual simultaneous cryptographic operations (digest and + * encrypt; decrypt and digest; sign and encrypt; and decrypt + * and sign) + */ +#define CKF_DUAL_CRYPTO_OPERATIONS 0x00000200UL + +/* CKF_TOKEN_INITIALIZED. If it is true, the + * token has been initialized using C_InitializeToken or an + * equivalent mechanism outside the scope of PKCS #11. + * Calling C_InitializeToken when this flag is set will cause + * the token to be reinitialized. + */ +#define CKF_TOKEN_INITIALIZED 0x00000400UL + +/* CKF_SECONDARY_AUTHENTICATION. If it is + * true, the token supports secondary authentication for + * private key objects. + */ +#define CKF_SECONDARY_AUTHENTICATION 0x00000800UL + +/* CKF_USER_PIN_COUNT_LOW. If it is true, an + * incorrect user login PIN has been entered at least once + * since the last successful authentication. + */ +#define CKF_USER_PIN_COUNT_LOW 0x00010000UL + +/* CKF_USER_PIN_FINAL_TRY. If it is true, + * supplying an incorrect user PIN will it to become locked. + */ +#define CKF_USER_PIN_FINAL_TRY 0x00020000UL + +/* CKF_USER_PIN_LOCKED. If it is true, the + * user PIN has been locked. User login to the token is not + * possible. + */ +#define CKF_USER_PIN_LOCKED 0x00040000UL + +/* CKF_USER_PIN_TO_BE_CHANGED. If it is true, + * the user PIN value is the default value set by token + * initialization or manufacturing, or the PIN has been + * expired by the card. + */ +#define CKF_USER_PIN_TO_BE_CHANGED 0x00080000UL + +/* CKF_SO_PIN_COUNT_LOW. If it is true, an + * incorrect SO login PIN has been entered at least once since + * the last successful authentication. + */ +#define CKF_SO_PIN_COUNT_LOW 0x00100000UL + +/* CKF_SO_PIN_FINAL_TRY. If it is true, + * supplying an incorrect SO PIN will it to become locked. + */ +#define CKF_SO_PIN_FINAL_TRY 0x00200000UL + +/* CKF_SO_PIN_LOCKED. If it is true, the SO + * PIN has been locked. SO login to the token is not possible. + */ +#define CKF_SO_PIN_LOCKED 0x00400000UL + +/* CKF_SO_PIN_TO_BE_CHANGED. If it is true, + * the SO PIN value is the default value set by token + * initialization or manufacturing, or the PIN has been + * expired by the card. + */ +#define CKF_SO_PIN_TO_BE_CHANGED 0x00800000UL + +#define CKF_ERROR_STATE 0x01000000UL + +typedef CK_TOKEN_INFO CK_PTR CK_TOKEN_INFO_PTR; + + +/* CK_SESSION_HANDLE is a Cryptoki-assigned value that + * identifies a session + */ +typedef CK_ULONG CK_SESSION_HANDLE; + +typedef CK_SESSION_HANDLE CK_PTR CK_SESSION_HANDLE_PTR; + + +/* CK_USER_TYPE enumerates the types of Cryptoki users */ +typedef CK_ULONG CK_USER_TYPE; +/* Security Officer */ +#define CKU_SO 0UL +/* Normal user */ +#define CKU_USER 1UL +/* Context specific */ +#define CKU_CONTEXT_SPECIFIC 2UL + +/* CK_STATE enumerates the session states */ +typedef CK_ULONG CK_STATE; +#define CKS_RO_PUBLIC_SESSION 0UL +#define CKS_RO_USER_FUNCTIONS 1UL +#define CKS_RW_PUBLIC_SESSION 2UL +#define CKS_RW_USER_FUNCTIONS 3UL +#define CKS_RW_SO_FUNCTIONS 4UL + +/* CK_SESSION_INFO provides information about a session */ +typedef struct CK_SESSION_INFO { + CK_SLOT_ID slotID; + CK_STATE state; + CK_FLAGS flags; /* see below */ + CK_ULONG ulDeviceError; /* device-dependent error code */ +} CK_SESSION_INFO; + +/* The flags are defined in the following table: + * Bit Flag Mask Meaning + */ +#define CKF_RW_SESSION 0x00000002UL /* session is r/w */ +#define CKF_SERIAL_SESSION 0x00000004UL /* no parallel */ + +typedef CK_SESSION_INFO CK_PTR CK_SESSION_INFO_PTR; + + +/* CK_OBJECT_HANDLE is a token-specific identifier for an + * object + */ +typedef CK_ULONG CK_OBJECT_HANDLE; + +typedef CK_OBJECT_HANDLE CK_PTR CK_OBJECT_HANDLE_PTR; + + +/* CK_OBJECT_CLASS is a value that identifies the classes (or + * types) of objects that Cryptoki recognizes. It is defined + * as follows: + */ +typedef CK_ULONG CK_OBJECT_CLASS; + +/* The following classes of objects are defined: */ +#define CKO_DATA 0x00000000UL +#define CKO_CERTIFICATE 0x00000001UL +#define CKO_PUBLIC_KEY 0x00000002UL +#define CKO_PRIVATE_KEY 0x00000003UL +#define CKO_SECRET_KEY 0x00000004UL +#define CKO_HW_FEATURE 0x00000005UL +#define CKO_DOMAIN_PARAMETERS 0x00000006UL +#define CKO_MECHANISM 0x00000007UL +#define CKO_OTP_KEY 0x00000008UL + +#define CKO_VENDOR_DEFINED 0x80000000UL + +typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR; + +/* CK_HW_FEATURE_TYPE is a value that identifies the hardware feature type + * of an object with CK_OBJECT_CLASS equal to CKO_HW_FEATURE. + */ +typedef CK_ULONG CK_HW_FEATURE_TYPE; + +/* The following hardware feature types are defined */ +#define CKH_MONOTONIC_COUNTER 0x00000001UL +#define CKH_CLOCK 0x00000002UL +#define CKH_USER_INTERFACE 0x00000003UL +#define CKH_VENDOR_DEFINED 0x80000000UL + +/* CK_KEY_TYPE is a value that identifies a key type */ +typedef CK_ULONG CK_KEY_TYPE; + +/* the following key types are defined: */ +#define CKK_RSA 0x00000000UL +#define CKK_DSA 0x00000001UL +#define CKK_DH 0x00000002UL +#define CKK_ECDSA 0x00000003UL /* Deprecated */ +#define CKK_EC 0x00000003UL +#define CKK_X9_42_DH 0x00000004UL +#define CKK_KEA 0x00000005UL +#define CKK_GENERIC_SECRET 0x00000010UL +#define CKK_RC2 0x00000011UL +#define CKK_RC4 0x00000012UL +#define CKK_DES 0x00000013UL +#define CKK_DES2 0x00000014UL +#define CKK_DES3 0x00000015UL +#define CKK_CAST 0x00000016UL +#define CKK_CAST3 0x00000017UL +#define CKK_CAST5 0x00000018UL /* Deprecated */ +#define CKK_CAST128 0x00000018UL +#define CKK_RC5 0x00000019UL +#define CKK_IDEA 0x0000001AUL +#define CKK_SKIPJACK 0x0000001BUL +#define CKK_BATON 0x0000001CUL +#define CKK_JUNIPER 0x0000001DUL +#define CKK_CDMF 0x0000001EUL +#define CKK_AES 0x0000001FUL +#define CKK_BLOWFISH 0x00000020UL +#define CKK_TWOFISH 0x00000021UL +#define CKK_SECURID 0x00000022UL +#define CKK_HOTP 0x00000023UL +#define CKK_ACTI 0x00000024UL +#define CKK_CAMELLIA 0x00000025UL +#define CKK_ARIA 0x00000026UL + +#define CKK_MD5_HMAC 0x00000027UL +#define CKK_SHA_1_HMAC 0x00000028UL +#define CKK_RIPEMD128_HMAC 0x00000029UL +#define CKK_RIPEMD160_HMAC 0x0000002AUL +#define CKK_SHA256_HMAC 0x0000002BUL +#define CKK_SHA384_HMAC 0x0000002CUL +#define CKK_SHA512_HMAC 0x0000002DUL +#define CKK_SHA224_HMAC 0x0000002EUL + +#define CKK_SEED 0x0000002FUL +#define CKK_GOSTR3410 0x00000030UL +#define CKK_GOSTR3411 0x00000031UL +#define CKK_GOST28147 0x00000032UL + + + +#define CKK_VENDOR_DEFINED 0x80000000UL + + +/* CK_CERTIFICATE_TYPE is a value that identifies a certificate + * type + */ +typedef CK_ULONG CK_CERTIFICATE_TYPE; + +#define CK_CERTIFICATE_CATEGORY_UNSPECIFIED 0UL +#define CK_CERTIFICATE_CATEGORY_TOKEN_USER 1UL +#define CK_CERTIFICATE_CATEGORY_AUTHORITY 2UL +#define CK_CERTIFICATE_CATEGORY_OTHER_ENTITY 3UL + +#define CK_SECURITY_DOMAIN_UNSPECIFIED 0UL +#define CK_SECURITY_DOMAIN_MANUFACTURER 1UL +#define CK_SECURITY_DOMAIN_OPERATOR 2UL +#define CK_SECURITY_DOMAIN_THIRD_PARTY 3UL + + +/* The following certificate types are defined: */ +#define CKC_X_509 0x00000000UL +#define CKC_X_509_ATTR_CERT 0x00000001UL +#define CKC_WTLS 0x00000002UL +#define CKC_VENDOR_DEFINED 0x80000000UL + + +/* CK_ATTRIBUTE_TYPE is a value that identifies an attribute + * type + */ +typedef CK_ULONG CK_ATTRIBUTE_TYPE; + +/* The CKF_ARRAY_ATTRIBUTE flag identifies an attribute which + * consists of an array of values. + */ +#define CKF_ARRAY_ATTRIBUTE 0x40000000UL + +/* The following OTP-related defines relate to the CKA_OTP_FORMAT attribute */ +#define CK_OTP_FORMAT_DECIMAL 0UL +#define CK_OTP_FORMAT_HEXADECIMAL 1UL +#define CK_OTP_FORMAT_ALPHANUMERIC 2UL +#define CK_OTP_FORMAT_BINARY 3UL + +/* The following OTP-related defines relate to the CKA_OTP_..._REQUIREMENT + * attributes + */ +#define CK_OTP_PARAM_IGNORED 0UL +#define CK_OTP_PARAM_OPTIONAL 1UL +#define CK_OTP_PARAM_MANDATORY 2UL + +/* The following attribute types are defined: */ +#define CKA_CLASS 0x00000000UL +#define CKA_TOKEN 0x00000001UL +#define CKA_PRIVATE 0x00000002UL +#define CKA_LABEL 0x00000003UL +#define CKA_APPLICATION 0x00000010UL +#define CKA_VALUE 0x00000011UL +#define CKA_OBJECT_ID 0x00000012UL +#define CKA_CERTIFICATE_TYPE 0x00000080UL +#define CKA_ISSUER 0x00000081UL +#define CKA_SERIAL_NUMBER 0x00000082UL +#define CKA_AC_ISSUER 0x00000083UL +#define CKA_OWNER 0x00000084UL +#define CKA_ATTR_TYPES 0x00000085UL +#define CKA_TRUSTED 0x00000086UL +#define CKA_CERTIFICATE_CATEGORY 0x00000087UL +#define CKA_JAVA_MIDP_SECURITY_DOMAIN 0x00000088UL +#define CKA_URL 0x00000089UL +#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY 0x0000008AUL +#define CKA_HASH_OF_ISSUER_PUBLIC_KEY 0x0000008BUL +#define CKA_NAME_HASH_ALGORITHM 0x0000008CUL +#define CKA_CHECK_VALUE 0x00000090UL + +#define CKA_KEY_TYPE 0x00000100UL +#define CKA_SUBJECT 0x00000101UL +#define CKA_ID 0x00000102UL +#define CKA_SENSITIVE 0x00000103UL +#define CKA_ENCRYPT 0x00000104UL +#define CKA_DECRYPT 0x00000105UL +#define CKA_WRAP 0x00000106UL +#define CKA_UNWRAP 0x00000107UL +#define CKA_SIGN 0x00000108UL +#define CKA_SIGN_RECOVER 0x00000109UL +#define CKA_VERIFY 0x0000010AUL +#define CKA_VERIFY_RECOVER 0x0000010BUL +#define CKA_DERIVE 0x0000010CUL +#define CKA_START_DATE 0x00000110UL +#define CKA_END_DATE 0x00000111UL +#define CKA_MODULUS 0x00000120UL +#define CKA_MODULUS_BITS 0x00000121UL +#define CKA_PUBLIC_EXPONENT 0x00000122UL +#define CKA_PRIVATE_EXPONENT 0x00000123UL +#define CKA_PRIME_1 0x00000124UL +#define CKA_PRIME_2 0x00000125UL +#define CKA_EXPONENT_1 0x00000126UL +#define CKA_EXPONENT_2 0x00000127UL +#define CKA_COEFFICIENT 0x00000128UL +#define CKA_PUBLIC_KEY_INFO 0x00000129UL +#define CKA_PRIME 0x00000130UL +#define CKA_SUBPRIME 0x00000131UL +#define CKA_BASE 0x00000132UL + +#define CKA_PRIME_BITS 0x00000133UL +#define CKA_SUBPRIME_BITS 0x00000134UL +#define CKA_SUB_PRIME_BITS CKA_SUBPRIME_BITS + +#define CKA_VALUE_BITS 0x00000160UL +#define CKA_VALUE_LEN 0x00000161UL +#define CKA_EXTRACTABLE 0x00000162UL +#define CKA_LOCAL 0x00000163UL +#define CKA_NEVER_EXTRACTABLE 0x00000164UL +#define CKA_ALWAYS_SENSITIVE 0x00000165UL +#define CKA_KEY_GEN_MECHANISM 0x00000166UL + +#define CKA_MODIFIABLE 0x00000170UL +#define CKA_COPYABLE 0x00000171UL + +#define CKA_DESTROYABLE 0x00000172UL + +#define CKA_ECDSA_PARAMS 0x00000180UL /* Deprecated */ +#define CKA_EC_PARAMS 0x00000180UL + +#define CKA_EC_POINT 0x00000181UL + +#define CKA_SECONDARY_AUTH 0x00000200UL /* Deprecated */ +#define CKA_AUTH_PIN_FLAGS 0x00000201UL /* Deprecated */ + +#define CKA_ALWAYS_AUTHENTICATE 0x00000202UL + +#define CKA_WRAP_WITH_TRUSTED 0x00000210UL +#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000211UL) +#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000212UL) +#define CKA_DERIVE_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000213UL) + +#define CKA_OTP_FORMAT 0x00000220UL +#define CKA_OTP_LENGTH 0x00000221UL +#define CKA_OTP_TIME_INTERVAL 0x00000222UL +#define CKA_OTP_USER_FRIENDLY_MODE 0x00000223UL +#define CKA_OTP_CHALLENGE_REQUIREMENT 0x00000224UL +#define CKA_OTP_TIME_REQUIREMENT 0x00000225UL +#define CKA_OTP_COUNTER_REQUIREMENT 0x00000226UL +#define CKA_OTP_PIN_REQUIREMENT 0x00000227UL +#define CKA_OTP_COUNTER 0x0000022EUL +#define CKA_OTP_TIME 0x0000022FUL +#define CKA_OTP_USER_IDENTIFIER 0x0000022AUL +#define CKA_OTP_SERVICE_IDENTIFIER 0x0000022BUL +#define CKA_OTP_SERVICE_LOGO 0x0000022CUL +#define CKA_OTP_SERVICE_LOGO_TYPE 0x0000022DUL + +#define CKA_GOSTR3410_PARAMS 0x00000250UL +#define CKA_GOSTR3411_PARAMS 0x00000251UL +#define CKA_GOST28147_PARAMS 0x00000252UL + +#define CKA_HW_FEATURE_TYPE 0x00000300UL +#define CKA_RESET_ON_INIT 0x00000301UL +#define CKA_HAS_RESET 0x00000302UL + +#define CKA_PIXEL_X 0x00000400UL +#define CKA_PIXEL_Y 0x00000401UL +#define CKA_RESOLUTION 0x00000402UL +#define CKA_CHAR_ROWS 0x00000403UL +#define CKA_CHAR_COLUMNS 0x00000404UL +#define CKA_COLOR 0x00000405UL +#define CKA_BITS_PER_PIXEL 0x00000406UL +#define CKA_CHAR_SETS 0x00000480UL +#define CKA_ENCODING_METHODS 0x00000481UL +#define CKA_MIME_TYPES 0x00000482UL +#define CKA_MECHANISM_TYPE 0x00000500UL +#define CKA_REQUIRED_CMS_ATTRIBUTES 0x00000501UL +#define CKA_DEFAULT_CMS_ATTRIBUTES 0x00000502UL +#define CKA_SUPPORTED_CMS_ATTRIBUTES 0x00000503UL +#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE|0x00000600UL) + +#define CKA_VENDOR_DEFINED 0x80000000UL + +/* CK_ATTRIBUTE is a structure that includes the type, length + * and value of an attribute + */ +typedef struct CK_ATTRIBUTE { + CK_ATTRIBUTE_TYPE type; + CK_VOID_PTR pValue; + CK_ULONG ulValueLen; /* in bytes */ +} CK_ATTRIBUTE; + +typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR; + +/* CK_DATE is a structure that defines a date */ +typedef struct CK_DATE{ + CK_CHAR year[4]; /* the year ("1900" - "9999") */ + CK_CHAR month[2]; /* the month ("01" - "12") */ + CK_CHAR day[2]; /* the day ("01" - "31") */ +} CK_DATE; + + +/* CK_MECHANISM_TYPE is a value that identifies a mechanism + * type + */ +typedef CK_ULONG CK_MECHANISM_TYPE; + +/* the following mechanism types are defined: */ +#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000UL +#define CKM_RSA_PKCS 0x00000001UL +#define CKM_RSA_9796 0x00000002UL +#define CKM_RSA_X_509 0x00000003UL + +#define CKM_MD2_RSA_PKCS 0x00000004UL +#define CKM_MD5_RSA_PKCS 0x00000005UL +#define CKM_SHA1_RSA_PKCS 0x00000006UL + +#define CKM_RIPEMD128_RSA_PKCS 0x00000007UL +#define CKM_RIPEMD160_RSA_PKCS 0x00000008UL +#define CKM_RSA_PKCS_OAEP 0x00000009UL + +#define CKM_RSA_X9_31_KEY_PAIR_GEN 0x0000000AUL +#define CKM_RSA_X9_31 0x0000000BUL +#define CKM_SHA1_RSA_X9_31 0x0000000CUL +#define CKM_RSA_PKCS_PSS 0x0000000DUL +#define CKM_SHA1_RSA_PKCS_PSS 0x0000000EUL + +#define CKM_DSA_KEY_PAIR_GEN 0x00000010UL +#define CKM_DSA 0x00000011UL +#define CKM_DSA_SHA1 0x00000012UL +#define CKM_DSA_SHA224 0x00000013UL +#define CKM_DSA_SHA256 0x00000014UL +#define CKM_DSA_SHA384 0x00000015UL +#define CKM_DSA_SHA512 0x00000016UL + +#define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020UL +#define CKM_DH_PKCS_DERIVE 0x00000021UL + +#define CKM_X9_42_DH_KEY_PAIR_GEN 0x00000030UL +#define CKM_X9_42_DH_DERIVE 0x00000031UL +#define CKM_X9_42_DH_HYBRID_DERIVE 0x00000032UL +#define CKM_X9_42_MQV_DERIVE 0x00000033UL + +#define CKM_SHA256_RSA_PKCS 0x00000040UL +#define CKM_SHA384_RSA_PKCS 0x00000041UL +#define CKM_SHA512_RSA_PKCS 0x00000042UL +#define CKM_SHA256_RSA_PKCS_PSS 0x00000043UL +#define CKM_SHA384_RSA_PKCS_PSS 0x00000044UL +#define CKM_SHA512_RSA_PKCS_PSS 0x00000045UL + +#define CKM_SHA224_RSA_PKCS 0x00000046UL +#define CKM_SHA224_RSA_PKCS_PSS 0x00000047UL + +#define CKM_SHA512_224 0x00000048UL +#define CKM_SHA512_224_HMAC 0x00000049UL +#define CKM_SHA512_224_HMAC_GENERAL 0x0000004AUL +#define CKM_SHA512_224_KEY_DERIVATION 0x0000004BUL +#define CKM_SHA512_256 0x0000004CUL +#define CKM_SHA512_256_HMAC 0x0000004DUL +#define CKM_SHA512_256_HMAC_GENERAL 0x0000004EUL +#define CKM_SHA512_256_KEY_DERIVATION 0x0000004FUL + +#define CKM_SHA512_T 0x00000050UL +#define CKM_SHA512_T_HMAC 0x00000051UL +#define CKM_SHA512_T_HMAC_GENERAL 0x00000052UL +#define CKM_SHA512_T_KEY_DERIVATION 0x00000053UL + +#define CKM_RC2_KEY_GEN 0x00000100UL +#define CKM_RC2_ECB 0x00000101UL +#define CKM_RC2_CBC 0x00000102UL +#define CKM_RC2_MAC 0x00000103UL + +#define CKM_RC2_MAC_GENERAL 0x00000104UL +#define CKM_RC2_CBC_PAD 0x00000105UL + +#define CKM_RC4_KEY_GEN 0x00000110UL +#define CKM_RC4 0x00000111UL +#define CKM_DES_KEY_GEN 0x00000120UL +#define CKM_DES_ECB 0x00000121UL +#define CKM_DES_CBC 0x00000122UL +#define CKM_DES_MAC 0x00000123UL + +#define CKM_DES_MAC_GENERAL 0x00000124UL +#define CKM_DES_CBC_PAD 0x00000125UL + +#define CKM_DES2_KEY_GEN 0x00000130UL +#define CKM_DES3_KEY_GEN 0x00000131UL +#define CKM_DES3_ECB 0x00000132UL +#define CKM_DES3_CBC 0x00000133UL +#define CKM_DES3_MAC 0x00000134UL + +#define CKM_DES3_MAC_GENERAL 0x00000135UL +#define CKM_DES3_CBC_PAD 0x00000136UL +#define CKM_DES3_CMAC_GENERAL 0x00000137UL +#define CKM_DES3_CMAC 0x00000138UL +#define CKM_CDMF_KEY_GEN 0x00000140UL +#define CKM_CDMF_ECB 0x00000141UL +#define CKM_CDMF_CBC 0x00000142UL +#define CKM_CDMF_MAC 0x00000143UL +#define CKM_CDMF_MAC_GENERAL 0x00000144UL +#define CKM_CDMF_CBC_PAD 0x00000145UL + +#define CKM_DES_OFB64 0x00000150UL +#define CKM_DES_OFB8 0x00000151UL +#define CKM_DES_CFB64 0x00000152UL +#define CKM_DES_CFB8 0x00000153UL + +#define CKM_MD2 0x00000200UL + +#define CKM_MD2_HMAC 0x00000201UL +#define CKM_MD2_HMAC_GENERAL 0x00000202UL + +#define CKM_MD5 0x00000210UL + +#define CKM_MD5_HMAC 0x00000211UL +#define CKM_MD5_HMAC_GENERAL 0x00000212UL + +#define CKM_SHA_1 0x00000220UL + +#define CKM_SHA_1_HMAC 0x00000221UL +#define CKM_SHA_1_HMAC_GENERAL 0x00000222UL + +#define CKM_RIPEMD128 0x00000230UL +#define CKM_RIPEMD128_HMAC 0x00000231UL +#define CKM_RIPEMD128_HMAC_GENERAL 0x00000232UL +#define CKM_RIPEMD160 0x00000240UL +#define CKM_RIPEMD160_HMAC 0x00000241UL +#define CKM_RIPEMD160_HMAC_GENERAL 0x00000242UL + +#define CKM_SHA256 0x00000250UL +#define CKM_SHA256_HMAC 0x00000251UL +#define CKM_SHA256_HMAC_GENERAL 0x00000252UL +#define CKM_SHA224 0x00000255UL +#define CKM_SHA224_HMAC 0x00000256UL +#define CKM_SHA224_HMAC_GENERAL 0x00000257UL +#define CKM_SHA384 0x00000260UL +#define CKM_SHA384_HMAC 0x00000261UL +#define CKM_SHA384_HMAC_GENERAL 0x00000262UL +#define CKM_SHA512 0x00000270UL +#define CKM_SHA512_HMAC 0x00000271UL +#define CKM_SHA512_HMAC_GENERAL 0x00000272UL +#define CKM_SECURID_KEY_GEN 0x00000280UL +#define CKM_SECURID 0x00000282UL +#define CKM_HOTP_KEY_GEN 0x00000290UL +#define CKM_HOTP 0x00000291UL +#define CKM_ACTI 0x000002A0UL +#define CKM_ACTI_KEY_GEN 0x000002A1UL + +#define CKM_CAST_KEY_GEN 0x00000300UL +#define CKM_CAST_ECB 0x00000301UL +#define CKM_CAST_CBC 0x00000302UL +#define CKM_CAST_MAC 0x00000303UL +#define CKM_CAST_MAC_GENERAL 0x00000304UL +#define CKM_CAST_CBC_PAD 0x00000305UL +#define CKM_CAST3_KEY_GEN 0x00000310UL +#define CKM_CAST3_ECB 0x00000311UL +#define CKM_CAST3_CBC 0x00000312UL +#define CKM_CAST3_MAC 0x00000313UL +#define CKM_CAST3_MAC_GENERAL 0x00000314UL +#define CKM_CAST3_CBC_PAD 0x00000315UL +/* Note that CAST128 and CAST5 are the same algorithm */ +#define CKM_CAST5_KEY_GEN 0x00000320UL +#define CKM_CAST128_KEY_GEN 0x00000320UL +#define CKM_CAST5_ECB 0x00000321UL +#define CKM_CAST128_ECB 0x00000321UL +#define CKM_CAST5_CBC 0x00000322UL /* Deprecated */ +#define CKM_CAST128_CBC 0x00000322UL +#define CKM_CAST5_MAC 0x00000323UL /* Deprecated */ +#define CKM_CAST128_MAC 0x00000323UL +#define CKM_CAST5_MAC_GENERAL 0x00000324UL /* Deprecated */ +#define CKM_CAST128_MAC_GENERAL 0x00000324UL +#define CKM_CAST5_CBC_PAD 0x00000325UL /* Deprecated */ +#define CKM_CAST128_CBC_PAD 0x00000325UL +#define CKM_RC5_KEY_GEN 0x00000330UL +#define CKM_RC5_ECB 0x00000331UL +#define CKM_RC5_CBC 0x00000332UL +#define CKM_RC5_MAC 0x00000333UL +#define CKM_RC5_MAC_GENERAL 0x00000334UL +#define CKM_RC5_CBC_PAD 0x00000335UL +#define CKM_IDEA_KEY_GEN 0x00000340UL +#define CKM_IDEA_ECB 0x00000341UL +#define CKM_IDEA_CBC 0x00000342UL +#define CKM_IDEA_MAC 0x00000343UL +#define CKM_IDEA_MAC_GENERAL 0x00000344UL +#define CKM_IDEA_CBC_PAD 0x00000345UL +#define CKM_GENERIC_SECRET_KEY_GEN 0x00000350UL +#define CKM_CONCATENATE_BASE_AND_KEY 0x00000360UL +#define CKM_CONCATENATE_BASE_AND_DATA 0x00000362UL +#define CKM_CONCATENATE_DATA_AND_BASE 0x00000363UL +#define CKM_XOR_BASE_AND_DATA 0x00000364UL +#define CKM_EXTRACT_KEY_FROM_KEY 0x00000365UL +#define CKM_SSL3_PRE_MASTER_KEY_GEN 0x00000370UL +#define CKM_SSL3_MASTER_KEY_DERIVE 0x00000371UL +#define CKM_SSL3_KEY_AND_MAC_DERIVE 0x00000372UL + +#define CKM_SSL3_MASTER_KEY_DERIVE_DH 0x00000373UL +#define CKM_TLS_PRE_MASTER_KEY_GEN 0x00000374UL +#define CKM_TLS_MASTER_KEY_DERIVE 0x00000375UL +#define CKM_TLS_KEY_AND_MAC_DERIVE 0x00000376UL +#define CKM_TLS_MASTER_KEY_DERIVE_DH 0x00000377UL + +#define CKM_TLS_PRF 0x00000378UL + +#define CKM_SSL3_MD5_MAC 0x00000380UL +#define CKM_SSL3_SHA1_MAC 0x00000381UL +#define CKM_MD5_KEY_DERIVATION 0x00000390UL +#define CKM_MD2_KEY_DERIVATION 0x00000391UL +#define CKM_SHA1_KEY_DERIVATION 0x00000392UL + +#define CKM_SHA256_KEY_DERIVATION 0x00000393UL +#define CKM_SHA384_KEY_DERIVATION 0x00000394UL +#define CKM_SHA512_KEY_DERIVATION 0x00000395UL +#define CKM_SHA224_KEY_DERIVATION 0x00000396UL + +#define CKM_PBE_MD2_DES_CBC 0x000003A0UL +#define CKM_PBE_MD5_DES_CBC 0x000003A1UL +#define CKM_PBE_MD5_CAST_CBC 0x000003A2UL +#define CKM_PBE_MD5_CAST3_CBC 0x000003A3UL +#define CKM_PBE_MD5_CAST5_CBC 0x000003A4UL /* Deprecated */ +#define CKM_PBE_MD5_CAST128_CBC 0x000003A4UL +#define CKM_PBE_SHA1_CAST5_CBC 0x000003A5UL /* Deprecated */ +#define CKM_PBE_SHA1_CAST128_CBC 0x000003A5UL +#define CKM_PBE_SHA1_RC4_128 0x000003A6UL +#define CKM_PBE_SHA1_RC4_40 0x000003A7UL +#define CKM_PBE_SHA1_DES3_EDE_CBC 0x000003A8UL +#define CKM_PBE_SHA1_DES2_EDE_CBC 0x000003A9UL +#define CKM_PBE_SHA1_RC2_128_CBC 0x000003AAUL +#define CKM_PBE_SHA1_RC2_40_CBC 0x000003ABUL + +#define CKM_PKCS5_PBKD2 0x000003B0UL + +#define CKM_PBA_SHA1_WITH_SHA1_HMAC 0x000003C0UL + +#define CKM_WTLS_PRE_MASTER_KEY_GEN 0x000003D0UL +#define CKM_WTLS_MASTER_KEY_DERIVE 0x000003D1UL +#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC 0x000003D2UL +#define CKM_WTLS_PRF 0x000003D3UL +#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE 0x000003D4UL +#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE 0x000003D5UL + +#define CKM_TLS10_MAC_SERVER 0x000003D6UL +#define CKM_TLS10_MAC_CLIENT 0x000003D7UL +#define CKM_TLS12_MAC 0x000003D8UL +#define CKM_TLS12_KDF 0x000003D9UL +#define CKM_TLS12_MASTER_KEY_DERIVE 0x000003E0UL +#define CKM_TLS12_KEY_AND_MAC_DERIVE 0x000003E1UL +#define CKM_TLS12_MASTER_KEY_DERIVE_DH 0x000003E2UL +#define CKM_TLS12_KEY_SAFE_DERIVE 0x000003E3UL +#define CKM_TLS_MAC 0x000003E4UL +#define CKM_TLS_KDF 0x000003E5UL + +#define CKM_KEY_WRAP_LYNKS 0x00000400UL +#define CKM_KEY_WRAP_SET_OAEP 0x00000401UL + +#define CKM_CMS_SIG 0x00000500UL +#define CKM_KIP_DERIVE 0x00000510UL +#define CKM_KIP_WRAP 0x00000511UL +#define CKM_KIP_MAC 0x00000512UL + +#define CKM_CAMELLIA_KEY_GEN 0x00000550UL +#define CKM_CAMELLIA_ECB 0x00000551UL +#define CKM_CAMELLIA_CBC 0x00000552UL +#define CKM_CAMELLIA_MAC 0x00000553UL +#define CKM_CAMELLIA_MAC_GENERAL 0x00000554UL +#define CKM_CAMELLIA_CBC_PAD 0x00000555UL +#define CKM_CAMELLIA_ECB_ENCRYPT_DATA 0x00000556UL +#define CKM_CAMELLIA_CBC_ENCRYPT_DATA 0x00000557UL +#define CKM_CAMELLIA_CTR 0x00000558UL + +#define CKM_ARIA_KEY_GEN 0x00000560UL +#define CKM_ARIA_ECB 0x00000561UL +#define CKM_ARIA_CBC 0x00000562UL +#define CKM_ARIA_MAC 0x00000563UL +#define CKM_ARIA_MAC_GENERAL 0x00000564UL +#define CKM_ARIA_CBC_PAD 0x00000565UL +#define CKM_ARIA_ECB_ENCRYPT_DATA 0x00000566UL +#define CKM_ARIA_CBC_ENCRYPT_DATA 0x00000567UL + +#define CKM_SEED_KEY_GEN 0x00000650UL +#define CKM_SEED_ECB 0x00000651UL +#define CKM_SEED_CBC 0x00000652UL +#define CKM_SEED_MAC 0x00000653UL +#define CKM_SEED_MAC_GENERAL 0x00000654UL +#define CKM_SEED_CBC_PAD 0x00000655UL +#define CKM_SEED_ECB_ENCRYPT_DATA 0x00000656UL +#define CKM_SEED_CBC_ENCRYPT_DATA 0x00000657UL + +#define CKM_SKIPJACK_KEY_GEN 0x00001000UL +#define CKM_SKIPJACK_ECB64 0x00001001UL +#define CKM_SKIPJACK_CBC64 0x00001002UL +#define CKM_SKIPJACK_OFB64 0x00001003UL +#define CKM_SKIPJACK_CFB64 0x00001004UL +#define CKM_SKIPJACK_CFB32 0x00001005UL +#define CKM_SKIPJACK_CFB16 0x00001006UL +#define CKM_SKIPJACK_CFB8 0x00001007UL +#define CKM_SKIPJACK_WRAP 0x00001008UL +#define CKM_SKIPJACK_PRIVATE_WRAP 0x00001009UL +#define CKM_SKIPJACK_RELAYX 0x0000100aUL +#define CKM_KEA_KEY_PAIR_GEN 0x00001010UL +#define CKM_KEA_KEY_DERIVE 0x00001011UL +#define CKM_KEA_DERIVE 0x00001012UL +#define CKM_FORTEZZA_TIMESTAMP 0x00001020UL +#define CKM_BATON_KEY_GEN 0x00001030UL +#define CKM_BATON_ECB128 0x00001031UL +#define CKM_BATON_ECB96 0x00001032UL +#define CKM_BATON_CBC128 0x00001033UL +#define CKM_BATON_COUNTER 0x00001034UL +#define CKM_BATON_SHUFFLE 0x00001035UL +#define CKM_BATON_WRAP 0x00001036UL + +#define CKM_ECDSA_KEY_PAIR_GEN 0x00001040UL /* Deprecated */ +#define CKM_EC_KEY_PAIR_GEN 0x00001040UL + +#define CKM_ECDSA 0x00001041UL +#define CKM_ECDSA_SHA1 0x00001042UL +#define CKM_ECDSA_SHA224 0x00001043UL +#define CKM_ECDSA_SHA256 0x00001044UL +#define CKM_ECDSA_SHA384 0x00001045UL +#define CKM_ECDSA_SHA512 0x00001046UL + +#define CKM_ECDH1_DERIVE 0x00001050UL +#define CKM_ECDH1_COFACTOR_DERIVE 0x00001051UL +#define CKM_ECMQV_DERIVE 0x00001052UL + +#define CKM_ECDH_AES_KEY_WRAP 0x00001053UL +#define CKM_RSA_AES_KEY_WRAP 0x00001054UL + +#define CKM_JUNIPER_KEY_GEN 0x00001060UL +#define CKM_JUNIPER_ECB128 0x00001061UL +#define CKM_JUNIPER_CBC128 0x00001062UL +#define CKM_JUNIPER_COUNTER 0x00001063UL +#define CKM_JUNIPER_SHUFFLE 0x00001064UL +#define CKM_JUNIPER_WRAP 0x00001065UL +#define CKM_FASTHASH 0x00001070UL + +#define CKM_AES_KEY_GEN 0x00001080UL +#define CKM_AES_ECB 0x00001081UL +#define CKM_AES_CBC 0x00001082UL +#define CKM_AES_MAC 0x00001083UL +#define CKM_AES_MAC_GENERAL 0x00001084UL +#define CKM_AES_CBC_PAD 0x00001085UL +#define CKM_AES_CTR 0x00001086UL +#define CKM_AES_GCM 0x00001087UL +#define CKM_AES_CCM 0x00001088UL +#define CKM_AES_CTS 0x00001089UL +#define CKM_AES_CMAC 0x0000108AUL +#define CKM_AES_CMAC_GENERAL 0x0000108BUL + +#define CKM_AES_XCBC_MAC 0x0000108CUL +#define CKM_AES_XCBC_MAC_96 0x0000108DUL +#define CKM_AES_GMAC 0x0000108EUL + +#define CKM_BLOWFISH_KEY_GEN 0x00001090UL +#define CKM_BLOWFISH_CBC 0x00001091UL +#define CKM_TWOFISH_KEY_GEN 0x00001092UL +#define CKM_TWOFISH_CBC 0x00001093UL +#define CKM_BLOWFISH_CBC_PAD 0x00001094UL +#define CKM_TWOFISH_CBC_PAD 0x00001095UL + +#define CKM_DES_ECB_ENCRYPT_DATA 0x00001100UL +#define CKM_DES_CBC_ENCRYPT_DATA 0x00001101UL +#define CKM_DES3_ECB_ENCRYPT_DATA 0x00001102UL +#define CKM_DES3_CBC_ENCRYPT_DATA 0x00001103UL +#define CKM_AES_ECB_ENCRYPT_DATA 0x00001104UL +#define CKM_AES_CBC_ENCRYPT_DATA 0x00001105UL + +#define CKM_GOSTR3410_KEY_PAIR_GEN 0x00001200UL +#define CKM_GOSTR3410 0x00001201UL +#define CKM_GOSTR3410_WITH_GOSTR3411 0x00001202UL +#define CKM_GOSTR3410_KEY_WRAP 0x00001203UL +#define CKM_GOSTR3410_DERIVE 0x00001204UL +#define CKM_GOSTR3411 0x00001210UL +#define CKM_GOSTR3411_HMAC 0x00001211UL +#define CKM_GOST28147_KEY_GEN 0x00001220UL +#define CKM_GOST28147_ECB 0x00001221UL +#define CKM_GOST28147 0x00001222UL +#define CKM_GOST28147_MAC 0x00001223UL +#define CKM_GOST28147_KEY_WRAP 0x00001224UL + +#define CKM_DSA_PARAMETER_GEN 0x00002000UL +#define CKM_DH_PKCS_PARAMETER_GEN 0x00002001UL +#define CKM_X9_42_DH_PARAMETER_GEN 0x00002002UL +#define CKM_DSA_PROBABLISTIC_PARAMETER_GEN 0x00002003UL +#define CKM_DSA_SHAWE_TAYLOR_PARAMETER_GEN 0x00002004UL + +#define CKM_AES_OFB 0x00002104UL +#define CKM_AES_CFB64 0x00002105UL +#define CKM_AES_CFB8 0x00002106UL +#define CKM_AES_CFB128 0x00002107UL + +#define CKM_AES_CFB1 0x00002108UL +#define CKM_AES_KEY_WRAP 0x00002109UL /* WAS: 0x00001090 */ +#define CKM_AES_KEY_WRAP_PAD 0x0000210AUL /* WAS: 0x00001091 */ + +#define CKM_RSA_PKCS_TPM_1_1 0x00004001UL +#define CKM_RSA_PKCS_OAEP_TPM_1_1 0x00004002UL + +#define CKM_VENDOR_DEFINED 0x80000000UL + +typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR; + + +/* CK_MECHANISM is a structure that specifies a particular + * mechanism + */ +typedef struct CK_MECHANISM { + CK_MECHANISM_TYPE mechanism; + CK_VOID_PTR pParameter; + CK_ULONG ulParameterLen; /* in bytes */ +} CK_MECHANISM; + +typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR; + + +/* CK_MECHANISM_INFO provides information about a particular + * mechanism + */ +typedef struct CK_MECHANISM_INFO { + CK_ULONG ulMinKeySize; + CK_ULONG ulMaxKeySize; + CK_FLAGS flags; +} CK_MECHANISM_INFO; + +/* The flags are defined as follows: + * Bit Flag Mask Meaning */ +#define CKF_HW 0x00000001UL /* performed by HW */ + +/* Specify whether or not a mechanism can be used for a particular task */ +#define CKF_ENCRYPT 0x00000100UL +#define CKF_DECRYPT 0x00000200UL +#define CKF_DIGEST 0x00000400UL +#define CKF_SIGN 0x00000800UL +#define CKF_SIGN_RECOVER 0x00001000UL +#define CKF_VERIFY 0x00002000UL +#define CKF_VERIFY_RECOVER 0x00004000UL +#define CKF_GENERATE 0x00008000UL +#define CKF_GENERATE_KEY_PAIR 0x00010000UL +#define CKF_WRAP 0x00020000UL +#define CKF_UNWRAP 0x00040000UL +#define CKF_DERIVE 0x00080000UL + +/* Describe a token's EC capabilities not available in mechanism + * information. + */ +#define CKF_EC_F_P 0x00100000UL +#define CKF_EC_F_2M 0x00200000UL +#define CKF_EC_ECPARAMETERS 0x00400000UL +#define CKF_EC_NAMEDCURVE 0x00800000UL +#define CKF_EC_UNCOMPRESS 0x01000000UL +#define CKF_EC_COMPRESS 0x02000000UL + +#define CKF_EXTENSION 0x80000000UL + +typedef CK_MECHANISM_INFO CK_PTR CK_MECHANISM_INFO_PTR; + +/* CK_RV is a value that identifies the return value of a + * Cryptoki function + */ +typedef CK_ULONG CK_RV; + +#define CKR_OK 0x00000000UL +#define CKR_CANCEL 0x00000001UL +#define CKR_HOST_MEMORY 0x00000002UL +#define CKR_SLOT_ID_INVALID 0x00000003UL + +#define CKR_GENERAL_ERROR 0x00000005UL +#define CKR_FUNCTION_FAILED 0x00000006UL + +#define CKR_ARGUMENTS_BAD 0x00000007UL +#define CKR_NO_EVENT 0x00000008UL +#define CKR_NEED_TO_CREATE_THREADS 0x00000009UL +#define CKR_CANT_LOCK 0x0000000AUL + +#define CKR_ATTRIBUTE_READ_ONLY 0x00000010UL +#define CKR_ATTRIBUTE_SENSITIVE 0x00000011UL +#define CKR_ATTRIBUTE_TYPE_INVALID 0x00000012UL +#define CKR_ATTRIBUTE_VALUE_INVALID 0x00000013UL + +#define CKR_ACTION_PROHIBITED 0x0000001BUL + +#define CKR_DATA_INVALID 0x00000020UL +#define CKR_DATA_LEN_RANGE 0x00000021UL +#define CKR_DEVICE_ERROR 0x00000030UL +#define CKR_DEVICE_MEMORY 0x00000031UL +#define CKR_DEVICE_REMOVED 0x00000032UL +#define CKR_ENCRYPTED_DATA_INVALID 0x00000040UL +#define CKR_ENCRYPTED_DATA_LEN_RANGE 0x00000041UL +#define CKR_FUNCTION_CANCELED 0x00000050UL +#define CKR_FUNCTION_NOT_PARALLEL 0x00000051UL + +#define CKR_FUNCTION_NOT_SUPPORTED 0x00000054UL + +#define CKR_KEY_HANDLE_INVALID 0x00000060UL + +#define CKR_KEY_SIZE_RANGE 0x00000062UL +#define CKR_KEY_TYPE_INCONSISTENT 0x00000063UL + +#define CKR_KEY_NOT_NEEDED 0x00000064UL +#define CKR_KEY_CHANGED 0x00000065UL +#define CKR_KEY_NEEDED 0x00000066UL +#define CKR_KEY_INDIGESTIBLE 0x00000067UL +#define CKR_KEY_FUNCTION_NOT_PERMITTED 0x00000068UL +#define CKR_KEY_NOT_WRAPPABLE 0x00000069UL +#define CKR_KEY_UNEXTRACTABLE 0x0000006AUL + +#define CKR_MECHANISM_INVALID 0x00000070UL +#define CKR_MECHANISM_PARAM_INVALID 0x00000071UL + +#define CKR_OBJECT_HANDLE_INVALID 0x00000082UL +#define CKR_OPERATION_ACTIVE 0x00000090UL +#define CKR_OPERATION_NOT_INITIALIZED 0x00000091UL +#define CKR_PIN_INCORRECT 0x000000A0UL +#define CKR_PIN_INVALID 0x000000A1UL +#define CKR_PIN_LEN_RANGE 0x000000A2UL + +#define CKR_PIN_EXPIRED 0x000000A3UL +#define CKR_PIN_LOCKED 0x000000A4UL + +#define CKR_SESSION_CLOSED 0x000000B0UL +#define CKR_SESSION_COUNT 0x000000B1UL +#define CKR_SESSION_HANDLE_INVALID 0x000000B3UL +#define CKR_SESSION_PARALLEL_NOT_SUPPORTED 0x000000B4UL +#define CKR_SESSION_READ_ONLY 0x000000B5UL +#define CKR_SESSION_EXISTS 0x000000B6UL + +#define CKR_SESSION_READ_ONLY_EXISTS 0x000000B7UL +#define CKR_SESSION_READ_WRITE_SO_EXISTS 0x000000B8UL + +#define CKR_SIGNATURE_INVALID 0x000000C0UL +#define CKR_SIGNATURE_LEN_RANGE 0x000000C1UL +#define CKR_TEMPLATE_INCOMPLETE 0x000000D0UL +#define CKR_TEMPLATE_INCONSISTENT 0x000000D1UL +#define CKR_TOKEN_NOT_PRESENT 0x000000E0UL +#define CKR_TOKEN_NOT_RECOGNIZED 0x000000E1UL +#define CKR_TOKEN_WRITE_PROTECTED 0x000000E2UL +#define CKR_UNWRAPPING_KEY_HANDLE_INVALID 0x000000F0UL +#define CKR_UNWRAPPING_KEY_SIZE_RANGE 0x000000F1UL +#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT 0x000000F2UL +#define CKR_USER_ALREADY_LOGGED_IN 0x00000100UL +#define CKR_USER_NOT_LOGGED_IN 0x00000101UL +#define CKR_USER_PIN_NOT_INITIALIZED 0x00000102UL +#define CKR_USER_TYPE_INVALID 0x00000103UL + +#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN 0x00000104UL +#define CKR_USER_TOO_MANY_TYPES 0x00000105UL + +#define CKR_WRAPPED_KEY_INVALID 0x00000110UL +#define CKR_WRAPPED_KEY_LEN_RANGE 0x00000112UL +#define CKR_WRAPPING_KEY_HANDLE_INVALID 0x00000113UL +#define CKR_WRAPPING_KEY_SIZE_RANGE 0x00000114UL +#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT 0x00000115UL +#define CKR_RANDOM_SEED_NOT_SUPPORTED 0x00000120UL + +#define CKR_RANDOM_NO_RNG 0x00000121UL + +#define CKR_DOMAIN_PARAMS_INVALID 0x00000130UL + +#define CKR_CURVE_NOT_SUPPORTED 0x00000140UL + +#define CKR_BUFFER_TOO_SMALL 0x00000150UL +#define CKR_SAVED_STATE_INVALID 0x00000160UL +#define CKR_INFORMATION_SENSITIVE 0x00000170UL +#define CKR_STATE_UNSAVEABLE 0x00000180UL + +#define CKR_CRYPTOKI_NOT_INITIALIZED 0x00000190UL +#define CKR_CRYPTOKI_ALREADY_INITIALIZED 0x00000191UL +#define CKR_MUTEX_BAD 0x000001A0UL +#define CKR_MUTEX_NOT_LOCKED 0x000001A1UL + +#define CKR_NEW_PIN_MODE 0x000001B0UL +#define CKR_NEXT_OTP 0x000001B1UL + +#define CKR_EXCEEDED_MAX_ITERATIONS 0x000001B5UL +#define CKR_FIPS_SELF_TEST_FAILED 0x000001B6UL +#define CKR_LIBRARY_LOAD_FAILED 0x000001B7UL +#define CKR_PIN_TOO_WEAK 0x000001B8UL +#define CKR_PUBLIC_KEY_INVALID 0x000001B9UL + +#define CKR_FUNCTION_REJECTED 0x00000200UL + +#define CKR_VENDOR_DEFINED 0x80000000UL + +/* private extra values */ +#define CKR_LIBRARY_ALREADY_INITIALIZED 0x000000FDUL +#define CKR_LIBRARY_FAILED_TO_LOAD 0x000000FEUL +#define CKR_SYMBOL_RESOLUTION_FAILED 0x000000FFUL + +/* CK_NOTIFY is an application callback that processes events */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_NOTIFY)( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_NOTIFICATION event, + CK_VOID_PTR pApplication /* passed to C_OpenSession */ +); + + +/* CK_FUNCTION_LIST is a structure holding a Cryptoki spec + * version and pointers of appropriate types to all the + * Cryptoki functions + */ +typedef struct CK_FUNCTION_LIST CK_FUNCTION_LIST; + +typedef CK_FUNCTION_LIST CK_PTR CK_FUNCTION_LIST_PTR; + +typedef CK_FUNCTION_LIST_PTR CK_PTR CK_FUNCTION_LIST_PTR_PTR; + + +/* CK_CREATEMUTEX is an application callback for creating a + * mutex object + */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_CREATEMUTEX)( + CK_VOID_PTR_PTR ppMutex /* location to receive ptr to mutex */ +); + + +/* CK_DESTROYMUTEX is an application callback for destroying a + * mutex object + */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_DESTROYMUTEX)( + CK_VOID_PTR pMutex /* pointer to mutex */ +); + + +/* CK_LOCKMUTEX is an application callback for locking a mutex */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_LOCKMUTEX)( + CK_VOID_PTR pMutex /* pointer to mutex */ +); + + +/* CK_UNLOCKMUTEX is an application callback for unlocking a + * mutex + */ +typedef CK_CALLBACK_FUNCTION(CK_RV, CK_UNLOCKMUTEX)( + CK_VOID_PTR pMutex /* pointer to mutex */ +); + + +/* CK_C_INITIALIZE_ARGS provides the optional arguments to + * C_Initialize + */ +typedef struct CK_C_INITIALIZE_ARGS { + CK_CREATEMUTEX CreateMutex; + CK_DESTROYMUTEX DestroyMutex; + CK_LOCKMUTEX LockMutex; + CK_UNLOCKMUTEX UnlockMutex; + CK_FLAGS flags; + CK_VOID_PTR pReserved; +} CK_C_INITIALIZE_ARGS; + +/* flags: bit flags that provide capabilities of the slot + * Bit Flag Mask Meaning + */ +#define CKF_LIBRARY_CANT_CREATE_OS_THREADS 0x00000001UL +#define CKF_OS_LOCKING_OK 0x00000002UL + +typedef CK_C_INITIALIZE_ARGS CK_PTR CK_C_INITIALIZE_ARGS_PTR; + + +/* additional flags for parameters to functions */ + +/* CKF_DONT_BLOCK is for the function C_WaitForSlotEvent */ +#define CKF_DONT_BLOCK 1 + +/* CK_RSA_PKCS_MGF_TYPE is used to indicate the Message + * Generation Function (MGF) applied to a message block when + * formatting a message block for the PKCS #1 OAEP encryption + * scheme. + */ +typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE; + +typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR; + +/* The following MGFs are defined */ +#define CKG_MGF1_SHA1 0x00000001UL +#define CKG_MGF1_SHA256 0x00000002UL +#define CKG_MGF1_SHA384 0x00000003UL +#define CKG_MGF1_SHA512 0x00000004UL +#define CKG_MGF1_SHA224 0x00000005UL + +/* CK_RSA_PKCS_OAEP_SOURCE_TYPE is used to indicate the source + * of the encoding parameter when formatting a message block + * for the PKCS #1 OAEP encryption scheme. + */ +typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE; + +typedef CK_RSA_PKCS_OAEP_SOURCE_TYPE CK_PTR CK_RSA_PKCS_OAEP_SOURCE_TYPE_PTR; + +/* The following encoding parameter sources are defined */ +#define CKZ_DATA_SPECIFIED 0x00000001UL + +/* CK_RSA_PKCS_OAEP_PARAMS provides the parameters to the + * CKM_RSA_PKCS_OAEP mechanism. + */ +typedef struct CK_RSA_PKCS_OAEP_PARAMS { + CK_MECHANISM_TYPE hashAlg; + CK_RSA_PKCS_MGF_TYPE mgf; + CK_RSA_PKCS_OAEP_SOURCE_TYPE source; + CK_VOID_PTR pSourceData; + CK_ULONG ulSourceDataLen; +} CK_RSA_PKCS_OAEP_PARAMS; + +typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR; + +/* CK_RSA_PKCS_PSS_PARAMS provides the parameters to the + * CKM_RSA_PKCS_PSS mechanism(s). + */ +typedef struct CK_RSA_PKCS_PSS_PARAMS { + CK_MECHANISM_TYPE hashAlg; + CK_RSA_PKCS_MGF_TYPE mgf; + CK_ULONG sLen; +} CK_RSA_PKCS_PSS_PARAMS; + +typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR; + +typedef CK_ULONG CK_EC_KDF_TYPE; + +/* The following EC Key Derivation Functions are defined */ +#define CKD_NULL 0x00000001UL +#define CKD_SHA1_KDF 0x00000002UL + +/* The following X9.42 DH key derivation functions are defined */ +#define CKD_SHA1_KDF_ASN1 0x00000003UL +#define CKD_SHA1_KDF_CONCATENATE 0x00000004UL +#define CKD_SHA224_KDF 0x00000005UL +#define CKD_SHA256_KDF 0x00000006UL +#define CKD_SHA384_KDF 0x00000007UL +#define CKD_SHA512_KDF 0x00000008UL +#define CKD_CPDIVERSIFY_KDF 0x00000009UL + + +/* CK_ECDH1_DERIVE_PARAMS provides the parameters to the + * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms, + * where each party contributes one key pair. + */ +typedef struct CK_ECDH1_DERIVE_PARAMS { + CK_EC_KDF_TYPE kdf; + CK_ULONG ulSharedDataLen; + CK_BYTE_PTR pSharedData; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; +} CK_ECDH1_DERIVE_PARAMS; + +typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR; + +/* + * CK_ECDH2_DERIVE_PARAMS provides the parameters to the + * CKM_ECMQV_DERIVE mechanism, where each party contributes two key pairs. + */ +typedef struct CK_ECDH2_DERIVE_PARAMS { + CK_EC_KDF_TYPE kdf; + CK_ULONG ulSharedDataLen; + CK_BYTE_PTR pSharedData; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; + CK_ULONG ulPrivateDataLen; + CK_OBJECT_HANDLE hPrivateData; + CK_ULONG ulPublicDataLen2; + CK_BYTE_PTR pPublicData2; +} CK_ECDH2_DERIVE_PARAMS; + +typedef CK_ECDH2_DERIVE_PARAMS CK_PTR CK_ECDH2_DERIVE_PARAMS_PTR; + +typedef struct CK_ECMQV_DERIVE_PARAMS { + CK_EC_KDF_TYPE kdf; + CK_ULONG ulSharedDataLen; + CK_BYTE_PTR pSharedData; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; + CK_ULONG ulPrivateDataLen; + CK_OBJECT_HANDLE hPrivateData; + CK_ULONG ulPublicDataLen2; + CK_BYTE_PTR pPublicData2; + CK_OBJECT_HANDLE publicKey; +} CK_ECMQV_DERIVE_PARAMS; + +typedef CK_ECMQV_DERIVE_PARAMS CK_PTR CK_ECMQV_DERIVE_PARAMS_PTR; + +/* Typedefs and defines for the CKM_X9_42_DH_KEY_PAIR_GEN and the + * CKM_X9_42_DH_PARAMETER_GEN mechanisms + */ +typedef CK_ULONG CK_X9_42_DH_KDF_TYPE; +typedef CK_X9_42_DH_KDF_TYPE CK_PTR CK_X9_42_DH_KDF_TYPE_PTR; + +/* CK_X9_42_DH1_DERIVE_PARAMS provides the parameters to the + * CKM_X9_42_DH_DERIVE key derivation mechanism, where each party + * contributes one key pair + */ +typedef struct CK_X9_42_DH1_DERIVE_PARAMS { + CK_X9_42_DH_KDF_TYPE kdf; + CK_ULONG ulOtherInfoLen; + CK_BYTE_PTR pOtherInfo; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; +} CK_X9_42_DH1_DERIVE_PARAMS; + +typedef struct CK_X9_42_DH1_DERIVE_PARAMS CK_PTR CK_X9_42_DH1_DERIVE_PARAMS_PTR; + +/* CK_X9_42_DH2_DERIVE_PARAMS provides the parameters to the + * CKM_X9_42_DH_HYBRID_DERIVE and CKM_X9_42_MQV_DERIVE key derivation + * mechanisms, where each party contributes two key pairs + */ +typedef struct CK_X9_42_DH2_DERIVE_PARAMS { + CK_X9_42_DH_KDF_TYPE kdf; + CK_ULONG ulOtherInfoLen; + CK_BYTE_PTR pOtherInfo; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; + CK_ULONG ulPrivateDataLen; + CK_OBJECT_HANDLE hPrivateData; + CK_ULONG ulPublicDataLen2; + CK_BYTE_PTR pPublicData2; +} CK_X9_42_DH2_DERIVE_PARAMS; + +typedef CK_X9_42_DH2_DERIVE_PARAMS CK_PTR CK_X9_42_DH2_DERIVE_PARAMS_PTR; + +typedef struct CK_X9_42_MQV_DERIVE_PARAMS { + CK_X9_42_DH_KDF_TYPE kdf; + CK_ULONG ulOtherInfoLen; + CK_BYTE_PTR pOtherInfo; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; + CK_ULONG ulPrivateDataLen; + CK_OBJECT_HANDLE hPrivateData; + CK_ULONG ulPublicDataLen2; + CK_BYTE_PTR pPublicData2; + CK_OBJECT_HANDLE publicKey; +} CK_X9_42_MQV_DERIVE_PARAMS; + +typedef CK_X9_42_MQV_DERIVE_PARAMS CK_PTR CK_X9_42_MQV_DERIVE_PARAMS_PTR; + +/* CK_KEA_DERIVE_PARAMS provides the parameters to the + * CKM_KEA_DERIVE mechanism + */ +typedef struct CK_KEA_DERIVE_PARAMS { + CK_BBOOL isSender; + CK_ULONG ulRandomLen; + CK_BYTE_PTR pRandomA; + CK_BYTE_PTR pRandomB; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; +} CK_KEA_DERIVE_PARAMS; + +typedef CK_KEA_DERIVE_PARAMS CK_PTR CK_KEA_DERIVE_PARAMS_PTR; + + +/* CK_RC2_PARAMS provides the parameters to the CKM_RC2_ECB and + * CKM_RC2_MAC mechanisms. An instance of CK_RC2_PARAMS just + * holds the effective keysize + */ +typedef CK_ULONG CK_RC2_PARAMS; + +typedef CK_RC2_PARAMS CK_PTR CK_RC2_PARAMS_PTR; + + +/* CK_RC2_CBC_PARAMS provides the parameters to the CKM_RC2_CBC + * mechanism + */ +typedef struct CK_RC2_CBC_PARAMS { + CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */ + CK_BYTE iv[8]; /* IV for CBC mode */ +} CK_RC2_CBC_PARAMS; + +typedef CK_RC2_CBC_PARAMS CK_PTR CK_RC2_CBC_PARAMS_PTR; + + +/* CK_RC2_MAC_GENERAL_PARAMS provides the parameters for the + * CKM_RC2_MAC_GENERAL mechanism + */ +typedef struct CK_RC2_MAC_GENERAL_PARAMS { + CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */ + CK_ULONG ulMacLength; /* Length of MAC in bytes */ +} CK_RC2_MAC_GENERAL_PARAMS; + +typedef CK_RC2_MAC_GENERAL_PARAMS CK_PTR \ + CK_RC2_MAC_GENERAL_PARAMS_PTR; + + +/* CK_RC5_PARAMS provides the parameters to the CKM_RC5_ECB and + * CKM_RC5_MAC mechanisms + */ +typedef struct CK_RC5_PARAMS { + CK_ULONG ulWordsize; /* wordsize in bits */ + CK_ULONG ulRounds; /* number of rounds */ +} CK_RC5_PARAMS; + +typedef CK_RC5_PARAMS CK_PTR CK_RC5_PARAMS_PTR; + + +/* CK_RC5_CBC_PARAMS provides the parameters to the CKM_RC5_CBC + * mechanism + */ +typedef struct CK_RC5_CBC_PARAMS { + CK_ULONG ulWordsize; /* wordsize in bits */ + CK_ULONG ulRounds; /* number of rounds */ + CK_BYTE_PTR pIv; /* pointer to IV */ + CK_ULONG ulIvLen; /* length of IV in bytes */ +} CK_RC5_CBC_PARAMS; + +typedef CK_RC5_CBC_PARAMS CK_PTR CK_RC5_CBC_PARAMS_PTR; + + +/* CK_RC5_MAC_GENERAL_PARAMS provides the parameters for the + * CKM_RC5_MAC_GENERAL mechanism + */ +typedef struct CK_RC5_MAC_GENERAL_PARAMS { + CK_ULONG ulWordsize; /* wordsize in bits */ + CK_ULONG ulRounds; /* number of rounds */ + CK_ULONG ulMacLength; /* Length of MAC in bytes */ +} CK_RC5_MAC_GENERAL_PARAMS; + +typedef CK_RC5_MAC_GENERAL_PARAMS CK_PTR \ + CK_RC5_MAC_GENERAL_PARAMS_PTR; + +/* CK_MAC_GENERAL_PARAMS provides the parameters to most block + * ciphers' MAC_GENERAL mechanisms. Its value is the length of + * the MAC + */ +typedef CK_ULONG CK_MAC_GENERAL_PARAMS; + +typedef CK_MAC_GENERAL_PARAMS CK_PTR CK_MAC_GENERAL_PARAMS_PTR; + +typedef struct CK_DES_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[8]; + CK_BYTE_PTR pData; + CK_ULONG length; +} CK_DES_CBC_ENCRYPT_DATA_PARAMS; + +typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR; + +typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[16]; + CK_BYTE_PTR pData; + CK_ULONG length; +} CK_AES_CBC_ENCRYPT_DATA_PARAMS; + +typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR; + +/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the + * CKM_SKIPJACK_PRIVATE_WRAP mechanism + */ +typedef struct CK_SKIPJACK_PRIVATE_WRAP_PARAMS { + CK_ULONG ulPasswordLen; + CK_BYTE_PTR pPassword; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pPublicData; + CK_ULONG ulPAndGLen; + CK_ULONG ulQLen; + CK_ULONG ulRandomLen; + CK_BYTE_PTR pRandomA; + CK_BYTE_PTR pPrimeP; + CK_BYTE_PTR pBaseG; + CK_BYTE_PTR pSubprimeQ; +} CK_SKIPJACK_PRIVATE_WRAP_PARAMS; + +typedef CK_SKIPJACK_PRIVATE_WRAP_PARAMS CK_PTR \ + CK_SKIPJACK_PRIVATE_WRAP_PARAMS_PTR; + + +/* CK_SKIPJACK_RELAYX_PARAMS provides the parameters to the + * CKM_SKIPJACK_RELAYX mechanism + */ +typedef struct CK_SKIPJACK_RELAYX_PARAMS { + CK_ULONG ulOldWrappedXLen; + CK_BYTE_PTR pOldWrappedX; + CK_ULONG ulOldPasswordLen; + CK_BYTE_PTR pOldPassword; + CK_ULONG ulOldPublicDataLen; + CK_BYTE_PTR pOldPublicData; + CK_ULONG ulOldRandomLen; + CK_BYTE_PTR pOldRandomA; + CK_ULONG ulNewPasswordLen; + CK_BYTE_PTR pNewPassword; + CK_ULONG ulNewPublicDataLen; + CK_BYTE_PTR pNewPublicData; + CK_ULONG ulNewRandomLen; + CK_BYTE_PTR pNewRandomA; +} CK_SKIPJACK_RELAYX_PARAMS; + +typedef CK_SKIPJACK_RELAYX_PARAMS CK_PTR \ + CK_SKIPJACK_RELAYX_PARAMS_PTR; + + +typedef struct CK_PBE_PARAMS { + CK_BYTE_PTR pInitVector; + CK_UTF8CHAR_PTR pPassword; + CK_ULONG ulPasswordLen; + CK_BYTE_PTR pSalt; + CK_ULONG ulSaltLen; + CK_ULONG ulIteration; +} CK_PBE_PARAMS; + +typedef CK_PBE_PARAMS CK_PTR CK_PBE_PARAMS_PTR; + + +/* CK_KEY_WRAP_SET_OAEP_PARAMS provides the parameters to the + * CKM_KEY_WRAP_SET_OAEP mechanism + */ +typedef struct CK_KEY_WRAP_SET_OAEP_PARAMS { + CK_BYTE bBC; /* block contents byte */ + CK_BYTE_PTR pX; /* extra data */ + CK_ULONG ulXLen; /* length of extra data in bytes */ +} CK_KEY_WRAP_SET_OAEP_PARAMS; + +typedef CK_KEY_WRAP_SET_OAEP_PARAMS CK_PTR CK_KEY_WRAP_SET_OAEP_PARAMS_PTR; + +typedef struct CK_SSL3_RANDOM_DATA { + CK_BYTE_PTR pClientRandom; + CK_ULONG ulClientRandomLen; + CK_BYTE_PTR pServerRandom; + CK_ULONG ulServerRandomLen; +} CK_SSL3_RANDOM_DATA; + + +typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS { + CK_SSL3_RANDOM_DATA RandomInfo; + CK_VERSION_PTR pVersion; +} CK_SSL3_MASTER_KEY_DERIVE_PARAMS; + +typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS CK_PTR \ + CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR; + +typedef struct CK_SSL3_KEY_MAT_OUT { + CK_OBJECT_HANDLE hClientMacSecret; + CK_OBJECT_HANDLE hServerMacSecret; + CK_OBJECT_HANDLE hClientKey; + CK_OBJECT_HANDLE hServerKey; + CK_BYTE_PTR pIVClient; + CK_BYTE_PTR pIVServer; +} CK_SSL3_KEY_MAT_OUT; + +typedef CK_SSL3_KEY_MAT_OUT CK_PTR CK_SSL3_KEY_MAT_OUT_PTR; + + +typedef struct CK_SSL3_KEY_MAT_PARAMS { + CK_ULONG ulMacSizeInBits; + CK_ULONG ulKeySizeInBits; + CK_ULONG ulIVSizeInBits; + CK_BBOOL bIsExport; + CK_SSL3_RANDOM_DATA RandomInfo; + CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial; +} CK_SSL3_KEY_MAT_PARAMS; + +typedef CK_SSL3_KEY_MAT_PARAMS CK_PTR CK_SSL3_KEY_MAT_PARAMS_PTR; + +typedef struct CK_TLS_PRF_PARAMS { + CK_BYTE_PTR pSeed; + CK_ULONG ulSeedLen; + CK_BYTE_PTR pLabel; + CK_ULONG ulLabelLen; + CK_BYTE_PTR pOutput; + CK_ULONG_PTR pulOutputLen; +} CK_TLS_PRF_PARAMS; + +typedef CK_TLS_PRF_PARAMS CK_PTR CK_TLS_PRF_PARAMS_PTR; + +typedef struct CK_WTLS_RANDOM_DATA { + CK_BYTE_PTR pClientRandom; + CK_ULONG ulClientRandomLen; + CK_BYTE_PTR pServerRandom; + CK_ULONG ulServerRandomLen; +} CK_WTLS_RANDOM_DATA; + +typedef CK_WTLS_RANDOM_DATA CK_PTR CK_WTLS_RANDOM_DATA_PTR; + +typedef struct CK_WTLS_MASTER_KEY_DERIVE_PARAMS { + CK_MECHANISM_TYPE DigestMechanism; + CK_WTLS_RANDOM_DATA RandomInfo; + CK_BYTE_PTR pVersion; +} CK_WTLS_MASTER_KEY_DERIVE_PARAMS; + +typedef CK_WTLS_MASTER_KEY_DERIVE_PARAMS CK_PTR \ + CK_WTLS_MASTER_KEY_DERIVE_PARAMS_PTR; + +typedef struct CK_WTLS_PRF_PARAMS { + CK_MECHANISM_TYPE DigestMechanism; + CK_BYTE_PTR pSeed; + CK_ULONG ulSeedLen; + CK_BYTE_PTR pLabel; + CK_ULONG ulLabelLen; + CK_BYTE_PTR pOutput; + CK_ULONG_PTR pulOutputLen; +} CK_WTLS_PRF_PARAMS; + +typedef CK_WTLS_PRF_PARAMS CK_PTR CK_WTLS_PRF_PARAMS_PTR; + +typedef struct CK_WTLS_KEY_MAT_OUT { + CK_OBJECT_HANDLE hMacSecret; + CK_OBJECT_HANDLE hKey; + CK_BYTE_PTR pIV; +} CK_WTLS_KEY_MAT_OUT; + +typedef CK_WTLS_KEY_MAT_OUT CK_PTR CK_WTLS_KEY_MAT_OUT_PTR; + +typedef struct CK_WTLS_KEY_MAT_PARAMS { + CK_MECHANISM_TYPE DigestMechanism; + CK_ULONG ulMacSizeInBits; + CK_ULONG ulKeySizeInBits; + CK_ULONG ulIVSizeInBits; + CK_ULONG ulSequenceNumber; + CK_BBOOL bIsExport; + CK_WTLS_RANDOM_DATA RandomInfo; + CK_WTLS_KEY_MAT_OUT_PTR pReturnedKeyMaterial; +} CK_WTLS_KEY_MAT_PARAMS; + +typedef CK_WTLS_KEY_MAT_PARAMS CK_PTR CK_WTLS_KEY_MAT_PARAMS_PTR; + +typedef struct CK_CMS_SIG_PARAMS { + CK_OBJECT_HANDLE certificateHandle; + CK_MECHANISM_PTR pSigningMechanism; + CK_MECHANISM_PTR pDigestMechanism; + CK_UTF8CHAR_PTR pContentType; + CK_BYTE_PTR pRequestedAttributes; + CK_ULONG ulRequestedAttributesLen; + CK_BYTE_PTR pRequiredAttributes; + CK_ULONG ulRequiredAttributesLen; +} CK_CMS_SIG_PARAMS; + +typedef CK_CMS_SIG_PARAMS CK_PTR CK_CMS_SIG_PARAMS_PTR; + +typedef struct CK_KEY_DERIVATION_STRING_DATA { + CK_BYTE_PTR pData; + CK_ULONG ulLen; +} CK_KEY_DERIVATION_STRING_DATA; + +typedef CK_KEY_DERIVATION_STRING_DATA CK_PTR \ + CK_KEY_DERIVATION_STRING_DATA_PTR; + + +/* The CK_EXTRACT_PARAMS is used for the + * CKM_EXTRACT_KEY_FROM_KEY mechanism. It specifies which bit + * of the base key should be used as the first bit of the + * derived key + */ +typedef CK_ULONG CK_EXTRACT_PARAMS; + +typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTRACT_PARAMS_PTR; + +/* CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is used to + * indicate the Pseudo-Random Function (PRF) used to generate + * key bits using PKCS #5 PBKDF2. + */ +typedef CK_ULONG CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE; + +typedef CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE CK_PTR \ + CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE_PTR; + +#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001UL +#define CKP_PKCS5_PBKD2_HMAC_GOSTR3411 0x00000002UL +#define CKP_PKCS5_PBKD2_HMAC_SHA224 0x00000003UL +#define CKP_PKCS5_PBKD2_HMAC_SHA256 0x00000004UL +#define CKP_PKCS5_PBKD2_HMAC_SHA384 0x00000005UL +#define CKP_PKCS5_PBKD2_HMAC_SHA512 0x00000006UL +#define CKP_PKCS5_PBKD2_HMAC_SHA512_224 0x00000007UL +#define CKP_PKCS5_PBKD2_HMAC_SHA512_256 0x00000008UL + +/* CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is used to indicate the + * source of the salt value when deriving a key using PKCS #5 + * PBKDF2. + */ +typedef CK_ULONG CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE; + +typedef CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE CK_PTR \ + CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE_PTR; + +/* The following salt value sources are defined in PKCS #5 v2.0. */ +#define CKZ_SALT_SPECIFIED 0x00000001UL + +/* CK_PKCS5_PBKD2_PARAMS is a structure that provides the + * parameters to the CKM_PKCS5_PBKD2 mechanism. + */ +typedef struct CK_PKCS5_PBKD2_PARAMS { + CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource; + CK_VOID_PTR pSaltSourceData; + CK_ULONG ulSaltSourceDataLen; + CK_ULONG iterations; + CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf; + CK_VOID_PTR pPrfData; + CK_ULONG ulPrfDataLen; + CK_UTF8CHAR_PTR pPassword; + CK_ULONG_PTR ulPasswordLen; +} CK_PKCS5_PBKD2_PARAMS; + +typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR; + +/* CK_PKCS5_PBKD2_PARAMS2 is a corrected version of the CK_PKCS5_PBKD2_PARAMS + * structure that provides the parameters to the CKM_PKCS5_PBKD2 mechanism + * noting that the ulPasswordLen field is a CK_ULONG and not a CK_ULONG_PTR. + */ +typedef struct CK_PKCS5_PBKD2_PARAMS2 { + CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource; + CK_VOID_PTR pSaltSourceData; + CK_ULONG ulSaltSourceDataLen; + CK_ULONG iterations; + CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf; + CK_VOID_PTR pPrfData; + CK_ULONG ulPrfDataLen; + CK_UTF8CHAR_PTR pPassword; + CK_ULONG ulPasswordLen; +} CK_PKCS5_PBKD2_PARAMS2; + +typedef CK_PKCS5_PBKD2_PARAMS2 CK_PTR CK_PKCS5_PBKD2_PARAMS2_PTR; + +typedef CK_ULONG CK_OTP_PARAM_TYPE; +typedef CK_OTP_PARAM_TYPE CK_PARAM_TYPE; /* backward compatibility */ + +typedef struct CK_OTP_PARAM { + CK_OTP_PARAM_TYPE type; + CK_VOID_PTR pValue; + CK_ULONG ulValueLen; +} CK_OTP_PARAM; + +typedef CK_OTP_PARAM CK_PTR CK_OTP_PARAM_PTR; + +typedef struct CK_OTP_PARAMS { + CK_OTP_PARAM_PTR pParams; + CK_ULONG ulCount; +} CK_OTP_PARAMS; + +typedef CK_OTP_PARAMS CK_PTR CK_OTP_PARAMS_PTR; + +typedef struct CK_OTP_SIGNATURE_INFO { + CK_OTP_PARAM_PTR pParams; + CK_ULONG ulCount; +} CK_OTP_SIGNATURE_INFO; + +typedef CK_OTP_SIGNATURE_INFO CK_PTR CK_OTP_SIGNATURE_INFO_PTR; + +#define CK_OTP_VALUE 0UL +#define CK_OTP_PIN 1UL +#define CK_OTP_CHALLENGE 2UL +#define CK_OTP_TIME 3UL +#define CK_OTP_COUNTER 4UL +#define CK_OTP_FLAGS 5UL +#define CK_OTP_OUTPUT_LENGTH 6UL +#define CK_OTP_OUTPUT_FORMAT 7UL + +#define CKF_NEXT_OTP 0x00000001UL +#define CKF_EXCLUDE_TIME 0x00000002UL +#define CKF_EXCLUDE_COUNTER 0x00000004UL +#define CKF_EXCLUDE_CHALLENGE 0x00000008UL +#define CKF_EXCLUDE_PIN 0x00000010UL +#define CKF_USER_FRIENDLY_OTP 0x00000020UL + +typedef struct CK_KIP_PARAMS { + CK_MECHANISM_PTR pMechanism; + CK_OBJECT_HANDLE hKey; + CK_BYTE_PTR pSeed; + CK_ULONG ulSeedLen; +} CK_KIP_PARAMS; + +typedef CK_KIP_PARAMS CK_PTR CK_KIP_PARAMS_PTR; + +typedef struct CK_AES_CTR_PARAMS { + CK_ULONG ulCounterBits; + CK_BYTE cb[16]; +} CK_AES_CTR_PARAMS; + +typedef CK_AES_CTR_PARAMS CK_PTR CK_AES_CTR_PARAMS_PTR; + +typedef struct CK_GCM_PARAMS { + CK_BYTE_PTR pIv; + CK_ULONG ulIvLen; + CK_ULONG ulIvBits; + CK_BYTE_PTR pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulTagBits; +} CK_GCM_PARAMS; + +typedef CK_GCM_PARAMS CK_PTR CK_GCM_PARAMS_PTR; + +typedef struct CK_CCM_PARAMS { + CK_ULONG ulDataLen; + CK_BYTE_PTR pNonce; + CK_ULONG ulNonceLen; + CK_BYTE_PTR pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulMACLen; +} CK_CCM_PARAMS; + +typedef CK_CCM_PARAMS CK_PTR CK_CCM_PARAMS_PTR; + +/* Deprecated. Use CK_GCM_PARAMS */ +typedef struct CK_AES_GCM_PARAMS { + CK_BYTE_PTR pIv; + CK_ULONG ulIvLen; + CK_ULONG ulIvBits; + CK_BYTE_PTR pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulTagBits; +} CK_AES_GCM_PARAMS; + +typedef CK_AES_GCM_PARAMS CK_PTR CK_AES_GCM_PARAMS_PTR; + +/* Deprecated. Use CK_CCM_PARAMS */ +typedef struct CK_AES_CCM_PARAMS { + CK_ULONG ulDataLen; + CK_BYTE_PTR pNonce; + CK_ULONG ulNonceLen; + CK_BYTE_PTR pAAD; + CK_ULONG ulAADLen; + CK_ULONG ulMACLen; +} CK_AES_CCM_PARAMS; + +typedef CK_AES_CCM_PARAMS CK_PTR CK_AES_CCM_PARAMS_PTR; + +typedef struct CK_CAMELLIA_CTR_PARAMS { + CK_ULONG ulCounterBits; + CK_BYTE cb[16]; +} CK_CAMELLIA_CTR_PARAMS; + +typedef CK_CAMELLIA_CTR_PARAMS CK_PTR CK_CAMELLIA_CTR_PARAMS_PTR; + +typedef struct CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[16]; + CK_BYTE_PTR pData; + CK_ULONG length; +} CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS; + +typedef CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR \ + CK_CAMELLIA_CBC_ENCRYPT_DATA_PARAMS_PTR; + +typedef struct CK_ARIA_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[16]; + CK_BYTE_PTR pData; + CK_ULONG length; +} CK_ARIA_CBC_ENCRYPT_DATA_PARAMS; + +typedef CK_ARIA_CBC_ENCRYPT_DATA_PARAMS CK_PTR \ + CK_ARIA_CBC_ENCRYPT_DATA_PARAMS_PTR; + +typedef struct CK_DSA_PARAMETER_GEN_PARAM { + CK_MECHANISM_TYPE hash; + CK_BYTE_PTR pSeed; + CK_ULONG ulSeedLen; + CK_ULONG ulIndex; +} CK_DSA_PARAMETER_GEN_PARAM; + +typedef CK_DSA_PARAMETER_GEN_PARAM CK_PTR CK_DSA_PARAMETER_GEN_PARAM_PTR; + +typedef struct CK_ECDH_AES_KEY_WRAP_PARAMS { + CK_ULONG ulAESKeyBits; + CK_EC_KDF_TYPE kdf; + CK_ULONG ulSharedDataLen; + CK_BYTE_PTR pSharedData; +} CK_ECDH_AES_KEY_WRAP_PARAMS; + +typedef CK_ECDH_AES_KEY_WRAP_PARAMS CK_PTR CK_ECDH_AES_KEY_WRAP_PARAMS_PTR; + +typedef CK_ULONG CK_JAVA_MIDP_SECURITY_DOMAIN; + +typedef CK_ULONG CK_CERTIFICATE_CATEGORY; + +typedef struct CK_RSA_AES_KEY_WRAP_PARAMS { + CK_ULONG ulAESKeyBits; + CK_RSA_PKCS_OAEP_PARAMS_PTR pOAEPParams; +} CK_RSA_AES_KEY_WRAP_PARAMS; + +typedef CK_RSA_AES_KEY_WRAP_PARAMS CK_PTR CK_RSA_AES_KEY_WRAP_PARAMS_PTR; + +typedef struct CK_TLS12_MASTER_KEY_DERIVE_PARAMS { + CK_SSL3_RANDOM_DATA RandomInfo; + CK_VERSION_PTR pVersion; + CK_MECHANISM_TYPE prfHashMechanism; +} CK_TLS12_MASTER_KEY_DERIVE_PARAMS; + +typedef CK_TLS12_MASTER_KEY_DERIVE_PARAMS CK_PTR \ + CK_TLS12_MASTER_KEY_DERIVE_PARAMS_PTR; + +typedef struct CK_TLS12_KEY_MAT_PARAMS { + CK_ULONG ulMacSizeInBits; + CK_ULONG ulKeySizeInBits; + CK_ULONG ulIVSizeInBits; + CK_BBOOL bIsExport; + CK_SSL3_RANDOM_DATA RandomInfo; + CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial; + CK_MECHANISM_TYPE prfHashMechanism; +} CK_TLS12_KEY_MAT_PARAMS; + +typedef CK_TLS12_KEY_MAT_PARAMS CK_PTR CK_TLS12_KEY_MAT_PARAMS_PTR; + +typedef struct CK_TLS_KDF_PARAMS { + CK_MECHANISM_TYPE prfMechanism; + CK_BYTE_PTR pLabel; + CK_ULONG ulLabelLength; + CK_SSL3_RANDOM_DATA RandomInfo; + CK_BYTE_PTR pContextData; + CK_ULONG ulContextDataLength; +} CK_TLS_KDF_PARAMS; + +typedef CK_TLS_KDF_PARAMS CK_PTR CK_TLS_KDF_PARAMS_PTR; + +typedef struct CK_TLS_MAC_PARAMS { + CK_MECHANISM_TYPE prfHashMechanism; + CK_ULONG ulMacLength; + CK_ULONG ulServerOrClient; +} CK_TLS_MAC_PARAMS; + +typedef CK_TLS_MAC_PARAMS CK_PTR CK_TLS_MAC_PARAMS_PTR; + +typedef struct CK_GOSTR3410_DERIVE_PARAMS { + CK_EC_KDF_TYPE kdf; + CK_BYTE_PTR pPublicData; + CK_ULONG ulPublicDataLen; + CK_BYTE_PTR pUKM; + CK_ULONG ulUKMLen; +} CK_GOSTR3410_DERIVE_PARAMS; + +typedef CK_GOSTR3410_DERIVE_PARAMS CK_PTR CK_GOSTR3410_DERIVE_PARAMS_PTR; + +typedef struct CK_GOSTR3410_KEY_WRAP_PARAMS { + CK_BYTE_PTR pWrapOID; + CK_ULONG ulWrapOIDLen; + CK_BYTE_PTR pUKM; + CK_ULONG ulUKMLen; + CK_OBJECT_HANDLE hKey; +} CK_GOSTR3410_KEY_WRAP_PARAMS; + +typedef CK_GOSTR3410_KEY_WRAP_PARAMS CK_PTR CK_GOSTR3410_KEY_WRAP_PARAMS_PTR; + +typedef struct CK_SEED_CBC_ENCRYPT_DATA_PARAMS { + CK_BYTE iv[16]; + CK_BYTE_PTR pData; + CK_ULONG length; +} CK_SEED_CBC_ENCRYPT_DATA_PARAMS; + +typedef CK_SEED_CBC_ENCRYPT_DATA_PARAMS CK_PTR \ + CK_SEED_CBC_ENCRYPT_DATA_PARAMS_PTR; + +#endif /* _PKCS11T_H_ */ + diff --git a/lib/isc/inet_aton.c b/lib/isc/inet_aton.c new file mode 100644 index 0000000..aa54667 --- /dev/null +++ b/lib/isc/inet_aton.c @@ -0,0 +1,187 @@ +/* + * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Copyright (c) 1983, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ +/*! \file */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)inet_addr.c 8.1 (Berkeley) 6/17/93"; +static char rcsid[] = "$Id: inet_aton.c,v 1.23 2008/12/01 23:47:45 tbox Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <config.h> + +#include <ctype.h> +#include <stddef.h> /* Required for NULL. */ + +#include <isc/types.h> +#include <isc/net.h> + +/*% + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ +int +isc_net_aton(const char *cp, struct in_addr *addr) { + uint32_t val; + int base; + ptrdiff_t n; + unsigned char c; + uint32_t parts[4]; + uint32_t *pp = parts; + int digit; + + c = *cp; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!isdigit(c & 0xff)) + return (0); + val = 0; base = 10; digit = 0; + if (c == '0') { + c = *++cp; + if (c == 'x' || c == 'X') + base = 16, c = *++cp; + else { + base = 8; + digit = 1; + } + } + for (;;) { + /* + * isascii() is valid for all integer values, and + * when it is true, c is known to be in scope + * for isdigit(). No cast necessary. Similar + * comment applies for later ctype uses. + */ + if (isascii(c) && isdigit(c)) { + if (base == 8 && (c == '8' || c == '9')) + return (0); + val = (val * base) + (c - '0'); + c = *++cp; + digit = 1; + } else if (base == 16 && isascii(c) && isxdigit(c)) { + val = (val << 4) | + (c + 10 - (islower(c) ? 'a' : 'A')); + c = *++cp; + digit = 1; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3 || val > 0xffU) + return (0); + *pp++ = val; + c = *++cp; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && (!isascii(c) || !isspace(c))) + return (0); + /* + * Did we get a valid digit? + */ + if (!digit) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffffU) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffffU) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xffU) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr != NULL) + addr->s_addr = htonl(val); + + return (1); +} diff --git a/lib/isc/inet_ntop.c b/lib/isc/inet_ntop.c new file mode 100644 index 0000000..fb06f85 --- /dev/null +++ b/lib/isc/inet_ntop.c @@ -0,0 +1,196 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> + +#include <isc/net.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/util.h> + +#define NS_INT16SZ 2 +#define NS_IN6ADDRSZ 16 + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static const char *inet_ntop4(const unsigned char *src, char *dst, + size_t size); + +#ifdef AF_INET6 +static const char *inet_ntop6(const unsigned char *src, char *dst, + size_t size); +#endif + +/*! char * + * isc_net_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * \return + * pointer to presentation format address (`dst'), or NULL (see errno). + * \author + * Paul Vixie, 1996. + */ +const char * +isc_net_ntop(int af, const void *src, char *dst, size_t size) +{ + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, size)); +#ifdef AF_INET6 + case AF_INET6: + return (inet_ntop6(src, dst, size)); +#endif + default: + errno = EAFNOSUPPORT; + return (NULL); + } + /* NOTREACHED */ +} + +/*! const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address + * \return + * `dst' (as a const) + * \note + * (1) uses no statics + * \note + * (2) takes a unsigned char* not an in_addr as input + * \author + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4(const unsigned char *src, char *dst, size_t size) +{ + static const char *fmt = "%u.%u.%u.%u"; + char tmp[sizeof("255.255.255.255")]; + int n; + + + n = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); + if (n < 0 || (size_t)n >= size) { + errno = ENOSPC; + return (NULL); + } + strlcpy(dst, tmp, size); + + return (dst); +} + +/*! const char * + * isc_inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * \author + * Paul Vixie, 1996. + */ +#ifdef AF_INET6 +static const char * +inet_ntop6(const unsigned char *src, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")], *tp; + struct { int base, len; } best, cur; + unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof(words)); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; /* silence compiler */ + cur.base = -1; + cur.len = 0; /* silence compiler */ + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && (best.len == 6 || + (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, + sizeof(tmp) - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + INSIST((size_t)(tp - tmp) < sizeof(tmp)); + tp += snprintf(tp, sizeof(tmp) - (tp - tmp), "%x", words[i]); + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == + (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strlcpy(dst, tmp, size); + return (dst); +} +#endif /* AF_INET6 */ diff --git a/lib/isc/inet_pton.c b/lib/isc/inet_pton.c new file mode 100644 index 0000000..bde88e3 --- /dev/null +++ b/lib/isc/inet_pton.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <errno.h> +#include <string.h> + +#include <isc/net.h> + +/*% INT16 Size */ +#define NS_INT16SZ 2 +/*% IPv4 Address Size */ +#define NS_INADDRSZ 4 +/*% IPv6 Address Size */ +#define NS_IN6ADDRSZ 16 + +/* + * WARNING: Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +static int inet_pton4(const char *src, unsigned char *dst); +static int inet_pton6(const char *src, unsigned char *dst); + +/*% + * convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * \return + * 1 if the address was valid for the specified address family + * 0 if the address wasn't valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * \author + * Paul Vixie, 1996. + */ +int +isc_net_pton(int af, const char *src, void *dst) { + switch (af) { + case AF_INET: + return (inet_pton4(src, dst)); + case AF_INET6: + return (inet_pton6(src, dst)); + default: + errno = EAFNOSUPPORT; + return (-1); + } + /* NOTREACHED */ +} + +/*!\fn static int inet_pton4(const char *src, unsigned char *dst) + * \brief + * like inet_aton() but without all the hexadecimal and shorthand. + * \return + * 1 if `src' is a valid dotted quad, else 0. + * \note + * does not touch `dst' unless it's returning 1. + * \author + * Paul Vixie, 1996. + */ +static int +inet_pton4(const char *src, unsigned char *dst) { + static const char digits[] = "0123456789"; + int saw_digit, octets, ch; + unsigned char tmp[NS_INADDRSZ], *tp; + + saw_digit = 0; + octets = 0; + *(tp = tmp) = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr(digits, ch)) != NULL) { + unsigned int byte = *tp * 10; + + byte += (int)(pch - digits); + if (saw_digit && *tp == 0) + return (0); + if (byte > 255) + return (0); + *tp = byte; + if (!saw_digit) { + if (++octets > 4) + return (0); + saw_digit = 1; + } + } else if (ch == '.' && saw_digit) { + if (octets == 4) + return (0); + *++tp = 0; + saw_digit = 0; + } else + return (0); + } + if (octets < 4) + return (0); + memmove(dst, tmp, NS_INADDRSZ); + return (1); +} + +/*% + * convert presentation level address to network order binary form. + * \return + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * \note + * (1) does not touch `dst' unless it's returning 1. + * \note + * (2) :: in a full address is silently ignored. + * \author + * inspired by Mark Andrews. + * \author + * Paul Vixie, 1996. + */ +static int +inet_pton6(const char *src, unsigned char *dst) { + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, seen_xdigits; + unsigned int val; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + seen_xdigits = 0; + val = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (++seen_xdigits > 4) + return (0); + continue; + } + if (ch == ':') { + curtok = src; + if (!seen_xdigits) { + if (colonp) + return (0); + colonp = tp; + continue; + } + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + seen_xdigits = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + inet_pton4(curtok, tp) > 0) { + tp += NS_INADDRSZ; + seen_xdigits = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return (0); + } + if (seen_xdigits) { + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (unsigned char) (val >> 8) & 0xff; + *tp++ = (unsigned char) val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = (int)(tp - colonp); + int i; + + if (tp == endp) + return (0); + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + memmove(dst, tmp, NS_IN6ADDRSZ); + return (1); +} diff --git a/lib/isc/iterated_hash.c b/lib/isc/iterated_hash.c new file mode 100644 index 0000000..cf4e67f --- /dev/null +++ b/lib/isc/iterated_hash.c @@ -0,0 +1,42 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include "config.h" + +#include <stdio.h> + +#include <isc/sha1.h> +#include <isc/iterated_hash.h> + +int +isc_iterated_hash(unsigned char out[ISC_SHA1_DIGESTLENGTH], + unsigned int hashalg, int iterations, + const unsigned char *salt, int saltlength, + const unsigned char *in, int inlength) +{ + isc_sha1_t ctx; + int n = 0; + + if (hashalg != 1) + return (0); + + do { + isc_sha1_init(&ctx); + isc_sha1_update(&ctx, in, inlength); + isc_sha1_update(&ctx, salt, saltlength); + isc_sha1_final(&ctx, out); + in = out; + inlength = ISC_SHA1_DIGESTLENGTH; + } while (n++ < iterations); + + return (ISC_SHA1_DIGESTLENGTH); +} diff --git a/lib/isc/lex.c b/lib/isc/lex.c new file mode 100644 index 0000000..a8955bc --- /dev/null +++ b/lib/isc/lex.c @@ -0,0 +1,1051 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <isc/buffer.h> +#include <isc/file.h> +#include <isc/lex.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/parseint.h> +#include <isc/print.h> +#include <isc/stdio.h> +#include <isc/string.h> +#include <isc/util.h> + +typedef struct inputsource { + isc_result_t result; + bool is_file; + bool need_close; + bool at_eof; + bool last_was_eol; + isc_buffer_t * pushback; + unsigned int ignored; + void * input; + char * name; + unsigned long line; + unsigned long saved_line; + ISC_LINK(struct inputsource) link; +} inputsource; + +#define LEX_MAGIC ISC_MAGIC('L', 'e', 'x', '!') +#define VALID_LEX(l) ISC_MAGIC_VALID(l, LEX_MAGIC) + +struct isc_lex { + /* Unlocked. */ + unsigned int magic; + isc_mem_t * mctx; + size_t max_token; + char * data; + unsigned int comments; + bool comment_ok; + bool last_was_eol; + unsigned int brace_count; + unsigned int paren_count; + unsigned int saved_paren_count; + isc_lexspecials_t specials; + LIST(struct inputsource) sources; +}; + +static inline isc_result_t +grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) { + char *tmp; + + tmp = isc_mem_get(lex->mctx, lex->max_token * 2 + 1); + if (tmp == NULL) + return (ISC_R_NOMEMORY); + memmove(tmp, lex->data, lex->max_token + 1); + *currp = tmp + (*currp - lex->data); + if (*prevp != NULL) + *prevp = tmp + (*prevp - lex->data); + isc_mem_put(lex->mctx, lex->data, lex->max_token + 1); + lex->data = tmp; + *remainingp += lex->max_token; + lex->max_token *= 2; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) { + isc_lex_t *lex; + + /* + * Create a lexer. + */ + REQUIRE(lexp != NULL && *lexp == NULL); + + if (max_token == 0U) + max_token = 1; + + lex = isc_mem_get(mctx, sizeof(*lex)); + if (lex == NULL) + return (ISC_R_NOMEMORY); + lex->data = isc_mem_get(mctx, max_token + 1); + if (lex->data == NULL) { + isc_mem_put(mctx, lex, sizeof(*lex)); + return (ISC_R_NOMEMORY); + } + lex->mctx = mctx; + lex->max_token = max_token; + lex->comments = 0; + lex->comment_ok = true; + lex->last_was_eol = true; + lex->brace_count = 0; + lex->paren_count = 0; + lex->saved_paren_count = 0; + memset(lex->specials, 0, 256); + INIT_LIST(lex->sources); + lex->magic = LEX_MAGIC; + + *lexp = lex; + + return (ISC_R_SUCCESS); +} + +void +isc_lex_destroy(isc_lex_t **lexp) { + isc_lex_t *lex; + + /* + * Destroy the lexer. + */ + + REQUIRE(lexp != NULL); + lex = *lexp; + REQUIRE(VALID_LEX(lex)); + + while (!EMPTY(lex->sources)) + RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS); + if (lex->data != NULL) + isc_mem_put(lex->mctx, lex->data, lex->max_token + 1); + lex->magic = 0; + isc_mem_put(lex->mctx, lex, sizeof(*lex)); + + *lexp = NULL; +} + +unsigned int +isc_lex_getcomments(isc_lex_t *lex) { + /* + * Return the current lexer commenting styles. + */ + + REQUIRE(VALID_LEX(lex)); + + return (lex->comments); +} + +void +isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) { + /* + * Set allowed lexer commenting styles. + */ + + REQUIRE(VALID_LEX(lex)); + + lex->comments = comments; +} + +void +isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials) { + /* + * Put the current list of specials into 'specials'. + */ + + REQUIRE(VALID_LEX(lex)); + + memmove(specials, lex->specials, 256); +} + +void +isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials) { + /* + * The characters in 'specials' are returned as tokens. Along with + * whitespace, they delimit strings and numbers. + */ + + REQUIRE(VALID_LEX(lex)); + + memmove(lex->specials, specials, 256); +} + +static inline isc_result_t +new_source(isc_lex_t *lex, bool is_file, bool need_close, + void *input, const char *name) +{ + inputsource *source; + isc_result_t result; + + source = isc_mem_get(lex->mctx, sizeof(*source)); + if (source == NULL) + return (ISC_R_NOMEMORY); + source->result = ISC_R_SUCCESS; + source->is_file = is_file; + source->need_close = need_close; + source->at_eof = false; + source->last_was_eol = lex->last_was_eol; + source->input = input; + source->name = isc_mem_strdup(lex->mctx, name); + if (source->name == NULL) { + isc_mem_put(lex->mctx, source, sizeof(*source)); + return (ISC_R_NOMEMORY); + } + source->pushback = NULL; + result = isc_buffer_allocate(lex->mctx, &source->pushback, + (unsigned int)lex->max_token); + if (result != ISC_R_SUCCESS) { + isc_mem_free(lex->mctx, source->name); + isc_mem_put(lex->mctx, source, sizeof(*source)); + return (result); + } + source->ignored = 0; + source->line = 1; + ISC_LIST_INITANDPREPEND(lex->sources, source, link); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_lex_openfile(isc_lex_t *lex, const char *filename) { + isc_result_t result; + FILE *stream = NULL; + + /* + * Open 'filename' and make it the current input source for 'lex'. + */ + + REQUIRE(VALID_LEX(lex)); + + result = isc_stdio_open(filename, "r", &stream); + if (result != ISC_R_SUCCESS) + return (result); + + result = new_source(lex, true, true, stream, filename); + if (result != ISC_R_SUCCESS) + (void)fclose(stream); + return (result); +} + +isc_result_t +isc_lex_openstream(isc_lex_t *lex, FILE *stream) { + char name[128]; + + /* + * Make 'stream' the current input source for 'lex'. + */ + + REQUIRE(VALID_LEX(lex)); + + snprintf(name, sizeof(name), "stream-%p", stream); + + return (new_source(lex, true, false, stream, name)); +} + +isc_result_t +isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) { + char name[128]; + + /* + * Make 'buffer' the current input source for 'lex'. + */ + + REQUIRE(VALID_LEX(lex)); + + snprintf(name, sizeof(name), "buffer-%p", buffer); + + return (new_source(lex, false, false, buffer, name)); +} + +isc_result_t +isc_lex_close(isc_lex_t *lex) { + inputsource *source; + + /* + * Close the most recently opened object (i.e. file or buffer). + */ + + REQUIRE(VALID_LEX(lex)); + + source = HEAD(lex->sources); + if (source == NULL) + return (ISC_R_NOMORE); + + ISC_LIST_UNLINK(lex->sources, source, link); + lex->last_was_eol = source->last_was_eol; + if (source->is_file) { + if (source->need_close) + (void)fclose((FILE *)(source->input)); + } + isc_mem_free(lex->mctx, source->name); + isc_buffer_free(&source->pushback); + isc_mem_put(lex->mctx, source, sizeof(*source)); + + return (ISC_R_SUCCESS); +} + +typedef enum { + lexstate_start, + lexstate_crlf, + lexstate_string, + lexstate_number, + lexstate_maybecomment, + lexstate_ccomment, + lexstate_ccommentend, + lexstate_eatline, + lexstate_qstring, + lexstate_btext +} lexstate; + +#define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL) + +static void +pushback(inputsource *source, int c) { + REQUIRE(source->pushback->current > 0); + if (c == EOF) { + source->at_eof = false; + return; + } + source->pushback->current--; + if (c == '\n') + source->line--; +} + +static isc_result_t +pushandgrow(isc_lex_t *lex, inputsource *source, int c) { + if (isc_buffer_availablelength(source->pushback) == 0) { + isc_buffer_t *tbuf = NULL; + unsigned int oldlen; + isc_region_t used; + isc_result_t result; + + oldlen = isc_buffer_length(source->pushback); + result = isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_usedregion(source->pushback, &used); + result = isc_buffer_copyregion(tbuf, &used); + INSIST(result == ISC_R_SUCCESS); + tbuf->current = source->pushback->current; + isc_buffer_free(&source->pushback); + source->pushback = tbuf; + } + isc_buffer_putuint8(source->pushback, (uint8_t)c); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { + inputsource *source; + int c; + bool done = false; + bool no_comments = false; + bool escaped = false; + lexstate state = lexstate_start; + lexstate saved_state = lexstate_start; + isc_buffer_t *buffer; + FILE *stream; + char *curr, *prev; + size_t remaining; + uint32_t as_ulong; + unsigned int saved_options; + isc_result_t result; + + /* + * Get the next token. + */ + + REQUIRE(VALID_LEX(lex)); + source = HEAD(lex->sources); + REQUIRE(tokenp != NULL); + + if (source == NULL) { + if ((options & ISC_LEXOPT_NOMORE) != 0) { + tokenp->type = isc_tokentype_nomore; + return (ISC_R_SUCCESS); + } + return (ISC_R_NOMORE); + } + + if (source->result != ISC_R_SUCCESS) + return (source->result); + + lex->saved_paren_count = lex->paren_count; + source->saved_line = source->line; + + if (isc_buffer_remaininglength(source->pushback) == 0 && + source->at_eof) + { + if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && + lex->paren_count != 0) + { + lex->paren_count = 0; + return (ISC_R_UNBALANCED); + } + if ((options & ISC_LEXOPT_BTEXT) != 0 && + lex->brace_count != 0) + { + lex->brace_count = 0; + return (ISC_R_UNBALANCED); + } + if ((options & ISC_LEXOPT_EOF) != 0) { + tokenp->type = isc_tokentype_eof; + return (ISC_R_SUCCESS); + } + return (ISC_R_EOF); + } + + isc_buffer_compact(source->pushback); + + saved_options = options; + if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0) + options &= ~IWSEOL; + + curr = lex->data; + *curr = '\0'; + + prev = NULL; + remaining = lex->max_token; + +#ifdef HAVE_FLOCKFILE + if (source->is_file) + flockfile(source->input); +#endif + + do { + if (isc_buffer_remaininglength(source->pushback) == 0) { + if (source->is_file) { + stream = source->input; + +#if defined(HAVE_FLOCKFILE) && defined(HAVE_GETCUNLOCKED) + c = getc_unlocked(stream); +#else + c = getc(stream); +#endif + if (c == EOF) { + if (ferror(stream)) { + source->result = ISC_R_IOERROR; + result = source->result; + goto done; + } + source->at_eof = true; + } + } else { + buffer = source->input; + + if (buffer->current == buffer->used) { + c = EOF; + source->at_eof = true; + } else { + c = *((unsigned char *)buffer->base + + buffer->current); + buffer->current++; + } + } + if (c != EOF) { + source->result = pushandgrow(lex, source, c); + if (source->result != ISC_R_SUCCESS) { + result = source->result; + goto done; + } + } + } + + if (!source->at_eof) { + if (state == lexstate_start) + /* Token has not started yet. */ + source->ignored = + isc_buffer_consumedlength(source->pushback); + c = isc_buffer_getuint8(source->pushback); + } else { + c = EOF; + } + + if (c == '\n') + source->line++; + + if (lex->comment_ok && !no_comments) { + if (!escaped && c == ';' && + ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE) + != 0)) { + saved_state = state; + state = lexstate_eatline; + no_comments = true; + continue; + } else if (c == '/' && + (lex->comments & + (ISC_LEXCOMMENT_C| + ISC_LEXCOMMENT_CPLUSPLUS)) != 0) { + saved_state = state; + state = lexstate_maybecomment; + no_comments = true; + continue; + } else if (c == '#' && + ((lex->comments & ISC_LEXCOMMENT_SHELL) + != 0)) { + saved_state = state; + state = lexstate_eatline; + no_comments = true; + continue; + } + } + + no_read: + /* INSIST(c == EOF || (c >= 0 && c <= 255)); */ + switch (state) { + case lexstate_start: + if (c == EOF) { + lex->last_was_eol = false; + if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && + lex->paren_count != 0) { + lex->paren_count = 0; + result = ISC_R_UNBALANCED; + goto done; + } + if ((options & ISC_LEXOPT_BTEXT) != 0 && + lex->brace_count != 0) { + lex->brace_count = 0; + result = ISC_R_UNBALANCED; + goto done; + } + if ((options & ISC_LEXOPT_EOF) == 0) { + result = ISC_R_EOF; + goto done; + } + tokenp->type = isc_tokentype_eof; + done = true; + } else if (c == ' ' || c == '\t') { + if (lex->last_was_eol && + (options & ISC_LEXOPT_INITIALWS) + != 0) { + lex->last_was_eol = false; + tokenp->type = isc_tokentype_initialws; + tokenp->value.as_char = c; + done = true; + } + } else if (c == '\n') { + if ((options & ISC_LEXOPT_EOL) != 0) { + tokenp->type = isc_tokentype_eol; + done = true; + } + lex->last_was_eol = true; + } else if (c == '\r') { + if ((options & ISC_LEXOPT_EOL) != 0) + state = lexstate_crlf; + } else if (c == '"' && + (options & ISC_LEXOPT_QSTRING) != 0) { + lex->last_was_eol = false; + no_comments = true; + state = lexstate_qstring; + } else if (lex->specials[c]) { + lex->last_was_eol = false; + if ((c == '(' || c == ')') && + (options & ISC_LEXOPT_DNSMULTILINE) != 0) + { + if (c == '(') { + if (lex->paren_count == 0) + options &= ~IWSEOL; + lex->paren_count++; + } else { + if (lex->paren_count == 0) { + result = + ISC_R_UNBALANCED; + goto done; + } + lex->paren_count--; + if (lex->paren_count == 0) + options = saved_options; + } + continue; + } else if (c == '{' && + (options & ISC_LEXOPT_BTEXT) != 0) + { + if (lex->brace_count != 0) { + result = ISC_R_UNBALANCED; + goto done; + } + lex->brace_count++; + options &= ~IWSEOL; + state = lexstate_btext; + no_comments = true; + continue; + } + tokenp->type = isc_tokentype_special; + tokenp->value.as_char = c; + done = true; + } else if (isdigit((unsigned char)c) && + (options & ISC_LEXOPT_NUMBER) != 0) { + lex->last_was_eol = false; + if ((options & ISC_LEXOPT_OCTAL) != 0 && + (c == '8' || c == '9')) + state = lexstate_string; + else + state = lexstate_number; + goto no_read; + } else { + lex->last_was_eol = false; + state = lexstate_string; + goto no_read; + } + break; + case lexstate_crlf: + if (c != '\n') + pushback(source, c); + tokenp->type = isc_tokentype_eol; + done = true; + lex->last_was_eol = true; + break; + case lexstate_number: + if (c == EOF || !isdigit((unsigned char)c)) { + if (c == ' ' || c == '\t' || c == '\r' || + c == '\n' || c == EOF || + lex->specials[c]) { + int base; + if ((options & ISC_LEXOPT_OCTAL) != 0) + base = 8; + else if ((options & ISC_LEXOPT_CNUMBER) != 0) + base = 0; + else + base = 10; + pushback(source, c); + + result = isc_parse_uint32(&as_ulong, + lex->data, + base); + if (result == ISC_R_SUCCESS) { + tokenp->type = + isc_tokentype_number; + tokenp->value.as_ulong = + as_ulong; + } else if (result == ISC_R_BADNUMBER) { + isc_tokenvalue_t *v; + + tokenp->type = + isc_tokentype_string; + v = &(tokenp->value); + v->as_textregion.base = + lex->data; + v->as_textregion.length = + (unsigned int) + (lex->max_token - + remaining); + } else + goto done; + done = true; + continue; + } else if (!(options & ISC_LEXOPT_CNUMBER) || + ((c != 'x' && c != 'X') || + (curr != &lex->data[1]) || + (lex->data[0] != '0'))) { + /* Above test supports hex numbers */ + state = lexstate_string; + } + } else if ((options & ISC_LEXOPT_OCTAL) != 0 && + (c == '8' || c == '9')) { + state = lexstate_string; + } + if (remaining == 0U) { + result = grow_data(lex, &remaining, + &curr, &prev); + if (result != ISC_R_SUCCESS) + goto done; + } + INSIST(remaining > 0U); + *curr++ = c; + *curr = '\0'; + remaining--; + break; + case lexstate_string: + /* + * EOF needs to be checked before lex->specials[c] + * as lex->specials[EOF] is not a good idea. + */ + if (c == '\r' || c == '\n' || c == EOF || + (!escaped && + (c == ' ' || c == '\t' || lex->specials[c]))) { + pushback(source, c); + if (source->result != ISC_R_SUCCESS) { + result = source->result; + goto done; + } + tokenp->type = isc_tokentype_string; + tokenp->value.as_textregion.base = lex->data; + tokenp->value.as_textregion.length = + (unsigned int) + (lex->max_token - remaining); + done = true; + continue; + } + if ((options & ISC_LEXOPT_ESCAPE) != 0) + escaped = (!escaped && c == '\\') ? + true : false; + if (remaining == 0U) { + result = grow_data(lex, &remaining, + &curr, &prev); + if (result != ISC_R_SUCCESS) + goto done; + } + INSIST(remaining > 0U); + *curr++ = c; + *curr = '\0'; + remaining--; + break; + case lexstate_maybecomment: + if (c == '*' && + (lex->comments & ISC_LEXCOMMENT_C) != 0) { + state = lexstate_ccomment; + continue; + } else if (c == '/' && + (lex->comments & ISC_LEXCOMMENT_CPLUSPLUS) != 0) { + state = lexstate_eatline; + continue; + } + pushback(source, c); + c = '/'; + no_comments = false; + state = saved_state; + goto no_read; + case lexstate_ccomment: + if (c == EOF) { + result = ISC_R_UNEXPECTEDEND; + goto done; + } + if (c == '*') + state = lexstate_ccommentend; + break; + case lexstate_ccommentend: + if (c == EOF) { + result = ISC_R_UNEXPECTEDEND; + goto done; + } + if (c == '/') { + /* + * C-style comments become a single space. + * We do this to ensure that a comment will + * act as a delimiter for strings and + * numbers. + */ + c = ' '; + no_comments = false; + state = saved_state; + goto no_read; + } else if (c != '*') + state = lexstate_ccomment; + break; + case lexstate_eatline: + if ((c == '\n') || (c == EOF)) { + no_comments = false; + state = saved_state; + goto no_read; + } + break; + case lexstate_qstring: + if (c == EOF) { + result = ISC_R_UNEXPECTEDEND; + goto done; + } + if (c == '"') { + if (escaped) { + escaped = false; + /* + * Overwrite the preceding backslash. + */ + INSIST(prev != NULL); + *prev = '"'; + } else { + tokenp->type = isc_tokentype_qstring; + tokenp->value.as_textregion.base = + lex->data; + tokenp->value.as_textregion.length = + (unsigned int) + (lex->max_token - remaining); + no_comments = false; + done = true; + } + } else { + if (c == '\n' && !escaped && + (options & ISC_LEXOPT_QSTRINGMULTILINE) == 0) { + pushback(source, c); + result = ISC_R_UNBALANCEDQUOTES; + goto done; + } + if (c == '\\' && !escaped) + escaped = true; + else + escaped = false; + if (remaining == 0U) { + result = grow_data(lex, &remaining, + &curr, &prev); + if (result != ISC_R_SUCCESS) + goto done; + } + INSIST(remaining > 0U); + prev = curr; + *curr++ = c; + *curr = '\0'; + remaining--; + } + break; + case lexstate_btext: + if (c == EOF) { + result = ISC_R_UNEXPECTEDEND; + goto done; + } + if (c == '{') { + if (escaped) { + escaped = false; + } else { + lex->brace_count++; + } + } else if (c == '}') { + if (escaped) { + escaped = false; + } else { + INSIST(lex->brace_count > 0); + lex->brace_count--; + } + + if (lex->brace_count == 0) { + tokenp->type = isc_tokentype_btext; + tokenp->value.as_textregion.base = + lex->data; + tokenp->value.as_textregion.length = + (unsigned int) (lex->max_token - + remaining); + no_comments = false; + done = true; + break; + } + } + + if (c == '\\' && !escaped) + escaped = true; + else + escaped = false; + + if (remaining == 0U) { + result = grow_data(lex, &remaining, + &curr, &prev); + if (result != ISC_R_SUCCESS) + goto done; + } + INSIST(remaining > 0U); + prev = curr; + *curr++ = c; + *curr = '\0'; + remaining--; + break; + default: + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_LEX, + ISC_MSG_UNEXPECTEDSTATE, + "Unexpected state %d"), + state); + /* Does not return. */ + } + + } while (!done); + + result = ISC_R_SUCCESS; + done: +#ifdef HAVE_FLOCKFILE + if (source->is_file) + funlockfile(source->input); +#endif + return (result); +} + +isc_result_t +isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token, + isc_tokentype_t expect, bool eol) +{ + unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | + ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE; + isc_result_t result; + + if (expect == isc_tokentype_qstring) + options |= ISC_LEXOPT_QSTRING; + else if (expect == isc_tokentype_number) + options |= ISC_LEXOPT_NUMBER; + result = isc_lex_gettoken(lex, options, token); + if (result == ISC_R_RANGE) + isc_lex_ungettoken(lex, token); + if (result != ISC_R_SUCCESS) + return (result); + + if (eol && ((token->type == isc_tokentype_eol) || + (token->type == isc_tokentype_eof))) + return (ISC_R_SUCCESS); + if (token->type == isc_tokentype_string && + expect == isc_tokentype_qstring) + return (ISC_R_SUCCESS); + if (token->type != expect) { + isc_lex_ungettoken(lex, token); + if (token->type == isc_tokentype_eol || + token->type == isc_tokentype_eof) + return (ISC_R_UNEXPECTEDEND); + if (expect == isc_tokentype_number) + return (ISC_R_BADNUMBER); + return (ISC_R_UNEXPECTEDTOKEN); + } + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol) +{ + unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | + ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE| + ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL; + isc_result_t result; + + result = isc_lex_gettoken(lex, options, token); + if (result == ISC_R_RANGE) + isc_lex_ungettoken(lex, token); + if (result != ISC_R_SUCCESS) + return (result); + + if (eol && ((token->type == isc_tokentype_eol) || + (token->type == isc_tokentype_eof))) + return (ISC_R_SUCCESS); + if (token->type != isc_tokentype_number) { + isc_lex_ungettoken(lex, token); + if (token->type == isc_tokentype_eol || + token->type == isc_tokentype_eof) + return (ISC_R_UNEXPECTEDEND); + return (ISC_R_BADNUMBER); + } + return (ISC_R_SUCCESS); +} + +void +isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) { + inputsource *source; + /* + * Unget the current token. + */ + + REQUIRE(VALID_LEX(lex)); + source = HEAD(lex->sources); + REQUIRE(source != NULL); + REQUIRE(tokenp != NULL); + REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 || + tokenp->type == isc_tokentype_eof); + + UNUSED(tokenp); + + isc_buffer_first(source->pushback); + lex->paren_count = lex->saved_paren_count; + source->line = source->saved_line; + source->at_eof = false; +} + +void +isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r) +{ + inputsource *source; + + REQUIRE(VALID_LEX(lex)); + source = HEAD(lex->sources); + REQUIRE(source != NULL); + REQUIRE(tokenp != NULL); + REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 || + tokenp->type == isc_tokentype_eof); + + UNUSED(tokenp); + + INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback)); + r->base = (unsigned char *)isc_buffer_base(source->pushback) + + source->ignored; + r->length = isc_buffer_consumedlength(source->pushback) - + source->ignored; +} + +char * +isc_lex_getsourcename(isc_lex_t *lex) { + inputsource *source; + + REQUIRE(VALID_LEX(lex)); + source = HEAD(lex->sources); + + if (source == NULL) + return (NULL); + + return (source->name); +} + +unsigned long +isc_lex_getsourceline(isc_lex_t *lex) { + inputsource *source; + + REQUIRE(VALID_LEX(lex)); + source = HEAD(lex->sources); + + if (source == NULL) + return (0); + + return (source->line); +} + +isc_result_t +isc_lex_setsourcename(isc_lex_t *lex, const char *name) { + inputsource *source; + char *newname; + + REQUIRE(VALID_LEX(lex)); + source = HEAD(lex->sources); + + if (source == NULL) + return (ISC_R_NOTFOUND); + newname = isc_mem_strdup(lex->mctx, name); + if (newname == NULL) + return (ISC_R_NOMEMORY); + isc_mem_free(lex->mctx, source->name); + source->name = newname; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_lex_setsourceline(isc_lex_t *lex, unsigned long line) { + inputsource *source; + + REQUIRE(VALID_LEX(lex)); + source = HEAD(lex->sources); + + if (source == NULL) + return (ISC_R_NOTFOUND); + + source->line = line; + return (ISC_R_SUCCESS); +} + +bool +isc_lex_isfile(isc_lex_t *lex) { + inputsource *source; + + REQUIRE(VALID_LEX(lex)); + + source = HEAD(lex->sources); + + if (source == NULL) + return (false); + + return (source->is_file); +} diff --git a/lib/isc/lfsr.c b/lib/isc/lfsr.c new file mode 100644 index 0000000..3b0b47f --- /dev/null +++ b/lib/isc/lfsr.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stddef.h> +#include <inttypes.h> +#include <stdlib.h> + +#include <isc/assertions.h> +#include <isc/lfsr.h> +#include <isc/util.h> + +#define VALID_LFSR(x) (x != NULL) + +void +isc_lfsr_init(isc_lfsr_t *lfsr, uint32_t state, unsigned int bits, + uint32_t tap, unsigned int count, + isc_lfsrreseed_t reseed, void *arg) +{ + REQUIRE(VALID_LFSR(lfsr)); + REQUIRE(8 <= bits && bits <= 32); + REQUIRE(tap != 0); + + lfsr->state = state; + lfsr->bits = bits; + lfsr->tap = tap; + lfsr->count = count; + lfsr->reseed = reseed; + lfsr->arg = arg; + + if (count == 0 && reseed != NULL) + reseed(lfsr, arg); + if (lfsr->state == 0) + lfsr->state = 0xffffffffU >> (32 - lfsr->bits); +} + +/*! + * Return the next state of the lfsr. + */ +static inline uint32_t +lfsr_generate(isc_lfsr_t *lfsr) +{ + + /* + * If the previous state is zero, we must fill it with something + * here, or we will begin to generate an extremely predictable output. + * + * First, give the reseed function a crack at it. If the state is + * still 0, set it to all ones. + */ + if (lfsr->state == 0) { + if (lfsr->reseed != NULL) + lfsr->reseed(lfsr, lfsr->arg); + if (lfsr->state == 0) + lfsr->state = 0xffffffffU >> (32 - lfsr->bits); + } + + if (lfsr->state & 0x01) { + lfsr->state = (lfsr->state >> 1) ^ lfsr->tap; + return (1); + } else { + lfsr->state >>= 1; + return (0); + } +} + +void +isc_lfsr_generate(isc_lfsr_t *lfsr, void *data, unsigned int count) +{ + unsigned char *p; + unsigned int bit; + unsigned int byte; + + REQUIRE(VALID_LFSR(lfsr)); + REQUIRE(data != NULL); + REQUIRE(count > 0); + + p = data; + byte = count; + + while (byte--) { + *p = 0; + for (bit = 0; bit < 7; bit++) { + *p |= lfsr_generate(lfsr); + *p <<= 1; + } + *p |= lfsr_generate(lfsr); + p++; + } + + if (lfsr->count != 0 && lfsr->reseed != NULL) { + if (lfsr->count <= count * 8) + lfsr->reseed(lfsr, lfsr->arg); + else + lfsr->count -= (count * 8); + } +} + +static inline uint32_t +lfsr_skipgenerate(isc_lfsr_t *lfsr, unsigned int skip) +{ + while (skip--) + (void)lfsr_generate(lfsr); + + (void)lfsr_generate(lfsr); + + return (lfsr->state); +} + +/* + * Skip "skip" states in "lfsr". + */ +void +isc_lfsr_skip(isc_lfsr_t *lfsr, unsigned int skip) +{ + REQUIRE(VALID_LFSR(lfsr)); + + while (skip--) + (void)lfsr_generate(lfsr); +} + +/* + * Skip states in lfsr1 and lfsr2 using the other's current state. + * Return the final state of lfsr1 ^ lfsr2. + */ +uint32_t +isc_lfsr_generate32(isc_lfsr_t *lfsr1, isc_lfsr_t *lfsr2) +{ + uint32_t state1, state2; + uint32_t skip1, skip2; + + REQUIRE(VALID_LFSR(lfsr1)); + REQUIRE(VALID_LFSR(lfsr2)); + + skip1 = lfsr1->state & 0x01; + skip2 = lfsr2->state & 0x01; + + /* cross-skip. */ + state1 = lfsr_skipgenerate(lfsr1, skip2); + state2 = lfsr_skipgenerate(lfsr2, skip1); + + return (state1 ^ state2); +} diff --git a/lib/isc/lib.c b/lib/isc/lib.c new file mode 100644 index 0000000..018cc3e --- /dev/null +++ b/lib/isc/lib.c @@ -0,0 +1,97 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> + +#include <isc/app.h> +#include <isc/lib.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/once.h> +#include <isc/print.h> +#include <isc/socket.h> +#include <isc/task.h> +#include <isc/timer.h> +#include <isc/util.h> + +/*** + *** Globals + ***/ + +LIBISC_EXTERNAL_DATA isc_msgcat_t * isc_msgcat = NULL; + + +/*** + *** Private + ***/ + +static isc_once_t msgcat_once = ISC_ONCE_INIT; + +/*** + *** Functions + ***/ + +static void +open_msgcat(void) { + isc_msgcat_open("libisc.cat", &isc_msgcat); +} + +void +isc_lib_initmsgcat(void) { + isc_result_t result; + + /*! + * Initialize the ISC library's message catalog, isc_msgcat, if it + * has not already been initialized. + */ + + result = isc_once_do(&msgcat_once, open_msgcat); + if (result != ISC_R_SUCCESS) { + /* + * Normally we'd use RUNTIME_CHECK() or FATAL_ERROR(), but + * we can't do that here, since they might call us! + * (Note that the catalog might be open anyway, so we might + * as well try to provide an internationalized message.) + */ + fprintf(stderr, "%s:%d: %s: isc_once_do() %s.\n", + __FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FATALERROR, "fatal error"), + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + abort(); + } +} + +static isc_once_t register_once = ISC_ONCE_INIT; + +static void +do_register(void) { + isc_bind9 = false; + + RUNTIME_CHECK(isc__mem_register() == ISC_R_SUCCESS); + RUNTIME_CHECK(isc__app_register() == ISC_R_SUCCESS); + RUNTIME_CHECK(isc__task_register() == ISC_R_SUCCESS); + RUNTIME_CHECK(isc__socket_register() == ISC_R_SUCCESS); + RUNTIME_CHECK(isc__timer_register() == ISC_R_SUCCESS); +} + +void +isc_lib_register(void) { + RUNTIME_CHECK(isc_once_do(®ister_once, do_register) + == ISC_R_SUCCESS); +} diff --git a/lib/isc/log.c b/lib/isc/log.c new file mode 100644 index 0000000..3387533 --- /dev/null +++ b/lib/isc/log.c @@ -0,0 +1,1761 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <limits.h> +#include <time.h> + +#include <sys/types.h> /* dev_t FreeBSD 2.1 */ + +#include <isc/dir.h> +#include <isc/file.h> +#include <isc/log.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/print.h> +#include <isc/stat.h> +#include <isc/stdio.h> +#include <isc/string.h> +#include <isc/time.h> +#include <isc/util.h> + +#define LCTX_MAGIC ISC_MAGIC('L', 'c', 't', 'x') +#define VALID_CONTEXT(lctx) ISC_MAGIC_VALID(lctx, LCTX_MAGIC) + +#define LCFG_MAGIC ISC_MAGIC('L', 'c', 'f', 'g') +#define VALID_CONFIG(lcfg) ISC_MAGIC_VALID(lcfg, LCFG_MAGIC) + +/* + * XXXDCL make dynamic? + */ +#define LOG_BUFFER_SIZE (8 * 1024) + +#ifndef PATH_MAX +#define PATH_MAX 1024 /* AIX and others don't define this. */ +#endif + +/*! + * This is the structure that holds each named channel. A simple linked + * list chains all of the channels together, so an individual channel is + * found by doing strcmp()s with the names down the list. Their should + * be no performance penalty from this as it is expected that the number + * of named channels will be no more than a dozen or so, and name lookups + * from the head of the list are only done when isc_log_usechannel() is + * called, which should also be very infrequent. + */ +typedef struct isc_logchannel isc_logchannel_t; + +struct isc_logchannel { + char * name; + unsigned int type; + int level; + unsigned int flags; + isc_logdestination_t destination; + ISC_LINK(isc_logchannel_t) link; +}; + +/*! + * The logchannellist structure associates categories and modules with + * channels. First the appropriate channellist is found based on the + * category, and then each structure in the linked list is checked for + * a matching module. It is expected that the number of channels + * associated with any given category will be very short, no more than + * three or four in the more unusual cases. + */ +typedef struct isc_logchannellist isc_logchannellist_t; + +struct isc_logchannellist { + const isc_logmodule_t * module; + isc_logchannel_t * channel; + ISC_LINK(isc_logchannellist_t) link; +}; + +/*! + * This structure is used to remember messages for pruning via + * isc_log_[v]write1(). + */ +typedef struct isc_logmessage isc_logmessage_t; + +struct isc_logmessage { + char * text; + isc_time_t time; + ISC_LINK(isc_logmessage_t) link; +}; + +/*! + * The isc_logconfig structure is used to store the configurable information + * about where messages are actually supposed to be sent -- the information + * that could changed based on some configuration file, as opposed to the + * the category/module specification of isc_log_[v]write[1] that is compiled + * into a program, or the debug_level which is dynamic state information. + */ +struct isc_logconfig { + unsigned int magic; + isc_log_t * lctx; + ISC_LIST(isc_logchannel_t) channels; + ISC_LIST(isc_logchannellist_t) *channellists; + unsigned int channellist_count; + unsigned int duplicate_interval; + int highest_level; + char * tag; + bool dynamic; +}; + +/*! + * This isc_log structure provides the context for the isc_log functions. + * The log context locks itself in isc_log_doit, the internal backend to + * isc_log_write. The locking is necessary both to provide exclusive access + * to the buffer into which the message is formatted and to guard against + * competing threads trying to write to the same syslog resource. (On + * some systems, such as BSD/OS, stdio is thread safe but syslog is not.) + * Unfortunately, the lock cannot guard against a _different_ logging + * context in the same program competing for syslog's attention. Thus + * There Can Be Only One, but this is not enforced. + * XXXDCL enforce it? + * + * Note that the category and module information is not locked. + * This is because in the usual case, only one isc_log_t is ever created + * in a program, and the category/module registration happens only once. + * XXXDCL it might be wise to add more locking overall. + */ +struct isc_log { + /* Not locked. */ + unsigned int magic; + isc_mem_t * mctx; + isc_logcategory_t * categories; + unsigned int category_count; + isc_logmodule_t * modules; + unsigned int module_count; + int debug_level; + isc_mutex_t lock; + /* Locked by isc_log lock. */ + isc_logconfig_t * logconfig; + char buffer[LOG_BUFFER_SIZE]; + ISC_LIST(isc_logmessage_t) messages; +}; + +/*! + * Used when ISC_LOG_PRINTLEVEL is enabled for a channel. + */ +static const char *log_level_strings[] = { + "debug", + "info", + "notice", + "warning", + "error", + "critical" +}; + +/*! + * Used to convert ISC_LOG_* priorities into syslog priorities. + * XXXDCL This will need modification for NT. + */ +static const int syslog_map[] = { + LOG_DEBUG, + LOG_INFO, + LOG_NOTICE, + LOG_WARNING, + LOG_ERR, + LOG_CRIT +}; + +/*! + * When adding new categories, a corresponding ISC_LOGCATEGORY_foo + * definition needs to be added to <isc/log.h>. + * + * The default category is provided so that the internal default can + * be overridden. Since the default is always looked up as the first + * channellist in the log context, it must come first in isc_categories[]. + */ +LIBISC_EXTERNAL_DATA isc_logcategory_t isc_categories[] = { + { "default", 0 }, /* "default" must come first. */ + { "general", 0 }, + { NULL, 0 } +}; + +/*! + * See above comment for categories on LIBISC_EXTERNAL_DATA, and apply it to modules. + */ +LIBISC_EXTERNAL_DATA isc_logmodule_t isc_modules[] = { + { "socket", 0 }, + { "time", 0 }, + { "interface", 0 }, + { "timer", 0 }, + { "file", 0 }, + { "other", 0 }, + { NULL, 0 } +}; + +/*! + * This essentially constant structure must be filled in at run time, + * because its channel member is pointed to a channel that is created + * dynamically with isc_log_createchannel. + */ +static isc_logchannellist_t default_channel; + +/*! + * libisc logs to this context. + */ +LIBISC_EXTERNAL_DATA isc_log_t *isc_lctx = NULL; + +/*! + * Forward declarations. + */ +static isc_result_t +assignchannel(isc_logconfig_t *lcfg, unsigned int category_id, + const isc_logmodule_t *module, isc_logchannel_t *channel); + +static isc_result_t +sync_channellist(isc_logconfig_t *lcfg); + +static isc_result_t +greatest_version(isc_logfile_t *file, int versions, int *greatest); + +static void +isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, bool write_once, + isc_msgcat_t *msgcat, int msgset, int msg, + const char *format, va_list args) + ISC_FORMAT_PRINTF(9, 0); + +/*@{*/ +/*! + * Convenience macros. + */ + +#define FACILITY(channel) (channel->destination.facility) +#define FILE_NAME(channel) (channel->destination.file.name) +#define FILE_STREAM(channel) (channel->destination.file.stream) +#define FILE_VERSIONS(channel) (channel->destination.file.versions) +#define FILE_MAXSIZE(channel) (channel->destination.file.maximum_size) +#define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached) + +/*@}*/ +/**** + **** Public interfaces. + ****/ + +/* + * Establish a new logging context, with default channels. + */ +isc_result_t +isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) { + isc_log_t *lctx; + isc_logconfig_t *lcfg = NULL; + isc_result_t result; + + REQUIRE(mctx != NULL); + REQUIRE(lctxp != NULL && *lctxp == NULL); + REQUIRE(lcfgp == NULL || *lcfgp == NULL); + + lctx = isc_mem_get(mctx, sizeof(*lctx)); + if (lctx != NULL) { + lctx->mctx = NULL; + isc_mem_attach(mctx, &lctx->mctx); + lctx->categories = NULL; + lctx->category_count = 0; + lctx->modules = NULL; + lctx->module_count = 0; + lctx->debug_level = 0; + + ISC_LIST_INIT(lctx->messages); + + result = isc_mutex_init(&lctx->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_putanddetach(&mctx, lctx, sizeof(*lctx)); + return (result); + } + + /* + * Normally setting the magic number is the last step done + * in a creation function, but a valid log context is needed + * by isc_log_registercategories and isc_logconfig_create. + * If either fails, the lctx is destroyed and not returned + * to the caller. + */ + lctx->magic = LCTX_MAGIC; + + isc_log_registercategories(lctx, isc_categories); + isc_log_registermodules(lctx, isc_modules); + result = isc_logconfig_create(lctx, &lcfg); + + } else + result = ISC_R_NOMEMORY; + + if (result == ISC_R_SUCCESS) + result = sync_channellist(lcfg); + + if (result == ISC_R_SUCCESS) { + lctx->logconfig = lcfg; + + *lctxp = lctx; + if (lcfgp != NULL) + *lcfgp = lcfg; + + } else { + if (lcfg != NULL) + isc_logconfig_destroy(&lcfg); + if (lctx != NULL) + isc_log_destroy(&lctx); + } + + return (result); +} + +isc_result_t +isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp) { + isc_logconfig_t *lcfg; + isc_logdestination_t destination; + isc_result_t result = ISC_R_SUCCESS; + int level = ISC_LOG_INFO; + + REQUIRE(lcfgp != NULL && *lcfgp == NULL); + REQUIRE(VALID_CONTEXT(lctx)); + + lcfg = isc_mem_get(lctx->mctx, sizeof(*lcfg)); + + if (lcfg != NULL) { + lcfg->lctx = lctx; + lcfg->channellists = NULL; + lcfg->channellist_count = 0; + lcfg->duplicate_interval = 0; + lcfg->highest_level = level; + lcfg->tag = NULL; + lcfg->dynamic = false; + + ISC_LIST_INIT(lcfg->channels); + + /* + * Normally the magic number is the last thing set in the + * structure, but isc_log_createchannel() needs a valid + * config. If the channel creation fails, the lcfg is not + * returned to the caller. + */ + lcfg->magic = LCFG_MAGIC; + + } else + result = ISC_R_NOMEMORY; + + /* + * Create the default channels: + * default_syslog, default_stderr, default_debug and null. + */ + if (result == ISC_R_SUCCESS) { + destination.facility = LOG_DAEMON; + result = isc_log_createchannel(lcfg, "default_syslog", + ISC_LOG_TOSYSLOG, level, + &destination, 0); + } + + if (result == ISC_R_SUCCESS) { + destination.file.stream = stderr; + destination.file.name = NULL; + destination.file.versions = ISC_LOG_ROLLNEVER; + destination.file.maximum_size = 0; + result = isc_log_createchannel(lcfg, "default_stderr", + ISC_LOG_TOFILEDESC, + level, + &destination, + ISC_LOG_PRINTTIME); + } + + if (result == ISC_R_SUCCESS) { + /* + * Set the default category's channel to default_stderr, + * which is at the head of the channels list because it was + * just created. + */ + default_channel.channel = ISC_LIST_HEAD(lcfg->channels); + + destination.file.stream = stderr; + destination.file.name = NULL; + destination.file.versions = ISC_LOG_ROLLNEVER; + destination.file.maximum_size = 0; + result = isc_log_createchannel(lcfg, "default_debug", + ISC_LOG_TOFILEDESC, + ISC_LOG_DYNAMIC, + &destination, + ISC_LOG_PRINTTIME); + } + + if (result == ISC_R_SUCCESS) + result = isc_log_createchannel(lcfg, "null", + ISC_LOG_TONULL, + ISC_LOG_DYNAMIC, + NULL, 0); + + if (result == ISC_R_SUCCESS) + *lcfgp = lcfg; + + else + if (lcfg != NULL) + isc_logconfig_destroy(&lcfg); + + return (result); +} + +isc_logconfig_t * +isc_logconfig_get(isc_log_t *lctx) { + REQUIRE(VALID_CONTEXT(lctx)); + + ENSURE(lctx->logconfig != NULL); + + return (lctx->logconfig); +} + +isc_result_t +isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg) { + isc_logconfig_t *old_cfg; + isc_result_t result; + + REQUIRE(VALID_CONTEXT(lctx)); + REQUIRE(VALID_CONFIG(lcfg)); + REQUIRE(lcfg->lctx == lctx); + + /* + * Ensure that lcfg->channellist_count == lctx->category_count. + * They won't be equal if isc_log_usechannel has not been called + * since any call to isc_log_registercategories. + */ + result = sync_channellist(lcfg); + if (result != ISC_R_SUCCESS) + return (result); + + LOCK(&lctx->lock); + + old_cfg = lctx->logconfig; + lctx->logconfig = lcfg; + + UNLOCK(&lctx->lock); + + isc_logconfig_destroy(&old_cfg); + + return (ISC_R_SUCCESS); +} + +void +isc_log_destroy(isc_log_t **lctxp) { + isc_log_t *lctx; + isc_logconfig_t *lcfg; + isc_mem_t *mctx; + isc_logmessage_t *message; + + REQUIRE(lctxp != NULL && VALID_CONTEXT(*lctxp)); + + lctx = *lctxp; + mctx = lctx->mctx; + + if (lctx->logconfig != NULL) { + lcfg = lctx->logconfig; + lctx->logconfig = NULL; + isc_logconfig_destroy(&lcfg); + } + + DESTROYLOCK(&lctx->lock); + + while ((message = ISC_LIST_HEAD(lctx->messages)) != NULL) { + ISC_LIST_UNLINK(lctx->messages, message, link); + + isc_mem_put(mctx, message, + sizeof(*message) + strlen(message->text) + 1); + } + + lctx->buffer[0] = '\0'; + lctx->debug_level = 0; + lctx->categories = NULL; + lctx->category_count = 0; + lctx->modules = NULL; + lctx->module_count = 0; + lctx->mctx = NULL; + lctx->magic = 0; + + isc_mem_putanddetach(&mctx, lctx, sizeof(*lctx)); + + *lctxp = NULL; +} + +void +isc_logconfig_destroy(isc_logconfig_t **lcfgp) { + isc_logconfig_t *lcfg; + isc_mem_t *mctx; + isc_logchannel_t *channel; + isc_logchannellist_t *item; + char *filename; + unsigned int i; + + REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp)); + + lcfg = *lcfgp; + + /* + * This function cannot be called with a logconfig that is in + * use by a log context. + */ + REQUIRE(lcfg->lctx != NULL && lcfg->lctx->logconfig != lcfg); + + mctx = lcfg->lctx->mctx; + + while ((channel = ISC_LIST_HEAD(lcfg->channels)) != NULL) { + ISC_LIST_UNLINK(lcfg->channels, channel, link); + + if (channel->type == ISC_LOG_TOFILE) { + /* + * The filename for the channel may have ultimately + * started its life in user-land as a const string, + * but in isc_log_createchannel it gets copied + * into writable memory and is not longer truly const. + */ + DE_CONST(FILE_NAME(channel), filename); + isc_mem_free(mctx, filename); + + if (FILE_STREAM(channel) != NULL) + (void)fclose(FILE_STREAM(channel)); + } + + isc_mem_free(mctx, channel->name); + isc_mem_put(mctx, channel, sizeof(*channel)); + } + + for (i = 0; i < lcfg->channellist_count; i++) + while ((item = ISC_LIST_HEAD(lcfg->channellists[i])) != NULL) { + ISC_LIST_UNLINK(lcfg->channellists[i], item, link); + isc_mem_put(mctx, item, sizeof(*item)); + } + + if (lcfg->channellist_count > 0) + isc_mem_put(mctx, lcfg->channellists, + lcfg->channellist_count * + sizeof(ISC_LIST(isc_logchannellist_t))); + + lcfg->dynamic = false; + if (lcfg->tag != NULL) + isc_mem_free(lcfg->lctx->mctx, lcfg->tag); + lcfg->tag = NULL; + lcfg->highest_level = 0; + lcfg->duplicate_interval = 0; + lcfg->magic = 0; + + isc_mem_put(mctx, lcfg, sizeof(*lcfg)); + + *lcfgp = NULL; +} + +void +isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]) { + isc_logcategory_t *catp; + + REQUIRE(VALID_CONTEXT(lctx)); + REQUIRE(categories != NULL && categories[0].name != NULL); + + /* + * XXXDCL This somewhat sleazy situation of using the last pointer + * in one category array to point to the next array exists because + * this registration function returns void and I didn't want to have + * change everything that used it by making it return an isc_result_t. + * It would need to do that if it had to allocate memory to store + * pointers to each array passed in. + */ + if (lctx->categories == NULL) + lctx->categories = categories; + + else { + /* + * Adjust the last (NULL) pointer of the already registered + * categories to point to the incoming array. + */ + for (catp = lctx->categories; catp->name != NULL; ) + if (catp->id == UINT_MAX) + /* + * The name pointer points to the next array. + * Ick. + */ + DE_CONST(catp->name, catp); + else + catp++; + + catp->name = (void *)categories; + catp->id = UINT_MAX; + } + + /* + * Update the id number of the category with its new global id. + */ + for (catp = categories; catp->name != NULL; catp++) + catp->id = lctx->category_count++; +} + +isc_logcategory_t * +isc_log_categorybyname(isc_log_t *lctx, const char *name) { + isc_logcategory_t *catp; + + REQUIRE(VALID_CONTEXT(lctx)); + REQUIRE(name != NULL); + + for (catp = lctx->categories; catp->name != NULL; ) + if (catp->id == UINT_MAX) + /* + * catp is neither modified nor returned to the + * caller, so removing its const qualifier is ok. + */ + DE_CONST(catp->name, catp); + else { + if (strcmp(catp->name, name) == 0) + return (catp); + catp++; + } + + return (NULL); +} + +void +isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]) { + isc_logmodule_t *modp; + + REQUIRE(VALID_CONTEXT(lctx)); + REQUIRE(modules != NULL && modules[0].name != NULL); + + /* + * XXXDCL This somewhat sleazy situation of using the last pointer + * in one category array to point to the next array exists because + * this registration function returns void and I didn't want to have + * change everything that used it by making it return an isc_result_t. + * It would need to do that if it had to allocate memory to store + * pointers to each array passed in. + */ + if (lctx->modules == NULL) + lctx->modules = modules; + + else { + /* + * Adjust the last (NULL) pointer of the already registered + * modules to point to the incoming array. + */ + for (modp = lctx->modules; modp->name != NULL; ) + if (modp->id == UINT_MAX) + /* + * The name pointer points to the next array. + * Ick. + */ + DE_CONST(modp->name, modp); + else + modp++; + + modp->name = (void *)modules; + modp->id = UINT_MAX; + } + + /* + * Update the id number of the module with its new global id. + */ + for (modp = modules; modp->name != NULL; modp++) + modp->id = lctx->module_count++; +} + +isc_logmodule_t * +isc_log_modulebyname(isc_log_t *lctx, const char *name) { + isc_logmodule_t *modp; + + REQUIRE(VALID_CONTEXT(lctx)); + REQUIRE(name != NULL); + + for (modp = lctx->modules; modp->name != NULL; ) + if (modp->id == UINT_MAX) + /* + * modp is neither modified nor returned to the + * caller, so removing its const qualifier is ok. + */ + DE_CONST(modp->name, modp); + else { + if (strcmp(modp->name, name) == 0) + return (modp); + modp++; + } + + return (NULL); +} + +isc_result_t +isc_log_createchannel(isc_logconfig_t *lcfg, const char *name, + unsigned int type, int level, + const isc_logdestination_t *destination, + unsigned int flags) +{ + isc_logchannel_t *channel; + isc_mem_t *mctx; + unsigned int permitted = ISC_LOG_PRINTALL | ISC_LOG_DEBUGONLY | + ISC_LOG_BUFFERED; + + REQUIRE(VALID_CONFIG(lcfg)); + REQUIRE(name != NULL); + REQUIRE(type == ISC_LOG_TOSYSLOG || type == ISC_LOG_TOFILE || + type == ISC_LOG_TOFILEDESC || type == ISC_LOG_TONULL); + REQUIRE(destination != NULL || type == ISC_LOG_TONULL); + REQUIRE(level >= ISC_LOG_CRITICAL); + REQUIRE((flags & ~permitted) == 0); + + /* XXXDCL find duplicate names? */ + + mctx = lcfg->lctx->mctx; + + channel = isc_mem_get(mctx, sizeof(*channel)); + if (channel == NULL) + return (ISC_R_NOMEMORY); + + channel->name = isc_mem_strdup(mctx, name); + if (channel->name == NULL) { + isc_mem_put(mctx, channel, sizeof(*channel)); + return (ISC_R_NOMEMORY); + } + + channel->type = type; + channel->level = level; + channel->flags = flags; + ISC_LINK_INIT(channel, link); + + switch (type) { + case ISC_LOG_TOSYSLOG: + FACILITY(channel) = destination->facility; + break; + + case ISC_LOG_TOFILE: + /* + * The file name is copied because greatest_version wants + * to scribble on it, so it needs to be definitely in + * writable memory. + */ + FILE_NAME(channel) = + isc_mem_strdup(mctx, destination->file.name); + FILE_STREAM(channel) = NULL; + FILE_VERSIONS(channel) = destination->file.versions; + FILE_MAXSIZE(channel) = destination->file.maximum_size; + FILE_MAXREACHED(channel) = false; + break; + + case ISC_LOG_TOFILEDESC: + FILE_NAME(channel) = NULL; + FILE_STREAM(channel) = destination->file.stream; + FILE_MAXSIZE(channel) = 0; + FILE_VERSIONS(channel) = ISC_LOG_ROLLNEVER; + break; + + case ISC_LOG_TONULL: + /* Nothing. */ + break; + + default: + isc_mem_free(mctx, channel->name); + isc_mem_put(mctx, channel, sizeof(*channel)); + return (ISC_R_UNEXPECTED); + } + + ISC_LIST_PREPEND(lcfg->channels, channel, link); + + /* + * If default_stderr was redefined, make the default category + * point to the new default_stderr. + */ + if (strcmp(name, "default_stderr") == 0) + default_channel.channel = channel; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_log_usechannel(isc_logconfig_t *lcfg, const char *name, + const isc_logcategory_t *category, + const isc_logmodule_t *module) +{ + isc_log_t *lctx; + isc_logchannel_t *channel; + isc_result_t result = ISC_R_SUCCESS; + unsigned int i; + + REQUIRE(VALID_CONFIG(lcfg)); + REQUIRE(name != NULL); + + lctx = lcfg->lctx; + + REQUIRE(category == NULL || category->id < lctx->category_count); + REQUIRE(module == NULL || module->id < lctx->module_count); + + for (channel = ISC_LIST_HEAD(lcfg->channels); channel != NULL; + channel = ISC_LIST_NEXT(channel, link)) + if (strcmp(name, channel->name) == 0) + break; + + if (channel == NULL) + return (ISC_R_NOTFOUND); + + if (category != NULL) + result = assignchannel(lcfg, category->id, module, channel); + + else + /* + * Assign to all categories. Note that this includes + * the default channel. + */ + for (i = 0; i < lctx->category_count; i++) { + result = assignchannel(lcfg, i, module, channel); + if (result != ISC_R_SUCCESS) + break; + } + + return (result); +} + +void +isc_log_write(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, const char *format, ...) +{ + va_list args; + + /* + * Contract checking is done in isc_log_doit(). + */ + + va_start(args, format); + isc_log_doit(lctx, category, module, level, false, + NULL, 0, 0, format, args); + va_end(args); +} + +void +isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + const char *format, va_list args) +{ + /* + * Contract checking is done in isc_log_doit(). + */ + isc_log_doit(lctx, category, module, level, false, + NULL, 0, 0, format, args); +} + +void +isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, const char *format, ...) +{ + va_list args; + + /* + * Contract checking is done in isc_log_doit(). + */ + + va_start(args, format); + isc_log_doit(lctx, category, module, level, true, + NULL, 0, 0, format, args); + va_end(args); +} + +void +isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + const char *format, va_list args) +{ + /* + * Contract checking is done in isc_log_doit(). + */ + isc_log_doit(lctx, category, module, level, true, + NULL, 0, 0, format, args); +} + +void +isc_log_iwrite(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int msg, + const char *format, ...) +{ + va_list args; + + /* + * Contract checking is done in isc_log_doit(). + */ + + va_start(args, format); + isc_log_doit(lctx, category, module, level, false, + msgcat, msgset, msg, format, args); + va_end(args); +} + +void +isc_log_ivwrite(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int msg, + const char *format, va_list args) +{ + /* + * Contract checking is done in isc_log_doit(). + */ + isc_log_doit(lctx, category, module, level, false, + msgcat, msgset, msg, format, args); +} + +void +isc_log_iwrite1(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int msg, + const char *format, ...) +{ + va_list args; + + /* + * Contract checking is done in isc_log_doit(). + */ + + va_start(args, format); + isc_log_doit(lctx, category, module, level, true, + msgcat, msgset, msg, format, args); + va_end(args); +} + +void +isc_log_ivwrite1(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int msg, + const char *format, va_list args) +{ + /* + * Contract checking is done in isc_log_doit(). + */ + isc_log_doit(lctx, category, module, level, true, + msgcat, msgset, msg, format, args); +} + +void +isc_log_setcontext(isc_log_t *lctx) { + isc_lctx = lctx; +} + +void +isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level) { + isc_logchannel_t *channel; + + REQUIRE(VALID_CONTEXT(lctx)); + + LOCK(&lctx->lock); + + lctx->debug_level = level; + /* + * Close ISC_LOG_DEBUGONLY channels if level is zero. + */ + if (lctx->debug_level == 0) + for (channel = ISC_LIST_HEAD(lctx->logconfig->channels); + channel != NULL; + channel = ISC_LIST_NEXT(channel, link)) + if (channel->type == ISC_LOG_TOFILE && + (channel->flags & ISC_LOG_DEBUGONLY) != 0 && + FILE_STREAM(channel) != NULL) { + (void)fclose(FILE_STREAM(channel)); + FILE_STREAM(channel) = NULL; + } + UNLOCK(&lctx->lock); +} + +unsigned int +isc_log_getdebuglevel(isc_log_t *lctx) { + REQUIRE(VALID_CONTEXT(lctx)); + + return (lctx->debug_level); +} + +void +isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval) { + REQUIRE(VALID_CONFIG(lcfg)); + + lcfg->duplicate_interval = interval; +} + +unsigned int +isc_log_getduplicateinterval(isc_logconfig_t *lcfg) { + REQUIRE(VALID_CONTEXT(lcfg)); + + return (lcfg->duplicate_interval); +} + +isc_result_t +isc_log_settag(isc_logconfig_t *lcfg, const char *tag) { + REQUIRE(VALID_CONFIG(lcfg)); + + if (tag != NULL && *tag != '\0') { + if (lcfg->tag != NULL) + isc_mem_free(lcfg->lctx->mctx, lcfg->tag); + lcfg->tag = isc_mem_strdup(lcfg->lctx->mctx, tag); + if (lcfg->tag == NULL) + return (ISC_R_NOMEMORY); + + } else { + if (lcfg->tag != NULL) + isc_mem_free(lcfg->lctx->mctx, lcfg->tag); + lcfg->tag = NULL; + } + + return (ISC_R_SUCCESS); +} + +char * +isc_log_gettag(isc_logconfig_t *lcfg) { + REQUIRE(VALID_CONFIG(lcfg)); + + return (lcfg->tag); +} + +/* XXXDCL NT -- This interface will assuredly be changing. */ +void +isc_log_opensyslog(const char *tag, int options, int facility) { + (void)openlog(tag, options, facility); +} + +void +isc_log_closefilelogs(isc_log_t *lctx) { + isc_logchannel_t *channel; + + REQUIRE(VALID_CONTEXT(lctx)); + + LOCK(&lctx->lock); + for (channel = ISC_LIST_HEAD(lctx->logconfig->channels); + channel != NULL; + channel = ISC_LIST_NEXT(channel, link)) + + if (channel->type == ISC_LOG_TOFILE && + FILE_STREAM(channel) != NULL) { + (void)fclose(FILE_STREAM(channel)); + FILE_STREAM(channel) = NULL; + } + UNLOCK(&lctx->lock); +} + +/**** + **** Internal functions + ****/ + +static isc_result_t +assignchannel(isc_logconfig_t *lcfg, unsigned int category_id, + const isc_logmodule_t *module, isc_logchannel_t *channel) +{ + isc_logchannellist_t *new_item; + isc_log_t *lctx; + isc_result_t result; + + REQUIRE(VALID_CONFIG(lcfg)); + + lctx = lcfg->lctx; + + REQUIRE(category_id < lctx->category_count); + REQUIRE(module == NULL || module->id < lctx->module_count); + REQUIRE(channel != NULL); + + /* + * Ensure lcfg->channellist_count == lctx->category_count. + */ + result = sync_channellist(lcfg); + if (result != ISC_R_SUCCESS) + return (result); + + new_item = isc_mem_get(lctx->mctx, sizeof(*new_item)); + if (new_item == NULL) + return (ISC_R_NOMEMORY); + + new_item->channel = channel; + new_item->module = module; + ISC_LIST_INITANDPREPEND(lcfg->channellists[category_id], + new_item, link); + + /* + * Remember the highest logging level set by any channel in the + * logging config, so isc_log_doit() can quickly return if the + * message is too high to be logged by any channel. + */ + if (channel->type != ISC_LOG_TONULL) { + if (lcfg->highest_level < channel->level) + lcfg->highest_level = channel->level; + if (channel->level == ISC_LOG_DYNAMIC) + lcfg->dynamic = true; + } + + return (ISC_R_SUCCESS); +} + +/* + * This would ideally be part of isc_log_registercategories(), except then + * that function would have to return isc_result_t instead of void. + */ +static isc_result_t +sync_channellist(isc_logconfig_t *lcfg) { + unsigned int bytes; + isc_log_t *lctx; + void *lists; + + REQUIRE(VALID_CONFIG(lcfg)); + + lctx = lcfg->lctx; + + REQUIRE(lctx->category_count != 0); + + if (lctx->category_count == lcfg->channellist_count) + return (ISC_R_SUCCESS); + + bytes = lctx->category_count * sizeof(ISC_LIST(isc_logchannellist_t)); + + lists = isc_mem_get(lctx->mctx, bytes); + + if (lists == NULL) + return (ISC_R_NOMEMORY); + + memset(lists, 0, bytes); + + if (lcfg->channellist_count != 0) { + bytes = lcfg->channellist_count * + sizeof(ISC_LIST(isc_logchannellist_t)); + memmove(lists, lcfg->channellists, bytes); + isc_mem_put(lctx->mctx, lcfg->channellists, bytes); + } + + lcfg->channellists = lists; + lcfg->channellist_count = lctx->category_count; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +greatest_version(isc_logfile_t *file, int versions, int *greatestp) { + char *bname, *digit_end; + const char *dirname; + int version, greatest = -1; + size_t bnamelen; + isc_dir_t dir; + isc_result_t result; + char sep = '/'; +#ifdef _WIN32 + char *bname2; +#endif + + /* + * It is safe to DE_CONST the file.name because it was copied + * with isc_mem_strdup(). + */ + bname = strrchr(file->name, sep); +#ifdef _WIN32 + bname2 = strrchr(file->name, '\\'); + if ((bname != NULL && bname2 != NULL && bname2 > bname) || + (bname == NULL && bname2 != NULL)) { + bname = bname2; + sep = '\\'; + } +#endif + if (bname != NULL) { + *bname++ = '\0'; + dirname = file->name; + } else { + DE_CONST(file->name, bname); + dirname = "."; + } + bnamelen = strlen(bname); + + isc_dir_init(&dir); + result = isc_dir_open(&dir, dirname); + + /* + * Replace the file separator if it was taken out. + */ + if (bname != file->name) + *(bname - 1) = sep; + + /* + * Return if the directory open failed. + */ + if (result != ISC_R_SUCCESS) + return (result); + + while (isc_dir_read(&dir) == ISC_R_SUCCESS) { + if (dir.entry.length > bnamelen && + strncmp(dir.entry.name, bname, bnamelen) == 0 && + dir.entry.name[bnamelen] == '.') + { + version = strtol(&dir.entry.name[bnamelen + 1], + &digit_end, 10); + /* + * Remove any backup files that exceed versions. + */ + if (*digit_end == '\0' && version >= versions) { + result = isc_file_remove(dir.entry.name); + if (result != ISC_R_SUCCESS && + result != ISC_R_FILENOTFOUND) + syslog(LOG_ERR, "unable to remove " + "log file '%s': %s", + dir.entry.name, + isc_result_totext(result)); + } else if (*digit_end == '\0' && version > greatest) + greatest = version; + } + } + isc_dir_close(&dir); + + *greatestp = greatest; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_logfile_roll(isc_logfile_t *file) { + int i, n, greatest; + char current[PATH_MAX + 1]; + char newpath[PATH_MAX + 1]; + const char *path; + isc_result_t result; + + REQUIRE(file != NULL); + + /* + * Do nothing (not even excess version trimming) if ISC_LOG_ROLLNEVER + * is specified. Apparently complete external control over the log + * files is desired. + */ + if (file->versions == ISC_LOG_ROLLNEVER) + return (ISC_R_SUCCESS); + + path = file->name; + + if (file->versions == ISC_LOG_ROLLINFINITE) { + /* + * Find the first missing entry in the log file sequence. + */ + for (greatest = 0; greatest < INT_MAX; greatest++) { + n = snprintf(current, sizeof(current), + "%s.%u", path, (unsigned)greatest) ; + if (n >= (int)sizeof(current) || n < 0 || + !isc_file_exists(current)) + break; + } + } else { + /* + * Get the largest existing version and remove any + * version greater than the permitted version. + */ + result = greatest_version(file, file->versions, &greatest); + if (result != ISC_R_SUCCESS) + return (result); + + /* + * Increment if greatest is not the actual maximum value. + */ + if (greatest < file->versions - 1) + greatest++; + } + + for (i = greatest; i > 0; i--) { + result = ISC_R_SUCCESS; + n = snprintf(current, sizeof(current), "%s.%u", path, + (unsigned)(i - 1)); + if (n >= (int)sizeof(current) || n < 0) { + result = ISC_R_NOSPACE; + } + if (result == ISC_R_SUCCESS) { + n = snprintf(newpath, sizeof(newpath), "%s.%u", + path, (unsigned)i); + if (n >= (int)sizeof(newpath) || n < 0) { + result = ISC_R_NOSPACE; + } + } + if (result == ISC_R_SUCCESS) + result = isc_file_rename(current, newpath); + if (result != ISC_R_SUCCESS && + result != ISC_R_FILENOTFOUND) + syslog(LOG_ERR, + "unable to rename log file '%s.%u' to " + "'%s.%u': %s", path, i - 1, path, i, + isc_result_totext(result)); + } + + if (file->versions != 0) { + n = snprintf(newpath, sizeof(newpath), "%s.0", path); + if (n >= (int)sizeof(newpath) || n < 0) + result = ISC_R_NOSPACE; + else + result = isc_file_rename(path, newpath); + if (result != ISC_R_SUCCESS && + result != ISC_R_FILENOTFOUND) + syslog(LOG_ERR, + "unable to rename log file '%s' to '%s.0': %s", + path, path, isc_result_totext(result)); + } else { + result = isc_file_remove(path); + if (result != ISC_R_SUCCESS && + result != ISC_R_FILENOTFOUND) + syslog(LOG_ERR, "unable to remove log file '%s': %s", + path, isc_result_totext(result)); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +isc_log_open(isc_logchannel_t *channel) { + struct stat statbuf; + bool regular_file; + bool roll = false; + isc_result_t result = ISC_R_SUCCESS; + const char *path; + + REQUIRE(channel->type == ISC_LOG_TOFILE); + REQUIRE(FILE_STREAM(channel) == NULL); + + path = FILE_NAME(channel); + + REQUIRE(path != NULL && *path != '\0'); + + /* + * Determine type of file; only regular files will be + * version renamed, and only if the base file exists + * and either has no size limit or has reached its size limit. + */ + if (stat(path, &statbuf) == 0) { + regular_file = S_ISREG(statbuf.st_mode) ? true : false; + /* XXXDCL if not regular_file complain? */ + if ((FILE_MAXSIZE(channel) == 0 && + FILE_VERSIONS(channel) != ISC_LOG_ROLLNEVER) || + (FILE_MAXSIZE(channel) > 0 && + statbuf.st_size >= FILE_MAXSIZE(channel))) + roll = regular_file; + } else if (errno == ENOENT) { + regular_file = true; + POST(regular_file); + } else + result = ISC_R_INVALIDFILE; + + /* + * Version control. + */ + if (result == ISC_R_SUCCESS && roll) { + if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) + return (ISC_R_MAXSIZE); + result = isc_logfile_roll(&channel->destination.file); + if (result != ISC_R_SUCCESS) { + if ((channel->flags & ISC_LOG_OPENERR) == 0) { + syslog(LOG_ERR, + "isc_log_open: isc_logfile_roll '%s' " + "failed: %s", + FILE_NAME(channel), + isc_result_totext(result)); + channel->flags |= ISC_LOG_OPENERR; + } + return (result); + } + } + + result = isc_stdio_open(path, "a", &FILE_STREAM(channel)); + + return (result); +} + +bool +isc_log_wouldlog(isc_log_t *lctx, int level) { + /* + * Try to avoid locking the mutex for messages which can't + * possibly be logged to any channels -- primarily debugging + * messages that the debug level is not high enough to print. + * + * If the level is (mathematically) less than or equal to the + * highest_level, or if there is a dynamic channel and the level is + * less than or equal to the debug level, the main loop must be + * entered to see if the message should really be output. + * + * NOTE: this is UNLOCKED access to the logconfig. However, + * the worst thing that can happen is that a bad decision is made + * about returning without logging, and that's not a big concern, + * because that's a risk anyway if the logconfig is being + * dynamically changed. + */ + + if (lctx == NULL || lctx->logconfig == NULL) + return (false); + + return (level <= lctx->logconfig->highest_level || + (lctx->logconfig->dynamic && + level <= lctx->debug_level)); +} + +static void +isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category, + isc_logmodule_t *module, int level, bool write_once, + isc_msgcat_t *msgcat, int msgset, int msg, + const char *format, va_list args) +{ + int syslog_level; + char time_string[64]; + char level_string[24]; + const char *iformat; + struct stat statbuf; + bool matched = false; + bool printtime, printtag, printcolon; + bool printcategory, printmodule, printlevel, buffered; + isc_logconfig_t *lcfg; + isc_logchannel_t *channel; + isc_logchannellist_t *category_channels; + isc_result_t result; + + REQUIRE(lctx == NULL || VALID_CONTEXT(lctx)); + REQUIRE(category != NULL); + REQUIRE(module != NULL); + REQUIRE(level != ISC_LOG_DYNAMIC); + REQUIRE(format != NULL); + + /* + * Programs can use libraries that use this logging code without + * wanting to do any logging, thus the log context is allowed to + * be non-existent. + */ + if (lctx == NULL) + return; + + REQUIRE(category->id < lctx->category_count); + REQUIRE(module->id < lctx->module_count); + + if (! isc_log_wouldlog(lctx, level)) + return; + + if (msgcat != NULL) + iformat = isc_msgcat_get(msgcat, msgset, msg, format); + else + iformat = format; + + time_string[0] = '\0'; + level_string[0] = '\0'; + + LOCK(&lctx->lock); + + lctx->buffer[0] = '\0'; + + lcfg = lctx->logconfig; + + category_channels = ISC_LIST_HEAD(lcfg->channellists[category->id]); + + /* + * XXXDCL add duplicate filtering? (To not write multiple times to + * the same source via various channels). + */ + do { + /* + * If the channel list end was reached and a match was made, + * everything is finished. + */ + if (category_channels == NULL && matched) + break; + + if (category_channels == NULL && ! matched && + category_channels != ISC_LIST_HEAD(lcfg->channellists[0])) + /* + * No category/module pair was explicitly configured. + * Try the category named "default". + */ + category_channels = + ISC_LIST_HEAD(lcfg->channellists[0]); + + if (category_channels == NULL && ! matched) + /* + * No matching module was explicitly configured + * for the category named "default". Use the internal + * default channel. + */ + category_channels = &default_channel; + + if (category_channels->module != NULL && + category_channels->module != module) { + category_channels = ISC_LIST_NEXT(category_channels, + link); + continue; + } + + matched = true; + + channel = category_channels->channel; + category_channels = ISC_LIST_NEXT(category_channels, link); + + if (((channel->flags & ISC_LOG_DEBUGONLY) != 0) && + lctx->debug_level == 0) + continue; + + if (channel->level == ISC_LOG_DYNAMIC) { + if (lctx->debug_level < level) + continue; + } else if (channel->level < level) + continue; + + if ((channel->flags & ISC_LOG_PRINTTIME) != 0 && + time_string[0] == '\0') { + isc_time_t isctime; + + TIME_NOW(&isctime); + isc_time_formattimestamp(&isctime, time_string, + sizeof(time_string)); + } + + if ((channel->flags & ISC_LOG_PRINTLEVEL) != 0 && + level_string[0] == '\0') { + if (level < ISC_LOG_CRITICAL) + snprintf(level_string, sizeof(level_string), + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_LOG, + ISC_MSG_LEVEL, + "level %d: "), + level); + else if (level > ISC_LOG_DYNAMIC) + snprintf(level_string, sizeof(level_string), + "%s %d: ", log_level_strings[0], + level); + else + snprintf(level_string, sizeof(level_string), + "%s: ", log_level_strings[-level]); + } + + /* + * Only format the message once. + */ + if (lctx->buffer[0] == '\0') { + (void)vsnprintf(lctx->buffer, sizeof(lctx->buffer), + iformat, args); + + /* + * Check for duplicates. + */ + if (write_once) { + isc_logmessage_t *message, *next; + isc_time_t oldest; + isc_interval_t interval; + size_t size; + + isc_interval_set(&interval, + lcfg->duplicate_interval, 0); + + /* + * 'oldest' is the age of the oldest messages + * which fall within the duplicate_interval + * range. + */ + TIME_NOW(&oldest); + if (isc_time_subtract(&oldest, &interval, + &oldest) + != ISC_R_SUCCESS) + /* + * Can't effectively do the checking + * without having a valid time. + */ + message = NULL; + else + message = ISC_LIST_HEAD(lctx->messages); + + while (message != NULL) { + if (isc_time_compare(&message->time, + &oldest) < 0) { + /* + * This message is older + * than the duplicate_interval, + * so it should be dropped from + * the history. + * + * Setting the interval to be + * to be longer will obviously + * not cause the expired + * message to spring back into + * existence. + */ + next = ISC_LIST_NEXT(message, + link); + + ISC_LIST_UNLINK(lctx->messages, + message, link); + + isc_mem_put(lctx->mctx, + message, + sizeof(*message) + 1 + + strlen(message->text)); + + message = next; + continue; + } + + /* + * This message is in the duplicate + * filtering interval ... + */ + if (strcmp(lctx->buffer, message->text) + == 0) { + /* + * ... and it is a duplicate. + * Unlock the mutex and + * get the hell out of Dodge. + */ + UNLOCK(&lctx->lock); + return; + } + + message = ISC_LIST_NEXT(message, link); + } + + /* + * It wasn't in the duplicate interval, + * so add it to the message list. + */ + size = sizeof(isc_logmessage_t) + + strlen(lctx->buffer) + 1; + message = isc_mem_get(lctx->mctx, size); + if (message != NULL) { + /* + * Put the text immediately after + * the struct. The strcpy is safe. + */ + message->text = (char *)(message + 1); + size -= sizeof(isc_logmessage_t); + strlcpy(message->text, lctx->buffer, + size); + + TIME_NOW(&message->time); + + ISC_LINK_INIT(message, link); + ISC_LIST_APPEND(lctx->messages, + message, link); + } + } + } + + printtime = (channel->flags & ISC_LOG_PRINTTIME); + printtag = ((channel->flags & + (ISC_LOG_PRINTTAG|ISC_LOG_PRINTPREFIX)) + && lcfg->tag != NULL); + printcolon = ((channel->flags & ISC_LOG_PRINTTAG) + && lcfg->tag != NULL); + printcategory = (channel->flags & ISC_LOG_PRINTCATEGORY); + printmodule = (channel->flags & ISC_LOG_PRINTMODULE); + printlevel = (channel->flags & ISC_LOG_PRINTLEVEL); + buffered = (channel->flags & ISC_LOG_BUFFERED); + + switch (channel->type) { + case ISC_LOG_TOFILE: + if (FILE_MAXREACHED(channel)) { + /* + * If the file can be rolled, OR + * If the file no longer exists, OR + * If the file is less than the maximum size, + * (such as if it had been renamed and + * a new one touched, or it was truncated + * in place) + * ... then close it to trigger reopening. + */ + if (FILE_VERSIONS(channel) != + ISC_LOG_ROLLNEVER || + (stat(FILE_NAME(channel), &statbuf) != 0 && + errno == ENOENT) || + statbuf.st_size < FILE_MAXSIZE(channel)) { + (void)fclose(FILE_STREAM(channel)); + FILE_STREAM(channel) = NULL; + FILE_MAXREACHED(channel) = false; + } else + /* + * Eh, skip it. + */ + break; + } + + if (FILE_STREAM(channel) == NULL) { + result = isc_log_open(channel); + if (result != ISC_R_SUCCESS && + result != ISC_R_MAXSIZE && + (channel->flags & ISC_LOG_OPENERR) == 0) { + syslog(LOG_ERR, + "isc_log_open '%s' failed: %s", + FILE_NAME(channel), + isc_result_totext(result)); + channel->flags |= ISC_LOG_OPENERR; + } + if (result != ISC_R_SUCCESS) + break; + channel->flags &= ~ISC_LOG_OPENERR; + } + /* FALLTHROUGH */ + + case ISC_LOG_TOFILEDESC: + fprintf(FILE_STREAM(channel), + "%s%s%s%s%s%s%s%s%s%s\n", + printtime ? time_string : "", + printtime ? " " : "", + printtag ? lcfg->tag : "", + printcolon ? ": " : "", + printcategory ? category->name : "", + printcategory ? ": " : "", + printmodule ? (module != NULL ? module->name + : "no_module") + : "", + printmodule ? ": " : "", + printlevel ? level_string : "", + lctx->buffer); + + if (!buffered) + fflush(FILE_STREAM(channel)); + + /* + * If the file now exceeds its maximum size + * threshold, note it so that it will not be logged + * to any more. + */ + if (FILE_MAXSIZE(channel) > 0) { + INSIST(channel->type == ISC_LOG_TOFILE); + + /* XXXDCL NT fstat/fileno */ + /* XXXDCL complain if fstat fails? */ + if (fstat(fileno(FILE_STREAM(channel)), + &statbuf) >= 0 && + statbuf.st_size > FILE_MAXSIZE(channel)) + FILE_MAXREACHED(channel) = true; + } + + break; + + case ISC_LOG_TOSYSLOG: + if (level > 0) + syslog_level = LOG_DEBUG; + else if (level < ISC_LOG_CRITICAL) + syslog_level = LOG_CRIT; + else + syslog_level = syslog_map[-level]; + + (void)syslog(FACILITY(channel) | syslog_level, + "%s%s%s%s%s%s%s%s%s%s", + printtime ? time_string : "", + printtime ? " " : "", + printtag ? lcfg->tag : "", + printcolon ? ": " : "", + printcategory ? category->name : "", + printcategory ? ": " : "", + printmodule ? (module != NULL + ? module->name + : "no_module") + : "", + printmodule ? ": " : "", + printlevel ? level_string : "", + lctx->buffer); + break; + + case ISC_LOG_TONULL: + break; + + } + + } while (1); + + UNLOCK(&lctx->lock); +} diff --git a/lib/isc/md5.c b/lib/isc/md5.c new file mode 100644 index 0000000..25c71a2 --- /dev/null +++ b/lib/isc/md5.c @@ -0,0 +1,395 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include "config.h" + +#include <pk11/site.h> + +#ifndef PK11_MD5_DISABLE + +#include <stdbool.h> + +#include <isc/assertions.h> +#include <isc/md5.h> +#include <isc/platform.h> +#include <isc/safe.h> +#include <isc/string.h> +#include <isc/types.h> + +#if PKCS11CRYPTO +#include <pk11/internal.h> +#include <pk11/pk11.h> +#endif + +#include <isc/util.h> + +#ifdef ISC_PLATFORM_OPENSSLHASH +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#define EVP_MD_CTX_new() &(ctx->_ctx) +#define EVP_MD_CTX_free(ptr) EVP_MD_CTX_cleanup(ptr) +#endif + +void +isc_md5_init(isc_md5_t *ctx) { + ctx->ctx = EVP_MD_CTX_new(); + RUNTIME_CHECK(ctx->ctx != NULL); + if (EVP_DigestInit(ctx->ctx, EVP_md5()) != 1) { + FATAL_ERROR(__FILE__, __LINE__, "Cannot initialize MD5."); + } +} + +void +isc_md5_invalidate(isc_md5_t *ctx) { + EVP_MD_CTX_free(ctx->ctx); + ctx->ctx = NULL; +} + +void +isc_md5_update(isc_md5_t *ctx, const unsigned char *buf, unsigned int len) { + if (len == 0U) + return; + RUNTIME_CHECK(EVP_DigestUpdate(ctx->ctx, + (const void *) buf, + (size_t) len) == 1); +} + +void +isc_md5_final(isc_md5_t *ctx, unsigned char *digest) { + RUNTIME_CHECK(EVP_DigestFinal(ctx->ctx, digest, NULL) == 1); + EVP_MD_CTX_free(ctx->ctx); + ctx->ctx = NULL; +} + +#elif PKCS11CRYPTO + +void +isc_md5_init(isc_md5_t *ctx) { + CK_RV rv; + CK_MECHANISM mech = { CKM_MD5, NULL, 0 }; + + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); +} + +void +isc_md5_invalidate(isc_md5_t *ctx) { + CK_BYTE garbage[ISC_MD5_DIGESTLENGTH]; + CK_ULONG len = ISC_MD5_DIGESTLENGTH; + + if (ctx->handle == NULL) + return; + (void) pkcs_C_DigestFinal(ctx->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + pk11_return_session(ctx); +} + +void +isc_md5_update(isc_md5_t *ctx, const unsigned char *buf, unsigned int len) { + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_md5_final(isc_md5_t *ctx, unsigned char *digest) { + CK_RV rv; + CK_ULONG len = ISC_MD5_DIGESTLENGTH; + + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, (CK_BYTE_PTR) digest, &len)); + pk11_return_session(ctx); +} + +#else + +static void +byteSwap(uint32_t *buf, unsigned words) +{ + unsigned char *p = (unsigned char *)buf; + + do { + *buf++ = (uint32_t)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} + +/*! + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +isc_md5_init(isc_md5_t *ctx) { + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +void +isc_md5_invalidate(isc_md5_t *ctx) { + isc_safe_memwipe(ctx, sizeof(*ctx)); +} + +/*@{*/ +/*! The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) +/*@}*/ + +/*! This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x) + +/*! + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void +transform(uint32_t buf[4], uint32_t const in[16]) { + register uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/*! + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +isc_md5_update(isc_md5_t *ctx, const unsigned char *buf, unsigned int len) { + uint32_t t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memmove((unsigned char *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memmove((unsigned char *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memmove(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memmove(ctx->in, buf, len); +} + +/*! + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +isc_md5_final(isc_md5_t *ctx, unsigned char *digest) { + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + unsigned char *p = (unsigned char *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + transform(ctx->buf, ctx->in); + p = (unsigned char *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memmove(digest, ctx->buf, 16); + isc_safe_memwipe(ctx, sizeof(*ctx)); /* In case it's sensitive */ +} +#endif + +/* + * Check for MD5 support; if it does not work, raise a fatal error. + * + * Use "a" as the test vector. + * + * Standard use is testing false and result true. + * Testing use is testing true and result false; + */ +bool +isc_md5_check(bool testing) { + isc_md5_t ctx; + unsigned char input = 'a'; + unsigned char digest[ISC_MD5_DIGESTLENGTH]; + unsigned char expected[] = { + 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, + 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 + }; + + INSIST(sizeof(expected) == ISC_MD5_DIGESTLENGTH); + + /* + * Introduce a fault for testing. + */ + if (testing) { + input ^= 0x01; + } + + /* + * These functions do not return anything; any failure will be fatal. + */ + isc_md5_init(&ctx); + isc_md5_update(&ctx, &input, 1U); + isc_md5_final(&ctx, digest); + + /* + * Must return true in standard case, should return false for testing. + */ + return (memcmp(digest, expected, ISC_MD5_DIGESTLENGTH) == 0); +} + +#else /* !PK11_MD5_DISABLE */ +#ifdef WIN32 +/* Make the Visual Studio linker happy */ +#include <isc/util.h> + +void isc_md5_final() { INSIST(0); } +void isc_md5_init() { INSIST(0); } +void isc_md5_invalidate() { INSIST(0); } +void isc_md5_update() { INSIST(0); } +void isc_md5_check() { INSIST(0); } +#endif +#endif /* PK11_MD5_DISABLE */ diff --git a/lib/isc/mem.c b/lib/isc/mem.c new file mode 100644 index 0000000..3fec795 --- /dev/null +++ b/lib/isc/mem.c @@ -0,0 +1,3015 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <limits.h> + +#include <isc/bind9.h> +#include <isc/json.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/once.h> +#include <isc/ondestroy.h> +#include <isc/string.h> +#include <isc/mutex.h> +#include <isc/print.h> +#include <isc/util.h> +#include <isc/xml.h> + +#define MCTXLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) LOCK(l) +#define MCTXUNLOCK(m, l) if (((m)->flags & ISC_MEMFLAG_NOLOCK) == 0) UNLOCK(l) + +#ifndef ISC_MEM_DEBUGGING +#define ISC_MEM_DEBUGGING 0 +#endif +LIBISC_EXTERNAL_DATA unsigned int isc_mem_debugging = ISC_MEM_DEBUGGING; +LIBISC_EXTERNAL_DATA unsigned int isc_mem_defaultflags = ISC_MEMFLAG_DEFAULT; + +/* + * Constants. + */ + +#define DEF_MAX_SIZE 1100 +#define DEF_MEM_TARGET 4096 +#define ALIGNMENT_SIZE 8U /*%< must be a power of 2 */ +#define NUM_BASIC_BLOCKS 64 /*%< must be > 1 */ +#define TABLE_INCREMENT 1024 +#define DEBUGLIST_COUNT 1024 + +/* + * Types. + */ +typedef struct isc__mem isc__mem_t; +typedef struct isc__mempool isc__mempool_t; + +#if ISC_MEM_TRACKLINES +typedef struct debuglink debuglink_t; +struct debuglink { + ISC_LINK(debuglink_t) link; + const void *ptr[DEBUGLIST_COUNT]; + size_t size[DEBUGLIST_COUNT]; + const char *file[DEBUGLIST_COUNT]; + unsigned int line[DEBUGLIST_COUNT]; + unsigned int count; +}; + +#define FLARG_PASS , file, line +#define FLARG , const char *file, unsigned int line +#else +#define FLARG_PASS +#define FLARG +#endif + +typedef struct element element; +struct element { + element * next; +}; + +typedef struct { + /*! + * This structure must be ALIGNMENT_SIZE bytes. + */ + union { + size_t size; + isc__mem_t *ctx; + char bytes[ALIGNMENT_SIZE]; + } u; +} size_info; + +struct stats { + unsigned long gets; + unsigned long totalgets; + unsigned long blocks; + unsigned long freefrags; +}; + +#define MEM_MAGIC ISC_MAGIC('M', 'e', 'm', 'C') +#define VALID_CONTEXT(c) ISC_MAGIC_VALID(c, MEM_MAGIC) + +#if ISC_MEM_TRACKLINES +typedef ISC_LIST(debuglink_t) debuglist_t; +#endif + +/* List of all active memory contexts. */ + +static ISC_LIST(isc__mem_t) contexts; + +static isc_once_t once = ISC_ONCE_INIT; +static isc_mutex_t contextslock; +static isc_mutex_t createlock; + +/*% + * Total size of lost memory due to a bug of external library. + * Locked by the global lock. + */ +static uint64_t totallost; + +struct isc__mem { + isc_mem_t common; + isc_ondestroy_t ondestroy; + unsigned int flags; + isc_mutex_t lock; + isc_memalloc_t memalloc; + isc_memfree_t memfree; + void * arg; + size_t max_size; + bool checkfree; + struct stats * stats; + unsigned int references; + char name[16]; + void * tag; + size_t quota; + size_t total; + size_t inuse; + size_t maxinuse; + size_t hi_water; + size_t lo_water; + bool hi_called; + bool is_overmem; + isc_mem_water_t water; + void * water_arg; + ISC_LIST(isc__mempool_t) pools; + unsigned int poolcnt; + + /* ISC_MEMFLAG_INTERNAL */ + size_t mem_target; + element ** freelists; + element * basic_blocks; + unsigned char ** basic_table; + unsigned int basic_table_count; + unsigned int basic_table_size; + unsigned char * lowest; + unsigned char * highest; + +#if ISC_MEM_TRACKLINES + debuglist_t * debuglist; + unsigned int debuglistcnt; +#endif + + unsigned int memalloc_failures; + ISC_LINK(isc__mem_t) link; +}; + +#define MEMPOOL_MAGIC ISC_MAGIC('M', 'E', 'M', 'p') +#define VALID_MEMPOOL(c) ISC_MAGIC_VALID(c, MEMPOOL_MAGIC) + +struct isc__mempool { + /* always unlocked */ + isc_mempool_t common; /*%< common header of mempool's */ + isc_mutex_t *lock; /*%< optional lock */ + isc__mem_t *mctx; /*%< our memory context */ + /*%< locked via the memory context's lock */ + ISC_LINK(isc__mempool_t) link; /*%< next pool in this mem context */ + /*%< optionally locked from here down */ + element *items; /*%< low water item list */ + size_t size; /*%< size of each item on this pool */ + unsigned int maxalloc; /*%< max number of items allowed */ + unsigned int allocated; /*%< # of items currently given out */ + unsigned int freecount; /*%< # of items on reserved list */ + unsigned int freemax; /*%< # of items allowed on free list */ + unsigned int fillcount; /*%< # of items to fetch on each fill */ + /*%< Stats only. */ + unsigned int gets; /*%< # of requests to this pool */ + /*%< Debugging only. */ +#if ISC_MEMPOOL_NAMES + char name[16]; /*%< printed name in stats reports */ +#endif +}; + +/* + * Private Inline-able. + */ + +#if ! ISC_MEM_TRACKLINES +#define ADD_TRACE(a, b, c, d, e) +#define DELETE_TRACE(a, b, c, d, e) +#define ISC_MEMFUNC_SCOPE +#else +#define TRACE_OR_RECORD (ISC_MEM_DEBUGTRACE|ISC_MEM_DEBUGRECORD) +#define ADD_TRACE(a, b, c, d, e) \ + do { \ + if ((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \ + b != NULL) \ + add_trace_entry(a, b, c, d, e); \ + } while (0) +#define DELETE_TRACE(a, b, c, d, e) \ + do { \ + if ((isc_mem_debugging & TRACE_OR_RECORD) != 0 && \ + b != NULL) \ + delete_trace_entry(a, b, c, d, e); \ + } while(0) + +static void +print_active(isc__mem_t *ctx, FILE *out); + +#endif /* ISC_MEM_TRACKLINES */ + +/*% + * The following are intended for internal use (indicated by "isc__" + * prefix) but are not declared as static, allowing direct access + * from unit tests, etc. + */ + +isc_result_t +isc__mem_create2(size_t init_max_size, size_t target_size, + isc_mem_t **ctxp, unsigned int flags); +void +isc__mem_attach(isc_mem_t *source, isc_mem_t **targetp); +void +isc__mem_detach(isc_mem_t **ctxp); +void +isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG); +void +isc__mem_destroy(isc_mem_t **ctxp); +isc_result_t +isc__mem_ondestroy(isc_mem_t *ctx, isc_task_t *task, isc_event_t **event); +void * +isc___mem_get(isc_mem_t *ctx, size_t size FLARG); +void +isc___mem_put(isc_mem_t *ctx, void *ptr, size_t size FLARG); +void +isc__mem_stats(isc_mem_t *ctx, FILE *out); +void * +isc___mem_allocate(isc_mem_t *ctx, size_t size FLARG); +void * +isc___mem_reallocate(isc_mem_t *ctx, void *ptr, size_t size FLARG); +void +isc___mem_free(isc_mem_t *ctx, void *ptr FLARG); +char * +isc___mem_strdup(isc_mem_t *mctx, const char *s FLARG); +void +isc__mem_setdestroycheck(isc_mem_t *ctx, bool flag); +void +isc__mem_setquota(isc_mem_t *ctx, size_t quota); +size_t +isc__mem_getquota(isc_mem_t *ctx); +size_t +isc__mem_inuse(isc_mem_t *ctx); +size_t +isc__mem_maxinuse(isc_mem_t *ctx); +size_t +isc__mem_total(isc_mem_t *ctx); +bool +isc__mem_isovermem(isc_mem_t *ctx); +void +isc__mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg, + size_t hiwater, size_t lowater); +void +isc__mem_waterack(isc_mem_t *ctx0, int flag); +void +isc__mem_setname(isc_mem_t *ctx, const char *name, void *tag); +const char * +isc__mem_getname(isc_mem_t *ctx); +void * +isc__mem_gettag(isc_mem_t *ctx); +isc_result_t +isc__mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp); +void +isc__mempool_setname(isc_mempool_t *mpctx, const char *name); +void +isc__mempool_destroy(isc_mempool_t **mpctxp); +void +isc__mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock); +void * +isc___mempool_get(isc_mempool_t *mpctx FLARG); +void +isc___mempool_put(isc_mempool_t *mpctx, void *mem FLARG); +void +isc__mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit); +unsigned int +isc__mempool_getfreemax(isc_mempool_t *mpctx); +unsigned int +isc__mempool_getfreecount(isc_mempool_t *mpctx); +void +isc__mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit); +unsigned int +isc__mempool_getmaxalloc(isc_mempool_t *mpctx); +unsigned int +isc__mempool_getallocated(isc_mempool_t *mpctx); +void +isc__mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit); +unsigned int +isc__mempool_getfillcount(isc_mempool_t *mpctx); +void +isc__mem_printactive(isc_mem_t *ctx0, FILE *file); +void +isc__mem_printallactive(FILE *file); +unsigned int +isc__mem_references(isc_mem_t *ctx0); + +static struct isc__memmethods { + isc_memmethods_t methods; + + /*% + * The following are defined just for avoiding unused static functions. + */ + void *createx, *create, *create2, *ondestroy, *stats, + *setquota, *getquota, *setname, *getname, *gettag; +} memmethods = { + { + isc__mem_attach, + isc__mem_detach, + isc__mem_destroy, + isc___mem_get, + isc___mem_put, + isc___mem_putanddetach, + isc___mem_allocate, + isc___mem_reallocate, + isc___mem_strdup, + isc___mem_free, + isc__mem_setdestroycheck, + isc__mem_setwater, + isc__mem_waterack, + isc__mem_inuse, + isc__mem_maxinuse, + isc__mem_total, + isc__mem_isovermem, + isc__mempool_create + }, + (void *)isc_mem_createx, + (void *)isc_mem_create, + (void *)isc_mem_create2, + (void *)isc_mem_ondestroy, + (void *)isc_mem_stats, + (void *)isc_mem_setquota, + (void *)isc_mem_getquota, + (void *)isc_mem_setname, + (void *)isc_mem_getname, + (void *)isc_mem_gettag +}; + +static struct isc__mempoolmethods { + isc_mempoolmethods_t methods; + + /*% + * The following are defined just for avoiding unused static functions. + */ + void *getfreemax, *getfreecount, *getmaxalloc, *getfillcount; +} mempoolmethods = { + { + isc__mempool_destroy, + isc___mempool_get, + isc___mempool_put, + isc__mempool_getallocated, + isc__mempool_setmaxalloc, + isc__mempool_setfreemax, + isc__mempool_setname, + isc__mempool_associatelock, + isc__mempool_setfillcount + }, + (void *)isc_mempool_getfreemax, + (void *)isc_mempool_getfreecount, + (void *)isc_mempool_getmaxalloc, + (void *)isc_mempool_getfillcount +}; + +#if ISC_MEM_TRACKLINES +/*! + * mctx must be locked. + */ +static inline void +add_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size FLARG) { + debuglink_t *dl; + unsigned int i; + size_t mysize = size; + + if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) + fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_ADDTRACE, + "add %p size %u " + "file %s line %u mctx %p\n"), + ptr, size, file, line, mctx); + + if (mctx->debuglist == NULL) + return; + + if (mysize > mctx->max_size) + mysize = mctx->max_size; + + dl = ISC_LIST_HEAD(mctx->debuglist[mysize]); + while (dl != NULL) { + if (dl->count == DEBUGLIST_COUNT) + goto next; + for (i = 0; i < DEBUGLIST_COUNT; i++) { + if (dl->ptr[i] == NULL) { + dl->ptr[i] = ptr; + dl->size[i] = size; + dl->file[i] = file; + dl->line[i] = line; + dl->count++; + return; + } + } + next: + dl = ISC_LIST_NEXT(dl, link); + } + + dl = malloc(sizeof(debuglink_t)); + INSIST(dl != NULL); + + ISC_LINK_INIT(dl, link); + for (i = 1; i < DEBUGLIST_COUNT; i++) { + dl->ptr[i] = NULL; + dl->size[i] = 0; + dl->file[i] = NULL; + dl->line[i] = 0; + } + + dl->ptr[0] = ptr; + dl->size[0] = size; + dl->file[0] = file; + dl->line[0] = line; + dl->count = 1; + + ISC_LIST_PREPEND(mctx->debuglist[mysize], dl, link); + mctx->debuglistcnt++; +} + +static inline void +delete_trace_entry(isc__mem_t *mctx, const void *ptr, size_t size, + const char *file, unsigned int line) +{ + debuglink_t *dl; + unsigned int i; + + if ((isc_mem_debugging & ISC_MEM_DEBUGTRACE) != 0) + fprintf(stderr, isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_DELTRACE, + "del %p size %u " + "file %s line %u mctx %p\n"), + ptr, size, file, line, mctx); + + if (mctx->debuglist == NULL) + return; + + if (size > mctx->max_size) + size = mctx->max_size; + + dl = ISC_LIST_HEAD(mctx->debuglist[size]); + while (dl != NULL) { + for (i = 0; i < DEBUGLIST_COUNT; i++) { + if (dl->ptr[i] == ptr) { + dl->ptr[i] = NULL; + dl->size[i] = 0; + dl->file[i] = NULL; + dl->line[i] = 0; + + INSIST(dl->count > 0); + dl->count--; + if (dl->count == 0) { + ISC_LIST_UNLINK(mctx->debuglist[size], + dl, link); + free(dl); + } + return; + } + } + dl = ISC_LIST_NEXT(dl, link); + } + + /* + * If we get here, we didn't find the item on the list. We're + * screwed. + */ + INSIST(dl != NULL); +} +#endif /* ISC_MEM_TRACKLINES */ + +static inline size_t +rmsize(size_t size) { + /* + * round down to ALIGNMENT_SIZE + */ + return (size & (~(ALIGNMENT_SIZE - 1))); +} + +static inline size_t +quantize(size_t size) { + /*! + * Round up the result in order to get a size big + * enough to satisfy the request and be aligned on ALIGNMENT_SIZE + * byte boundaries. + */ + + if (size == 0U) + return (ALIGNMENT_SIZE); + return ((size + ALIGNMENT_SIZE - 1) & (~(ALIGNMENT_SIZE - 1))); +} + +static inline bool +more_basic_blocks(isc__mem_t *ctx) { + void *tmp; + unsigned char *curr, *next; + unsigned char *first, *last; + unsigned char **table; + unsigned int table_size; + size_t increment; + int i; + + /* Require: we hold the context lock. */ + + /* + * Did we hit the quota for this context? + */ + increment = NUM_BASIC_BLOCKS * ctx->mem_target; + if (ctx->quota != 0U && ctx->total + increment > ctx->quota) + return (false); + + INSIST(ctx->basic_table_count <= ctx->basic_table_size); + if (ctx->basic_table_count == ctx->basic_table_size) { + table_size = ctx->basic_table_size + TABLE_INCREMENT; + table = (ctx->memalloc)(ctx->arg, + table_size * sizeof(unsigned char *)); + if (table == NULL) { + ctx->memalloc_failures++; + return (false); + } + if (ctx->basic_table_size != 0) { + memmove(table, ctx->basic_table, + ctx->basic_table_size * + sizeof(unsigned char *)); + (ctx->memfree)(ctx->arg, ctx->basic_table); + } + ctx->basic_table = table; + ctx->basic_table_size = table_size; + } + + tmp = (ctx->memalloc)(ctx->arg, NUM_BASIC_BLOCKS * ctx->mem_target); + if (tmp == NULL) { + ctx->memalloc_failures++; + return (false); + } + ctx->total += increment; + ctx->basic_table[ctx->basic_table_count] = tmp; + ctx->basic_table_count++; + + curr = tmp; + next = curr + ctx->mem_target; + for (i = 0; i < (NUM_BASIC_BLOCKS - 1); i++) { + ((element *)curr)->next = (element *)next; + curr = next; + next += ctx->mem_target; + } + /* + * curr is now pointing at the last block in the + * array. + */ + ((element *)curr)->next = NULL; + first = tmp; + last = first + NUM_BASIC_BLOCKS * ctx->mem_target - 1; + if (first < ctx->lowest || ctx->lowest == NULL) + ctx->lowest = first; + if (last > ctx->highest) + ctx->highest = last; + ctx->basic_blocks = tmp; + + return (true); +} + +static inline bool +more_frags(isc__mem_t *ctx, size_t new_size) { + int i, frags; + size_t total_size; + void *tmp; + unsigned char *curr, *next; + + /*! + * Try to get more fragments by chopping up a basic block. + */ + + if (ctx->basic_blocks == NULL) { + if (!more_basic_blocks(ctx)) { + /* + * We can't get more memory from the OS, or we've + * hit the quota for this context. + */ + /* + * XXXRTH "At quota" notification here. + */ + return (false); + } + } + + total_size = ctx->mem_target; + tmp = ctx->basic_blocks; + ctx->basic_blocks = ctx->basic_blocks->next; + frags = (int)(total_size / new_size); + ctx->stats[new_size].blocks++; + ctx->stats[new_size].freefrags += frags; + /* + * Set up a linked-list of blocks of size + * "new_size". + */ + curr = tmp; + next = curr + new_size; + total_size -= new_size; + for (i = 0; i < (frags - 1); i++) { + ((element *)curr)->next = (element *)next; + curr = next; + next += new_size; + total_size -= new_size; + } + /* + * Add the remaining fragment of the basic block to a free list. + */ + total_size = rmsize(total_size); + if (total_size > 0U) { + ((element *)next)->next = ctx->freelists[total_size]; + ctx->freelists[total_size] = (element *)next; + ctx->stats[total_size].freefrags++; + } + /* + * curr is now pointing at the last block in the + * array. + */ + ((element *)curr)->next = NULL; + ctx->freelists[new_size] = tmp; + + return (true); +} + +static inline void * +mem_getunlocked(isc__mem_t *ctx, size_t size) { + size_t new_size = quantize(size); + void *ret; + + if (new_size >= ctx->max_size) { + /* + * memget() was called on something beyond our upper limit. + */ + if (ctx->quota != 0U && ctx->total + size > ctx->quota) { + ret = NULL; + goto done; + } + ret = (ctx->memalloc)(ctx->arg, size); + if (ret == NULL) { + ctx->memalloc_failures++; + goto done; + } + ctx->total += size; + ctx->inuse += size; + ctx->stats[ctx->max_size].gets++; + ctx->stats[ctx->max_size].totalgets++; + /* + * If we don't set new_size to size, then the + * ISC_MEM_FILL code might write over bytes we + * don't own. + */ + new_size = size; + goto done; + } + /* + * If there are no blocks in the free list for this size, get a chunk + * of memory and then break it up into "new_size"-sized blocks, adding + * them to the free list. + */ + if (ctx->freelists[new_size] == NULL && !more_frags(ctx, new_size)) + return (NULL); + + /* + * The free list uses the "rounded-up" size "new_size". + */ + + ret = ctx->freelists[new_size]; + ctx->freelists[new_size] = ctx->freelists[new_size]->next; + + + /* + * The stats[] uses the _actual_ "size" requested by the + * caller, with the caveat (in the code above) that "size" >= the + * max. size (max_size) ends up getting recorded as a call to + * max_size. + */ + ctx->stats[size].gets++; + ctx->stats[size].totalgets++; + ctx->stats[new_size].freefrags--; + ctx->inuse += new_size; + + done: + +#if ISC_MEM_FILL + if (ret != NULL) + memset(ret, 0xbe, new_size); /* Mnemonic for "beef". */ +#endif + + return (ret); +} + +#if ISC_MEM_FILL && ISC_MEM_CHECKOVERRUN +static inline void +check_overrun(void *mem, size_t size, size_t new_size) { + unsigned char *cp; + + cp = (unsigned char *)mem; + cp += size; + while (size < new_size) { + INSIST(*cp == 0xbe); + cp++; + size++; + } +} +#endif + +/* coverity[+free : arg-1] */ +static inline void +mem_putunlocked(isc__mem_t *ctx, void *mem, size_t size) { + size_t new_size = quantize(size); + + if (new_size >= ctx->max_size) { + /* + * memput() called on something beyond our upper limit. + */ +#if ISC_MEM_FILL + memset(mem, 0xde, size); /* Mnemonic for "dead". */ +#endif + (ctx->memfree)(ctx->arg, mem); + INSIST(ctx->stats[ctx->max_size].gets != 0U); + ctx->stats[ctx->max_size].gets--; + INSIST(size <= ctx->inuse); + ctx->inuse -= size; + return; + } + +#if ISC_MEM_FILL +#if ISC_MEM_CHECKOVERRUN + check_overrun(mem, size, new_size); +#endif + memset(mem, 0xde, new_size); /* Mnemonic for "dead". */ +#endif + + /* + * The free list uses the "rounded-up" size "new_size". + */ + ((element *)mem)->next = ctx->freelists[new_size]; + ctx->freelists[new_size] = (element *)mem; + + /* + * The stats[] uses the _actual_ "size" requested by the + * caller, with the caveat (in the code above) that "size" >= the + * max. size (max_size) ends up getting recorded as a call to + * max_size. + */ + INSIST(ctx->stats[size].gets != 0U); + ctx->stats[size].gets--; + ctx->stats[new_size].freefrags++; + ctx->inuse -= new_size; +} + +/*! + * Perform a malloc, doing memory filling and overrun detection as necessary. + */ +static inline void * +mem_get(isc__mem_t *ctx, size_t size) { + char *ret; + +#if ISC_MEM_CHECKOVERRUN + size += 1; +#endif + + ret = (ctx->memalloc)(ctx->arg, size); + if (ret == NULL) + ctx->memalloc_failures++; + +#if ISC_MEM_FILL + if (ret != NULL) + memset(ret, 0xbe, size); /* Mnemonic for "beef". */ +#else +# if ISC_MEM_CHECKOVERRUN + if (ret != NULL) + ret[size-1] = 0xbe; +# endif +#endif + + return (ret); +} + +/*! + * Perform a free, doing memory filling and overrun detection as necessary. + */ +/* coverity[+free : arg-1] */ +static inline void +mem_put(isc__mem_t *ctx, void *mem, size_t size) { +#if ISC_MEM_CHECKOVERRUN + INSIST(((unsigned char *)mem)[size] == 0xbe); +#endif +#if ISC_MEM_FILL + memset(mem, 0xde, size); /* Mnemonic for "dead". */ +#else + UNUSED(size); +#endif + (ctx->memfree)(ctx->arg, mem); +} + +/*! + * Update internal counters after a memory get. + */ +static inline void +mem_getstats(isc__mem_t *ctx, size_t size) { + ctx->total += size; + ctx->inuse += size; + + if (size > ctx->max_size) { + ctx->stats[ctx->max_size].gets++; + ctx->stats[ctx->max_size].totalgets++; + } else { + ctx->stats[size].gets++; + ctx->stats[size].totalgets++; + } +} + +/*! + * Update internal counters after a memory put. + */ +static inline void +mem_putstats(isc__mem_t *ctx, void *ptr, size_t size) { + UNUSED(ptr); + + INSIST(ctx->inuse >= size); + ctx->inuse -= size; + + if (size > ctx->max_size) { + INSIST(ctx->stats[ctx->max_size].gets > 0U); + ctx->stats[ctx->max_size].gets--; + } else { + INSIST(ctx->stats[size].gets > 0U); + ctx->stats[size].gets--; + } +} + +/* + * Private. + */ + +static void * +default_memalloc(void *arg, size_t size) { + UNUSED(arg); + if (size == 0U) + size = 1; + return (malloc(size)); +} + +static void +default_memfree(void *arg, void *ptr) { + UNUSED(arg); + free(ptr); +} + +static void +initialize_action(void) { + RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS); + RUNTIME_CHECK(isc_mutex_init(&contextslock) == ISC_R_SUCCESS); + ISC_LIST_INIT(contexts); + totallost = 0; +} + +/* + * Public. + */ + +isc_result_t +isc_mem_createx(size_t init_max_size, size_t target_size, + isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg, + isc_mem_t **ctxp) +{ + return (isc_mem_createx2(init_max_size, target_size, memalloc, memfree, + arg, ctxp, isc_mem_defaultflags)); + +} + +isc_result_t +isc_mem_createx2(size_t init_max_size, size_t target_size, + isc_memalloc_t memalloc, isc_memfree_t memfree, void *arg, + isc_mem_t **ctxp, unsigned int flags) +{ + isc__mem_t *ctx; + isc_result_t result; + + REQUIRE(ctxp != NULL && *ctxp == NULL); + REQUIRE(memalloc != NULL); + REQUIRE(memfree != NULL); + + INSIST((ALIGNMENT_SIZE & (ALIGNMENT_SIZE - 1)) == 0); + + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); + + ctx = (memalloc)(arg, sizeof(*ctx)); + if (ctx == NULL) + return (ISC_R_NOMEMORY); + + if ((flags & ISC_MEMFLAG_NOLOCK) == 0) { + result = isc_mutex_init(&ctx->lock); + if (result != ISC_R_SUCCESS) { + (memfree)(arg, ctx); + return (result); + } + } + + if (init_max_size == 0U) + ctx->max_size = DEF_MAX_SIZE; + else + ctx->max_size = init_max_size; + ctx->flags = flags; + ctx->references = 1; + memset(ctx->name, 0, sizeof(ctx->name)); + ctx->tag = NULL; + ctx->quota = 0; + ctx->total = 0; + ctx->inuse = 0; + ctx->maxinuse = 0; + ctx->hi_water = 0; + ctx->lo_water = 0; + ctx->hi_called = false; + ctx->is_overmem = false; + ctx->water = NULL; + ctx->water_arg = NULL; + ctx->common.impmagic = MEM_MAGIC; + ctx->common.magic = ISCAPI_MCTX_MAGIC; + ctx->common.methods = (isc_memmethods_t *)&memmethods; + isc_ondestroy_init(&ctx->ondestroy); + ctx->memalloc = memalloc; + ctx->memfree = memfree; + ctx->arg = arg; + ctx->stats = NULL; + ctx->checkfree = true; +#if ISC_MEM_TRACKLINES + ctx->debuglist = NULL; + ctx->debuglistcnt = 0; +#endif + ISC_LIST_INIT(ctx->pools); + ctx->poolcnt = 0; + ctx->freelists = NULL; + ctx->basic_blocks = NULL; + ctx->basic_table = NULL; + ctx->basic_table_count = 0; + ctx->basic_table_size = 0; + ctx->lowest = NULL; + ctx->highest = NULL; + + ctx->stats = (memalloc)(arg, + (ctx->max_size+1) * sizeof(struct stats)); + if (ctx->stats == NULL) { + result = ISC_R_NOMEMORY; + goto error; + } + memset(ctx->stats, 0, (ctx->max_size + 1) * sizeof(struct stats)); + + if ((flags & ISC_MEMFLAG_INTERNAL) != 0) { + if (target_size == 0U) + ctx->mem_target = DEF_MEM_TARGET; + else + ctx->mem_target = target_size; + ctx->freelists = (memalloc)(arg, ctx->max_size * + sizeof(element *)); + if (ctx->freelists == NULL) { + result = ISC_R_NOMEMORY; + goto error; + } + memset(ctx->freelists, 0, + ctx->max_size * sizeof(element *)); + } + +#if ISC_MEM_TRACKLINES + if ((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0) { + unsigned int i; + + ctx->debuglist = (memalloc)(arg, + (ctx->max_size+1) * sizeof(debuglist_t)); + if (ctx->debuglist == NULL) { + result = ISC_R_NOMEMORY; + goto error; + } + for (i = 0; i <= ctx->max_size; i++) + ISC_LIST_INIT(ctx->debuglist[i]); + } +#endif + + ctx->memalloc_failures = 0; + + LOCK(&contextslock); + ISC_LIST_INITANDAPPEND(contexts, ctx, link); + UNLOCK(&contextslock); + + *ctxp = (isc_mem_t *)ctx; + return (ISC_R_SUCCESS); + + error: + if (ctx != NULL) { + if (ctx->stats != NULL) + (memfree)(arg, ctx->stats); + if (ctx->freelists != NULL) + (memfree)(arg, ctx->freelists); +#if ISC_MEM_TRACKLINES + if (ctx->debuglist != NULL) + (ctx->memfree)(ctx->arg, ctx->debuglist); +#endif /* ISC_MEM_TRACKLINES */ + if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0) + DESTROYLOCK(&ctx->lock); + (memfree)(arg, ctx); + } + + return (result); +} + +static void +destroy(isc__mem_t *ctx) { + unsigned int i; + isc_ondestroy_t ondest; + + LOCK(&contextslock); + ISC_LIST_UNLINK(contexts, ctx, link); + totallost += ctx->inuse; + UNLOCK(&contextslock); + + ctx->common.impmagic = 0; + ctx->common.magic = 0; + + INSIST(ISC_LIST_EMPTY(ctx->pools)); + +#if ISC_MEM_TRACKLINES + if (ctx->debuglist != NULL) { + if (ctx->checkfree) { + for (i = 0; i <= ctx->max_size; i++) { + if (!ISC_LIST_EMPTY(ctx->debuglist[i])) + print_active(ctx, stderr); + INSIST(ISC_LIST_EMPTY(ctx->debuglist[i])); + } + } else { + debuglink_t *dl; + + for (i = 0; i <= ctx->max_size; i++) + for (dl = ISC_LIST_HEAD(ctx->debuglist[i]); + dl != NULL; + dl = ISC_LIST_HEAD(ctx->debuglist[i])) { + ISC_LIST_UNLINK(ctx->debuglist[i], + dl, link); + free(dl); + } + } + (ctx->memfree)(ctx->arg, ctx->debuglist); + } +#endif + INSIST(ctx->references == 0); + + if (ctx->checkfree) { + for (i = 0; i <= ctx->max_size; i++) { + if (ctx->stats[i].gets != 0U) { + fprintf(stderr, + "Failing assertion due to probable " + "leaked memory in context %p (\"%s\") " + "(stats[%u].gets == %lu).\n", + ctx, ctx->name, i, ctx->stats[i].gets); +#if ISC_MEM_TRACKLINES + print_active(ctx, stderr); +#endif + INSIST(ctx->stats[i].gets == 0U); + } + } + } + + (ctx->memfree)(ctx->arg, ctx->stats); + + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + for (i = 0; i < ctx->basic_table_count; i++) + (ctx->memfree)(ctx->arg, ctx->basic_table[i]); + (ctx->memfree)(ctx->arg, ctx->freelists); + if (ctx->basic_table != NULL) + (ctx->memfree)(ctx->arg, ctx->basic_table); + } + + ondest = ctx->ondestroy; + + if ((ctx->flags & ISC_MEMFLAG_NOLOCK) == 0) + DESTROYLOCK(&ctx->lock); + (ctx->memfree)(ctx->arg, ctx); + + isc_ondestroy_notify(&ondest, ctx); +} + +void +isc__mem_attach(isc_mem_t *source0, isc_mem_t **targetp) { + isc__mem_t *source = (isc__mem_t *)source0; + + REQUIRE(VALID_CONTEXT(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + MCTXLOCK(source, &source->lock); + source->references++; + MCTXUNLOCK(source, &source->lock); + + *targetp = (isc_mem_t *)source; +} + +void +isc__mem_detach(isc_mem_t **ctxp) { + isc__mem_t *ctx; + bool want_destroy = false; + + REQUIRE(ctxp != NULL); + ctx = (isc__mem_t *)*ctxp; + REQUIRE(VALID_CONTEXT(ctx)); + + MCTXLOCK(ctx, &ctx->lock); + INSIST(ctx->references > 0); + ctx->references--; + if (ctx->references == 0) + want_destroy = true; + MCTXUNLOCK(ctx, &ctx->lock); + + if (want_destroy) + destroy(ctx); + + *ctxp = NULL; +} + +/* + * isc_mem_putanddetach() is the equivalent of: + * + * mctx = NULL; + * isc_mem_attach(ptr->mctx, &mctx); + * isc_mem_detach(&ptr->mctx); + * isc_mem_put(mctx, ptr, sizeof(*ptr); + * isc_mem_detach(&mctx); + */ + +void +isc___mem_putanddetach(isc_mem_t **ctxp, void *ptr, size_t size FLARG) { + isc__mem_t *ctx; + bool want_destroy = false; + size_info *si; + size_t oldsize; + + REQUIRE(ctxp != NULL); + ctx = (isc__mem_t *)*ctxp; + REQUIRE(VALID_CONTEXT(ctx)); + REQUIRE(ptr != NULL); + + /* + * Must be before mem_putunlocked() as ctxp is usually within + * [ptr..ptr+size). + */ + *ctxp = NULL; + + if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) { + if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) { + si = &(((size_info *)ptr)[-1]); + oldsize = si->u.size - ALIGNMENT_SIZE; + if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) + oldsize -= ALIGNMENT_SIZE; + INSIST(oldsize == size); + } + isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS); + + MCTXLOCK(ctx, &ctx->lock); + ctx->references--; + if (ctx->references == 0) + want_destroy = true; + MCTXUNLOCK(ctx, &ctx->lock); + if (want_destroy) + destroy(ctx); + + return; + } + + MCTXLOCK(ctx, &ctx->lock); + + DELETE_TRACE(ctx, ptr, size, file, line); + + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + mem_putunlocked(ctx, ptr, size); + } else { + mem_putstats(ctx, ptr, size); + mem_put(ctx, ptr, size); + } + + INSIST(ctx->references > 0); + ctx->references--; + if (ctx->references == 0) + want_destroy = true; + + MCTXUNLOCK(ctx, &ctx->lock); + + if (want_destroy) + destroy(ctx); +} + +void +isc__mem_destroy(isc_mem_t **ctxp) { + isc__mem_t *ctx; + + /* + * This routine provides legacy support for callers who use mctxs + * without attaching/detaching. + */ + + REQUIRE(ctxp != NULL); + ctx = (isc__mem_t *)*ctxp; + REQUIRE(VALID_CONTEXT(ctx)); + + MCTXLOCK(ctx, &ctx->lock); +#if ISC_MEM_TRACKLINES + if (ctx->references != 1) + print_active(ctx, stderr); +#endif + REQUIRE(ctx->references == 1); + ctx->references--; + MCTXUNLOCK(ctx, &ctx->lock); + + destroy(ctx); + + *ctxp = NULL; +} + +isc_result_t +isc_mem_ondestroy(isc_mem_t *ctx0, isc_task_t *task, isc_event_t **event) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + isc_result_t res; + + MCTXLOCK(ctx, &ctx->lock); + res = isc_ondestroy_register(&ctx->ondestroy, task, event); + MCTXUNLOCK(ctx, &ctx->lock); + + return (res); +} + +void * +isc___mem_get(isc_mem_t *ctx0, size_t size FLARG) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + void *ptr; + bool call_water = false; + + REQUIRE(VALID_CONTEXT(ctx)); + + if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) + return (isc__mem_allocate(ctx0, size FLARG_PASS)); + + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + MCTXLOCK(ctx, &ctx->lock); + ptr = mem_getunlocked(ctx, size); + } else { + ptr = mem_get(ctx, size); + MCTXLOCK(ctx, &ctx->lock); + if (ptr != NULL) + mem_getstats(ctx, size); + } + + ADD_TRACE(ctx, ptr, size, file, line); + if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water) { + ctx->is_overmem = true; + if (!ctx->hi_called) + call_water = true; + } + if (ctx->inuse > ctx->maxinuse) { + ctx->maxinuse = ctx->inuse; + if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water && + (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) + fprintf(stderr, "maxinuse = %lu\n", + (unsigned long)ctx->inuse); + } + MCTXUNLOCK(ctx, &ctx->lock); + + if (call_water && (ctx->water != NULL)) + (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER); + + return (ptr); +} + +void +isc___mem_put(isc_mem_t *ctx0, void *ptr, size_t size FLARG) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + bool call_water = false; + size_info *si; + size_t oldsize; + + REQUIRE(VALID_CONTEXT(ctx)); + REQUIRE(ptr != NULL); + + if ((isc_mem_debugging & (ISC_MEM_DEBUGSIZE|ISC_MEM_DEBUGCTX)) != 0) { + if ((isc_mem_debugging & ISC_MEM_DEBUGSIZE) != 0) { + si = &(((size_info *)ptr)[-1]); + oldsize = si->u.size - ALIGNMENT_SIZE; + if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) + oldsize -= ALIGNMENT_SIZE; + INSIST(oldsize == size); + } + isc__mem_free((isc_mem_t *)ctx, ptr FLARG_PASS); + return; + } + + MCTXLOCK(ctx, &ctx->lock); + + DELETE_TRACE(ctx, ptr, size, file, line); + + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + mem_putunlocked(ctx, ptr, size); + } else { + mem_putstats(ctx, ptr, size); + mem_put(ctx, ptr, size); + } + + /* + * The check against ctx->lo_water == 0 is for the condition + * when the context was pushed over hi_water but then had + * isc_mem_setwater() called with 0 for hi_water and lo_water. + */ + if ((ctx->inuse < ctx->lo_water) || (ctx->lo_water == 0U)) { + ctx->is_overmem = false; + if (ctx->hi_called) + call_water = true; + } + + MCTXUNLOCK(ctx, &ctx->lock); + + if (call_water && (ctx->water != NULL)) + (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER); +} + +void +isc__mem_waterack(isc_mem_t *ctx0, int flag) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + + REQUIRE(VALID_CONTEXT(ctx)); + + MCTXLOCK(ctx, &ctx->lock); + if (flag == ISC_MEM_LOWATER) + ctx->hi_called = false; + else if (flag == ISC_MEM_HIWATER) + ctx->hi_called = true; + MCTXUNLOCK(ctx, &ctx->lock); +} + +#if ISC_MEM_TRACKLINES +static void +print_active(isc__mem_t *mctx, FILE *out) { + if (mctx->debuglist != NULL) { + debuglink_t *dl; + unsigned int i, j; + const char *format; + bool found; + + fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_DUMPALLOC, + "Dump of all outstanding " + "memory allocations:\n")); + found = false; + format = isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_PTRFILELINE, + "\tptr %p size %u file %s line %u\n"); + for (i = 0; i <= mctx->max_size; i++) { + dl = ISC_LIST_HEAD(mctx->debuglist[i]); + + if (dl != NULL) + found = true; + + while (dl != NULL) { + for (j = 0; j < DEBUGLIST_COUNT; j++) + if (dl->ptr[j] != NULL) + fprintf(out, format, + dl->ptr[j], + dl->size[j], + dl->file[j], + dl->line[j]); + dl = ISC_LIST_NEXT(dl, link); + } + } + if (!found) + fputs(isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_NONE, "\tNone.\n"), out); + } +} +#endif + +/* + * Print the stats[] on the stream "out" with suitable formatting. + */ +void +isc_mem_stats(isc_mem_t *ctx0, FILE *out) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + size_t i; + const struct stats *s; + const isc__mempool_t *pool; + + REQUIRE(VALID_CONTEXT(ctx)); + MCTXLOCK(ctx, &ctx->lock); + + for (i = 0; i <= ctx->max_size; i++) { + s = &ctx->stats[i]; + + if (s->totalgets == 0U && s->gets == 0U) + continue; + fprintf(out, "%s%5lu: %11lu gets, %11lu rem", + (i == ctx->max_size) ? ">=" : " ", + (unsigned long) i, s->totalgets, s->gets); + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0 && + (s->blocks != 0U || s->freefrags != 0U)) + fprintf(out, " (%lu bl, %lu ff)", + s->blocks, s->freefrags); + fputc('\n', out); + } + + /* + * Note that since a pool can be locked now, these stats might be + * somewhat off if the pool is in active use at the time the stats + * are dumped. The link fields are protected by the isc_mem_t's + * lock, however, so walking this list and extracting integers from + * stats fields is always safe. + */ + pool = ISC_LIST_HEAD(ctx->pools); + if (pool != NULL) { + fprintf(out, "%s", isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_POOLSTATS, + "[Pool statistics]\n")); + fprintf(out, "%15s %10s %10s %10s %10s %10s %10s %10s %1s\n", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_POOLNAME, "name"), + isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_POOLSIZE, "size"), + isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_POOLMAXALLOC, "maxalloc"), + isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_POOLALLOCATED, "allocated"), + isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_POOLFREECOUNT, "freecount"), + isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_POOLFREEMAX, "freemax"), + isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_POOLFILLCOUNT, "fillcount"), + isc_msgcat_get(isc_msgcat, ISC_MSGSET_MEM, + ISC_MSG_POOLGETS, "gets"), + "L"); + } + while (pool != NULL) { + fprintf(out, "%15s %10lu %10u %10u %10u %10u %10u %10u %s\n", +#if ISC_MEMPOOL_NAMES + pool->name, +#else + "(not tracked)", +#endif + (unsigned long) pool->size, pool->maxalloc, + pool->allocated, pool->freecount, pool->freemax, + pool->fillcount, pool->gets, + (pool->lock == NULL ? "N" : "Y")); + pool = ISC_LIST_NEXT(pool, link); + } + +#if ISC_MEM_TRACKLINES + print_active(ctx, out); +#endif + + MCTXUNLOCK(ctx, &ctx->lock); +} + +/* + * Replacements for malloc() and free() -- they implicitly remember the + * size of the object allocated (with some additional overhead). + */ + +static void * +mem_allocateunlocked(isc_mem_t *ctx0, size_t size) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + size_info *si; + + size += ALIGNMENT_SIZE; + if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) + size += ALIGNMENT_SIZE; + + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) + si = mem_getunlocked(ctx, size); + else + si = mem_get(ctx, size); + + if (si == NULL) + return (NULL); + if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) { + si->u.ctx = ctx; + si++; + } + si->u.size = size; + return (&si[1]); +} + +void * +isc___mem_allocate(isc_mem_t *ctx0, size_t size FLARG) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + size_info *si; + bool call_water = false; + + REQUIRE(VALID_CONTEXT(ctx)); + + MCTXLOCK(ctx, &ctx->lock); + si = mem_allocateunlocked((isc_mem_t *)ctx, size); + if (((ctx->flags & ISC_MEMFLAG_INTERNAL) == 0) && (si != NULL)) + mem_getstats(ctx, si[-1].u.size); + + ADD_TRACE(ctx, si, si[-1].u.size, file, line); + if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water && + !ctx->is_overmem) { + ctx->is_overmem = true; + } + + if (ctx->hi_water != 0U && !ctx->hi_called && + ctx->inuse > ctx->hi_water) { + ctx->hi_called = true; + call_water = true; + } + if (ctx->inuse > ctx->maxinuse) { + ctx->maxinuse = ctx->inuse; + if (ctx->hi_water != 0U && ctx->inuse > ctx->hi_water && + (isc_mem_debugging & ISC_MEM_DEBUGUSAGE) != 0) + fprintf(stderr, "maxinuse = %lu\n", + (unsigned long)ctx->inuse); + } + MCTXUNLOCK(ctx, &ctx->lock); + + if (call_water) + (ctx->water)(ctx->water_arg, ISC_MEM_HIWATER); + + return (si); +} + +void * +isc___mem_reallocate(isc_mem_t *ctx0, void *ptr, size_t size FLARG) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + void *new_ptr = NULL; + size_t oldsize, copysize; + + REQUIRE(VALID_CONTEXT(ctx)); + + /* + * This function emulates the realloc(3) standard library function: + * - if size > 0, allocate new memory; and if ptr is non NULL, copy + * as much of the old contents to the new buffer and free the old one. + * Note that when allocation fails the original pointer is intact; + * the caller must free it. + * - if size is 0 and ptr is non NULL, simply free the given ptr. + * - this function returns: + * pointer to the newly allocated memory, or + * NULL if allocation fails or doesn't happen. + */ + if (size > 0U) { + new_ptr = isc__mem_allocate(ctx0, size FLARG_PASS); + if (new_ptr != NULL && ptr != NULL) { + oldsize = (((size_info *)ptr)[-1]).u.size; + INSIST(oldsize >= ALIGNMENT_SIZE); + oldsize -= ALIGNMENT_SIZE; + if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) { + INSIST(oldsize >= ALIGNMENT_SIZE); + oldsize -= ALIGNMENT_SIZE; + } + copysize = (oldsize > size) ? size : oldsize; + memmove(new_ptr, ptr, copysize); + isc__mem_free(ctx0, ptr FLARG_PASS); + } + } else if (ptr != NULL) + isc__mem_free(ctx0, ptr FLARG_PASS); + + return (new_ptr); +} + +void +isc___mem_free(isc_mem_t *ctx0, void *ptr FLARG) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + size_info *si; + size_t size; + bool call_water= false; + + REQUIRE(VALID_CONTEXT(ctx)); + REQUIRE(ptr != NULL); + + if ((isc_mem_debugging & ISC_MEM_DEBUGCTX) != 0) { + si = &(((size_info *)ptr)[-2]); + REQUIRE(si->u.ctx == ctx); + size = si[1].u.size; + } else { + si = &(((size_info *)ptr)[-1]); + size = si->u.size; + } + + MCTXLOCK(ctx, &ctx->lock); + + DELETE_TRACE(ctx, ptr, size, file, line); + + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + mem_putunlocked(ctx, si, size); + } else { + mem_putstats(ctx, si, size); + mem_put(ctx, si, size); + } + + /* + * The check against ctx->lo_water == 0 is for the condition + * when the context was pushed over hi_water but then had + * isc_mem_setwater() called with 0 for hi_water and lo_water. + */ + if (ctx->is_overmem && + (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) { + ctx->is_overmem = false; + } + + if (ctx->hi_called && + (ctx->inuse < ctx->lo_water || ctx->lo_water == 0U)) { + ctx->hi_called = false; + + if (ctx->water != NULL) + call_water = true; + } + MCTXUNLOCK(ctx, &ctx->lock); + + if (call_water) + (ctx->water)(ctx->water_arg, ISC_MEM_LOWATER); +} + + +/* + * Other useful things. + */ + +char * +isc___mem_strdup(isc_mem_t *mctx0, const char *s FLARG) { + isc__mem_t *mctx = (isc__mem_t *)mctx0; + size_t len; + char *ns; + + REQUIRE(VALID_CONTEXT(mctx)); + REQUIRE(s != NULL); + + len = strlen(s) + 1; + + ns = isc__mem_allocate((isc_mem_t *)mctx, len FLARG_PASS); + + if (ns != NULL) + strlcpy(ns, s, len); + + return (ns); +} + +void +isc__mem_setdestroycheck(isc_mem_t *ctx0, bool flag) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + + REQUIRE(VALID_CONTEXT(ctx)); + MCTXLOCK(ctx, &ctx->lock); + + ctx->checkfree = flag; + + MCTXUNLOCK(ctx, &ctx->lock); +} + +/* + * Quotas + */ + +void +isc_mem_setquota(isc_mem_t *ctx0, size_t quota) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + + REQUIRE(VALID_CONTEXT(ctx)); + MCTXLOCK(ctx, &ctx->lock); + + ctx->quota = quota; + + MCTXUNLOCK(ctx, &ctx->lock); +} + +size_t +isc_mem_getquota(isc_mem_t *ctx0) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + size_t quota; + + REQUIRE(VALID_CONTEXT(ctx)); + MCTXLOCK(ctx, &ctx->lock); + + quota = ctx->quota; + + MCTXUNLOCK(ctx, &ctx->lock); + + return (quota); +} + +size_t +isc__mem_inuse(isc_mem_t *ctx0) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + size_t inuse; + + REQUIRE(VALID_CONTEXT(ctx)); + MCTXLOCK(ctx, &ctx->lock); + + inuse = ctx->inuse; + + MCTXUNLOCK(ctx, &ctx->lock); + + return (inuse); +} + +size_t +isc__mem_maxinuse(isc_mem_t *ctx0) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + size_t maxinuse; + + REQUIRE(VALID_CONTEXT(ctx)); + MCTXLOCK(ctx, &ctx->lock); + + maxinuse = ctx->maxinuse; + + MCTXUNLOCK(ctx, &ctx->lock); + + return (maxinuse); +} + +size_t +isc__mem_total(isc_mem_t *ctx0) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + size_t total; + + REQUIRE(VALID_CONTEXT(ctx)); + MCTXLOCK(ctx, &ctx->lock); + + total = ctx->total; + + MCTXUNLOCK(ctx, &ctx->lock); + + return (total); +} + +void +isc__mem_setwater(isc_mem_t *ctx0, isc_mem_water_t water, void *water_arg, + size_t hiwater, size_t lowater) +{ + isc__mem_t *ctx = (isc__mem_t *)ctx0; + bool callwater = false; + isc_mem_water_t oldwater; + void *oldwater_arg; + + REQUIRE(VALID_CONTEXT(ctx)); + REQUIRE(hiwater >= lowater); + + MCTXLOCK(ctx, &ctx->lock); + oldwater = ctx->water; + oldwater_arg = ctx->water_arg; + if (water == NULL) { + callwater = ctx->hi_called; + ctx->water = NULL; + ctx->water_arg = NULL; + ctx->hi_water = 0; + ctx->lo_water = 0; + } else { + if (ctx->hi_called && + (ctx->water != water || ctx->water_arg != water_arg || + ctx->inuse < lowater || lowater == 0U)) + callwater = true; + ctx->water = water; + ctx->water_arg = water_arg; + ctx->hi_water = hiwater; + ctx->lo_water = lowater; + } + MCTXUNLOCK(ctx, &ctx->lock); + + if (callwater && oldwater != NULL) + (oldwater)(oldwater_arg, ISC_MEM_LOWATER); +} + +bool +isc__mem_isovermem(isc_mem_t *ctx0) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + + REQUIRE(VALID_CONTEXT(ctx)); + + /* + * We don't bother to lock the context because 100% accuracy isn't + * necessary (and even if we locked the context the returned value + * could be different from the actual state when it's used anyway) + */ + return (ctx->is_overmem); +} + +void +isc_mem_setname(isc_mem_t *ctx0, const char *name, void *tag) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + + REQUIRE(VALID_CONTEXT(ctx)); + + LOCK(&ctx->lock); + strlcpy(ctx->name, name, sizeof(ctx->name)); + ctx->tag = tag; + UNLOCK(&ctx->lock); +} + +const char * +isc_mem_getname(isc_mem_t *ctx0) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + + REQUIRE(VALID_CONTEXT(ctx)); + + if (ctx->name[0] == 0) + return (""); + + return (ctx->name); +} + +void * +isc_mem_gettag(isc_mem_t *ctx0) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + + REQUIRE(VALID_CONTEXT(ctx)); + + return (ctx->tag); +} + +/* + * Memory pool stuff + */ + +isc_result_t +isc__mempool_create(isc_mem_t *mctx0, size_t size, isc_mempool_t **mpctxp) { + isc__mem_t *mctx = (isc__mem_t *)mctx0; + isc__mempool_t *mpctx; + + REQUIRE(VALID_CONTEXT(mctx)); + REQUIRE(size > 0U); + REQUIRE(mpctxp != NULL && *mpctxp == NULL); + + /* + * Allocate space for this pool, initialize values, and if all works + * well, attach to the memory context. + */ + mpctx = isc_mem_get((isc_mem_t *)mctx, sizeof(isc__mempool_t)); + if (mpctx == NULL) + return (ISC_R_NOMEMORY); + + mpctx->common.methods = (isc_mempoolmethods_t *)&mempoolmethods; + mpctx->common.impmagic = MEMPOOL_MAGIC; + mpctx->common.magic = ISCAPI_MPOOL_MAGIC; + mpctx->lock = NULL; + mpctx->mctx = mctx; + /* + * Mempools are stored as a linked list of element. + */ + if (size < sizeof(element)) { + size = sizeof(element); + } + mpctx->size = size; + mpctx->maxalloc = UINT_MAX; + mpctx->allocated = 0; + mpctx->freecount = 0; + mpctx->freemax = 1; + mpctx->fillcount = 1; + mpctx->gets = 0; +#if ISC_MEMPOOL_NAMES + mpctx->name[0] = 0; +#endif + mpctx->items = NULL; + + *mpctxp = (isc_mempool_t *)mpctx; + + MCTXLOCK(mctx, &mctx->lock); + ISC_LIST_INITANDAPPEND(mctx->pools, mpctx, link); + mctx->poolcnt++; + MCTXUNLOCK(mctx, &mctx->lock); + + return (ISC_R_SUCCESS); +} + +void +isc__mempool_setname(isc_mempool_t *mpctx0, const char *name) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + + REQUIRE(name != NULL); + REQUIRE(VALID_MEMPOOL(mpctx)); + +#if ISC_MEMPOOL_NAMES + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + strlcpy(mpctx->name, name, sizeof(mpctx->name)); + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); +#else + UNUSED(mpctx); + UNUSED(name); +#endif +} + +void +isc__mempool_destroy(isc_mempool_t **mpctxp) { + isc__mempool_t *mpctx; + isc__mem_t *mctx; + isc_mutex_t *lock; + element *item; + + REQUIRE(mpctxp != NULL); + mpctx = (isc__mempool_t *)*mpctxp; + REQUIRE(VALID_MEMPOOL(mpctx)); +#if ISC_MEMPOOL_NAMES + if (mpctx->allocated > 0) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc__mempool_destroy(): mempool %s " + "leaked memory", + mpctx->name); +#endif + REQUIRE(mpctx->allocated == 0); + + mctx = mpctx->mctx; + + lock = mpctx->lock; + + if (lock != NULL) + LOCK(lock); + + /* + * Return any items on the free list + */ + MCTXLOCK(mctx, &mctx->lock); + while (mpctx->items != NULL) { + INSIST(mpctx->freecount > 0); + mpctx->freecount--; + item = mpctx->items; + mpctx->items = item->next; + + if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + mem_putunlocked(mctx, item, mpctx->size); + } else { + mem_putstats(mctx, item, mpctx->size); + mem_put(mctx, item, mpctx->size); + } + } + MCTXUNLOCK(mctx, &mctx->lock); + + /* + * Remove our linked list entry from the memory context. + */ + MCTXLOCK(mctx, &mctx->lock); + ISC_LIST_UNLINK(mctx->pools, mpctx, link); + mctx->poolcnt--; + MCTXUNLOCK(mctx, &mctx->lock); + + mpctx->common.impmagic = 0; + mpctx->common.magic = 0; + + isc_mem_put((isc_mem_t *)mpctx->mctx, mpctx, sizeof(isc__mempool_t)); + + if (lock != NULL) + UNLOCK(lock); + + *mpctxp = NULL; +} + +void +isc__mempool_associatelock(isc_mempool_t *mpctx0, isc_mutex_t *lock) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + + REQUIRE(VALID_MEMPOOL(mpctx)); + REQUIRE(mpctx->lock == NULL); + REQUIRE(lock != NULL); + + mpctx->lock = lock; +} + +void * +isc___mempool_get(isc_mempool_t *mpctx0 FLARG) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + element *item; + isc__mem_t *mctx; + unsigned int i; + + REQUIRE(VALID_MEMPOOL(mpctx)); + + mctx = mpctx->mctx; + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + /* + * Don't let the caller go over quota + */ + if (ISC_UNLIKELY(mpctx->allocated >= mpctx->maxalloc)) { + item = NULL; + goto out; + } + + if (ISC_UNLIKELY(mpctx->items == NULL)) { + /* + * We need to dip into the well. Lock the memory context + * here and fill up our free list. + */ + MCTXLOCK(mctx, &mctx->lock); + for (i = 0; i < mpctx->fillcount; i++) { + if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + item = mem_getunlocked(mctx, mpctx->size); + } else { + item = mem_get(mctx, mpctx->size); + if (item != NULL) + mem_getstats(mctx, mpctx->size); + } + if (ISC_UNLIKELY(item == NULL)) + break; + item->next = mpctx->items; + mpctx->items = item; + mpctx->freecount++; + } + MCTXUNLOCK(mctx, &mctx->lock); + } + + /* + * If we didn't get any items, return NULL. + */ + item = mpctx->items; + if (ISC_UNLIKELY(item == NULL)) + goto out; + + mpctx->items = item->next; + INSIST(mpctx->freecount > 0); + mpctx->freecount--; + mpctx->gets++; + mpctx->allocated++; + + out: + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); + +#if ISC_MEM_TRACKLINES + if (((isc_mem_debugging & TRACE_OR_RECORD) != 0) && item != NULL) { + MCTXLOCK(mctx, &mctx->lock); + ADD_TRACE(mctx, item, mpctx->size, file, line); + MCTXUNLOCK(mctx, &mctx->lock); + } +#endif /* ISC_MEM_TRACKLINES */ + + return (item); +} + +/* coverity[+free : arg-1] */ +void +isc___mempool_put(isc_mempool_t *mpctx0, void *mem FLARG) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + isc__mem_t *mctx; + element *item; + + REQUIRE(VALID_MEMPOOL(mpctx)); + REQUIRE(mem != NULL); + + mctx = mpctx->mctx; + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + INSIST(mpctx->allocated > 0); + mpctx->allocated--; + +#if ISC_MEM_TRACKLINES + if ((isc_mem_debugging & TRACE_OR_RECORD) != 0) { + MCTXLOCK(mctx, &mctx->lock); + DELETE_TRACE(mctx, mem, mpctx->size, file, line); + MCTXUNLOCK(mctx, &mctx->lock); + } +#endif /* ISC_MEM_TRACKLINES */ + + /* + * If our free list is full, return this to the mctx directly. + */ + if (mpctx->freecount >= mpctx->freemax) { + MCTXLOCK(mctx, &mctx->lock); + if ((mctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + mem_putunlocked(mctx, mem, mpctx->size); + } else { + mem_putstats(mctx, mem, mpctx->size); + mem_put(mctx, mem, mpctx->size); + } + MCTXUNLOCK(mctx, &mctx->lock); + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); + return; + } + + /* + * Otherwise, attach it to our free list and bump the counter. + */ + mpctx->freecount++; + item = (element *)mem; + item->next = mpctx->items; + mpctx->items = item; + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); +} + +/* + * Quotas + */ + +void +isc__mempool_setfreemax(isc_mempool_t *mpctx0, unsigned int limit) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + + REQUIRE(VALID_MEMPOOL(mpctx)); + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + mpctx->freemax = limit; + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); +} + +unsigned int +isc_mempool_getfreemax(isc_mempool_t *mpctx0) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + unsigned int freemax; + + REQUIRE(VALID_MEMPOOL(mpctx)); + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + freemax = mpctx->freemax; + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); + + return (freemax); +} + +unsigned int +isc_mempool_getfreecount(isc_mempool_t *mpctx0) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + unsigned int freecount; + + REQUIRE(VALID_MEMPOOL(mpctx)); + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + freecount = mpctx->freecount; + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); + + return (freecount); +} + +void +isc__mempool_setmaxalloc(isc_mempool_t *mpctx0, unsigned int limit) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + + REQUIRE(limit > 0); + + REQUIRE(VALID_MEMPOOL(mpctx)); + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + mpctx->maxalloc = limit; + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); +} + +unsigned int +isc_mempool_getmaxalloc(isc_mempool_t *mpctx0) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + unsigned int maxalloc; + + REQUIRE(VALID_MEMPOOL(mpctx)); + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + maxalloc = mpctx->maxalloc; + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); + + return (maxalloc); +} + +unsigned int +isc__mempool_getallocated(isc_mempool_t *mpctx0) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + unsigned int allocated; + + REQUIRE(VALID_MEMPOOL(mpctx)); + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + allocated = mpctx->allocated; + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); + + return (allocated); +} + +void +isc__mempool_setfillcount(isc_mempool_t *mpctx0, unsigned int limit) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + + REQUIRE(limit > 0); + REQUIRE(VALID_MEMPOOL(mpctx)); + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + mpctx->fillcount = limit; + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); +} + +unsigned int +isc_mempool_getfillcount(isc_mempool_t *mpctx0) { + isc__mempool_t *mpctx = (isc__mempool_t *)mpctx0; + + unsigned int fillcount; + + REQUIRE(VALID_MEMPOOL(mpctx)); + + if (mpctx->lock != NULL) + LOCK(mpctx->lock); + + fillcount = mpctx->fillcount; + + if (mpctx->lock != NULL) + UNLOCK(mpctx->lock); + + return (fillcount); +} + +isc_result_t +isc__mem_register(void) { + return (isc_mem_register(isc_mem_create2)); +} + +void +isc__mem_printactive(isc_mem_t *ctx0, FILE *file) { +#if ISC_MEM_TRACKLINES + isc__mem_t *ctx = (isc__mem_t *)ctx0; + + REQUIRE(VALID_CONTEXT(ctx)); + REQUIRE(file != NULL); + + print_active(ctx, file); +#else + UNUSED(ctx0); + UNUSED(file); +#endif +} + +void +isc_mem_printallactive(FILE *file) { +#if !ISC_MEM_TRACKLINES + UNUSED(file); +#else + isc__mem_t *ctx; + + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); + + LOCK(&contextslock); + for (ctx = ISC_LIST_HEAD(contexts); + ctx != NULL; + ctx = ISC_LIST_NEXT(ctx, link)) { + fprintf(file, "context: %p\n", ctx); + print_active(ctx, file); + } + UNLOCK(&contextslock); +#endif +} + +void +isc_mem_checkdestroyed(FILE *file) { +#if !ISC_MEM_TRACKLINES + UNUSED(file); +#endif + + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); + + LOCK(&contextslock); + if (!ISC_LIST_EMPTY(contexts)) { +#if ISC_MEM_TRACKLINES + if ((isc_mem_debugging & TRACE_OR_RECORD) != 0) { + isc__mem_t *ctx; + + for (ctx = ISC_LIST_HEAD(contexts); + ctx != NULL; + ctx = ISC_LIST_NEXT(ctx, link)) { + fprintf(file, "context: %p\n", ctx); + print_active(ctx, file); + } + fflush(file); + } +#endif + INSIST(0); + } + UNLOCK(&contextslock); +} + +unsigned int +isc_mem_references(isc_mem_t *ctx0) { + isc__mem_t *ctx = (isc__mem_t *)ctx0; + unsigned int references; + + REQUIRE(VALID_CONTEXT(ctx)); + + MCTXLOCK(ctx, &ctx->lock); + references = ctx->references; + MCTXUNLOCK(ctx, &ctx->lock); + + return (references); +} + +typedef struct summarystat { + uint64_t total; + uint64_t inuse; + uint64_t blocksize; + uint64_t contextsize; +} summarystat_t; + +#ifdef HAVE_LIBXML2 +#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0) +static int +xml_renderctx(isc__mem_t *ctx, summarystat_t *summary, + xmlTextWriterPtr writer) +{ + int xmlrc; + + REQUIRE(VALID_CONTEXT(ctx)); + + MCTXLOCK(ctx, &ctx->lock); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "context")); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id")); + TRY0(xmlTextWriterWriteFormatString(writer, "%p", ctx)); + TRY0(xmlTextWriterEndElement(writer)); /* id */ + + if (ctx->name[0] != 0) { + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name")); + TRY0(xmlTextWriterWriteFormatString(writer, "%s", ctx->name)); + TRY0(xmlTextWriterEndElement(writer)); /* name */ + } + + summary->contextsize += sizeof(*ctx) + + (ctx->max_size + 1) * sizeof(struct stats) + + ctx->max_size * sizeof(element *) + + ctx->basic_table_count * sizeof(char *); +#if ISC_MEM_TRACKLINES + if (ctx->debuglist != NULL) { + summary->contextsize += + (ctx->max_size + 1) * sizeof(debuglist_t) + + ctx->debuglistcnt * sizeof(debuglink_t); + } +#endif + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", ctx->references)); + TRY0(xmlTextWriterEndElement(writer)); /* references */ + + summary->total += ctx->total; + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "total")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + (uint64_t)ctx->total)); + TRY0(xmlTextWriterEndElement(writer)); /* total */ + + summary->inuse += ctx->inuse; + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "inuse")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + (uint64_t)ctx->inuse)); + TRY0(xmlTextWriterEndElement(writer)); /* inuse */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "maxinuse")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + (uint64_t)ctx->maxinuse)); + TRY0(xmlTextWriterEndElement(writer)); /* maxinuse */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "blocksize")); + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + summary->blocksize += ctx->basic_table_count * + NUM_BASIC_BLOCKS * ctx->mem_target; + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + (uint64_t) + ctx->basic_table_count * + NUM_BASIC_BLOCKS * + ctx->mem_target)); + } else + TRY0(xmlTextWriterWriteFormatString(writer, "%s", "-")); + TRY0(xmlTextWriterEndElement(writer)); /* blocksize */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "pools")); + TRY0(xmlTextWriterWriteFormatString(writer, "%u", ctx->poolcnt)); + TRY0(xmlTextWriterEndElement(writer)); /* pools */ + summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "hiwater")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + (uint64_t)ctx->hi_water)); + TRY0(xmlTextWriterEndElement(writer)); /* hiwater */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "lowater")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64 "", + (uint64_t)ctx->lo_water)); + TRY0(xmlTextWriterEndElement(writer)); /* lowater */ + + TRY0(xmlTextWriterEndElement(writer)); /* context */ + + error: + MCTXUNLOCK(ctx, &ctx->lock); + + return (xmlrc); +} + +int +isc_mem_renderxml(xmlTextWriterPtr writer) { + isc__mem_t *ctx; + summarystat_t summary; + uint64_t lost; + int xmlrc; + + memset(&summary, 0, sizeof(summary)); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "contexts")); + + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); + + LOCK(&contextslock); + lost = totallost; + for (ctx = ISC_LIST_HEAD(contexts); + ctx != NULL; + ctx = ISC_LIST_NEXT(ctx, link)) { + xmlrc = xml_renderctx(ctx, &summary, writer); + if (xmlrc < 0) { + UNLOCK(&contextslock); + goto error; + } + } + UNLOCK(&contextslock); + + TRY0(xmlTextWriterEndElement(writer)); /* contexts */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "summary")); + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "TotalUse")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + summary.total)); + TRY0(xmlTextWriterEndElement(writer)); /* TotalUse */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "InUse")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + summary.inuse)); + TRY0(xmlTextWriterEndElement(writer)); /* InUse */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "BlockSize")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + summary.blocksize)); + TRY0(xmlTextWriterEndElement(writer)); /* BlockSize */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ContextSize")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + summary.contextsize)); + TRY0(xmlTextWriterEndElement(writer)); /* ContextSize */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "Lost")); + TRY0(xmlTextWriterWriteFormatString(writer, + "%" PRIu64, + lost)); + TRY0(xmlTextWriterEndElement(writer)); /* Lost */ + + TRY0(xmlTextWriterEndElement(writer)); /* summary */ + error: + return (xmlrc); +} + +#endif /* HAVE_LIBXML2 */ + +#ifdef HAVE_JSON +#define CHECKMEM(m) do { \ + if (m == NULL) { \ + result = ISC_R_NOMEMORY;\ + goto error;\ + } \ +} while(0) + +static isc_result_t +json_renderctx(isc__mem_t *ctx, summarystat_t *summary, json_object *array) { + isc_result_t result = ISC_R_FAILURE; + json_object *ctxobj, *obj; + char buf[1024]; + + REQUIRE(VALID_CONTEXT(ctx)); + REQUIRE(summary != NULL); + REQUIRE(array != NULL); + + MCTXLOCK(ctx, &ctx->lock); + + summary->contextsize += sizeof(*ctx) + + (ctx->max_size + 1) * sizeof(struct stats) + + ctx->max_size * sizeof(element *) + + ctx->basic_table_count * sizeof(char *); + summary->total += ctx->total; + summary->inuse += ctx->inuse; + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) + summary->blocksize += ctx->basic_table_count * + NUM_BASIC_BLOCKS * ctx->mem_target; +#if ISC_MEM_TRACKLINES + if (ctx->debuglist != NULL) { + summary->contextsize += + (ctx->max_size + 1) * sizeof(debuglist_t) + + ctx->debuglistcnt * sizeof(debuglink_t); + } +#endif + + ctxobj = json_object_new_object(); + CHECKMEM(ctxobj); + + snprintf(buf, sizeof(buf), "%p", ctx); + obj = json_object_new_string(buf); + CHECKMEM(obj); + json_object_object_add(ctxobj, "id", obj); + + if (ctx->name[0] != 0) { + obj = json_object_new_string(ctx->name); + CHECKMEM(obj); + json_object_object_add(ctxobj, "name", obj); + } + + obj = json_object_new_int64(ctx->references); + CHECKMEM(obj); + json_object_object_add(ctxobj, "references", obj); + + obj = json_object_new_int64(ctx->total); + CHECKMEM(obj); + json_object_object_add(ctxobj, "total", obj); + + obj = json_object_new_int64(ctx->inuse); + CHECKMEM(obj); + json_object_object_add(ctxobj, "inuse", obj); + + obj = json_object_new_int64(ctx->maxinuse); + CHECKMEM(obj); + json_object_object_add(ctxobj, "maxinuse", obj); + + if ((ctx->flags & ISC_MEMFLAG_INTERNAL) != 0) { + uint64_t blocksize; + blocksize = ctx->basic_table_count * NUM_BASIC_BLOCKS * + ctx->mem_target; + obj = json_object_new_int64(blocksize); + CHECKMEM(obj); + json_object_object_add(ctxobj, "blocksize", obj); + } + + obj = json_object_new_int64(ctx->poolcnt); + CHECKMEM(obj); + json_object_object_add(ctxobj, "pools", obj); + + summary->contextsize += ctx->poolcnt * sizeof(isc_mempool_t); + + obj = json_object_new_int64(ctx->hi_water); + CHECKMEM(obj); + json_object_object_add(ctxobj, "hiwater", obj); + + obj = json_object_new_int64(ctx->lo_water); + CHECKMEM(obj); + json_object_object_add(ctxobj, "lowater", obj); + + MCTXUNLOCK(ctx, &ctx->lock); + json_object_array_add(array, ctxobj); + return (ISC_R_SUCCESS); + + error: + MCTXUNLOCK(ctx, &ctx->lock); + if (ctxobj != NULL) + json_object_put(ctxobj); + return (result); +} + +isc_result_t +isc_mem_renderjson(json_object *memobj) { + isc_result_t result = ISC_R_SUCCESS; + isc__mem_t *ctx; + summarystat_t summary; + uint64_t lost; + json_object *ctxarray, *obj; + + memset(&summary, 0, sizeof(summary)); + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); + + ctxarray = json_object_new_array(); + CHECKMEM(ctxarray); + + LOCK(&contextslock); + lost = totallost; + for (ctx = ISC_LIST_HEAD(contexts); + ctx != NULL; + ctx = ISC_LIST_NEXT(ctx, link)) { + result = json_renderctx(ctx, &summary, ctxarray); + if (result != ISC_R_SUCCESS) { + UNLOCK(&contextslock); + goto error; + } + } + UNLOCK(&contextslock); + + obj = json_object_new_int64(summary.total); + CHECKMEM(obj); + json_object_object_add(memobj, "TotalUse", obj); + + obj = json_object_new_int64(summary.inuse); + CHECKMEM(obj); + json_object_object_add(memobj, "InUse", obj); + + obj = json_object_new_int64(summary.blocksize); + CHECKMEM(obj); + json_object_object_add(memobj, "BlockSize", obj); + + obj = json_object_new_int64(summary.contextsize); + CHECKMEM(obj); + json_object_object_add(memobj, "ContextSize", obj); + + obj = json_object_new_int64(lost); + CHECKMEM(obj); + json_object_object_add(memobj, "Lost", obj); + + json_object_object_add(memobj, "contexts", ctxarray); + return (ISC_R_SUCCESS); + + error: + if (ctxarray != NULL) + json_object_put(ctxarray); + return (result); +} +#endif /* HAVE_JSON */ + +static isc_memcreatefunc_t mem_createfunc = NULL; + +isc_result_t +isc_mem_register(isc_memcreatefunc_t createfunc) { + isc_result_t result = ISC_R_SUCCESS; + + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); + + LOCK(&createlock); + if (mem_createfunc == NULL) + mem_createfunc = createfunc; + else + result = ISC_R_EXISTS; + UNLOCK(&createlock); + + return (result); +} + + +isc_result_t +isc__mem_create2(size_t init_max_size, size_t target_size, isc_mem_t **mctxp, + unsigned int flags) +{ + isc_result_t result; + + LOCK(&createlock); + + REQUIRE(mem_createfunc != NULL); + result = (*mem_createfunc)(init_max_size, target_size, mctxp, flags); + + UNLOCK(&createlock); + + return (result); +} + +isc_result_t +isc_mem_create(size_t init_max_size, size_t target_size, isc_mem_t **mctxp) { + isc_result_t result; + + if (isc_bind9) + return (isc_mem_createx2(init_max_size, target_size, + default_memalloc, default_memfree, + NULL, mctxp, isc_mem_defaultflags)); + LOCK(&createlock); + + REQUIRE(mem_createfunc != NULL); + result = (*mem_createfunc)(init_max_size, target_size, mctxp, + isc_mem_defaultflags); + + UNLOCK(&createlock); + + return (result); +} + +isc_result_t +isc_mem_create2(size_t init_max_size, size_t target_size, isc_mem_t **mctxp, + unsigned int flags) +{ + if (isc_bind9) + return (isc_mem_createx2(init_max_size, target_size, + default_memalloc, default_memfree, + NULL, mctxp, flags)); + + return (isc_mem_createx2(init_max_size, target_size, + default_memalloc, default_memfree, + NULL, mctxp, flags)); +} + +void +isc_mem_attach(isc_mem_t *source, isc_mem_t **targetp) { + REQUIRE(ISCAPI_MCTX_VALID(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + if (isc_bind9) + isc__mem_attach(source, targetp); + else + source->methods->attach(source, targetp); + + ENSURE(*targetp == source); +} + +void +isc_mem_detach(isc_mem_t **mctxp) { + REQUIRE(mctxp != NULL && ISCAPI_MCTX_VALID(*mctxp)); + + if (isc_bind9) + isc__mem_detach(mctxp); + else + (*mctxp)->methods->detach(mctxp); + + ENSURE(*mctxp == NULL); +} + +void +isc_mem_destroy(isc_mem_t **mctxp) { + REQUIRE(mctxp != NULL && ISCAPI_MCTX_VALID(*mctxp)); + + if (isc_bind9) + isc__mem_destroy(mctxp); + else + (*mctxp)->methods->destroy(mctxp); + + ENSURE(*mctxp == NULL); +} + +void +isc_mem_setdestroycheck(isc_mem_t *mctx, bool flag) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + mctx->methods->setdestroycheck(mctx, flag); +} + +void +isc_mem_setwater(isc_mem_t *ctx, isc_mem_water_t water, void *water_arg, + size_t hiwater, size_t lowater) +{ + REQUIRE(ISCAPI_MCTX_VALID(ctx)); + + if (isc_bind9) + isc__mem_setwater(ctx, water, water_arg, hiwater, lowater); + else + ctx->methods->setwater(ctx, water, water_arg, hiwater, lowater); +} + +void +isc_mem_waterack(isc_mem_t *ctx, int flag) { + REQUIRE(ISCAPI_MCTX_VALID(ctx)); + + if (isc_bind9) + isc__mem_waterack(ctx, flag); + else + ctx->methods->waterack(ctx, flag); +} + +size_t +isc_mem_inuse(isc_mem_t *mctx) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + return (isc__mem_inuse(mctx)); + + return (mctx->methods->inuse(mctx)); +} + +size_t +isc_mem_maxinuse(isc_mem_t *mctx) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + return (isc__mem_maxinuse(mctx)); + + return (mctx->methods->maxinuse(mctx)); +} + +size_t +isc_mem_total(isc_mem_t *mctx) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + return (isc__mem_total(mctx)); + + return (mctx->methods->total(mctx)); +} + +bool +isc_mem_isovermem(isc_mem_t *mctx) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + return (isc__mem_isovermem(mctx)); + + return (mctx->methods->isovermem(mctx)); +} + + +isc_result_t +isc_mempool_create(isc_mem_t *mctx, size_t size, isc_mempool_t **mpctxp) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + return (mctx->methods->mpcreate(mctx, size, mpctxp)); +} + +void +isc_mempool_destroy(isc_mempool_t **mpctxp) { + REQUIRE(mpctxp != NULL && ISCAPI_MPOOL_VALID(*mpctxp)); + + if (isc_bind9) + isc__mempool_destroy(mpctxp); + else + (*mpctxp)->methods->destroy(mpctxp); + + ENSURE(*mpctxp == NULL); +} + +unsigned int +isc_mempool_getallocated(isc_mempool_t *mpctx) { + REQUIRE(ISCAPI_MPOOL_VALID(mpctx)); + + if (isc_bind9) + return (isc__mempool_getallocated(mpctx)); + + return (mpctx->methods->getallocated(mpctx)); +} + +void +isc_mempool_setmaxalloc(isc_mempool_t *mpctx, unsigned int limit) { + REQUIRE(ISCAPI_MPOOL_VALID(mpctx)); + + if (isc_bind9) + isc__mempool_setmaxalloc(mpctx, limit); + else + mpctx->methods->setmaxalloc(mpctx, limit); +} + +void +isc_mempool_setfreemax(isc_mempool_t *mpctx, unsigned int limit) { + REQUIRE(ISCAPI_MPOOL_VALID(mpctx)); + + if (isc_bind9) + isc__mempool_setfreemax(mpctx, limit); + else + mpctx->methods->setfreemax(mpctx, limit); +} + +void +isc_mempool_setname(isc_mempool_t *mpctx, const char *name) { + REQUIRE(ISCAPI_MPOOL_VALID(mpctx)); + + if (isc_bind9) + isc__mempool_setname(mpctx, name); + else + mpctx->methods->setname(mpctx, name); +} + +void +isc_mempool_associatelock(isc_mempool_t *mpctx, isc_mutex_t *lock) { + REQUIRE(ISCAPI_MPOOL_VALID(mpctx)); + + if (isc_bind9) + isc__mempool_associatelock(mpctx, lock); + else + mpctx->methods->associatelock(mpctx, lock); +} + +void +isc_mempool_setfillcount(isc_mempool_t *mpctx, unsigned int limit) { + REQUIRE(ISCAPI_MPOOL_VALID(mpctx)); + + if (isc_bind9) + isc__mempool_setfillcount(mpctx, limit); + else + mpctx->methods->setfillcount(mpctx, limit); +} + +void * +isc__mem_get(isc_mem_t *mctx, size_t size FLARG) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + return (isc___mem_get(mctx, size FLARG_PASS)); + + return (mctx->methods->memget(mctx, size FLARG_PASS)); + +} + +void +isc__mem_put(isc_mem_t *mctx, void *ptr, size_t size FLARG) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + isc___mem_put(mctx, ptr, size FLARG_PASS); + else + mctx->methods->memput(mctx, ptr, size FLARG_PASS); +} + +void +isc__mem_putanddetach(isc_mem_t **mctxp, void *ptr, size_t size FLARG) { + REQUIRE(mctxp != NULL && ISCAPI_MCTX_VALID(*mctxp)); + + if (isc_bind9) + isc___mem_putanddetach(mctxp, ptr, size FLARG_PASS); + else + (*mctxp)->methods->memputanddetach(mctxp, ptr, size FLARG_PASS); + + /* + * XXX: We cannot always ensure *mctxp == NULL here + * (see lib/isc/mem.c). + */ +} + +void * +isc__mem_allocate(isc_mem_t *mctx, size_t size FLARG) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + return (isc___mem_allocate(mctx, size FLARG_PASS)); + + return (mctx->methods->memallocate(mctx, size FLARG_PASS)); +} + +void * +isc__mem_reallocate(isc_mem_t *mctx, void *ptr, size_t size FLARG) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + return (isc___mem_reallocate(mctx, ptr, size FLARG_PASS)); + + return (mctx->methods->memreallocate(mctx, ptr, size FLARG_PASS)); +} + +char * +isc__mem_strdup(isc_mem_t *mctx, const char *s FLARG) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + return (isc___mem_strdup(mctx, s FLARG_PASS)); + + return (mctx->methods->memstrdup(mctx, s FLARG_PASS)); +} + +void +isc__mem_free(isc_mem_t *mctx, void *ptr FLARG) { + REQUIRE(ISCAPI_MCTX_VALID(mctx)); + + if (isc_bind9) + isc___mem_free(mctx, ptr FLARG_PASS); + else + mctx->methods->memfree(mctx, ptr FLARG_PASS); +} + +void * +isc__mempool_get(isc_mempool_t *mpctx FLARG) { + REQUIRE(ISCAPI_MPOOL_VALID(mpctx)); + + if (isc_bind9) + return (isc___mempool_get(mpctx FLARG_PASS)); + + return (mpctx->methods->get(mpctx FLARG_PASS)); +} + +void +isc__mempool_put(isc_mempool_t *mpctx, void *mem FLARG) { + REQUIRE(ISCAPI_MPOOL_VALID(mpctx)); + + if (isc_bind9) + isc___mempool_put(mpctx, mem FLARG_PASS); + else + mpctx->methods->put(mpctx, mem FLARG_PASS); +} diff --git a/lib/isc/mips/Makefile.in b/lib/isc/mips/Makefile.in new file mode 100644 index 0000000..419cf9f --- /dev/null +++ b/lib/isc/mips/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = include +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/mips/include/Makefile.in b/lib/isc/mips/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/mips/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/mips/include/isc/Makefile.in b/lib/isc/mips/include/isc/Makefile.in new file mode 100644 index 0000000..97b6b41 --- /dev/null +++ b/lib/isc/mips/include/isc/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = atomic.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/mips/include/isc/atomic.h b/lib/isc/mips/include/isc/atomic.h new file mode 100644 index 0000000..caba82f --- /dev/null +++ b/lib/isc/mips/include/isc/atomic.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_ATOMIC_H +#define ISC_ATOMIC_H 1 + +#include <inttypes.h> + +#include <isc/platform.h> +#include <isc/types.h> + +#ifdef ISC_PLATFORM_USEGCCASM +/* + * This routine atomically increments the value stored in 'p' by 'val', and + * returns the previous value. + */ +static inline int32_t +isc_atomic_xadd(int32_t *p, int val) { + int32_t orig; + + __asm__ __volatile__ ( + " .set push \n" + " .set mips2 \n" + " .set noreorder \n" + " .set noat \n" + "1: ll $1, %1 \n" + " addu %0, $1, %2 \n" + " sc %0, %1 \n" + " beqz %0, 1b \n" + " move %0, $1 \n" + " .set pop \n" + : "=&r" (orig), "+R" (*p) + : "r" (val) + : "memory"); + + return (orig); +} + +/* + * This routine atomically stores the value 'val' in 'p'. + */ +static inline void +isc_atomic_store(int32_t *p, int32_t val) { + *p = val; +} + +/* + * This routine atomically replaces the value in 'p' with 'val', if the + * original value is equal to 'cmpval'. The original value is returned in any + * case. + */ +static inline int32_t +isc_atomic_cmpxchg(int32_t *p, int cmpval, int val) { + int32_t orig; + int32_t tmp; + + __asm__ __volatile__ ( + " .set push \n" + " .set mips2 \n" + " .set noreorder \n" + " .set noat \n" + "1: ll $1, %1 \n" + " bne $1, %3, 2f \n" + " move %2, %4 \n" + " sc %2, %1 \n" + " beqz %2, 1b \n" + "2: move %0, $1 \n" + " .set pop \n" + : "=&r"(orig), "+R" (*p), "=r" (tmp) + : "r"(cmpval), "r"(val) + : "memory"); + + return (orig); +} + +#else /* !ISC_PLATFORM_USEGCCASM */ + +#error "unsupported compiler. disable atomic ops by --disable-atomic" + +#endif +#endif /* ISC_ATOMIC_H */ diff --git a/lib/isc/mutexblock.c b/lib/isc/mutexblock.c new file mode 100644 index 0000000..6a629e7 --- /dev/null +++ b/lib/isc/mutexblock.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <isc/mutexblock.h> +#include <isc/util.h> + +isc_result_t +isc_mutexblock_init(isc_mutex_t *block, unsigned int count) { + isc_result_t result; + unsigned int i; + + for (i = 0; i < count; i++) { + result = isc_mutex_init(&block[i]); + if (result != ISC_R_SUCCESS) { + while (i > 0U) { + i--; + DESTROYLOCK(&block[i]); + } + return (result); + } + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_mutexblock_destroy(isc_mutex_t *block, unsigned int count) { + isc_result_t result; + unsigned int i; + + for (i = 0; i < count; i++) { + result = isc_mutex_destroy(&block[i]); + if (result != ISC_R_SUCCESS) + return (result); + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/netaddr.c b/lib/isc/netaddr.c new file mode 100644 index 0000000..81851a0 --- /dev/null +++ b/lib/isc/netaddr.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> +#include <stdio.h> +#include <inttypes.h> + +#include <isc/buffer.h> +#include <isc/msgs.h> +#include <isc/net.h> +#include <isc/netaddr.h> +#include <isc/print.h> +#include <isc/sockaddr.h> +#include <isc/string.h> +#include <isc/util.h> + +bool +isc_netaddr_equal(const isc_netaddr_t *a, const isc_netaddr_t *b) { + REQUIRE(a != NULL && b != NULL); + + if (a->family != b->family) + return (false); + + if (a->zone != b->zone) + return (false); + + switch (a->family) { + case AF_INET: + if (a->type.in.s_addr != b->type.in.s_addr) + return (false); + break; + case AF_INET6: + if (memcmp(&a->type.in6, &b->type.in6, + sizeof(a->type.in6)) != 0 || + a->zone != b->zone) + return (false); + break; +#ifdef ISC_PLATFORM_HAVESYSUNH + case AF_UNIX: + if (strcmp(a->type.un, b->type.un) != 0) + return (false); + break; +#endif + default: + return (false); + } + return (true); +} + +bool +isc_netaddr_eqprefix(const isc_netaddr_t *a, const isc_netaddr_t *b, + unsigned int prefixlen) +{ + const unsigned char *pa = NULL, *pb = NULL; + unsigned int ipabytes = 0; /* Length of whole IP address in bytes */ + unsigned int nbytes; /* Number of significant whole bytes */ + unsigned int nbits; /* Number of significant leftover bits */ + + REQUIRE(a != NULL && b != NULL); + + if (a->family != b->family) + return (false); + + if (a->zone != b->zone && b->zone != 0) + return (false); + + switch (a->family) { + case AF_INET: + pa = (const unsigned char *) &a->type.in; + pb = (const unsigned char *) &b->type.in; + ipabytes = 4; + break; + case AF_INET6: + pa = (const unsigned char *) &a->type.in6; + pb = (const unsigned char *) &b->type.in6; + ipabytes = 16; + break; + default: + return (false); + } + + /* + * Don't crash if we get a pattern like 10.0.0.1/9999999. + */ + if (prefixlen > ipabytes * 8) + prefixlen = ipabytes * 8; + + nbytes = prefixlen / 8; + nbits = prefixlen % 8; + + if (nbytes > 0) { + if (memcmp(pa, pb, nbytes) != 0) + return (false); + } + if (nbits > 0) { + unsigned int bytea, byteb, mask; + INSIST(nbytes < ipabytes); + INSIST(nbits < 8); + bytea = pa[nbytes]; + byteb = pb[nbytes]; + mask = (0xFF << (8-nbits)) & 0xFF; + if ((bytea & mask) != (byteb & mask)) + return (false); + } + return (true); +} + +isc_result_t +isc_netaddr_totext(const isc_netaddr_t *netaddr, isc_buffer_t *target) { + char abuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")]; + char zbuf[sizeof("%4294967295")]; + unsigned int alen; + int zlen; + const char *r; + const void *type; + + REQUIRE(netaddr != NULL); + + switch (netaddr->family) { + case AF_INET: + type = &netaddr->type.in; + break; + case AF_INET6: + type = &netaddr->type.in6; + break; +#ifdef ISC_PLATFORM_HAVESYSUNH + case AF_UNIX: + alen = strlen(netaddr->type.un); + if (alen > isc_buffer_availablelength(target)) + return (ISC_R_NOSPACE); + isc_buffer_putmem(target, + (const unsigned char *)(netaddr->type.un), + alen); + return (ISC_R_SUCCESS); +#endif + default: + return (ISC_R_FAILURE); + } + r = inet_ntop(netaddr->family, type, abuf, sizeof(abuf)); + if (r == NULL) + return (ISC_R_FAILURE); + + alen = strlen(abuf); + INSIST(alen < sizeof(abuf)); + + zlen = 0; + if (netaddr->family == AF_INET6 && netaddr->zone != 0) { + zlen = snprintf(zbuf, sizeof(zbuf), "%%%u", netaddr->zone); + if (zlen < 0) + return (ISC_R_FAILURE); + INSIST((unsigned int)zlen < sizeof(zbuf)); + } + + if (alen + zlen > isc_buffer_availablelength(target)) + return (ISC_R_NOSPACE); + + isc_buffer_putmem(target, (unsigned char *)abuf, alen); + isc_buffer_putmem(target, (unsigned char *)zbuf, (unsigned int)zlen); + + return (ISC_R_SUCCESS); +} + +void +isc_netaddr_format(const isc_netaddr_t *na, char *array, unsigned int size) { + isc_result_t result; + isc_buffer_t buf; + + isc_buffer_init(&buf, array, size); + result = isc_netaddr_totext(na, &buf); + + if (size == 0) + return; + + /* + * Null terminate. + */ + if (result == ISC_R_SUCCESS) { + if (isc_buffer_availablelength(&buf) >= 1) + isc_buffer_putuint8(&buf, 0); + else + result = ISC_R_NOSPACE; + } + + if (result != ISC_R_SUCCESS) { + snprintf(array, size, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_NETADDR, + ISC_MSG_UNKNOWNADDR, + "<unknown address, family %u>"), + na->family); + array[size - 1] = '\0'; + } +} + + +isc_result_t +isc_netaddr_prefixok(const isc_netaddr_t *na, unsigned int prefixlen) { + static const unsigned char zeros[16]; + unsigned int nbits, nbytes, ipbytes = 0; + const unsigned char *p; + + switch (na->family) { + case AF_INET: + p = (const unsigned char *) &na->type.in; + ipbytes = 4; + if (prefixlen > 32) + return (ISC_R_RANGE); + break; + case AF_INET6: + p = (const unsigned char *) &na->type.in6; + ipbytes = 16; + if (prefixlen > 128) + return (ISC_R_RANGE); + break; + default: + return (ISC_R_NOTIMPLEMENTED); + } + nbytes = prefixlen / 8; + nbits = prefixlen % 8; + if (nbits != 0) { + INSIST(nbytes < ipbytes); + if ((p[nbytes] & (0xff>>nbits)) != 0U) + return (ISC_R_FAILURE); + nbytes++; + } + if (nbytes < ipbytes && memcmp(p + nbytes, zeros, ipbytes - nbytes) != 0) + return (ISC_R_FAILURE); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_netaddr_masktoprefixlen(const isc_netaddr_t *s, unsigned int *lenp) { + unsigned int nbits = 0, nbytes = 0, ipbytes = 0, i; + const unsigned char *p; + + switch (s->family) { + case AF_INET: + p = (const unsigned char *) &s->type.in; + ipbytes = 4; + break; + case AF_INET6: + p = (const unsigned char *) &s->type.in6; + ipbytes = 16; + break; + default: + return (ISC_R_NOTIMPLEMENTED); + } + for (i = 0; i < ipbytes; i++) { + if (p[i] != 0xFF) + break; + } + nbytes = i; + if (i < ipbytes) { + unsigned int c = p[nbytes]; + while ((c & 0x80) != 0 && nbits < 8) { + c <<= 1; nbits++; + } + if ((c & 0xFF) != 0) + return (ISC_R_MASKNONCONTIG); + i++; + } + for (; i < ipbytes; i++) { + if (p[i] != 0) + return (ISC_R_MASKNONCONTIG); + } + *lenp = nbytes * 8 + nbits; + return (ISC_R_SUCCESS); +} + +void +isc_netaddr_fromin(isc_netaddr_t *netaddr, const struct in_addr *ina) { + memset(netaddr, 0, sizeof(*netaddr)); + netaddr->family = AF_INET; + netaddr->type.in = *ina; +} + +void +isc_netaddr_fromin6(isc_netaddr_t *netaddr, const struct in6_addr *ina6) { + memset(netaddr, 0, sizeof(*netaddr)); + netaddr->family = AF_INET6; + netaddr->type.in6 = *ina6; +} + +isc_result_t +isc_netaddr_frompath(isc_netaddr_t *netaddr, const char *path) { +#ifdef ISC_PLATFORM_HAVESYSUNH + if (strlen(path) > sizeof(netaddr->type.un) - 1) + return (ISC_R_NOSPACE); + + memset(netaddr, 0, sizeof(*netaddr)); + netaddr->family = AF_UNIX; + strlcpy(netaddr->type.un, path, sizeof(netaddr->type.un)); + netaddr->zone = 0; + return (ISC_R_SUCCESS); +#else + UNUSED(netaddr); + UNUSED(path); + return (ISC_R_NOTIMPLEMENTED); +#endif +} + + +void +isc_netaddr_setzone(isc_netaddr_t *netaddr, uint32_t zone) { + /* we currently only support AF_INET6. */ + REQUIRE(netaddr->family == AF_INET6); + + netaddr->zone = zone; +} + +uint32_t +isc_netaddr_getzone(const isc_netaddr_t *netaddr) { + return (netaddr->zone); +} + +void +isc_netaddr_fromsockaddr(isc_netaddr_t *t, const isc_sockaddr_t *s) { + int family = s->type.sa.sa_family; + t->family = family; + switch (family) { + case AF_INET: + t->type.in = s->type.sin.sin_addr; + t->zone = 0; + break; + case AF_INET6: + memmove(&t->type.in6, &s->type.sin6.sin6_addr, 16); +#ifdef ISC_PLATFORM_HAVESCOPEID + t->zone = s->type.sin6.sin6_scope_id; +#else + t->zone = 0; +#endif + break; +#ifdef ISC_PLATFORM_HAVESYSUNH + case AF_UNIX: + memmove(t->type.un, s->type.sunix.sun_path, sizeof(t->type.un)); + t->zone = 0; + break; +#endif + default: + INSIST(0); + } +} + +void +isc_netaddr_any(isc_netaddr_t *netaddr) { + memset(netaddr, 0, sizeof(*netaddr)); + netaddr->family = AF_INET; + netaddr->type.in.s_addr = INADDR_ANY; +} + +void +isc_netaddr_any6(isc_netaddr_t *netaddr) { + memset(netaddr, 0, sizeof(*netaddr)); + netaddr->family = AF_INET6; + netaddr->type.in6 = in6addr_any; +} + +bool +isc_netaddr_ismulticast(isc_netaddr_t *na) { + switch (na->family) { + case AF_INET: + return (ISC_IPADDR_ISMULTICAST(na->type.in.s_addr)); + case AF_INET6: + return (IN6_IS_ADDR_MULTICAST(&na->type.in6)); + default: + return (false); /* XXXMLG ? */ + } +} + +bool +isc_netaddr_isexperimental(isc_netaddr_t *na) { + switch (na->family) { + case AF_INET: + return (ISC_IPADDR_ISEXPERIMENTAL(na->type.in.s_addr)); + default: + return (false); /* XXXMLG ? */ + } +} + +bool +isc_netaddr_islinklocal(isc_netaddr_t *na) { + switch (na->family) { + case AF_INET: + return (false); + case AF_INET6: + return (IN6_IS_ADDR_LINKLOCAL(&na->type.in6)); + default: + return (false); + } +} + +bool +isc_netaddr_issitelocal(isc_netaddr_t *na) { + switch (na->family) { + case AF_INET: + return (false); + case AF_INET6: + return (IN6_IS_ADDR_SITELOCAL(&na->type.in6)); + default: + return (false); + } +} + +#define ISC_IPADDR_ISNETZERO(i) \ + (((uint32_t)(i) & ISC__IPADDR(0xff000000)) \ + == ISC__IPADDR(0x00000000)) + +bool +isc_netaddr_isnetzero(isc_netaddr_t *na) { + switch (na->family) { + case AF_INET: + return (ISC_IPADDR_ISNETZERO(na->type.in.s_addr)); + case AF_INET6: + return (false); + default: + return (false); + } +} + +void +isc_netaddr_fromv4mapped(isc_netaddr_t *t, const isc_netaddr_t *s) { + isc_netaddr_t *src; + + DE_CONST(s, src); /* Must come before IN6_IS_ADDR_V4MAPPED. */ + + REQUIRE(s->family == AF_INET6); + REQUIRE(IN6_IS_ADDR_V4MAPPED(&src->type.in6)); + + memset(t, 0, sizeof(*t)); + t->family = AF_INET; + memmove(&t->type.in, (char *)&src->type.in6 + 12, 4); + return; +} + +bool +isc_netaddr_isloopback(const isc_netaddr_t *na) { + switch (na->family) { + case AF_INET: + return ((ntohl(na->type.in.s_addr) & 0xff000000U) == + 0x7f000000U); + case AF_INET6: + return (IN6_IS_ADDR_LOOPBACK(&na->type.in6)); + default: + return (false); + } +} diff --git a/lib/isc/netscope.c b/lib/isc/netscope.c new file mode 100644 index 0000000..1c51d35 --- /dev/null +++ b/lib/isc/netscope.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <isc/string.h> +#include <isc/net.h> +#include <isc/netscope.h> +#include <isc/result.h> + +isc_result_t +isc_netscope_pton(int af, char *scopename, void *addr, uint32_t *zoneid) { + char *ep; +#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX + unsigned int ifid; + struct in6_addr *in6; +#endif + uint32_t zone; + uint64_t llz; + + /* at this moment, we only support AF_INET6 */ + if (af != AF_INET6) + return (ISC_R_FAILURE); + + /* + * Basically, "names" are more stable than numeric IDs in terms of + * renumbering, and are more preferred. However, since there is no + * standard naming convention and APIs to deal with the names. Thus, + * we only handle the case of link-local addresses, for which we use + * interface names as link names, assuming one to one mapping between + * interfaces and links. + */ +#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX + in6 = (struct in6_addr *)addr; + if (IN6_IS_ADDR_LINKLOCAL(in6) && + (ifid = if_nametoindex((const char *)scopename)) != 0) + zone = (uint32_t)ifid; + else { +#endif + llz = isc_string_touint64(scopename, &ep, 10); + if (ep == scopename) + return (ISC_R_FAILURE); + + /* check overflow */ + zone = (uint32_t)(llz & 0xffffffffUL); + if (zone != llz) + return (ISC_R_FAILURE); +#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX + } +#endif + + *zoneid = zone; + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/nls/Makefile.in b/lib/isc/nls/Makefile.in new file mode 100644 index 0000000..704dead --- /dev/null +++ b/lib/isc/nls/Makefile.in @@ -0,0 +1,29 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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../unix/include \ + -I${srcdir}/../unix/include \ + -I../include \ + -I${srcdir}/../include + +CDEFINES = +CWARNINGS = + +OBJS = msgcat.@O@ + +SRCS = msgcat.c + +SUBDIRS = +TARGETS = ${OBJS} + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/nls/msgcat.c b/lib/isc/nls/msgcat.c new file mode 100644 index 0000000..ab09b94 --- /dev/null +++ b/lib/isc/nls/msgcat.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file msgcat.c */ + +#include <config.h> + +#include <stddef.h> +#include <stdlib.h> + +#include <isc/magic.h> +#include <isc/msgcat.h> +#include <isc/util.h> + +#ifdef HAVE_CATGETS +#include <nl_types.h> /* Required for nl_catd. */ +#endif + +/* + * Implementation Notes: + * + * We use malloc() and free() instead of isc_mem_get() and isc_mem_put() + * because we don't want to require a memory context to be specified + * in order to use a message catalog. + */ + +struct isc_msgcat { + unsigned int magic; +#ifdef HAVE_CATGETS + nl_catd catalog; +#endif +}; + +#define MSGCAT_MAGIC ISC_MAGIC('M', 'C', 'a', 't') +#define VALID_MSGCAT(m) ISC_MAGIC_VALID(m, MSGCAT_MAGIC) + +void +isc_msgcat_open(const char *name, isc_msgcat_t **msgcatp) { + isc_msgcat_t *msgcat; + + /* + * Open a message catalog. + */ + + REQUIRE(name != NULL); + REQUIRE(msgcatp != NULL && *msgcatp == NULL); + + msgcat = malloc(sizeof(*msgcat)); + if (msgcat == NULL) { + *msgcatp = NULL; + return; + } + +#ifdef HAVE_CATGETS + /* + * We don't check if catopen() fails because we don't care. + * If it does fail, then when we call catgets(), it will use + * the default string. + */ + msgcat->catalog = catopen(name, 0); +#endif + msgcat->magic = MSGCAT_MAGIC; + + *msgcatp = msgcat; +} + +void +isc_msgcat_close(isc_msgcat_t **msgcatp) { + isc_msgcat_t *msgcat; + + /* + * Close a message catalog. + */ + + REQUIRE(msgcatp != NULL); + msgcat = *msgcatp; + REQUIRE(VALID_MSGCAT(msgcat) || msgcat == NULL); + + if (msgcat != NULL) { +#ifdef HAVE_CATGETS + if (msgcat->catalog != (nl_catd)(-1)) + (void)catclose(msgcat->catalog); +#endif + msgcat->magic = 0; + free(msgcat); + } + + *msgcatp = NULL; +} + +const char * +isc_msgcat_get(isc_msgcat_t *msgcat, int set, int message, + const char *default_text) +{ + /* + * Get message 'message' from message set 'set' in 'msgcat'. If it + * is not available, use 'default'. + */ + + REQUIRE(VALID_MSGCAT(msgcat) || msgcat == NULL); + REQUIRE(set > 0); + REQUIRE(message > 0); + REQUIRE(default_text != NULL); + +#ifdef HAVE_CATGETS + if (msgcat == NULL) + return (default_text); + return (catgets(msgcat->catalog, set, message, default_text)); +#else + return (default_text); +#endif +} diff --git a/lib/isc/noatomic/Makefile.in b/lib/isc/noatomic/Makefile.in new file mode 100644 index 0000000..419cf9f --- /dev/null +++ b/lib/isc/noatomic/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = include +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/noatomic/include/Makefile.in b/lib/isc/noatomic/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/noatomic/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/noatomic/include/isc/Makefile.in b/lib/isc/noatomic/include/isc/Makefile.in new file mode 100644 index 0000000..97b6b41 --- /dev/null +++ b/lib/isc/noatomic/include/isc/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = atomic.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/noatomic/include/isc/atomic.h b/lib/isc/noatomic/include/isc/atomic.h new file mode 100644 index 0000000..5953e4b --- /dev/null +++ b/lib/isc/noatomic/include/isc/atomic.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_ATOMIC_H +#define ISC_ATOMIC_H 1 + +/* This file is inherently empty. */ + +#endif /* ISC_ATOMIC_H */ diff --git a/lib/isc/nothreads/Makefile.in b/lib/isc/nothreads/Makefile.in new file mode 100644 index 0000000..c794773 --- /dev/null +++ b/lib/isc/nothreads/Makefile.in @@ -0,0 +1,32 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +CINCLUDES = -I${srcdir}/include \ + -I${srcdir}/../unix/include \ + -I../include \ + -I${srcdir}/../include \ + -I${srcdir}/.. + +CDEFINES = +CWARNINGS = + +THREADOPTOBJS = condition.@O@ mutex.@O@ +OBJS = @THREADOPTOBJS@ thread.@O@ + +THREADOPTSRCS = condition.c mutex.c +SRCS = @THREADOPTSRCS@ thread.c + +SUBDIRS = include +TARGETS = ${OBJS} + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/nothreads/condition.c b/lib/isc/nothreads/condition.c new file mode 100644 index 0000000..a549c30 --- /dev/null +++ b/lib/isc/nothreads/condition.c @@ -0,0 +1,17 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <isc/util.h> + +EMPTY_TRANSLATION_UNIT diff --git a/lib/isc/nothreads/include/Makefile.in b/lib/isc/nothreads/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/nothreads/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/nothreads/include/isc/Makefile.in b/lib/isc/nothreads/include/isc/Makefile.in new file mode 100644 index 0000000..185534f --- /dev/null +++ b/lib/isc/nothreads/include/isc/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = condition.h mutex.h once.h thread.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/nothreads/include/isc/condition.h b/lib/isc/nothreads/include/isc/condition.h new file mode 100644 index 0000000..0bea23d --- /dev/null +++ b/lib/isc/nothreads/include/isc/condition.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/* + * This provides a limited subset of the isc_condition_t + * functionality for use by single-threaded programs that + * need to block waiting for events. Only a single + * call to isc_condition_wait() may be blocked at any given + * time, and the _waituntil and _broadcast functions are not + * supported. This is intended primarily for use by the omapi + * library, and may go away once omapi goes away. Use for + * other purposes is strongly discouraged. + */ + +#ifndef ISC_CONDITION_H +#define ISC_CONDITION_H 1 + +#include <isc/mutex.h> + +typedef int isc_condition_t; + +isc_result_t isc__nothread_wait_hack(isc_condition_t *cp, isc_mutex_t *mp); +isc_result_t isc__nothread_signal_hack(isc_condition_t *cp); + +#define isc_condition_init(cp) \ + (*(cp) = 0, ISC_R_SUCCESS) + +#define isc_condition_wait(cp, mp) \ + isc__nothread_wait_hack(cp, mp) + +#define isc_condition_waituntil(cp, mp, tp) \ + ((void)(cp), (void)(mp), (void)(tp), ISC_R_NOTIMPLEMENTED) + +#define isc_condition_signal(cp) \ + isc__nothread_signal_hack(cp) + +#define isc_condition_broadcast(cp) \ + ((void)(cp), ISC_R_NOTIMPLEMENTED) + +#define isc_condition_destroy(cp) \ + (*(cp) == 0 ? (*(cp) = -1, ISC_R_SUCCESS) : ISC_R_UNEXPECTED) + +#endif /* ISC_CONDITION_H */ diff --git a/lib/isc/nothreads/include/isc/mutex.h b/lib/isc/nothreads/include/isc/mutex.h new file mode 100644 index 0000000..a9c55f6 --- /dev/null +++ b/lib/isc/nothreads/include/isc/mutex.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <isc/result.h> /* for ISC_R_ codes */ + +typedef int isc_mutex_t; + +#define isc_mutex_init(mp) \ + (*(mp) = 0, ISC_R_SUCCESS) +#define isc_mutex_lock(mp) \ + ((*(mp))++ == 0 ? ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#define isc_mutex_unlock(mp) \ + (--(*(mp)) == 0 ? ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#define isc_mutex_trylock(mp) \ + (*(mp) == 0 ? ((*(mp))++, ISC_R_SUCCESS) : ISC_R_LOCKBUSY) +#define isc_mutex_destroy(mp) \ + (*(mp) == 0 ? (*(mp) = -1, ISC_R_SUCCESS) : ISC_R_UNEXPECTED) +#define isc_mutex_stats(fp) + +#endif /* ISC_MUTEX_H */ diff --git a/lib/isc/nothreads/include/isc/once.h b/lib/isc/nothreads/include/isc/once.h new file mode 100644 index 0000000..c291814 --- /dev/null +++ b/lib/isc/nothreads/include/isc/once.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <stdbool.h> + +#include <isc/result.h> + +typedef bool isc_once_t; + +#define ISC_ONCE_INIT false + +#define isc_once_do(op, f) \ + (!*(op) ? (f(), *(op) = true, ISC_R_SUCCESS) : ISC_R_SUCCESS) + +#endif /* ISC_ONCE_H */ diff --git a/lib/isc/nothreads/include/isc/thread.h b/lib/isc/nothreads/include/isc/thread.h new file mode 100644 index 0000000..8486c3f --- /dev/null +++ b/lib/isc/nothreads/include/isc/thread.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <isc/lang.h> +#include <isc/result.h> + +ISC_LANG_BEGINDECLS + +/* Placeholder types (they are not accessed) */ + +typedef void * isc_thread_t; +typedef void * isc_threadresult_t; +typedef void * isc_threadarg_t; +typedef void * isc_threadfunc_t; +typedef void * isc_thread_key_t; + +void +isc_thread_setconcurrency(unsigned int level); + +void +isc_thread_setname(isc_thread_t thread, const char *name); + +#define isc_thread_self() ((unsigned long)0) +#define isc_thread_yield() ((void)0) + +ISC_LANG_ENDDECLS + +#endif /* ISC_THREAD_H */ diff --git a/lib/isc/nothreads/mutex.c b/lib/isc/nothreads/mutex.c new file mode 100644 index 0000000..e092ac9 --- /dev/null +++ b/lib/isc/nothreads/mutex.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <isc/util.h> + +EMPTY_TRANSLATION_UNIT + diff --git a/lib/isc/nothreads/thread.c b/lib/isc/nothreads/thread.c new file mode 100644 index 0000000..671261f --- /dev/null +++ b/lib/isc/nothreads/thread.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <isc/thread.h> +#include <isc/util.h> + +void +isc_thread_setconcurrency(unsigned int level) { + UNUSED(level); +} + +void isc_thread_setname(isc_thread_t thread, const char *name) { + UNUSED(thread); + UNUSED(name); +} diff --git a/lib/isc/ondestroy.c b/lib/isc/ondestroy.c new file mode 100644 index 0000000..64d4d91 --- /dev/null +++ b/lib/isc/ondestroy.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: ondestroy.c,v 1.16 2007/06/19 23:47:17 tbox Exp $ */ + +/*! \file */ + +#include <config.h> + +#include <stddef.h> + +#include <isc/event.h> +#include <isc/magic.h> +#include <isc/ondestroy.h> +#include <isc/task.h> +#include <isc/util.h> + +#define ONDESTROY_MAGIC ISC_MAGIC('D', 'e', 'S', 't') +#define VALID_ONDESTROY(s) ISC_MAGIC_VALID(s, ONDESTROY_MAGIC) + +void +isc_ondestroy_init(isc_ondestroy_t *ondest) { + ondest->magic = ONDESTROY_MAGIC; + ISC_LIST_INIT(ondest->events); +} + +isc_result_t +isc_ondestroy_register(isc_ondestroy_t *ondest, isc_task_t *task, + isc_event_t **eventp) +{ + isc_event_t *theevent; + isc_task_t *thetask = NULL; + + REQUIRE(VALID_ONDESTROY(ondest)); + REQUIRE(task != NULL); + REQUIRE(eventp != NULL); + + theevent = *eventp; + + REQUIRE(theevent != NULL); + + isc_task_attach(task, &thetask); + + theevent->ev_sender = thetask; + + ISC_LIST_APPEND(ondest->events, theevent, ev_link); + + return (ISC_R_SUCCESS); +} + +void +isc_ondestroy_notify(isc_ondestroy_t *ondest, void *sender) { + isc_event_t *eventp; + isc_task_t *task; + + REQUIRE(VALID_ONDESTROY(ondest)); + + eventp = ISC_LIST_HEAD(ondest->events); + while (eventp != NULL) { + ISC_LIST_UNLINK(ondest->events, eventp, ev_link); + + task = eventp->ev_sender; + eventp->ev_sender = sender; + + isc_task_sendanddetach(&task, &eventp); + + eventp = ISC_LIST_HEAD(ondest->events); + } +} + + diff --git a/lib/isc/parseint.c b/lib/isc/parseint.c new file mode 100644 index 0000000..5859369 --- /dev/null +++ b/lib/isc/parseint.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <inttypes.h> + +#include <isc/parseint.h> +#include <isc/result.h> +#include <isc/stdlib.h> + +isc_result_t +isc_parse_uint32(uint32_t *uip, const char *string, int base) { + unsigned long n; + uint32_t r; + char *e; + if (! isalnum((unsigned char)(string[0]))) + return (ISC_R_BADNUMBER); + errno = 0; + n = strtoul(string, &e, base); + if (*e != '\0') + return (ISC_R_BADNUMBER); + /* + * Where long is 64 bits we need to convert to 32 bits then test for + * equality. This is a no-op on 32 bit machines and a good compiler + * will optimise it away. + */ + r = (uint32_t)n; + if ((n == ULONG_MAX && errno == ERANGE) || (n != (unsigned long)r)) + return (ISC_R_RANGE); + *uip = r; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_parse_uint16(uint16_t *uip, const char *string, int base) { + uint32_t val; + isc_result_t result; + result = isc_parse_uint32(&val, string, base); + if (result != ISC_R_SUCCESS) + return (result); + if (val > 0xFFFF) + return (ISC_R_RANGE); + *uip = (uint16_t) val; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_parse_uint8(uint8_t *uip, const char *string, int base) { + uint32_t val; + isc_result_t result; + result = isc_parse_uint32(&val, string, base); + if (result != ISC_R_SUCCESS) + return (result); + if (val > 0xFF) + return (ISC_R_RANGE); + *uip = (uint8_t) val; + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/pk11.c b/lib/isc/pk11.c new file mode 100644 index 0000000..c5d2310 --- /dev/null +++ b/lib/isc/pk11.c @@ -0,0 +1,1399 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <stdio.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/once.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/stdio.h> +#include <isc/string.h> +#include <isc/thread.h> +#include <isc/util.h> + +#include <dst/result.h> + +#include <pk11/pk11.h> +#include <pk11/internal.h> +#include <pk11/result.h> +#include <pk11/site.h> + +#include <pkcs11/cryptoki.h> +#include <pkcs11/pkcs11.h> +#include <pkcs11/eddsa.h> + +/* was 32 octets, Petr Spacek suggested 1024, SoftHSMv2 uses 256... */ +#ifndef PINLEN +#define PINLEN 256 +#endif + +#ifndef PK11_NO_LOGERR +#define PK11_NO_LOGERR 1 +#endif + +LIBISC_EXTERNAL_DATA bool pk11_verbose_init = false; + +static isc_once_t once = ISC_ONCE_INIT; +static isc_mem_t *pk11_mctx = NULL; +static int32_t allocsize = 0; +static bool initialized = false; + +typedef struct pk11_session pk11_session_t; +typedef struct pk11_token pk11_token_t; +typedef ISC_LIST(pk11_session_t) pk11_sessionlist_t; + +struct pk11_session { + unsigned int magic; + CK_SESSION_HANDLE session; + ISC_LINK(pk11_session_t) link; + pk11_token_t *token; +}; + +struct pk11_token { + unsigned int magic; + unsigned int operations; + ISC_LINK(pk11_token_t) link; + CK_SLOT_ID slotid; + pk11_sessionlist_t sessions; + bool logged; + char name[32]; + char manuf[32]; + char model[16]; + char serial[16]; + char pin[PINLEN + 1]; +}; +static ISC_LIST(pk11_token_t) tokens; + +static pk11_token_t *rand_token; +static pk11_token_t *best_rsa_token; +static pk11_token_t *best_dsa_token; +static pk11_token_t *best_dh_token; +static pk11_token_t *digest_token; +static pk11_token_t *best_ec_token; +static pk11_token_t *best_gost_token; +static pk11_token_t *aes_token; + +static isc_result_t free_all_sessions(void); +static isc_result_t free_session_list(pk11_sessionlist_t *slist); +static isc_result_t setup_session(pk11_session_t *sp, + pk11_token_t *token, + bool rw); +static void scan_slots(void); +static isc_result_t token_login(pk11_session_t *sp); +static char *percent_decode(char *x, size_t *len); +static bool pk11strcmp(const char *x, size_t lenx, + const char *y, size_t leny); +static CK_ATTRIBUTE *push_attribute(pk11_object_t *obj, + isc_mem_t *mctx, + size_t len); + +static isc_mutex_t alloclock; +static isc_mutex_t sessionlock; + +static pk11_sessionlist_t actives; + +static CK_C_INITIALIZE_ARGS pk11_init_args = { + NULL_PTR, /* CreateMutex */ + NULL_PTR, /* DestroyMutex */ + NULL_PTR, /* LockMutex */ + NULL_PTR, /* UnlockMutex */ + CKF_OS_LOCKING_OK, /* flags */ + NULL_PTR, /* pReserved */ +}; + +#ifndef PK11_LIB_LOCATION +#define PK11_LIB_LOCATION "unknown_provider" +#endif + +#ifndef WIN32 +static const char *lib_name = PK11_LIB_LOCATION; +#else +static const char *lib_name = PK11_LIB_LOCATION ".dll"; +#endif + +void +pk11_set_lib_name(const char *name) { + lib_name = name; +} + +const char * +pk11_get_lib_name(void) { + return (lib_name); +} + +static void +initialize(void) { + char *pk11_provider; + + RUNTIME_CHECK(isc_mutex_init(&alloclock) == ISC_R_SUCCESS); + RUNTIME_CHECK(isc_mutex_init(&sessionlock) == ISC_R_SUCCESS); + + pk11_provider = getenv("PKCS11_PROVIDER"); + if (pk11_provider != NULL) + lib_name = pk11_provider; +} + +void * +pk11_mem_get(size_t size) { + void *ptr; + + LOCK(&alloclock); + if (pk11_mctx != NULL) + ptr = isc_mem_get(pk11_mctx, size); + else { + ptr = malloc(size); + if (ptr != NULL) + allocsize += (int)size; + } + UNLOCK(&alloclock); + + if (ptr != NULL) + memset(ptr, 0, size); + return (ptr); +} + +void +pk11_mem_put(void *ptr, size_t size) { + if (ptr != NULL) + memset(ptr, 0, size); + LOCK(&alloclock); + if (pk11_mctx != NULL) + isc_mem_put(pk11_mctx, ptr, size); + else { + if (ptr != NULL) + allocsize -= (int)size; + free(ptr); + } + UNLOCK(&alloclock); +} + +isc_result_t +pk11_initialize(isc_mem_t *mctx, const char *engine) { + isc_result_t result = ISC_R_SUCCESS; + CK_RV rv; + + RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS); + + LOCK(&sessionlock); + LOCK(&alloclock); + if ((mctx != NULL) && (pk11_mctx == NULL) && (allocsize == 0)) + isc_mem_attach(mctx, &pk11_mctx); + UNLOCK(&alloclock); + if (initialized) { + goto unlock; + } else { + initialized = true; + } + + ISC_LIST_INIT(tokens); + ISC_LIST_INIT(actives); + + if (engine != NULL) + lib_name = engine; + + /* Initialize the CRYPTOKI library */ + rv = pkcs_C_Initialize((CK_VOID_PTR) &pk11_init_args); + + if (rv == 0xfe) { + result = PK11_R_NOPROVIDER; + fprintf(stderr, "Can't load PKCS#11 provider: %s\n", + pk11_get_load_error_message()); + goto unlock; + } + if (rv != CKR_OK) { + result = PK11_R_INITFAILED; + goto unlock; + } + + scan_slots(); +#ifdef PKCS11CRYPTO + if (rand_token == NULL) { + result = PK11_R_NORANDOMSERVICE; + goto unlock; + } + if (digest_token == NULL) { + result = PK11_R_NODIGESTSERVICE; + goto unlock; + } +#if defined(AES_CC) + if (aes_token == NULL) { + result = PK11_R_NOAESSERVICE; + goto unlock; + } +#endif +#endif /* PKCS11CRYPTO */ + unlock: + UNLOCK(&sessionlock); + return (result); +} + +isc_result_t +pk11_finalize(void) { + pk11_token_t *token, *next; + isc_result_t ret; + + ret = free_all_sessions(); + (void) pkcs_C_Finalize(NULL_PTR); + token = ISC_LIST_HEAD(tokens); + while (token != NULL) { + next = ISC_LIST_NEXT(token, link); + ISC_LIST_UNLINK(tokens, token, link); + if (token == rand_token) + rand_token = NULL; + if (token == best_rsa_token) + best_rsa_token = NULL; + if (token == best_dsa_token) + best_dsa_token = NULL; + if (token == best_dh_token) + best_dh_token = NULL; + if (token == digest_token) + digest_token = NULL; + if (token == best_ec_token) + best_ec_token = NULL; + if (token == best_gost_token) + best_gost_token = NULL; + if (token == aes_token) + aes_token = NULL; + pk11_mem_put(token, sizeof(*token)); + token = next; + } + if (pk11_mctx != NULL) + isc_mem_detach(&pk11_mctx); + initialized = false; + return (ret); +} + +isc_result_t +pk11_rand_bytes(unsigned char *buf, int num) { + isc_result_t ret; + CK_RV rv; + pk11_context_t ctx; + + ret = pk11_get_session(&ctx, OP_RAND, false, false, + false, NULL, 0); + if ((ret != ISC_R_SUCCESS) && + (ret != PK11_R_NODIGESTSERVICE) && + (ret != PK11_R_NOAESSERVICE)) + return (ret); + RUNTIME_CHECK(ctx.session != CK_INVALID_HANDLE); + rv = pkcs_C_GenerateRandom(ctx.session, + (CK_BYTE_PTR) buf, (CK_ULONG) num); + pk11_return_session(&ctx); + if (rv == CKR_OK) + return (ISC_R_SUCCESS); + else + return (DST_R_CRYPTOFAILURE); +} + +#define SEEDSIZE 1024 + +static CK_BYTE seed[SEEDSIZE]; + +void +pk11_rand_seed_fromfile(const char *randomfile) { + pk11_context_t ctx; + FILE *stream = NULL; + size_t cc = 0; + isc_result_t ret; + + ret = pk11_get_session(&ctx, OP_RAND, false, false, + false, NULL, 0); + if ((ret != ISC_R_SUCCESS) && + (ret != PK11_R_NODIGESTSERVICE) && + (ret != PK11_R_NOAESSERVICE)) + return; + RUNTIME_CHECK(ctx.session != CK_INVALID_HANDLE); + ret = isc_stdio_open(randomfile, "r", &stream); + if (ret != ISC_R_SUCCESS) + goto cleanup; + ret = isc_stdio_read(seed, 1, SEEDSIZE, stream, &cc); + if (ret!= ISC_R_SUCCESS) + goto cleanup; + ret = isc_stdio_close(stream); + stream = NULL; + if (ret!= ISC_R_SUCCESS) + goto cleanup; + (void) pkcs_C_SeedRandom(ctx.session, seed, (CK_ULONG) cc); + + cleanup: + if (stream != NULL) + (void) isc_stdio_close(stream); + pk11_return_session(&ctx); +} + +isc_result_t +pk11_get_session(pk11_context_t *ctx, pk11_optype_t optype, + bool need_services, bool rw, + bool logon, const char *pin, CK_SLOT_ID slot) +{ + pk11_token_t *token = NULL; + pk11_sessionlist_t *freelist; + pk11_session_t *sp; + isc_result_t ret; +#ifdef PKCS11CRYPTO + isc_result_t service_ret = ISC_R_SUCCESS; +#else + UNUSED(need_services); +#endif + + memset(ctx, 0, sizeof(pk11_context_t)); + ctx->handle = NULL; + ctx->session = CK_INVALID_HANDLE; + + ret = pk11_initialize(NULL, NULL); +#ifdef PKCS11CRYPTO + if (ret == PK11_R_NORANDOMSERVICE || + ret == PK11_R_NODIGESTSERVICE || + ret == PK11_R_NOAESSERVICE) { + if (need_services) + return (ret); + service_ret = ret; + } + else +#endif /* PKCS11CRYPTO */ + if (ret != ISC_R_SUCCESS) + return (ret); + + LOCK(&sessionlock); + /* wait for initialization to finish */ + UNLOCK(&sessionlock); + + switch(optype) { +#ifdef PKCS11CRYPTO + case OP_RAND: + token = rand_token; + break; + case OP_DIGEST: + token = digest_token; + break; + case OP_AES: + token = aes_token; + break; + case OP_ANY: + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + if (token->slotid == slot) + break; + break; +#endif + default: + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + if (token->slotid == slot) + break; +#ifdef PKCS11CRYPTO + if ((token == NULL) || + ((token->operations & (1 << optype)) == 0)) + return (ISC_R_NOTFOUND); +#endif + break; + } + if (token == NULL) + return (ISC_R_NOTFOUND); + + /* Override the token's PIN */ + if (logon && pin != NULL && *pin != '\0') { + if (strlen(pin) > PINLEN) + return (ISC_R_RANGE); + /* + * We want to zero out the old pin before + * overwriting with a new one. + */ + memset(token->pin, 0, sizeof(token->pin)); + strlcpy(token->pin, pin, sizeof(token->pin)); + } + + freelist = &token->sessions; + + LOCK(&sessionlock); + sp = ISC_LIST_HEAD(*freelist); + if (sp != NULL) { + ISC_LIST_UNLINK(*freelist, sp, link); + ISC_LIST_APPEND(actives, sp, link); + UNLOCK(&sessionlock); + if (logon) + ret = token_login(sp); + ctx->handle = sp; + ctx->session = sp->session; + return (ret); + } + UNLOCK(&sessionlock); + + sp = pk11_mem_get(sizeof(*sp)); + if (sp == NULL) + return (ISC_R_NOMEMORY); + sp->magic = SES_MAGIC; + sp->token = token; + sp->session = CK_INVALID_HANDLE; + ISC_LINK_INIT(sp, link); + ret = setup_session(sp, token, rw); + if ((ret == ISC_R_SUCCESS) && logon) + ret = token_login(sp); + LOCK(&sessionlock); + ISC_LIST_APPEND(actives, sp, link); + UNLOCK(&sessionlock); + ctx->handle = sp; + ctx->session = sp->session; +#ifdef PKCS11CRYPTO + if (ret == ISC_R_SUCCESS) + ret = service_ret; +#endif + return (ret); +} + +void +pk11_return_session(pk11_context_t *ctx) { + pk11_session_t *sp = (pk11_session_t *) ctx->handle; + + if (sp == NULL) + return; + ctx->handle = NULL; + ctx->session = CK_INVALID_HANDLE; + + LOCK(&sessionlock); + ISC_LIST_UNLINK(actives, sp, link); + UNLOCK(&sessionlock); + if (sp->session == CK_INVALID_HANDLE) { + pk11_mem_put(sp, sizeof(*sp)); + return; + } + + LOCK(&sessionlock); + ISC_LIST_APPEND(sp->token->sessions, sp, link); + UNLOCK(&sessionlock); +} + +static isc_result_t +free_all_sessions(void) { + pk11_token_t *token; + isc_result_t ret = ISC_R_SUCCESS; + isc_result_t oret; + + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) { + oret = free_session_list(&token->sessions); + if (oret != ISC_R_SUCCESS) + ret = oret; + } + if (!ISC_LIST_EMPTY(actives)) { + ret = ISC_R_ADDRINUSE; + oret = free_session_list(&actives); + if (oret != ISC_R_SUCCESS) + ret = oret; + } + return (ret); +} + +static isc_result_t +free_session_list(pk11_sessionlist_t *slist) { + pk11_session_t *sp; + CK_RV rv; + isc_result_t ret; + + ret = ISC_R_SUCCESS; + LOCK(&sessionlock); + while (!ISC_LIST_EMPTY(*slist)) { + sp = ISC_LIST_HEAD(*slist); + ISC_LIST_UNLINK(*slist, sp, link); + UNLOCK(&sessionlock); + if (sp->session != CK_INVALID_HANDLE) { + rv = pkcs_C_CloseSession(sp->session); + if (rv != CKR_OK) + ret = DST_R_CRYPTOFAILURE; + } + LOCK(&sessionlock); + pk11_mem_put(sp, sizeof(*sp)); + } + UNLOCK(&sessionlock); + + return (ret); +} + +static isc_result_t +setup_session(pk11_session_t *sp, pk11_token_t *token, + bool rw) +{ + CK_RV rv; + CK_FLAGS flags = CKF_SERIAL_SESSION; + + if (rw) + flags += CKF_RW_SESSION; + + rv = pkcs_C_OpenSession(token->slotid, flags, NULL_PTR, + NULL_PTR, &sp->session); + if (rv != CKR_OK) + return (DST_R_CRYPTOFAILURE); + return (ISC_R_SUCCESS); +} + +static isc_result_t +token_login(pk11_session_t *sp) { + CK_RV rv; + pk11_token_t *token = sp->token; + isc_result_t ret = ISC_R_SUCCESS; + + LOCK(&sessionlock); + if (!token->logged) { + rv = pkcs_C_Login(sp->session, CKU_USER, + (CK_UTF8CHAR_PTR) token->pin, + (CK_ULONG) strlen(token->pin)); + if (rv != CKR_OK) { + ret = ISC_R_NOPERM; +#if PK11_NO_LOGERR + pk11_error_fatalcheck(__FILE__, __LINE__, + "pkcs_C_Login", rv); +#endif + } else + token->logged = true; + } + UNLOCK(&sessionlock); + return (ret); +} + +#define PK11_TRACE(fmt) \ + if (pk11_verbose_init) fprintf(stderr, fmt) +#define PK11_TRACE1(fmt, arg) \ + if (pk11_verbose_init) fprintf(stderr, fmt, arg) +#define PK11_TRACE2(fmt, arg1, arg2) \ + if (pk11_verbose_init) fprintf(stderr, fmt, arg1, arg2) +#define PK11_TRACEM(mech) \ + if (pk11_verbose_init) fprintf(stderr, #mech ": 0x%lx\n", rv) + +static void +scan_slots(void) { + CK_MECHANISM_INFO mechInfo; + CK_TOKEN_INFO tokenInfo; + CK_RV rv; + CK_SLOT_ID slot; + CK_SLOT_ID_PTR slotList; + CK_ULONG slotCount; + pk11_token_t *token; + unsigned int i; + bool bad; + + slotCount = 0; + PK11_FATALCHECK(pkcs_C_GetSlotList, (CK_FALSE, NULL_PTR, &slotCount)); + PK11_TRACE1("slotCount=%lu\n", slotCount); + /* it's not an error if we didn't find any providers */ + if (slotCount == 0) + return; + slotList = pk11_mem_get(sizeof(CK_SLOT_ID) * slotCount); + RUNTIME_CHECK(slotList != NULL); + PK11_FATALCHECK(pkcs_C_GetSlotList, (CK_FALSE, slotList, &slotCount)); + + for (i = 0; i < slotCount; i++) { + slot = slotList[i]; + PK11_TRACE2("slot#%u=0x%lx\n", i, slot); + + rv = pkcs_C_GetTokenInfo(slot, &tokenInfo); + if (rv != CKR_OK) + continue; + token = pk11_mem_get(sizeof(*token)); + RUNTIME_CHECK(token != NULL); + token->magic = TOK_MAGIC; + token->slotid = slot; + ISC_LINK_INIT(token, link); + ISC_LIST_INIT(token->sessions); + memmove(token->name, tokenInfo.label, 32); + memmove(token->manuf, tokenInfo.manufacturerID, 32); + memmove(token->model, tokenInfo.model, 16); + memmove(token->serial, tokenInfo.serialNumber, 16); + ISC_LIST_APPEND(tokens, token, link); + if ((tokenInfo.flags & CKF_RNG) == 0) { + PK11_TRACE("no CKF_RNG\n"); + goto try_rsa; + } + token->operations |= 1 << OP_RAND; + if (rand_token == NULL) + rand_token = token; + + try_rsa: + bad = false; + rv = pkcs_C_GetMechanismInfo(slot, CKM_RSA_PKCS_KEY_PAIR_GEN, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) { + bad = true; + PK11_TRACEM(CKM_RSA_PKCS_KEY_PAIR_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5_RSA_PKCS, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) { +#if !defined(PK11_MD5_DISABLE) && !defined(PK11_RSA_PKCS_REPLACE) + bad = true; +#endif + PK11_TRACEM(CKM_MD5_RSA_PKCS); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA1_RSA_PKCS, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) { +#ifndef PK11_RSA_PKCS_REPLACE + bad = true; +#endif + PK11_TRACEM(CKM_SHA1_RSA_PKCS); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256_RSA_PKCS, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) { +#ifndef PK11_RSA_PKCS_REPLACE + bad = true; +#endif + PK11_TRACEM(CKM_SHA256_RSA_PKCS); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512_RSA_PKCS, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) { +#ifndef PK11_RSA_PKCS_REPLACE + bad = true; +#endif + PK11_TRACEM(CKM_SHA512_RSA_PKCS); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_RSA_PKCS, &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) { +#ifdef PK11_RSA_PKCS_REPLACE + bad = true; +#endif + PK11_TRACEM(CKM_RSA_PKCS); + } + if (bad) + goto try_dsa; + token->operations |= 1 << OP_RSA; + if (best_rsa_token == NULL) + best_rsa_token = token; + + try_dsa: + bad = false; + rv = pkcs_C_GetMechanismInfo(slot, CKM_DSA_PARAMETER_GEN, + &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_GENERATE) == 0)) { +#ifndef PK11_DSA_PARAMETER_GEN_SKIP + bad = true; +#endif + PK11_TRACEM(CKM_DSA_PARAMETER_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_DSA_KEY_PAIR_GEN, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) { + bad = true; + PK11_TRACEM(CKM_DSA_PARAMETER_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_DSA_SHA1, &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) { + bad = true; + PK11_TRACEM(CKM_DSA_SHA1); + } + if (bad) + goto try_dh; +#ifndef PK11_DSA_DISABLE + token->operations |= 1 << OP_DSA; + if (best_dsa_token == NULL) + best_dsa_token = token; +#endif + + try_dh: + bad = false; + rv = pkcs_C_GetMechanismInfo(slot, CKM_DH_PKCS_PARAMETER_GEN, + &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_GENERATE) == 0)) { + PK11_TRACEM(CKM_DH_PKCS_PARAMETER_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_DH_PKCS_KEY_PAIR_GEN, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) { +#ifndef PK11_DH_PKCS_PARAMETER_GEN_SKIP + bad = true; +#endif + PK11_TRACEM(CKM_DH_PKCS_KEY_PAIR_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_DH_PKCS_DERIVE, + &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DERIVE) == 0)) { + bad = true; + PK11_TRACEM(CKM_DH_PKCS_DERIVE); + } + if (bad) + goto try_digest; +#ifndef PK11_DH_DISABLE + token->operations |= 1 << OP_DH; + if (best_dh_token == NULL) + best_dh_token = token; +#endif + + try_digest: + bad = false; + rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) { +#ifndef PK11_MD5_DISABLE + bad = true; +#endif + PK11_TRACEM(CKM_MD5); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA_1, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) { + bad = true; + PK11_TRACEM(CKM_SHA_1); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA224, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) { + bad = true; + PK11_TRACEM(CKM_SHA224); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) { + bad = true; + PK11_TRACEM(CKM_SHA256); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA384, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) { + bad = true; + PK11_TRACEM(CKM_SHA384); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) { + bad = true; + PK11_TRACEM(CKM_SHA512); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_MD5_HMAC, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) { +#if !defined(PK11_MD5_DISABLE) && !defined(PK11_MD5_HMAC_REPLACE) + bad = true; +#endif + PK11_TRACEM(CKM_MD5_HMAC); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA_1_HMAC, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) { +#ifndef PK11_SHA_1_HMAC_REPLACE + bad = true; +#endif + PK11_TRACEM(CKM_SHA_1_HMAC); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA224_HMAC, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) { +#ifndef PK11_SHA224_HMAC_REPLACE + bad = true; +#endif + PK11_TRACEM(CKM_SHA224_HMAC); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA256_HMAC, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) { +#ifndef PK11_SHA256_HMAC_REPLACE + bad = true; +#endif + PK11_TRACEM(CKM_SHA256_HMAC); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA384_HMAC, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) { +#ifndef PK11_SHA384_HMAC_REPLACE + bad = true; +#endif + PK11_TRACEM(CKM_SHA384_HMAC); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_SHA512_HMAC, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_SIGN) == 0)) { +#ifndef PK11_SHA512_HMAC_REPLACE + bad = true; +#endif + PK11_TRACEM(CKM_SHA512_HMAC); + } + if (!bad) { + token->operations |= 1 << OP_DIGEST; + if (digest_token == NULL) + digest_token = token; + } + + /* ECDSA requires digest */ + rv = pkcs_C_GetMechanismInfo(slot, CKM_EC_KEY_PAIR_GEN, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) { + bad = true; + PK11_TRACEM(CKM_EC_KEY_PAIR_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_ECDSA, &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) { + bad = true; + PK11_TRACEM(CKM_ECDSA); + } + if (bad) + goto try_gost; + token->operations |= 1 << OP_EC; + if (best_ec_token == NULL) + best_ec_token = token; + + try_gost: + bad = false; + /* does GOST require digest too? */ + rv = pkcs_C_GetMechanismInfo(slot, CKM_GOSTR3411, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_DIGEST) == 0)) { + bad = true; + PK11_TRACEM(CKM_GOSTR3411); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_GOSTR3410_KEY_PAIR_GEN, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) { + bad = true; + PK11_TRACEM(CKM_GOSTR3410_KEY_PAIR_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, + CKM_GOSTR3410_WITH_GOSTR3411, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) { + bad = true; + PK11_TRACEM(CKM_GOSTR3410_WITH_GOSTR3411); + } + if (bad) + goto try_eddsa; + token->operations |= 1 << OP_GOST; + if (best_gost_token == NULL) + best_gost_token = token; + + try_eddsa: +#if defined(CKM_EDDSA_KEY_PAIR_GEN) && defined(CKM_EDDSA) && defined(CKK_EDDSA) + bad = false; + rv = pkcs_C_GetMechanismInfo(slot, CKM_EDDSA_KEY_PAIR_GEN, + &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_GENERATE_KEY_PAIR) == 0)) { + bad = true; + PK11_TRACEM(CKM_EDDSA_KEY_PAIR_GEN); + } + rv = pkcs_C_GetMechanismInfo(slot, CKM_EDDSA, &mechInfo); + if ((rv != CKR_OK) || + ((mechInfo.flags & CKF_SIGN) == 0) || + ((mechInfo.flags & CKF_VERIFY) == 0)) { + bad = true; + PK11_TRACEM(CKM_EDDSA); + } + if (bad) + goto try_aes; + + try_aes: +#endif + bad = false; + rv = pkcs_C_GetMechanismInfo(slot, CKM_AES_ECB, &mechInfo); + if ((rv != CKR_OK) || ((mechInfo.flags & CKF_ENCRYPT) == 0)) { + bad = true; + PK11_TRACEM(CKM_AES_ECB); + } + if (bad) + continue; + token->operations |= 1 << OP_AES; + if (aes_token == NULL) + aes_token = token; + } + + if (slotList != NULL) { + pk11_mem_put(slotList, sizeof(CK_SLOT_ID) * slotCount); + } +} + +CK_SLOT_ID +pk11_get_best_token(pk11_optype_t optype) { + pk11_token_t *token = NULL; + + switch (optype) { + case OP_RAND: + token = rand_token; + break; + case OP_RSA: + token = best_rsa_token; + break; + case OP_DSA: + token = best_dsa_token; + break; + case OP_DH: + token = best_dh_token; + break; + case OP_DIGEST: + token = digest_token; + break; + case OP_EC: + token = best_ec_token; + break; + case OP_GOST: + token = best_gost_token; + break; + case OP_AES: + token = aes_token; + break; + default: + break; + } + if (token == NULL) + return (0); + return (token->slotid); +} + +unsigned int +pk11_numbits(CK_BYTE_PTR data, unsigned int bytecnt) { + unsigned int bitcnt, i; + CK_BYTE top; + + if (bytecnt == 0) + return (0); + bitcnt = bytecnt * 8; + for (i = 0; i < bytecnt; i++) { + top = data[i]; + if (top == 0) { + bitcnt -= 8; + continue; + } + if (top & 0x80) + return (bitcnt); + if (top & 0x40) + return (bitcnt - 1); + if (top & 0x20) + return (bitcnt - 2); + if (top & 0x10) + return (bitcnt - 3); + if (top & 0x08) + return (bitcnt - 4); + if (top & 0x04) + return (bitcnt - 5); + if (top & 0x02) + return (bitcnt - 6); + if (top & 0x01) + return (bitcnt - 7); + break; + } + INSIST(0); +} + +CK_ATTRIBUTE * +pk11_attribute_first(const pk11_object_t *obj) { + return (obj->repr); +} + +CK_ATTRIBUTE * +pk11_attribute_next(const pk11_object_t *obj, CK_ATTRIBUTE *attr) { + CK_ATTRIBUTE *next; + + next = attr + 1; + if ((next - obj->repr) >= obj->attrcnt) + return (NULL); + return (next); +} + +CK_ATTRIBUTE * +pk11_attribute_bytype(const pk11_object_t *obj, CK_ATTRIBUTE_TYPE type) { + CK_ATTRIBUTE *attr; + + for(attr = pk11_attribute_first(obj); + attr != NULL; + attr = pk11_attribute_next(obj, attr)) + if (attr->type == type) + return (attr); + return (NULL); +} + +static char * +percent_decode(char *x, size_t *len) { + char *p, *c; + unsigned char v; + + INSIST(len != NULL); + + for (p = c = x; p[0] != '\0'; p++, c++) { + switch (p[0]) { + case '%': + v = 0; + switch (p[1]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + v = (p[1] - '0') << 4; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + v = (p[1] - 'A' + 10) << 4; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + v = (p[1] - 'a' + 10) << 4; + break; + default: + return (NULL); + } + switch (p[2]) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + v |= (p[2] - '0') & 0x0f; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + v = (p[2] - 'A' + 10) & 0x0f; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + v = (p[2] - 'a' + 10) & 0x0f; + break; + default: + return (NULL); + } + p += 2; + *c = (char) v; + (*len)++; + break; + default: + *c = *p; + (*len)++; + } + } + return (x); +} + +static bool +pk11strcmp(const char *x, size_t lenx, const char *y, size_t leny) { + char buf[32]; + + INSIST((leny == 32) || (leny == 16)); + + memset(buf, ' ', 32); + if (lenx > leny) + lenx = leny; + memmove(buf, x, lenx); + return (memcmp(buf, y, leny) == 0); +} + +static CK_ATTRIBUTE * +push_attribute(pk11_object_t *obj, isc_mem_t *mctx, size_t len) { + CK_ATTRIBUTE *old = obj->repr; + CK_ATTRIBUTE *attr; + CK_BYTE cnt = obj->attrcnt; + + obj->repr = isc_mem_get(mctx, (cnt + 1) * sizeof(*attr)); + if (obj->repr == NULL) { + obj->repr = old; + return (NULL); + } + memset(obj->repr, 0, (cnt + 1) * sizeof(*attr)); + memmove(obj->repr, old, cnt * sizeof(*attr)); + attr = obj->repr + cnt; + attr->ulValueLen = (CK_ULONG) len; + attr->pValue = isc_mem_get(mctx, len); + if (attr->pValue == NULL) { + memset(obj->repr, 0, (cnt + 1) * sizeof(*attr)); + isc_mem_put(mctx, obj->repr, (cnt + 1) * sizeof(*attr)); + obj->repr = old; + return (NULL); + } + memset(attr->pValue, 0, len); + if (old != NULL) { + memset(old, 0, cnt * sizeof(*attr)); + isc_mem_put(mctx, old, cnt * sizeof(*attr)); + } + obj->attrcnt++; + return (attr); +} + +#define DST_RET(a) { ret = a; goto err; } + +isc_result_t +pk11_parse_uri(pk11_object_t *obj, const char *label, + isc_mem_t *mctx, pk11_optype_t optype) +{ + CK_ATTRIBUTE *attr; + pk11_token_t *token = NULL; + char *uri, *p, *a, *na, *v; + size_t len, l; + FILE *stream = NULL; + char pin[PINLEN + 1]; + bool gotpin = false; + isc_result_t ret; + + /* get values to work on */ + len = strlen(label) + 1; + uri = isc_mem_get(mctx, len); + if (uri == NULL) + return (ISC_R_NOMEMORY); + memmove(uri, label, len); + + /* get the URI scheme */ + p = strchr(uri, ':'); + if (p == NULL) + DST_RET(PK11_R_NOPROVIDER); + *p++ = '\0'; + if (strcmp(uri, "pkcs11") != 0) + DST_RET(PK11_R_NOPROVIDER); + + /* get attributes */ + for (na = p; na != NULL;) { + a = na; + p = strchr(a, ';'); + if (p == NULL) { + /* last attribute */ + na = NULL; + } else { + *p++ = '\0'; + na = p; + } + p = strchr(a, '='); + if (p != NULL) { + *p++ = '\0'; + v = p; + } else + v = a; + l = 0; + v = percent_decode(v, &l); + if (v == NULL) + DST_RET(PK11_R_NOPROVIDER); + if ((a == v) || (strcmp(a, "object") == 0)) { + /* object: CKA_LABEL */ + attr = pk11_attribute_bytype(obj, CKA_LABEL); + if (attr != NULL) + DST_RET(PK11_R_NOPROVIDER); + attr = push_attribute(obj, mctx, l); + if (attr == NULL) + DST_RET(ISC_R_NOMEMORY); + attr->type = CKA_LABEL; + memmove(attr->pValue, v, l); + } else if (strcmp(a, "token") == 0) { + /* token: CK_TOKEN_INFO label */ + if (token == NULL) + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + if (pk11strcmp(v, l, token->name, 32)) + break; + } else if (strcmp(a, "manufacturer") == 0) { + /* manufacturer: CK_TOKEN_INFO manufacturerID */ + if (token == NULL) + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + if (pk11strcmp(v, l, token->manuf, 32)) + break; + } else if (strcmp(a, "serial") == 0) { + /* serial: CK_TOKEN_INFO serialNumber */ + if (token == NULL) + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + if (pk11strcmp(v, l, token->serial, 16)) + break; + } else if (strcmp(a, "model") == 0) { + /* model: CK_TOKEN_INFO model */ + if (token == NULL) + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) + if (pk11strcmp(v, l, token->model, 16)) + break; + } else if (strcmp(a, "library-manufacturer") == 0) { + /* ignored */ + } else if (strcmp(a, "library-description") == 0) { + /* ignored */ + } else if (strcmp(a, "library-version") == 0) { + /* ignored */ + } else if (strcmp(a, "object-type") == 0) { + /* object-type: CKA_CLASS */ + /* only private makes sense */ + if (strcmp(v, "private") != 0) + DST_RET(PK11_R_NOPROVIDER); + } else if (strcmp(a, "id") == 0) { + /* id: CKA_ID */ + attr = pk11_attribute_bytype(obj, CKA_ID); + if (attr != NULL) + DST_RET(PK11_R_NOPROVIDER); + attr = push_attribute(obj, mctx, l); + if (attr == NULL) + DST_RET(ISC_R_NOMEMORY); + attr->type = CKA_ID; + memmove(attr->pValue, v, l); + } else if (strcmp(a, "pin-source") == 0) { + /* pin-source: PIN */ + ret = isc_stdio_open(v, "r", &stream); + if (ret != ISC_R_SUCCESS) + goto err; + memset(pin, 0, PINLEN + 1); + ret = isc_stdio_read(pin, 1, PINLEN + 1, stream, &l); + if ((ret != ISC_R_SUCCESS) && (ret != ISC_R_EOF)) + goto err; + if (l > PINLEN) + DST_RET(ISC_R_RANGE); + ret = isc_stdio_close(stream); + stream = NULL; + if (ret != ISC_R_SUCCESS) + goto err; + gotpin = true; + } else + DST_RET(PK11_R_NOPROVIDER); + } + + if ((pk11_attribute_bytype(obj, CKA_LABEL) == NULL) && + (pk11_attribute_bytype(obj, CKA_ID) == NULL)) + DST_RET(ISC_R_NOTFOUND); + + if (token == NULL) { + if (optype == OP_RSA) + token = best_rsa_token; + else if (optype == OP_DSA) + token = best_dsa_token; + else if (optype == OP_DH) + token = best_dh_token; + else if (optype == OP_EC) + token = best_ec_token; + } + if (token == NULL) + DST_RET(ISC_R_NOTFOUND); + obj->slot = token->slotid; + if (gotpin) { + memmove(token->pin, pin, PINLEN + 1); + obj->reqlogon = true; + } + + ret = ISC_R_SUCCESS; + + err: + if (stream != NULL) + (void) isc_stdio_close(stream); + isc_mem_put(mctx, uri, len); + return (ret); +} + +void +pk11_error_fatalcheck(const char *file, int line, + const char *funcname, CK_RV rv) +{ + isc_error_fatal(file, line, "%s: Error = 0x%.8lX\n", funcname, rv); +} + +void +pk11_dump_tokens(void) { + pk11_token_t *token; + bool first; + + printf("DEFAULTS\n"); + printf("\trand_token=%p\n", rand_token); + printf("\tbest_rsa_token=%p\n", best_rsa_token); + printf("\tbest_dsa_token=%p\n", best_dsa_token); + printf("\tbest_dh_token=%p\n", best_dh_token); + printf("\tdigest_token=%p\n", digest_token); + printf("\tbest_ec_token=%p\n", best_ec_token); + printf("\tbest_gost_token=%p\n", best_gost_token); + printf("\taes_token=%p\n", aes_token); + + for (token = ISC_LIST_HEAD(tokens); + token != NULL; + token = ISC_LIST_NEXT(token, link)) { + printf("\nTOKEN\n"); + printf("\taddress=%p\n", token); + printf("\tslotID=%lu\n", token->slotid); + printf("\tlabel=%.32s\n", token->name); + printf("\tmanufacturerID=%.32s\n", token->manuf); + printf("\tmodel=%.16s\n", token->model); + printf("\tserialNumber=%.16s\n", token->serial); + printf("\tsupported operations=0x%x (", token->operations); + first = true; + if (token->operations & (1 << OP_RAND)) { + if (!first) + printf(","); + first = false; + printf("RAND"); + } + if (token->operations & (1 << OP_RSA)) { + first = false; + printf("RSA"); + } + if (token->operations & (1 << OP_DSA)) { + if (!first) + printf(","); + first = false; + printf("DSA"); + } + if (token->operations & (1 << OP_DH)) { + if (!first) + printf(","); + first = false; + printf("DH"); + } + if (token->operations & (1 << OP_DIGEST)) { + if (!first) + printf(","); + first = false; + printf("DIGEST"); + } + if (token->operations & (1 << OP_EC)) { + if (!first) + printf(","); + first = false; + printf("EC"); + } + if (token->operations & (1 << OP_AES)) { + if (!first) + printf(","); + first = false; + printf("AES"); + } + printf(")\n"); + } +} diff --git a/lib/isc/pk11_result.c b/lib/isc/pk11_result.c new file mode 100644 index 0000000..33143f8 --- /dev/null +++ b/lib/isc/pk11_result.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> +#include <stddef.h> + +#include <isc/once.h> +#include <isc/msgcat.h> +#include <isc/util.h> + +#include <pk11/result.h> + +LIBISC_EXTERNAL_DATA isc_msgcat_t * pk11_msgcat = NULL; + +static isc_once_t msgcat_once = ISC_ONCE_INIT; + +static const char *text[PK11_R_NRESULTS] = { + "PKCS#11 initialization failed", /*%< 0 */ + "no PKCS#11 provider", /*%< 1 */ + "PKCS#11 provider has no random service", /*%< 2 */ + "PKCS#11 provider has no digest service", /*%< 3 */ + "PKCS#11 provider has no AES service", /*%< 4 */ +}; + +static const char *ids[PK11_R_NRESULTS] = { + "PK11_R_INITFAILED", + "PK11_R_NOPROVIDER", + "PK11_R_NORANDOMSERVICE", + "PK11_R_NODIGESTSERVICE", + "PK11_R_NOAESSERVICE", +}; + +#define PK11_RESULT_RESULTSET 2 + +static isc_once_t once = ISC_ONCE_INIT; + +static void +open_msgcat(void) { + isc_msgcat_open("libpk11.cat", &pk11_msgcat); +} + +void +pk11_initmsgcat(void) { + + /* + * Initialize the PKCS#11 support's message catalog, + * pk11_msgcat, if it has not already been initialized. + */ + + RUNTIME_CHECK(isc_once_do(&msgcat_once, open_msgcat) == ISC_R_SUCCESS); +} + +static void +initialize_action(void) { + isc_result_t result; + + result = isc_result_register(ISC_RESULTCLASS_PK11, PK11_R_NRESULTS, + text, pk11_msgcat, PK11_RESULT_RESULTSET); + if (result != ISC_R_SUCCESS) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_result_register() failed: %u", result); + + result = isc_result_registerids(ISC_RESULTCLASS_PK11, PK11_R_NRESULTS, + ids, pk11_msgcat, + PK11_RESULT_RESULTSET); + if (result != ISC_R_SUCCESS) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_result_registerids() failed: %u", result); +} + +static void +initialize(void) { + pk11_initmsgcat(); + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); +} + +const char * +pk11_result_totext(isc_result_t result) { + initialize(); + + return (isc_result_totext(result)); +} + +void +pk11_result_register(void) { + initialize(); +} diff --git a/lib/isc/pool.c b/lib/isc/pool.c new file mode 100644 index 0000000..a445d8b --- /dev/null +++ b/lib/isc/pool.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <string.h> + +#include <isc/mem.h> +#include <isc/random.h> +#include <isc/pool.h> +#include <isc/util.h> + +/*** + *** Types. + ***/ + +struct isc_pool { + isc_mem_t * mctx; + unsigned int count; + isc_pooldeallocator_t free; + isc_poolinitializer_t init; + void * initarg; + void ** pool; +}; + +/*** + *** Functions. + ***/ + +static isc_result_t +alloc_pool(isc_mem_t *mctx, unsigned int count, isc_pool_t **poolp) { + isc_pool_t *pool; + + pool = isc_mem_get(mctx, sizeof(*pool)); + if (pool == NULL) + return (ISC_R_NOMEMORY); + pool->count = count; + pool->free = NULL; + pool->init = NULL; + pool->initarg = NULL; + pool->mctx = NULL; + isc_mem_attach(mctx, &pool->mctx); + pool->pool = isc_mem_get(mctx, count * sizeof(void *)); + if (pool->pool == NULL) { + isc_mem_put(mctx, pool, sizeof(*pool)); + return (ISC_R_NOMEMORY); + } + memset(pool->pool, 0, count * sizeof(void *)); + + *poolp = pool; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_pool_create(isc_mem_t *mctx, unsigned int count, + isc_pooldeallocator_t release, + isc_poolinitializer_t init, void *initarg, + isc_pool_t **poolp) +{ + isc_pool_t *pool = NULL; + isc_result_t result; + unsigned int i; + + INSIST(count > 0); + + /* Allocate the pool structure */ + result = alloc_pool(mctx, count, &pool); + if (result != ISC_R_SUCCESS) + return (result); + + pool->free = release; + pool->init = init; + pool->initarg = initarg; + + /* Populate the pool */ + for (i = 0; i < count; i++) { + result = init(&pool->pool[i], initarg); + if (result != ISC_R_SUCCESS) { + isc_pool_destroy(&pool); + return (result); + } + } + + *poolp = pool; + return (ISC_R_SUCCESS); +} + +void * +isc_pool_get(isc_pool_t *pool) { + uint32_t i; + isc_random_get(&i); + return (pool->pool[i % pool->count]); +} + +int +isc_pool_count(isc_pool_t *pool) { + REQUIRE(pool != NULL); + return (pool->count); +} + +isc_result_t +isc_pool_expand(isc_pool_t **sourcep, unsigned int count, + isc_pool_t **targetp) +{ + isc_result_t result; + isc_pool_t *pool; + + REQUIRE(sourcep != NULL && *sourcep != NULL); + REQUIRE(targetp != NULL && *targetp == NULL); + + pool = *sourcep; + if (count > pool->count) { + isc_pool_t *newpool = NULL; + unsigned int i; + + /* Allocate a new pool structure */ + result = alloc_pool(pool->mctx, count, &newpool); + if (result != ISC_R_SUCCESS) + return (result); + + newpool->free = pool->free; + newpool->init = pool->init; + newpool->initarg = pool->initarg; + + /* Copy over the objects from the old pool */ + for (i = 0; i < pool->count; i++) { + newpool->pool[i] = pool->pool[i]; + pool->pool[i] = NULL; + } + + /* Populate the new entries */ + for (i = pool->count; i < count; i++) { + result = pool->init(&newpool->pool[i], pool->initarg); + if (result != ISC_R_SUCCESS) { + isc_pool_destroy(&pool); + return (result); + } + } + + isc_pool_destroy(&pool); + pool = newpool; + } + + *sourcep = NULL; + *targetp = pool; + return (ISC_R_SUCCESS); +} + +void +isc_pool_destroy(isc_pool_t **poolp) { + unsigned int i; + isc_pool_t *pool = *poolp; + for (i = 0; i < pool->count; i++) { + if (pool->free != NULL && pool->pool[i] != NULL) + pool->free(&pool->pool[i]); + } + isc_mem_put(pool->mctx, pool->pool, pool->count * sizeof(void *)); + isc_mem_putanddetach(&pool->mctx, pool, sizeof(*pool)); + *poolp = NULL; +} diff --git a/lib/isc/portset.c b/lib/isc/portset.c new file mode 100644 index 0000000..b3af46d --- /dev/null +++ b/lib/isc/portset.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/mem.h> +#include <isc/portset.h> +#include <isc/string.h> +#include <isc/types.h> +#include <isc/util.h> + +#define ISC_PORTSET_BUFSIZE (65536 / (sizeof(uint32_t) * 8)) + +/*% + * Internal representation of portset. It's an array of 32-bit integers, each + * bit corresponding to a single port in the ascending order. For example, + * the second most significant bit of buf[0] corresponds to port 1. + */ +struct isc_portset { + unsigned int nports; /*%< number of ports in the set */ + uint32_t buf[ISC_PORTSET_BUFSIZE]; +}; + +static inline bool +portset_isset(isc_portset_t *portset, in_port_t port) { + return (portset->buf[port >> 5] & ((uint32_t)1 << (port & 31))); +} + +static inline void +portset_add(isc_portset_t *portset, in_port_t port) { + if (!portset_isset(portset, port)) { + portset->nports++; + portset->buf[port >> 5] |= ((uint32_t)1 << (port & 31)); + } +} + +static inline void +portset_remove(isc_portset_t *portset, in_port_t port) { + if (portset_isset(portset, port)) { + portset->nports--; + portset->buf[port >> 5] &= ~((uint32_t)1 << (port & 31)); + } +} + +isc_result_t +isc_portset_create(isc_mem_t *mctx, isc_portset_t **portsetp) { + isc_portset_t *portset; + + REQUIRE(portsetp != NULL && *portsetp == NULL); + + portset = isc_mem_get(mctx, sizeof(*portset)); + if (portset == NULL) + return (ISC_R_NOMEMORY); + + /* Make the set 'empty' by default */ + memset(portset, 0, sizeof(*portset)); + *portsetp = portset; + + return (ISC_R_SUCCESS); +} + +void +isc_portset_destroy(isc_mem_t *mctx, isc_portset_t **portsetp) { + isc_portset_t *portset; + + REQUIRE(portsetp != NULL); + portset = *portsetp; + + isc_mem_put(mctx, portset, sizeof(*portset)); +} + +bool +isc_portset_isset(isc_portset_t *portset, in_port_t port) { + REQUIRE(portset != NULL); + + return (portset_isset(portset, port)); +} + +unsigned int +isc_portset_nports(isc_portset_t *portset) { + REQUIRE(portset != NULL); + + return (portset->nports); +} + +void +isc_portset_add(isc_portset_t *portset, in_port_t port) { + REQUIRE(portset != NULL); + + portset_add(portset, port); +} + +void +isc_portset_remove(isc_portset_t *portset, in_port_t port) { + portset_remove(portset, port); +} + +void +isc_portset_addrange(isc_portset_t *portset, in_port_t port_lo, + in_port_t port_hi) +{ + in_port_t p; + + REQUIRE(portset != NULL); + REQUIRE(port_lo <= port_hi); + + p = port_lo; + do { + portset_add(portset, p); + } while (p++ < port_hi); +} + +void +isc_portset_removerange(isc_portset_t *portset, in_port_t port_lo, + in_port_t port_hi) +{ + in_port_t p; + + REQUIRE(portset != NULL); + REQUIRE(port_lo <= port_hi); + + p = port_lo; + do { + portset_remove(portset, p); + } while (p++ < port_hi); +} diff --git a/lib/isc/powerpc/Makefile.in b/lib/isc/powerpc/Makefile.in new file mode 100644 index 0000000..419cf9f --- /dev/null +++ b/lib/isc/powerpc/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = include +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/powerpc/include/Makefile.in b/lib/isc/powerpc/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/powerpc/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/powerpc/include/isc/Makefile.in b/lib/isc/powerpc/include/isc/Makefile.in new file mode 100644 index 0000000..97b6b41 --- /dev/null +++ b/lib/isc/powerpc/include/isc/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = atomic.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/powerpc/include/isc/atomic.h b/lib/isc/powerpc/include/isc/atomic.h new file mode 100644 index 0000000..9899934 --- /dev/null +++ b/lib/isc/powerpc/include/isc/atomic.h @@ -0,0 +1,193 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_ATOMIC_H +#define ISC_ATOMIC_H 1 + +#include <inttypes.h> + +#include <isc/platform.h> +#include <isc/types.h> + +/*!\file + * static inline int32_t + * isc_atomic_xadd(int32_t *p, int32_t val); + * + * This routine atomically increments the value stored in 'p' by 'val', and + * returns the previous value. + * + * static inline void + * isc_atomic_store(void *p, int32_t val); + * + * This routine atomically stores the value 'val' in 'p'. + * + * static inline int32_t + * isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val); + * + * This routine atomically replaces the value in 'p' with 'val', if the + * original value is equal to 'cmpval'. The original value is returned in any + * case. + */ + +#if defined(_AIX) + +#include <sys/atomic_op.h> + +#define isc_atomic_store(p, v) _clear_lock(p, v) + +#ifdef __GNUC__ +static inline int32_t +#else +static int32_t +#endif +isc_atomic_xadd(int32_t *p, int32_t val) { + int ret; + +#ifdef __GNUC__ + asm("ics"); +#else + __isync(); +#endif + + ret = fetch_and_add((atomic_p)p, (int)val); + +#ifdef __GNUC__ + asm("ics"); +#else + __isync(); +#endif + + return (ret); +} + +#ifdef __GNUC__ +static inline int +#else +static int +#endif +isc_atomic_cmpxchg(atomic_p p, int old, int replacement) { + int orig = old; + +#ifdef __GNUC__ + asm("ics"); +#else + __isync(); +#endif + if (compare_and_swap(p, &orig, replacement)) + orig = old; + +#ifdef __GNUC__ + asm("ics"); +#else + __isync(); +#endif + + return (orig); +} + +#elif defined(ISC_PLATFORM_USEGCCASM) || defined(ISC_PLATFORM_USEMACASM) +static inline int32_t +isc_atomic_xadd(int32_t *p, int32_t val) { + int32_t orig; + + __asm__ volatile ( +#ifdef ISC_PLATFORM_USEMACASM + "1:" + "lwarx r6, 0, %1\n" + "mr %0, r6\n" + "add r6, r6, %2\n" + "stwcx. r6, 0, %1\n" + "bne- 1b\n" + "sync" +#else + "1:" + "lwarx 6, 0, %1\n" + "mr %0, 6\n" + "add 6, 6, %2\n" + "stwcx. 6, 0, %1\n" + "bne- 1b\n" + "sync" +#endif + : "=&r"(orig) + : "r"(p), "r"(val) + : "r6", "memory" + ); + + return (orig); +} + +static inline void +isc_atomic_store(void *p, int32_t val) { + __asm__ volatile ( +#ifdef ISC_PLATFORM_USEMACASM + "1:" + "lwarx r6, 0, %0\n" + "lwz r6, %1\n" + "stwcx. r6, 0, %0\n" + "bne- 1b\n" + "sync" +#else + "1:" + "lwarx 6, 0, %0\n" + "lwz 6, %1\n" + "stwcx. 6, 0, %0\n" + "bne- 1b\n" + "sync" +#endif + : + : "r"(p), "m"(val) + : "r6", "memory" + ); +} + +static inline int32_t +isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val) { + int32_t orig; + + __asm__ volatile ( +#ifdef ISC_PLATFORM_USEMACASM + "1:" + "lwarx r6, 0, %1\n" + "mr %0,r6\n" + "cmpw r6, %2\n" + "bne 2f\n" + "mr r6, %3\n" + "stwcx. r6, 0, %1\n" + "bne- 1b\n" + "2:\n" + "sync" +#else + "1:" + "lwarx 6, 0, %1\n" + "mr %0,6\n" + "cmpw 6, %2\n" + "bne 2f\n" + "mr 6, %3\n" + "stwcx. 6, 0, %1\n" + "bne- 1b\n" + "2:\n" + "sync" +#endif + : "=&r" (orig) + : "r"(p), "r"(cmpval), "r"(val) + : "r6", "memory" + ); + + return (orig); +} + +#else + +#error "unsupported compiler. disable atomic ops by --disable-atomic" + +#endif +#endif /* ISC_ATOMIC_H */ diff --git a/lib/isc/print.c b/lib/isc/print.c new file mode 100644 index 0000000..d4d4880 --- /dev/null +++ b/lib/isc/print.c @@ -0,0 +1,706 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <ctype.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdio.h> /* for sprintf() */ +#include <string.h> /* for strlen() */ +#include <assert.h> /* for assert() */ + +#define ISC__PRINT_SOURCE /* Used to get the isc_print_* prototypes. */ + +#include <isc/assertions.h> +#include <isc/msgs.h> +#include <isc/print.h> +#include <isc/stdlib.h> +#include <isc/util.h> + +/* + * We use the system's sprintf so we undef it here. + */ +#undef sprintf + +static int +isc__print_printf(void (*emit)(char, void *), void *arg, + const char *format, va_list ap); + +static void +file_emit(char c, void *arg) { + FILE *fp = arg; + int i = c & 0xff; + + putc(i, fp); +} + +#if 0 +static int +isc_print_vfprintf(FILE *fp, const char *format, va_list ap) { + assert(fp != NULL); + assert(format != NULL); + + return (isc__print_printf(file_emit, fp, format, ap)); +} +#endif + +int +isc_print_printf(const char *format, ...) { + va_list ap; + int n; + + assert(format != NULL); + + va_start(ap, format); + n = isc__print_printf(file_emit, stdout, format, ap); + va_end(ap); + return (n); +} + +int +isc_print_fprintf(FILE *fp, const char *format, ...) { + va_list ap; + int n; + + assert(fp != NULL); + assert(format != NULL); + + va_start(ap, format); + n = isc__print_printf(file_emit, fp, format, ap); + va_end(ap); + return (n); +} + +static void +nocheck_emit(char c, void *arg) { + struct { char *str; } *a = arg; + + *(a->str)++ = c; +} + +int +isc_print_sprintf(char *str, const char *format, ...) { + struct { char *str; } arg; + int n; + va_list ap; + + arg.str = str; + + va_start(ap, format); + n = isc__print_printf(nocheck_emit, &arg, format, ap); + va_end(ap); + return (n); +} + +/*! + * Return length of string that would have been written if not truncated. + */ + +int +isc_print_snprintf(char *str, size_t size, const char *format, ...) { + va_list ap; + int ret; + + va_start(ap, format); + ret = isc_print_vsnprintf(str, size, format, ap); + va_end(ap); + return (ret); + +} + +/*! + * Return length of string that would have been written if not truncated. + */ + +static void +string_emit(char c, void *arg) { + struct { char *str; size_t size; } *p = arg; + + if (p->size > 0U) { + *(p->str)++ = c; + p->size--; + } +} + +int +isc_print_vsnprintf(char *str, size_t size, const char *format, va_list ap) { + struct { char *str; size_t size; } arg; + int n; + + assert(str != NULL); + assert(format != NULL); + + arg.str = str; + arg.size = size; + + n = isc__print_printf(string_emit, &arg, format, ap); + if (arg.size > 0U) + *arg.str = '\0'; + return (n); +} + +static int +isc__print_printf(void (*emit)(char, void *), void *arg, + const char *format, va_list ap) +{ + int h; + int l; + int z; + int q; + int alt; + int zero; + int left; + int plus; + int space; + int neg; + int64_t tmpi; + uint64_t tmpui; + unsigned long width; + unsigned long precision; + unsigned int length; + char buf[1024]; + char c; + void *v; + const char *cp; + const char *head; + int count = 0; + int pad; + int zeropad; + int dot; + double dbl; + bool precision_set; +#ifdef HAVE_LONG_DOUBLE + long double ldbl; +#endif + char fmt[32]; + + assert(emit != NULL); + assert(arg != NULL); + assert(format != NULL); + + while (*format != '\0') { + if (*format != '%') { + emit(*format++, arg); + count++; + continue; + } + format++; + + /* + * Reset flags. + */ + dot = neg = space = plus = left = zero = alt = h = l = q = z = 0; + width = precision = 0; + head = ""; + pad = zeropad = 0; + precision_set = false; + + do { + if (*format == '#') { + alt = 1; + format++; + } else if (*format == '-') { + left = 1; + zero = 0; + format++; + } else if (*format == ' ') { + if (!plus) + space = 1; + format++; + } else if (*format == '+') { + plus = 1; + space = 0; + format++; + } else if (*format == '0') { + if (!left) + zero = 1; + format++; + } else + break; + } while (1); + + /* + * Width. + */ + if (*format == '*') { + width = va_arg(ap, int); + format++; + } else if (isdigit((unsigned char)*format)) { + char *e; + width = strtoul(format, &e, 10); + format = e; + } + + /* + * Precision. + */ + if (*format == '.') { + format++; + dot = 1; + if (*format == '*') { + precision = va_arg(ap, int); + precision_set = true; + format++; + } else if (isdigit((unsigned char)*format)) { + char *e; + precision = strtoul(format, &e, 10); + precision_set = true; + format = e; + } + } + + switch (*format) { + case '\0': + continue; + case '%': + emit(*format, arg); + count++; + break; + case 'q': + q = 1; + format++; + goto doint; + case 'h': + h = 1; + format++; + goto doint; + case 'l': + l = 1; + format++; + if (*format == 'l') { + q = 1; + format++; + } + goto doint; + case 'z': + z = 1; + format++; + goto doint; +#ifdef WIN32 + case 'I': + /* Windows has I64 as a modifier for a quad. */ + if (format[1] == '6' && format[2] == '4') { + q = 1; + format += 3; + goto doint; + } + continue; +#endif + case 'n': + case 'i': + case 'd': + case 'o': + case 'u': + case 'x': + case 'X': + doint: + if (precision != 0U) + zero = 0; + switch (*format) { + case 'n': + if (h) { + short int *p; + p = va_arg(ap, short *); + assert(p != NULL); + *p = count; + } else if (l) { + long int *p; + p = va_arg(ap, long *); + assert(p != NULL); + *p = count; + } else if (z) { + size_t *p; + p = va_arg(ap, size_t *); + assert(p != NULL); + *p = count; + } else { + int *p; + p = va_arg(ap, int *); + assert(p != NULL); + *p = count; + } + break; + case 'i': + case 'd': + if (q) + tmpi = va_arg(ap, int64_t); + else if (l) + tmpi = va_arg(ap, long int); + else if (z) + tmpi = va_arg(ap, ssize_t); + else + tmpi = va_arg(ap, int); + if (tmpi < 0) { + head = "-"; + tmpui = -tmpi; + } else { + if (plus) + head = "+"; + else if (space) + head = " "; + else + head = ""; + tmpui = tmpi; + } + if (tmpui <= 0xffffffffU) + sprintf(buf, "%lu", + (unsigned long)tmpui); + else { + unsigned long mid; + unsigned long lo; + unsigned long hi; + lo = tmpui % 1000000000; + tmpui /= 1000000000; + mid = tmpui % 1000000000; + hi = tmpui / 1000000000; + if (hi != 0U) { + sprintf(buf, "%lu", hi); + sprintf(buf + strlen(buf), + "%09lu", mid); + } else + sprintf(buf, "%lu", mid); + sprintf(buf + strlen(buf), "%09lu", + lo); + } + goto printint; + case 'o': + if (q) + tmpui = va_arg(ap, uint64_t); + else if (l) + tmpui = va_arg(ap, long int); + else if (z) + tmpui = va_arg(ap, size_t); + else + tmpui = va_arg(ap, int); + if (tmpui <= 0xffffffffU) + sprintf(buf, alt ? "%#lo" : "%lo", + (unsigned long)tmpui); + else { + unsigned long mid; + unsigned long lo; + unsigned long hi; + lo = tmpui % 010000000000; + tmpui /= 010000000000; + mid = tmpui % 010000000000; + hi = tmpui / 010000000000; + if (hi != 0U) { + sprintf(buf, + alt ? "%#lo" : "%lo", + hi); + sprintf(buf + strlen(buf), + "%09lo", mid); + } else + sprintf(buf, + alt ? "%#lo" : "%lo", + mid); + sprintf(buf + strlen(buf), "%09lo", lo); + } + goto printint; + case 'u': + if (q) + tmpui = va_arg(ap, uint64_t); + else if (l) + tmpui = va_arg(ap, unsigned long int); + else if (z) + tmpui = va_arg(ap, size_t); + else + tmpui = va_arg(ap, unsigned int); + if (tmpui <= 0xffffffffU) + sprintf(buf, "%lu", + (unsigned long)tmpui); + else { + unsigned long mid; + unsigned long lo; + unsigned long hi; + lo = tmpui % 1000000000; + tmpui /= 1000000000; + mid = tmpui % 1000000000; + hi = tmpui / 1000000000; + if (hi != 0U) { + sprintf(buf, "%lu", hi); + sprintf(buf + strlen(buf), + "%09lu", mid); + } else + sprintf(buf, "%lu", mid); + sprintf(buf + strlen(buf), "%09lu", + lo); + } + goto printint; + case 'x': + if (q) + tmpui = va_arg(ap, uint64_t); + else if (l) + tmpui = va_arg(ap, unsigned long int); + else if (z) + tmpui = va_arg(ap, size_t); + else + tmpui = va_arg(ap, unsigned int); + if (alt) { + head = "0x"; + if (precision > 2U) + precision -= 2; + } + if (tmpui <= 0xffffffffU) + sprintf(buf, "%lx", + (unsigned long)tmpui); + else { + unsigned long hi = tmpui>>32; + unsigned long lo = tmpui & 0xffffffff; + sprintf(buf, "%lx", hi); + sprintf(buf + strlen(buf), "%08lx", lo); + } + goto printint; + case 'X': + if (q) + tmpui = va_arg(ap, uint64_t); + else if (l) + tmpui = va_arg(ap, unsigned long int); + else if (z) + tmpui = va_arg(ap, size_t); + else + tmpui = va_arg(ap, unsigned int); + if (alt) { + head = "0X"; + if (precision > 2U) + precision -= 2; + } + if (tmpui <= 0xffffffffU) + sprintf(buf, "%lX", + (unsigned long)tmpui); + else { + unsigned long hi = tmpui>>32; + unsigned long lo = tmpui & 0xffffffff; + sprintf(buf, "%lX", hi); + sprintf(buf + strlen(buf), "%08lX", lo); + } + goto printint; + printint: + if (precision_set || width != 0U) { + length = strlen(buf); + if (length < precision) + zeropad = precision - length; + else if (length < width && zero) + zeropad = width - length; + if (width != 0U) { + pad = width - length - + zeropad - strlen(head); + if (pad < 0) + pad = 0; + } + } + count += strlen(head) + strlen(buf) + pad + + zeropad; + if (!left) { + while (pad > 0) { + emit(' ', arg); + pad--; + } + } + cp = head; + while (*cp != '\0') + emit(*cp++, arg); + while (zeropad > 0) { + emit('0', arg); + zeropad--; + } + cp = buf; + while (*cp != '\0') + emit(*cp++, arg); + while (pad > 0) { + emit(' ', arg); + pad--; + } + break; + default: + break; + } + break; + case 's': + cp = va_arg(ap, char *); + + if (precision_set) { + /* + * cp need not be NULL terminated. + */ + const char *tp; + unsigned long n; + + if (precision != 0U) + assert(cp != NULL); + n = precision; + tp = cp; + while (n != 0U && *tp != '\0') + n--, tp++; + length = precision - n; + } else { + assert(cp != NULL); + length = strlen(cp); + } + if (width != 0U) { + pad = width - length; + if (pad < 0) + pad = 0; + } + count += pad + length; + if (!left) + while (pad > 0) { + emit(' ', arg); + pad--; + } + if (precision_set) + while (precision > 0U && *cp != '\0') { + emit(*cp++, arg); + precision--; + } + else + while (*cp != '\0') + emit(*cp++, arg); + while (pad > 0) { + emit(' ', arg); + pad--; + } + break; + case 'c': + c = va_arg(ap, int); + if (width > 0U) { + count += width; + width--; + if (left) + emit(c, arg); + while (width-- > 0U) + emit(' ', arg); + if (!left) + emit(c, arg); + } else { + count++; + emit(c, arg); + } + break; + case 'p': + v = va_arg(ap, void *); + sprintf(buf, "%p", v); + length = strlen(buf); + if (precision > length) + zeropad = precision - length; + if (width > 0U) { + pad = width - length - zeropad; + if (pad < 0) + pad = 0; + } + count += length + pad + zeropad; + if (!left) + while (pad > 0) { + emit(' ', arg); + pad--; + } + cp = buf; + if (zeropad > 0 && buf[0] == '0' && + (buf[1] == 'x' || buf[1] == 'X')) { + emit(*cp++, arg); + emit(*cp++, arg); + while (zeropad > 0) { + emit('0', arg); + zeropad--; + } + } + while (*cp != '\0') + emit(*cp++, arg); + while (pad > 0) { + emit(' ', arg); + pad--; + } + break; + case 'D': /*deprecated*/ + assert("use %ld instead of %D" == NULL); + case 'O': /*deprecated*/ + assert("use %lo instead of %O" == NULL); + case 'U': /*deprecated*/ + assert("use %lu instead of %U" == NULL); + + case 'L': +#ifdef HAVE_LONG_DOUBLE + l = 1; +#else + assert("long doubles are not supported" == NULL); +#endif + /* FALLTHROUGH */ + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (!dot) + precision = 6; + /* + * IEEE floating point. + * MIN 2.2250738585072014E-308 + * MAX 1.7976931348623157E+308 + * VAX floating point has a smaller range than IEEE. + * + * precisions > 324 don't make much sense. + * if we cap the precision at 512 we will not + * overflow buf. + */ + if (precision > 512U) + precision = 512; + sprintf(fmt, "%%%s%s.%lu%s%c", alt ? "#" : "", + plus ? "+" : space ? " " : "", + precision, l ? "L" : "", *format); + switch (*format) { + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': +#ifdef HAVE_LONG_DOUBLE + if (l) { + ldbl = va_arg(ap, long double); + sprintf(buf, fmt, ldbl); + } else +#endif + { + dbl = va_arg(ap, double); + sprintf(buf, fmt, dbl); + } + length = strlen(buf); + if (width > 0U) { + pad = width - length; + if (pad < 0) + pad = 0; + } + count += length + pad; + if (!left) + while (pad > 0) { + emit(' ', arg); + pad--; + } + cp = buf; + while (*cp != '\0') + emit(*cp++, arg); + while (pad > 0) { + emit(' ', arg); + pad--; + } + break; + default: + continue; + } + break; + default: + continue; + } + format++; + } + return (count); +} diff --git a/lib/isc/pthreads/Makefile.in b/lib/isc/pthreads/Makefile.in new file mode 100644 index 0000000..af4fd6e --- /dev/null +++ b/lib/isc/pthreads/Makefile.in @@ -0,0 +1,30 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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}/include \ + -I${srcdir}/../unix/include \ + -I../include \ + -I${srcdir}/../include \ + -I${srcdir}/.. + +CDEFINES = +CWARNINGS = + +OBJS = condition.@O@ mutex.@O@ thread.@O@ + +SRCS = condition.c mutex.c thread.c + +SUBDIRS = include +TARGETS = ${OBJS} + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/pthreads/condition.c b/lib/isc/pthreads/condition.c new file mode 100644 index 0000000..a7f1928 --- /dev/null +++ b/lib/isc/pthreads/condition.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <errno.h> + +#include <isc/condition.h> +#include <isc/msgs.h> +#include <isc/strerror.h> +#include <isc/string.h> +#include <isc/time.h> +#include <isc/util.h> + +isc_result_t +isc_condition_waituntil(isc_condition_t *c, isc_mutex_t *m, isc_time_t *t) { + int presult; + isc_result_t result; + struct timespec ts; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(c != NULL && m != NULL && t != NULL); + + /* + * POSIX defines a timespec's tv_sec as time_t. + */ + result = isc_time_secondsastimet(t, &ts.tv_sec); + + /* + * If we have a range error ts.tv_sec is most probably a signed + * 32 bit value. Set ts.tv_sec to INT_MAX. This is a kludge. + */ + if (result == ISC_R_RANGE) + ts.tv_sec = INT_MAX; + else if (result != ISC_R_SUCCESS) + return (result); + + /*! + * POSIX defines a timespec's tv_nsec as long. isc_time_nanoseconds + * ensures its return value is < 1 billion, which will fit in a long. + */ + ts.tv_nsec = (long)isc_time_nanoseconds(t); + + do { +#if ISC_MUTEX_PROFILE + presult = pthread_cond_timedwait(c, &m->mutex, &ts); +#else + presult = pthread_cond_timedwait(c, m, &ts); +#endif + if (presult == 0) + return (ISC_R_SUCCESS); + if (presult == ETIMEDOUT) + return (ISC_R_TIMEDOUT); + } while (presult == EINTR); + + isc__strerror(presult, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "pthread_cond_timedwait() %s %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_RETURNED, "returned"), + strbuf); + return (ISC_R_UNEXPECTED); +} diff --git a/lib/isc/pthreads/include/Makefile.in b/lib/isc/pthreads/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/pthreads/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/pthreads/include/isc/Makefile.in b/lib/isc/pthreads/include/isc/Makefile.in new file mode 100644 index 0000000..185534f --- /dev/null +++ b/lib/isc/pthreads/include/isc/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = condition.h mutex.h once.h thread.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/pthreads/include/isc/condition.h b/lib/isc/pthreads/include/isc/condition.h new file mode 100644 index 0000000..28338c7 --- /dev/null +++ b/lib/isc/pthreads/include/isc/condition.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +#include <isc/lang.h> +#include <isc/mutex.h> +#include <isc/result.h> +#include <isc/types.h> + +typedef pthread_cond_t isc_condition_t; + +#define isc_condition_init(cp) \ + ((pthread_cond_init((cp), NULL) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +#if ISC_MUTEX_PROFILE +#define isc_condition_wait(cp, mp) \ + ((pthread_cond_wait((cp), &((mp)->mutex)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#else +#define isc_condition_wait(cp, mp) \ + ((pthread_cond_wait((cp), (mp)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#endif + +#define isc_condition_signal(cp) \ + ((pthread_cond_signal((cp)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +#define isc_condition_broadcast(cp) \ + ((pthread_cond_broadcast((cp)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +#define isc_condition_destroy(cp) \ + ((pthread_cond_destroy((cp)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +ISC_LANG_BEGINDECLS + +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/pthreads/include/isc/mutex.h b/lib/isc/pthreads/include/isc/mutex.h new file mode 100644 index 0000000..6c34a69 --- /dev/null +++ b/lib/isc/pthreads/include/isc/mutex.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +#include <pthread.h> +#include <stdio.h> + +#include <isc/lang.h> +#include <isc/result.h> /* for ISC_R_ codes */ + +ISC_LANG_BEGINDECLS + +/*! + * Supply mutex attributes that enable deadlock detection + * (helpful when debugging). This is system dependent and + * currently only supported on NetBSD. + */ +#if ISC_MUTEX_DEBUG && defined(__NetBSD__) && defined(PTHREAD_MUTEX_ERRORCHECK) +extern pthread_mutexattr_t isc__mutex_attrs; +#define ISC__MUTEX_ATTRS &isc__mutex_attrs +#else +#define ISC__MUTEX_ATTRS NULL +#endif + +/* XXX We could do fancier error handling... */ + +/*! + * Define ISC_MUTEX_PROFILE to turn on profiling of mutexes by line. When + * enabled, isc_mutex_stats() can be used to print a table showing the + * number of times each type of mutex was locked and the amount of time + * waiting to obtain the lock. + */ +#ifndef ISC_MUTEX_PROFILE +#define ISC_MUTEX_PROFILE 0 +#endif + +#if ISC_MUTEX_PROFILE +typedef struct isc_mutexstats isc_mutexstats_t; + +typedef struct { + pthread_mutex_t mutex; /*%< The actual mutex. */ + isc_mutexstats_t * stats; /*%< Mutex statistics. */ +} isc_mutex_t; +#else +typedef pthread_mutex_t isc_mutex_t; +#endif + + +#if ISC_MUTEX_PROFILE +#define isc_mutex_init(mp) \ + isc_mutex_init_profile((mp), __FILE__, __LINE__) +#else +#if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) +#define isc_mutex_init(mp) \ + isc_mutex_init_errcheck((mp)) +#else +#define isc_mutex_init(mp) \ + isc__mutex_init((mp), __FILE__, __LINE__) +isc_result_t isc__mutex_init(isc_mutex_t *mp, const char *file, unsigned int line); +#endif +#endif + +#if ISC_MUTEX_PROFILE +#define isc_mutex_lock(mp) \ + isc_mutex_lock_profile((mp), __FILE__, __LINE__) +#else +#define isc_mutex_lock(mp) \ + ((pthread_mutex_lock((mp)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#endif + +#if ISC_MUTEX_PROFILE +#define isc_mutex_unlock(mp) \ + isc_mutex_unlock_profile((mp), __FILE__, __LINE__) +#else +#define isc_mutex_unlock(mp) \ + ((pthread_mutex_unlock((mp)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#endif + +#if ISC_MUTEX_PROFILE +#define isc_mutex_trylock(mp) \ + ((pthread_mutex_trylock((&(mp)->mutex)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_LOCKBUSY) +#else +#define isc_mutex_trylock(mp) \ + ((pthread_mutex_trylock((mp)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_LOCKBUSY) +#endif + +#if ISC_MUTEX_PROFILE +#define isc_mutex_destroy(mp) \ + ((pthread_mutex_destroy((&(mp)->mutex)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#else +#define isc_mutex_destroy(mp) \ + ((pthread_mutex_destroy((mp)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) +#endif + +#if ISC_MUTEX_PROFILE +#define isc_mutex_stats(fp) isc_mutex_statsprofile(fp); +#else +#define isc_mutex_stats(fp) +#endif + +#if ISC_MUTEX_PROFILE + +isc_result_t +isc_mutex_init_profile(isc_mutex_t *mp, const char * _file, int _line); +isc_result_t +isc_mutex_lock_profile(isc_mutex_t *mp, const char * _file, int _line); +isc_result_t +isc_mutex_unlock_profile(isc_mutex_t *mp, const char * _file, int _line); + +void +isc_mutex_statsprofile(FILE *fp); + +isc_result_t +isc_mutex_init_errcheck(isc_mutex_t *mp); + +#endif /* ISC_MUTEX_PROFILE */ + +ISC_LANG_ENDDECLS +#endif /* ISC_MUTEX_H */ diff --git a/lib/isc/pthreads/include/isc/once.h b/lib/isc/pthreads/include/isc/once.h new file mode 100644 index 0000000..a0e9559 --- /dev/null +++ b/lib/isc/pthreads/include/isc/once.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +#include <pthread.h> + +#include <isc/platform.h> +#include <isc/result.h> + +typedef pthread_once_t isc_once_t; + +#ifdef ISC_PLATFORM_BRACEPTHREADONCEINIT +/*! + * This accomodates systems that define PTHRAD_ONCE_INIT improperly. + */ +#define ISC_ONCE_INIT { PTHREAD_ONCE_INIT } +#else +/*! + * This is the usual case. + */ +#define ISC_ONCE_INIT PTHREAD_ONCE_INIT +#endif + +/* XXX We could do fancier error handling... */ + +#define isc_once_do(op, f) \ + ((pthread_once((op), (f)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +#endif /* ISC_ONCE_H */ diff --git a/lib/isc/pthreads/include/isc/thread.h b/lib/isc/pthreads/include/isc/thread.h new file mode 100644 index 0000000..798af33 --- /dev/null +++ b/lib/isc/pthreads/include/isc/thread.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +#include <pthread.h> + +#if defined(HAVE_PTHREAD_NP_H) +#include <pthread_np.h> +#endif + +#include <isc/lang.h> +#include <isc/result.h> + +ISC_LANG_BEGINDECLS + +typedef pthread_t isc_thread_t; +typedef void * isc_threadresult_t; +typedef void * isc_threadarg_t; +typedef isc_threadresult_t (*isc_threadfunc_t)(isc_threadarg_t); +typedef pthread_key_t isc_thread_key_t; + +isc_result_t +isc_thread_create(isc_threadfunc_t, isc_threadarg_t, isc_thread_t *); + +void +isc_thread_setconcurrency(unsigned int level); + +void +isc_thread_yield(void); + +void +isc_thread_setname(isc_thread_t thread, const char *name); + +/* XXX We could do fancier error handling... */ + +#define isc_thread_join(t, rp) \ + ((pthread_join((t), (rp)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED) + +#define isc_thread_self \ + (unsigned long)pthread_self + +#define isc_thread_key_create pthread_key_create +#define isc_thread_key_getspecific pthread_getspecific +#define isc_thread_key_setspecific pthread_setspecific +#define isc_thread_key_delete pthread_key_delete + +ISC_LANG_ENDDECLS + +#endif /* ISC_THREAD_H */ diff --git a/lib/isc/pthreads/mutex.c b/lib/isc/pthreads/mutex.c new file mode 100644 index 0000000..cd45e6b --- /dev/null +++ b/lib/isc/pthreads/mutex.c @@ -0,0 +1,302 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> +#include <stdio.h> +#include <time.h> +#include <sys/time.h> +#include <errno.h> + +#include <isc/mutex.h> +#include <isc/util.h> +#include <isc/print.h> +#include <isc/strerror.h> +#include <isc/once.h> + +#if ISC_MUTEX_PROFILE + +/*@{*/ +/*% Operations on timevals; adapted from FreeBSD's sys/time.h */ +#define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) +#define timevaladd(vvp, uvp) \ + do { \ + (vvp)->tv_sec += (uvp)->tv_sec; \ + (vvp)->tv_usec += (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + } while (0) +#define timevalsub(vvp, uvp) \ + do { \ + (vvp)->tv_sec -= (uvp)->tv_sec; \ + (vvp)->tv_usec -= (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + } while (0) + +/*@}*/ + +#define ISC_MUTEX_MAX_LOCKERS 32 + +typedef struct { + const char * file; + int line; + unsigned count; + struct timeval locked_total; + struct timeval wait_total; +} isc_mutexlocker_t; + +struct isc_mutexstats { + const char * file; /*%< File mutex was created in. */ + int line; /*%< Line mutex was created on. */ + unsigned count; + struct timeval lock_t; + struct timeval locked_total; + struct timeval wait_total; + isc_mutexlocker_t * cur_locker; + isc_mutexlocker_t lockers[ISC_MUTEX_MAX_LOCKERS]; +}; + +#ifndef ISC_MUTEX_PROFTABLESIZE +#define ISC_MUTEX_PROFTABLESIZE (1024 * 1024) +#endif +static isc_mutexstats_t stats[ISC_MUTEX_PROFTABLESIZE]; +static int stats_next = 0; +static bool stats_init = false; +static pthread_mutex_t statslock = PTHREAD_MUTEX_INITIALIZER; + + +isc_result_t +isc_mutex_init_profile(isc_mutex_t *mp, const char *file, int line) { + int i, err; + + err = pthread_mutex_init(&mp->mutex, NULL); + if (err == ENOMEM) + return (ISC_R_NOMEMORY); + if (err != 0) + return (ISC_R_UNEXPECTED); + + RUNTIME_CHECK(pthread_mutex_lock(&statslock) == 0); + + if (stats_init == false) + stats_init = true; + + /* + * If all statistics entries have been used, give up and trigger an + * assertion failure. There would be no other way to deal with this + * because we'd like to keep record of all locks for the purpose of + * debugging and the number of necessary locks is unpredictable. + * If this failure is triggered while debugging, named should be + * rebuilt with an increased ISC_MUTEX_PROFTABLESIZE. + */ + RUNTIME_CHECK(stats_next < ISC_MUTEX_PROFTABLESIZE); + mp->stats = &stats[stats_next++]; + + RUNTIME_CHECK(pthread_mutex_unlock(&statslock) == 0); + + mp->stats->file = file; + mp->stats->line = line; + mp->stats->count = 0; + timevalclear(&mp->stats->locked_total); + timevalclear(&mp->stats->wait_total); + for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { + mp->stats->lockers[i].file = NULL; + mp->stats->lockers[i].line = 0; + mp->stats->lockers[i].count = 0; + timevalclear(&mp->stats->lockers[i].locked_total); + timevalclear(&mp->stats->lockers[i].wait_total); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_mutex_lock_profile(isc_mutex_t *mp, const char *file, int line) { + struct timeval prelock_t; + struct timeval postlock_t; + isc_mutexlocker_t *locker = NULL; + int i; + + gettimeofday(&prelock_t, NULL); + + if (pthread_mutex_lock(&mp->mutex) != 0) + return (ISC_R_UNEXPECTED); + + gettimeofday(&postlock_t, NULL); + mp->stats->lock_t = postlock_t; + + timevalsub(&postlock_t, &prelock_t); + + mp->stats->count++; + timevaladd(&mp->stats->wait_total, &postlock_t); + + for (i = 0; i < ISC_MUTEX_MAX_LOCKERS; i++) { + if (mp->stats->lockers[i].file == NULL) { + locker = &mp->stats->lockers[i]; + locker->file = file; + locker->line = line; + break; + } else if (mp->stats->lockers[i].file == file && + mp->stats->lockers[i].line == line) { + locker = &mp->stats->lockers[i]; + break; + } + } + + if (locker != NULL) { + locker->count++; + timevaladd(&locker->wait_total, &postlock_t); + } + + mp->stats->cur_locker = locker; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_mutex_unlock_profile(isc_mutex_t *mp, const char *file, int line) { + struct timeval unlock_t; + + UNUSED(file); + UNUSED(line); + + if (mp->stats->cur_locker != NULL) { + gettimeofday(&unlock_t, NULL); + timevalsub(&unlock_t, &mp->stats->lock_t); + timevaladd(&mp->stats->locked_total, &unlock_t); + timevaladd(&mp->stats->cur_locker->locked_total, &unlock_t); + mp->stats->cur_locker = NULL; + } + + return ((pthread_mutex_unlock((&mp->mutex)) == 0) ? \ + ISC_R_SUCCESS : ISC_R_UNEXPECTED); +} + + +void +isc_mutex_statsprofile(FILE *fp) { + isc_mutexlocker_t *locker; + int i, j; + + fprintf(fp, "Mutex stats (in us)\n"); + for (i = 0; i < stats_next; i++) { + fprintf(fp, "%-12s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", + stats[i].file, stats[i].line, stats[i].count, + stats[i].locked_total.tv_sec, + stats[i].locked_total.tv_usec, + stats[i].wait_total.tv_sec, + stats[i].wait_total.tv_usec, + i); + for (j = 0; j < ISC_MUTEX_MAX_LOCKERS; j++) { + locker = &stats[i].lockers[j]; + if (locker->file == NULL) + continue; + fprintf(fp, " %-11s %4d: %10u %lu.%06lu %lu.%06lu %5d\n", + locker->file, locker->line, locker->count, + locker->locked_total.tv_sec, + locker->locked_total.tv_usec, + locker->wait_total.tv_sec, + locker->wait_total.tv_usec, + i); + } + } +} + +#endif /* ISC_MUTEX_PROFILE */ + +#if ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK) + +static bool errcheck_initialized = false; +static pthread_mutexattr_t errcheck; +static isc_once_t once_errcheck = ISC_ONCE_INIT; + +static void +initialize_errcheck(void) { + RUNTIME_CHECK(pthread_mutexattr_init(&errcheck) == 0); + RUNTIME_CHECK(pthread_mutexattr_settype + (&errcheck, PTHREAD_MUTEX_ERRORCHECK) == 0); + errcheck_initialized = true; +} + +isc_result_t +isc_mutex_init_errcheck(isc_mutex_t *mp) { + isc_result_t result; + int err; + + result = isc_once_do(&once_errcheck, initialize_errcheck); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + err = pthread_mutex_init(mp, &errcheck); + if (err == ENOMEM) + return (ISC_R_NOMEMORY); + return ((err == 0) ? ISC_R_SUCCESS : ISC_R_UNEXPECTED); +} +#endif + +#if ISC_MUTEX_DEBUG && defined(__NetBSD__) && defined(PTHREAD_MUTEX_ERRORCHECK) +pthread_mutexattr_t isc__mutex_attrs = { + PTHREAD_MUTEX_ERRORCHECK, /* m_type */ + 0 /* m_flags, which appears to be unused. */ +}; +#endif + +#if !(ISC_MUTEX_DEBUG && defined(PTHREAD_MUTEX_ERRORCHECK)) && !ISC_MUTEX_PROFILE + +#ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP +static bool attr_initialized = false; +static pthread_mutexattr_t attr; +static isc_once_t once_attr = ISC_ONCE_INIT; +#endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */ + +#ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP +static void +initialize_attr(void) { + RUNTIME_CHECK(pthread_mutexattr_init(&attr) == 0); + RUNTIME_CHECK(pthread_mutexattr_settype + (&attr, PTHREAD_MUTEX_ADAPTIVE_NP) == 0); + attr_initialized = true; +} +#endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */ + +isc_result_t +isc__mutex_init(isc_mutex_t *mp, const char *file, unsigned int line) { + char strbuf[ISC_STRERRORSIZE]; + isc_result_t result = ISC_R_SUCCESS; + int err; + +#ifdef HAVE_PTHREAD_MUTEX_ADAPTIVE_NP + result = isc_once_do(&once_attr, initialize_attr); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + err = pthread_mutex_init(mp, &attr); +#else /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */ + err = pthread_mutex_init(mp, ISC__MUTEX_ATTRS); +#endif /* HAVE_PTHREAD_MUTEX_ADAPTIVE_NP */ + + if (err == ENOMEM) + return (ISC_R_NOMEMORY); + if (err != 0) { + isc__strerror(err, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(file, line, "isc_mutex_init() failed: %s", + strbuf); + result = ISC_R_UNEXPECTED; + } + return (result); +} +#endif diff --git a/lib/isc/pthreads/thread.c b/lib/isc/pthreads/thread.c new file mode 100644 index 0000000..94fb2c2 --- /dev/null +++ b/lib/isc/pthreads/thread.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#if defined(HAVE_SCHED_H) +#include <sched.h> +#endif + +#include <isc/thread.h> +#include <isc/util.h> + +#ifndef THREAD_MINSTACKSIZE +#define THREAD_MINSTACKSIZE (1024U * 1024) +#endif + +isc_result_t +isc_thread_create(isc_threadfunc_t func, isc_threadarg_t arg, + isc_thread_t *thread) +{ + pthread_attr_t attr; +#if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \ + defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) + size_t stacksize; +#endif + int ret; + + pthread_attr_init(&attr); + +#if defined(HAVE_PTHREAD_ATTR_GETSTACKSIZE) && \ + defined(HAVE_PTHREAD_ATTR_SETSTACKSIZE) + ret = pthread_attr_getstacksize(&attr, &stacksize); + if (ret != 0) + return (ISC_R_UNEXPECTED); + + if (stacksize < THREAD_MINSTACKSIZE) { + ret = pthread_attr_setstacksize(&attr, THREAD_MINSTACKSIZE); + if (ret != 0) + return (ISC_R_UNEXPECTED); + } +#endif + +#if defined(PTHREAD_SCOPE_SYSTEM) && defined(NEED_PTHREAD_SCOPE_SYSTEM) + ret = pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + if (ret != 0) + return (ISC_R_UNEXPECTED); +#endif + + ret = pthread_create(thread, &attr, func, arg); + if (ret != 0) + return (ISC_R_UNEXPECTED); + + pthread_attr_destroy(&attr); + + return (ISC_R_SUCCESS); +} + +void +isc_thread_setconcurrency(unsigned int level) { +#if defined(CALL_PTHREAD_SETCONCURRENCY) + (void)pthread_setconcurrency(level); +#else + UNUSED(level); +#endif +} + +void +isc_thread_setname(isc_thread_t thread, const char *name) { +#if defined(HAVE_PTHREAD_SETNAME_NP) && defined(_GNU_SOURCE) + /* + * macOS has pthread_setname_np but only works on the + * current thread so it's not used here + */ + (void)pthread_setname_np(thread, name); +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + (void)pthread_set_name_np(thread, name); +#else + UNUSED(thread); + UNUSED(name); +#endif +} + +void +isc_thread_yield(void) { +#if defined(HAVE_SCHED_YIELD) + sched_yield(); +#elif defined( HAVE_PTHREAD_YIELD) + pthread_yield(); +#elif defined( HAVE_PTHREAD_YIELD_NP) + pthread_yield_np(); +#endif +} diff --git a/lib/isc/quota.c b/lib/isc/quota.c new file mode 100644 index 0000000..3ddff0d --- /dev/null +++ b/lib/isc/quota.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stddef.h> + +#include <isc/quota.h> +#include <isc/util.h> + +isc_result_t +isc_quota_init(isc_quota_t *quota, int max) { + quota->max = max; + quota->used = 0; + quota->soft = 0; + return (isc_mutex_init("a->lock)); +} + +void +isc_quota_destroy(isc_quota_t *quota) { + INSIST(quota->used == 0); + quota->max = 0; + quota->used = 0; + quota->soft = 0; + DESTROYLOCK("a->lock); +} + +void +isc_quota_soft(isc_quota_t *quota, int soft) { + LOCK("a->lock); + quota->soft = soft; + UNLOCK("a->lock); +} + +void +isc_quota_max(isc_quota_t *quota, int max) { + LOCK("a->lock); + quota->max = max; + UNLOCK("a->lock); +} + +isc_result_t +isc_quota_reserve(isc_quota_t *quota) { + isc_result_t result; + LOCK("a->lock); + if (quota->max == 0 || quota->used < quota->max) { + if (quota->soft == 0 || quota->used < quota->soft) + result = ISC_R_SUCCESS; + else + result = ISC_R_SOFTQUOTA; + quota->used++; + } else + result = ISC_R_QUOTA; + UNLOCK("a->lock); + return (result); +} + +void +isc_quota_release(isc_quota_t *quota) { + LOCK("a->lock); + INSIST(quota->used > 0); + quota->used--; + UNLOCK("a->lock); +} + +isc_result_t +isc_quota_attach(isc_quota_t *quota, isc_quota_t **p) +{ + isc_result_t result; + INSIST(p != NULL && *p == NULL); + result = isc_quota_reserve(quota); + if (result == ISC_R_SUCCESS || result == ISC_R_SOFTQUOTA) + *p = quota; + return (result); +} + +void +isc_quota_detach(isc_quota_t **p) +{ + INSIST(p != NULL && *p != NULL); + isc_quota_release(*p); + *p = NULL; +} diff --git a/lib/isc/radix.c b/lib/isc/radix.c new file mode 100644 index 0000000..6299dfe --- /dev/null +++ b/lib/isc/radix.c @@ -0,0 +1,728 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/* + * This source was adapted from MRT's RCS Ids: + * Id: radix.c,v 1.10.2.1 1999/11/29 05:16:24 masaki Exp + * Id: prefix.c,v 1.37.2.9 2000/03/10 02:53:19 labovit Exp + */ + +#include <config.h> + +#include <inttypes.h> + +#include <isc/mem.h> +#include <isc/types.h> +#include <isc/util.h> +#include <isc/radix.h> + +static isc_result_t +_new_prefix(isc_mem_t *mctx, isc_prefix_t **target, int family, + void *dest, int bitlen); + +static void +_deref_prefix(isc_prefix_t *prefix); + +static isc_result_t +_ref_prefix(isc_mem_t *mctx, isc_prefix_t **target, isc_prefix_t *prefix); + +static int +_comp_with_mask(void *addr, void *dest, u_int mask); + +static void +_clear_radix(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func); + +static isc_result_t +_new_prefix(isc_mem_t *mctx, isc_prefix_t **target, int family, void *dest, + int bitlen) +{ + isc_prefix_t *prefix; + + REQUIRE(target != NULL); + + if (family != AF_INET6 && family != AF_INET && family != AF_UNSPEC) + return (ISC_R_NOTIMPLEMENTED); + + prefix = isc_mem_get(mctx, sizeof(isc_prefix_t)); + if (prefix == NULL) + return (ISC_R_NOMEMORY); + + if (family == AF_INET6) { + prefix->bitlen = (bitlen >= 0) ? bitlen : 128; + memmove(&prefix->add.sin6, dest, 16); + } else { + /* AF_UNSPEC is "any" or "none"--treat it as AF_INET */ + prefix->bitlen = (bitlen >= 0) ? bitlen : 32; + memmove(&prefix->add.sin, dest, 4); + } + + prefix->family = family; + prefix->ecs = false; + prefix->mctx = NULL; + isc_mem_attach(mctx, &prefix->mctx); + + isc_refcount_init(&prefix->refcount, 1); + + *target = prefix; + return (ISC_R_SUCCESS); +} + +static void +_deref_prefix(isc_prefix_t *prefix) { + int refs; + + if (prefix == NULL) + return; + + isc_refcount_decrement(&prefix->refcount, &refs); + + if (refs <= 0) { + isc_refcount_destroy(&prefix->refcount); + isc_mem_putanddetach(&prefix->mctx, prefix, + sizeof(isc_prefix_t)); + } +} + +static isc_result_t +_ref_prefix(isc_mem_t *mctx, isc_prefix_t **target, isc_prefix_t *prefix) { + INSIST(prefix != NULL); + INSIST((prefix->family == AF_INET && prefix->bitlen <= 32) || + (prefix->family == AF_INET6 && prefix->bitlen <= 128) || + (prefix->family == AF_UNSPEC && prefix->bitlen == 0)); + REQUIRE(target != NULL && *target == NULL); + + /* + * If this prefix is a static allocation, copy it into new memory. + * (Note, the refcount still has to be destroyed by the calling + * routine.) + */ + if (isc_refcount_current(&prefix->refcount) == 0) { + isc_result_t ret; + ret = _new_prefix(mctx, target, prefix->family, + &prefix->add, prefix->bitlen); + return (ret); + } + + isc_refcount_increment(&prefix->refcount, NULL); + + *target = prefix; + return (ISC_R_SUCCESS); +} + +static int +_comp_with_mask(void *addr, void *dest, u_int mask) { + + /* Mask length of zero matches everything */ + if (mask == 0) + return (1); + + if (memcmp(addr, dest, mask / 8) == 0) { + u_int n = mask / 8; + u_int m = ((~0U) << (8 - (mask % 8))); + + if ((mask % 8) == 0 || + (((u_char *)addr)[n] & m) == (((u_char *)dest)[n] & m)) + return (1); + } + return (0); +} + +isc_result_t +isc_radix_create(isc_mem_t *mctx, isc_radix_tree_t **target, int maxbits) { + isc_radix_tree_t *radix; + + REQUIRE(target != NULL && *target == NULL); + + radix = isc_mem_get(mctx, sizeof(isc_radix_tree_t)); + if (radix == NULL) + return (ISC_R_NOMEMORY); + + radix->mctx = NULL; + isc_mem_attach(mctx, &radix->mctx); + radix->maxbits = maxbits; + radix->head = NULL; + radix->num_active_node = 0; + radix->num_added_node = 0; + RUNTIME_CHECK(maxbits <= RADIX_MAXBITS); /* XXX */ + radix->magic = RADIX_TREE_MAGIC; + *target = radix; + return (ISC_R_SUCCESS); +} + +/* + * if func is supplied, it will be called as func(node->data) + * before deleting the node + */ + +static void +_clear_radix(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) { + REQUIRE(radix != NULL); + + if (radix->head != NULL) { + isc_radix_node_t *Xstack[RADIX_MAXBITS+1]; + isc_radix_node_t **Xsp = Xstack; + isc_radix_node_t *Xrn = radix->head; + + while (Xrn != NULL) { + isc_radix_node_t *l = Xrn->l; + isc_radix_node_t *r = Xrn->r; + + if (Xrn->prefix != NULL) { + _deref_prefix(Xrn->prefix); + if (func != NULL) + func(Xrn->data); + } else { + INSIST(Xrn->data[RADIX_V4] == NULL && + Xrn->data[RADIX_V6] == NULL && + Xrn->data[RADIX_V4_ECS] == NULL && + Xrn->data[RADIX_V6_ECS] == NULL); + } + + isc_mem_put(radix->mctx, Xrn, sizeof(*Xrn)); + radix->num_active_node--; + + if (l != NULL) { + if (r != NULL) { + *Xsp++ = r; + } + Xrn = l; + } else if (r != NULL) { + Xrn = r; + } else if (Xsp != Xstack) { + Xrn = *(--Xsp); + } else { + Xrn = NULL; + } + } + } + RUNTIME_CHECK(radix->num_active_node == 0); +} + + +void +isc_radix_destroy(isc_radix_tree_t *radix, isc_radix_destroyfunc_t func) { + REQUIRE(radix != NULL); + _clear_radix(radix, func); + isc_mem_putanddetach(&radix->mctx, radix, sizeof(*radix)); +} + + +/* + * func will be called as func(node->prefix, node->data) + */ +void +isc_radix_process(isc_radix_tree_t *radix, isc_radix_processfunc_t func) { + isc_radix_node_t *node; + + REQUIRE(func != NULL); + + RADIX_WALK(radix->head, node) { + func(node->prefix, node->data); + } RADIX_WALK_END; +} + + +isc_result_t +isc_radix_search(isc_radix_tree_t *radix, isc_radix_node_t **target, + isc_prefix_t *prefix) +{ + isc_radix_node_t *node; + isc_radix_node_t *stack[RADIX_MAXBITS + 1]; + u_char *addr; + uint32_t bitlen; + int tfam = -1, cnt = 0; + + REQUIRE(radix != NULL); + REQUIRE(prefix != NULL); + REQUIRE(target != NULL && *target == NULL); + RUNTIME_CHECK(prefix->bitlen <= radix->maxbits); + + *target = NULL; + + if (radix->head == NULL) { + return (ISC_R_NOTFOUND); + } + + node = radix->head; + addr = isc_prefix_touchar(prefix); + bitlen = prefix->bitlen; + + while (node->bit < bitlen) { + if (node->prefix) + stack[cnt++] = node; + + if (BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) + node = node->r; + else + node = node->l; + + if (node == NULL) + break; + } + + if (node && node->prefix) + stack[cnt++] = node; + + while (cnt-- > 0) { + node = stack[cnt]; + + if (prefix->bitlen < node->bit) + continue; + + if (_comp_with_mask(isc_prefix_tochar(node->prefix), + isc_prefix_tochar(prefix), + node->prefix->bitlen)) + { + int fam = ISC_RADIX_FAMILY(prefix); + if (node->node_num[fam] != -1 && + ((*target == NULL) || + (*target)->node_num[tfam] > node->node_num[fam])) + { + *target = node; + tfam = fam; + } + } + } + + if (*target == NULL) { + return (ISC_R_NOTFOUND); + } else { + return (ISC_R_SUCCESS); + } +} + +isc_result_t +isc_radix_insert(isc_radix_tree_t *radix, isc_radix_node_t **target, + isc_radix_node_t *source, isc_prefix_t *prefix) +{ + isc_radix_node_t *node, *new_node, *parent, *glue = NULL; + u_char *addr, *test_addr; + uint32_t bitlen, fam, check_bit, differ_bit; + uint32_t i, j, r; + isc_result_t result; + + REQUIRE(radix != NULL); + REQUIRE(target != NULL && *target == NULL); + REQUIRE(prefix != NULL || (source != NULL && source->prefix != NULL)); + RUNTIME_CHECK(prefix == NULL || prefix->bitlen <= radix->maxbits); + + if (prefix == NULL) + prefix = source->prefix; + + INSIST(prefix != NULL); + + bitlen = prefix->bitlen; + fam = prefix->family; + + if (radix->head == NULL) { + node = isc_mem_get(radix->mctx, sizeof(isc_radix_node_t)); + if (node == NULL) + return (ISC_R_NOMEMORY); + node->bit = bitlen; + for (i = 0; i < RADIX_FAMILIES; i++) { + node->node_num[i] = -1; + } + node->prefix = NULL; + result = _ref_prefix(radix->mctx, &node->prefix, prefix); + if (result != ISC_R_SUCCESS) { + isc_mem_put(radix->mctx, node, + sizeof(isc_radix_node_t)); + return (result); + } + node->parent = NULL; + node->l = node->r = NULL; + if (source != NULL) { + /* + * If source is non-NULL, then we're merging in a + * node from an existing radix tree. To keep + * the node_num values consistent, the calling + * function will add the total number of nodes + * added to num_added_node at the end of + * the merge operation--we don't do it here. + */ + for (i = 0; i < RADIX_FAMILIES; i++) { + if (source->node_num[i] != -1) { + node->node_num[i] = + radix->num_added_node + + source->node_num[i]; + } + node->data[i] = source->data[i]; + } + } else { + int next = ++radix->num_added_node; + if (fam == AF_UNSPEC) { + /* "any" or "none" */ + for (i = 0; i < RADIX_FAMILIES; i++) { + node->node_num[i] = next; + } + } else { + node->node_num[ISC_RADIX_FAMILY(prefix)] = next; + } + + memset(node->data, 0, sizeof(node->data)); + } + radix->head = node; + radix->num_active_node++; + *target = node; + return (ISC_R_SUCCESS); + } + + addr = isc_prefix_touchar(prefix); + node = radix->head; + + while (node->bit < bitlen || node->prefix == NULL) { + if (node->bit < radix->maxbits && + BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) + { + if (node->r == NULL) + break; + node = node->r; + } else { + if (node->l == NULL) + break; + node = node->l; + } + + INSIST(node != NULL); + } + + INSIST(node->prefix != NULL); + + test_addr = isc_prefix_touchar(node->prefix); + /* Find the first bit different. */ + check_bit = (node->bit < bitlen) ? node->bit : bitlen; + differ_bit = 0; + for (i = 0; i * 8 < check_bit; i++) { + if ((r = (addr[i] ^ test_addr[i])) == 0) { + differ_bit = (i + 1) * 8; + continue; + } + /* I know the better way, but for now. */ + for (j = 0; j < 8; j++) { + if (BIT_TEST(r, (0x80 >> j))) + break; + } + /* Must be found. */ + INSIST(j < 8); + differ_bit = i * 8 + j; + break; + } + + if (differ_bit > check_bit) + differ_bit = check_bit; + + parent = node->parent; + while (parent != NULL && parent->bit >= differ_bit) { + node = parent; + parent = node->parent; + } + + if (differ_bit == bitlen && node->bit == bitlen) { + if (node->prefix != NULL) { + /* Set node_num only if it hasn't been set before */ + if (source != NULL) { + /* Merging nodes */ + for (i = 0; i < RADIX_FAMILIES; i++) { + if (node->node_num[i] == -1 && + source->node_num[i] != -1) { + node->node_num[i] = + radix->num_added_node + + source->node_num[i]; + node->data[i] = source->data[i]; + } + } + } else { + if (fam == AF_UNSPEC) { + /* "any" or "none" */ + int next = radix->num_added_node + 1; + for (i = 0; i < RADIX_FAMILIES; i++) { + if (node->node_num[i] == -1) { + node->node_num[i] = + next; + radix->num_added_node = + next; + } + } + } else { + int foff = ISC_RADIX_FAMILY(prefix); + if (node->node_num[foff] == -1) { + node->node_num[foff] = + ++radix->num_added_node; + } + } + } + *target = node; + return (ISC_R_SUCCESS); + } else { + result = _ref_prefix(radix->mctx, + &node->prefix, prefix); + if (result != ISC_R_SUCCESS) + return (result); + } + + /* Check everything is null and empty before we proceed */ + INSIST(node->data[RADIX_V4] == NULL && + node->node_num[RADIX_V4] == -1 && + node->data[RADIX_V6] == NULL && + node->node_num[RADIX_V6] == -1 && + node->data[RADIX_V4_ECS] == NULL && + node->node_num[RADIX_V4_ECS] == -1 && + node->data[RADIX_V6_ECS] == NULL && + node->node_num[RADIX_V6_ECS] == -1); + + if (source != NULL) { + /* Merging node */ + for (i = 0; i < RADIX_FAMILIES; i++) { + int cur = radix->num_added_node; + if (source->node_num[i] != -1) { + node->node_num[i] = + source->node_num[i] + cur; + node->data[i] = source->data[i]; + } + } + } else { + int next = ++radix->num_added_node; + if (fam == AF_UNSPEC) { + /* "any" or "none" */ + for (i = 0; i < RADIX_FAMILIES; i++) { + node->node_num[i] = next; + } + } else { + node->node_num[ISC_RADIX_FAMILY(prefix)] = next; + } + } + *target = node; + return (ISC_R_SUCCESS); + } + + new_node = isc_mem_get(radix->mctx, sizeof(isc_radix_node_t)); + if (new_node == NULL) + return (ISC_R_NOMEMORY); + if (node->bit != differ_bit && bitlen != differ_bit) { + glue = isc_mem_get(radix->mctx, sizeof(isc_radix_node_t)); + if (glue == NULL) { + isc_mem_put(radix->mctx, new_node, + sizeof(isc_radix_node_t)); + return (ISC_R_NOMEMORY); + } + } + new_node->bit = bitlen; + new_node->prefix = NULL; + result = _ref_prefix(radix->mctx, &new_node->prefix, prefix); + if (result != ISC_R_SUCCESS) { + isc_mem_put(radix->mctx, new_node, sizeof(isc_radix_node_t)); + if (glue != NULL) + isc_mem_put(radix->mctx, glue, + sizeof(isc_radix_node_t)); + return (result); + } + new_node->parent = NULL; + new_node->l = new_node->r = NULL; + for (i = 0; i < RADIX_FAMILIES; i++) { + new_node->node_num[i] = -1; + new_node->data[i] = NULL; + } + radix->num_active_node++; + + if (source != NULL) { + /* Merging node */ + for (i = 0; i < RADIX_FAMILIES; i++) { + int cur = radix->num_added_node; + if (source->node_num[i] != -1) { + new_node->node_num[i] = + source->node_num[i] + cur; + new_node->data[i] = source->data[i]; + } + } + } else { + int next = ++radix->num_added_node; + if (fam == AF_UNSPEC) { + /* "any" or "none" */ + for (i = 0; i < RADIX_FAMILIES; i++) { + new_node->node_num[i] = next; + } + } else { + new_node->node_num[ISC_RADIX_FAMILY(prefix)] = next; + } + memset(new_node->data, 0, sizeof(new_node->data)); + } + + if (node->bit == differ_bit) { + INSIST(glue == NULL); + new_node->parent = node; + if (node->bit < radix->maxbits && + BIT_TEST(addr[node->bit >> 3], 0x80 >> (node->bit & 0x07))) + { + INSIST(node->r == NULL); + node->r = new_node; + } else { + INSIST(node->l == NULL); + node->l = new_node; + } + *target = new_node; + return (ISC_R_SUCCESS); + } + + if (bitlen == differ_bit) { + INSIST(glue == NULL); + if (bitlen < radix->maxbits && + BIT_TEST(test_addr[bitlen >> 3], 0x80 >> (bitlen & 0x07))) { + new_node->r = node; + } else { + new_node->l = node; + } + new_node->parent = node->parent; + if (node->parent == NULL) { + INSIST(radix->head == node); + radix->head = new_node; + } else if (node->parent->r == node) { + node->parent->r = new_node; + } else { + node->parent->l = new_node; + } + node->parent = new_node; + } else { + INSIST(glue != NULL); + glue->bit = differ_bit; + glue->prefix = NULL; + glue->parent = node->parent; + for (i = 0; i < RADIX_FAMILIES; i++) { + glue->data[i] = NULL; + glue->node_num[i] = -1; + } + radix->num_active_node++; + if (differ_bit < radix->maxbits && + BIT_TEST(addr[differ_bit>>3], 0x80 >> (differ_bit & 07))) { + glue->r = new_node; + glue->l = node; + } else { + glue->r = node; + glue->l = new_node; + } + new_node->parent = glue; + + if (node->parent == NULL) { + INSIST(radix->head == node); + radix->head = glue; + } else if (node->parent->r == node) { + node->parent->r = glue; + } else { + node->parent->l = glue; + } + node->parent = glue; + } + + *target = new_node; + return (ISC_R_SUCCESS); +} + +void +isc_radix_remove(isc_radix_tree_t *radix, isc_radix_node_t *node) { + isc_radix_node_t *parent, *child; + + REQUIRE(radix != NULL); + REQUIRE(node != NULL); + + if (node->r && node->l) { + /* + * This might be a placeholder node -- have to check and + * make sure there is a prefix associated with it! + */ + if (node->prefix != NULL) + _deref_prefix(node->prefix); + + node->prefix = NULL; + memset(node->data, 0, sizeof(node->data)); + return; + } + + if (node->r == NULL && node->l == NULL) { + parent = node->parent; + _deref_prefix(node->prefix); + + if (parent == NULL) { + INSIST(radix->head == node); + radix->head = NULL; + isc_mem_put(radix->mctx, node, sizeof(*node)); + radix->num_active_node--; + return; + } + + if (parent->r == node) { + parent->r = NULL; + child = parent->l; + } else { + INSIST(parent->l == node); + parent->l = NULL; + child = parent->r; + } + + isc_mem_put(radix->mctx, node, sizeof(*node)); + radix->num_active_node--; + + if (parent->prefix) + return; + + /* We need to remove parent too. */ + if (parent->parent == NULL) { + INSIST(radix->head == parent); + radix->head = child; + } else if (parent->parent->r == parent) { + parent->parent->r = child; + } else { + INSIST(parent->parent->l == parent); + parent->parent->l = child; + } + + child->parent = parent->parent; + isc_mem_put(radix->mctx, parent, sizeof(*parent)); + radix->num_active_node--; + return; + } + + if (node->r) { + child = node->r; + } else { + INSIST(node->l != NULL); + child = node->l; + } + + parent = node->parent; + child->parent = parent; + + _deref_prefix(node->prefix); + + if (parent == NULL) { + INSIST(radix->head == node); + radix->head = child; + isc_mem_put(radix->mctx, node, sizeof(*node)); + radix->num_active_node--; + return; + } + + isc_mem_put(radix->mctx, node, sizeof(*node)); + radix->num_active_node--; + + if (parent->r == node) { + parent->r = child; + } else { + INSIST(parent->l == node); + parent->l = child; + } +} + +/* +Local Variables: +c-basic-offset: 4 +indent-tabs-mode: t +End: +*/ diff --git a/lib/isc/random.c b/lib/isc/random.c new file mode 100644 index 0000000..b79b689 --- /dev/null +++ b/lib/isc/random.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*% + * ChaCha based random number generator derived from OpenBSD. + * + * The original copyright follows: + * Copyright (c) 1996, David Mazieres <dm@uun.org> + * Copyright (c) 2008, Damien Miller <djm@openbsd.org> + * Copyright (c) 2013, Markus Friedl <markus@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <time.h> /* Required for time(). */ +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <isc/magic.h> +#include <isc/mutex.h> +#include <isc/once.h> +#include <isc/mem.h> +#include <isc/entropy.h> +#include <isc/random.h> +#include <isc/safe.h> +#include <isc/string.h> +#include <isc/util.h> + +#define RNG_MAGIC ISC_MAGIC('R', 'N', 'G', 'x') +#define VALID_RNG(r) ISC_MAGIC_VALID(r, RNG_MAGIC) + +#define KEYSTREAM_ONLY +#include "chacha_private.h" + +#define CHACHA_KEYSIZE 32U +#define CHACHA_IVSIZE 8U +#define CHACHA_BLOCKSIZE 64 +#define CHACHA_BUFFERSIZE (16 * CHACHA_BLOCKSIZE) + +/* ChaCha RNG state */ +struct isc_rng { + unsigned int magic; + isc_mem_t *mctx; + chacha_ctx cpctx; + uint8_t buffer[CHACHA_BUFFERSIZE]; + size_t have; + unsigned int references; + int count; + isc_entropy_t *entropy; /*%< entropy source */ + isc_mutex_t lock; +}; + +static isc_once_t once = ISC_ONCE_INIT; + +static void +initialize_rand(void) { +#ifndef HAVE_ARC4RANDOM + unsigned int pid = getpid(); + + /* + * The low bits of pid generally change faster. + * Xor them with the high bits of time which change slowly. + */ + pid = ((pid << 16) & 0xffff0000) | ((pid >> 16) & 0xffff); + + srand((unsigned)time(NULL) ^ pid); +#endif +} + +static void +initialize(void) { + RUNTIME_CHECK(isc_once_do(&once, initialize_rand) == ISC_R_SUCCESS); +} + +void +isc_random_seed(uint32_t seed) { + initialize(); + +#ifndef HAVE_ARC4RANDOM + srand(seed); +#elif defined(HAVE_ARC4RANDOM_STIR) + /* Formally not necessary... */ + UNUSED(seed); + arc4random_stir(); +#elif defined(HAVE_ARC4RANDOM_ADDRANDOM) + arc4random_addrandom((u_char *) &seed, sizeof(uint32_t)); +#else + /* + * If arc4random() is available and no corresponding seeding + * function arc4random_addrandom() is available, no seeding is + * done on such platforms (e.g., OpenBSD 5.5). This is because + * the OS itself is supposed to seed the RNG and it is assumed + * that no explicit seeding is required. + */ + UNUSED(seed); +#endif +} + +void +isc_random_get(uint32_t *val) { + REQUIRE(val != NULL); + + initialize(); + +#ifndef HAVE_ARC4RANDOM + /* + * rand()'s lower bits are not random. + * rand()'s upper bit is zero. + */ +#if RAND_MAX >= 0xfffff + /* We have at least 20 bits. Use lower 16 excluding lower most 4 */ + *val = ((((unsigned int)rand()) & 0xffff0) >> 4) | + ((((unsigned int)rand()) & 0xffff0) << 12); +#elif RAND_MAX >= 0x7fff + /* We have at least 15 bits. Use lower 10/11 excluding lower most 4 */ + *val = ((rand() >> 4) & 0x000007ff) | ((rand() << 7) & 0x003ff800) | + ((rand() << 18) & 0xffc00000); +#else +#error RAND_MAX is too small +#endif +#else + *val = arc4random(); +#endif +} + +uint32_t +isc_random_jitter(uint32_t max, uint32_t jitter) { + uint32_t rnd; + + REQUIRE(jitter < max || (jitter == 0 && max == 0)); + + if (jitter == 0) + return (max); + + isc_random_get(&rnd); + return (max - rnd % jitter); +} + +static void +chacha_reinit(isc_rng_t *rng, uint8_t *buffer, size_t n) { + REQUIRE(rng != NULL); + + if (n < CHACHA_KEYSIZE + CHACHA_IVSIZE) + return; + + chacha_keysetup(&rng->cpctx, buffer, CHACHA_KEYSIZE * 8, 0); + chacha_ivsetup(&rng->cpctx, buffer + CHACHA_KEYSIZE); +} + +isc_result_t +isc_rng_create(isc_mem_t *mctx, isc_entropy_t *entropy, isc_rng_t **rngp) { + union { + unsigned char rnd[128]; + uint32_t rnd32[32]; + } rnd; + isc_result_t result; + isc_rng_t *rng; + + REQUIRE(mctx != NULL); + REQUIRE(rngp != NULL && *rngp == NULL); + + if (entropy != NULL) { + /* + * We accept any quality of random data to avoid blocking. + */ + result = isc_entropy_getdata(entropy, rnd.rnd, + sizeof(rnd), NULL, 0); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + } else { + int i; + for (i = 0; i < 32; i++) + isc_random_get(&rnd.rnd32[i]); + } + + rng = isc_mem_get(mctx, sizeof(*rng)); + if (rng == NULL) + return (ISC_R_NOMEMORY); + + chacha_reinit(rng, rnd.rnd, sizeof(rnd.rnd)); + + rng->have = 0; + memset(rng->buffer, 0, CHACHA_BUFFERSIZE); + + /* Create lock */ + result = isc_mutex_init(&rng->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, rng, sizeof(*rng)); + return (result); + } + + /* Attach to memory context */ + rng->mctx = NULL; + isc_mem_attach(mctx, &rng->mctx); + + /* Local non-algorithm initializations. */ + rng->count = 0; + rng->entropy = entropy; /* don't have to attach */ + rng->references = 1; + rng->magic = RNG_MAGIC; + + *rngp = rng; + + return (ISC_R_SUCCESS); +} + +void +isc_rng_attach(isc_rng_t *source, isc_rng_t **targetp) { + + REQUIRE(VALID_RNG(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + LOCK(&source->lock); + source->references++; + UNLOCK(&source->lock); + + *targetp = (isc_rng_t *)source; +} + +static void +destroy(isc_rng_t *rng) { + + REQUIRE(VALID_RNG(rng)); + + rng->magic = 0; + isc_mutex_destroy(&rng->lock); + isc_mem_putanddetach(&rng->mctx, rng, sizeof(isc_rng_t)); +} + +void +isc_rng_detach(isc_rng_t **rngp) { + isc_rng_t *rng; + bool dest = false; + + REQUIRE(rngp != NULL && VALID_RNG(*rngp)); + + rng = *rngp; + *rngp = NULL; + + LOCK(&rng->lock); + + INSIST(rng->references > 0); + rng->references--; + if (rng->references == 0) + dest = true; + UNLOCK(&rng->lock); + + if (dest) + destroy(rng); +} + +static void +chacha_rekey(isc_rng_t *rng, u_char *dat, size_t datlen) { + REQUIRE(VALID_RNG(rng)); + +#ifndef KEYSTREAM_ONLY + memset(rng->buffer, 0, CHACHA_BUFFERSIZE); +#endif + + /* Fill buffer with the keystream. */ + chacha_encrypt_bytes(&rng->cpctx, rng->buffer, rng->buffer, + CHACHA_BUFFERSIZE); + + /* Mix in optional user provided data. */ + if (dat != NULL) { + size_t i, m; + + m = ISC_MIN(datlen, CHACHA_KEYSIZE + CHACHA_IVSIZE); + for (i = 0; i < m; i++) + rng->buffer[i] ^= dat[i]; + } + + /* Immediately reinit for backtracking resistance. */ + chacha_reinit(rng, rng->buffer, + CHACHA_KEYSIZE + CHACHA_IVSIZE); + memset(rng->buffer, 0, CHACHA_KEYSIZE + CHACHA_IVSIZE); + rng->have = CHACHA_BUFFERSIZE - CHACHA_KEYSIZE - CHACHA_IVSIZE; +} + +static inline uint16_t +chacha_getuint16(isc_rng_t *rng) { + uint16_t val; + + REQUIRE(VALID_RNG(rng)); + + if (rng->have < sizeof(val)) + chacha_rekey(rng, NULL, 0); + + memmove(&val, rng->buffer + CHACHA_BUFFERSIZE - rng->have, + sizeof(val)); + /* Clear the copied region. */ + memset(rng->buffer + CHACHA_BUFFERSIZE - rng->have, + 0, sizeof(val)); + rng->have -= sizeof(val); + + return (val); +} + +static void +chacha_stir(isc_rng_t *rng) { + union { + unsigned char rnd[128]; + uint32_t rnd32[32]; + } rnd; + isc_result_t result; + + REQUIRE(VALID_RNG(rng)); + + if (rng->entropy != NULL) { + /* + * We accept any quality of random data to avoid blocking. + */ + result = isc_entropy_getdata(rng->entropy, rnd.rnd, + sizeof(rnd), NULL, 0); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + } else { + int i; + for (i = 0; i < 32; i++) + isc_random_get(&rnd.rnd32[i]); + } + + chacha_rekey(rng, rnd.rnd, sizeof(rnd.rnd)); + + isc_safe_memwipe(rnd.rnd, sizeof(rnd.rnd)); + + /* Invalidate the buffer too. */ + rng->have = 0; + memset(rng->buffer, 0, CHACHA_BUFFERSIZE); + + /* + * Derived from OpenBSD's implementation. The rationale is not clear, + * but should be conservative enough in safety, and reasonably large + * for efficiency. + */ + rng->count = 1600000; +} + +uint16_t +isc_rng_random(isc_rng_t *rng) { + uint16_t result; + + REQUIRE(VALID_RNG(rng)); + + LOCK(&rng->lock); + + rng->count -= sizeof(uint16_t); + if (rng->count <= 0) + chacha_stir(rng); + result = chacha_getuint16(rng); + + UNLOCK(&rng->lock); + + return (result); +} + +uint16_t +isc_rng_uniformrandom(isc_rng_t *rng, uint16_t upper_bound) { + uint16_t min, r; + + REQUIRE(VALID_RNG(rng)); + + if (upper_bound < 2) + return (0); + + /* + * Ensure the range of random numbers [min, 0xffff] be a multiple of + * upper_bound and contain at least a half of the 16 bit range. + */ + + if (upper_bound > 0x8000) + min = 1 + ~upper_bound; /* 0x8000 - upper_bound */ + else + min = (uint16_t)(0x10000 % (uint32_t)upper_bound); + + /* + * This could theoretically loop forever but each retry has + * p > 0.5 (worst case, usually far better) of selecting a + * number inside the range we need, so it should rarely need + * to re-roll. + */ + for (;;) { + r = isc_rng_random(rng); + if (r >= min) + break; + } + + return (r % upper_bound); +} diff --git a/lib/isc/ratelimiter.c b/lib/isc/ratelimiter.c new file mode 100644 index 0000000..b950298 --- /dev/null +++ b/lib/isc/ratelimiter.c @@ -0,0 +1,375 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/mem.h> +#include <isc/ratelimiter.h> +#include <isc/task.h> +#include <isc/time.h> +#include <isc/timer.h> +#include <isc/util.h> + +typedef enum { + isc_ratelimiter_stalled = 0, + isc_ratelimiter_ratelimited = 1, + isc_ratelimiter_idle = 2, + isc_ratelimiter_shuttingdown = 3 +} isc_ratelimiter_state_t; + +struct isc_ratelimiter { + isc_mem_t * mctx; + isc_mutex_t lock; + int refs; + isc_task_t * task; + isc_timer_t * timer; + isc_interval_t interval; + uint32_t pertic; + bool pushpop; + isc_ratelimiter_state_t state; + isc_event_t shutdownevent; + ISC_LIST(isc_event_t) pending; +}; + +#define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1) + +static void +ratelimiter_tick(isc_task_t *task, isc_event_t *event); + +static void +ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event); + +isc_result_t +isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, + isc_task_t *task, isc_ratelimiter_t **ratelimiterp) +{ + isc_result_t result; + isc_ratelimiter_t *rl; + INSIST(ratelimiterp != NULL && *ratelimiterp == NULL); + + rl = isc_mem_get(mctx, sizeof(*rl)); + if (rl == NULL) + return ISC_R_NOMEMORY; + rl->mctx = mctx; + rl->refs = 1; + rl->task = task; + isc_interval_set(&rl->interval, 0, 0); + rl->timer = NULL; + rl->pertic = 1; + rl->pushpop = false; + rl->state = isc_ratelimiter_idle; + ISC_LIST_INIT(rl->pending); + + result = isc_mutex_init(&rl->lock); + if (result != ISC_R_SUCCESS) + goto free_mem; + + result = isc_timer_create(timermgr, isc_timertype_inactive, + NULL, NULL, rl->task, ratelimiter_tick, + rl, &rl->timer); + if (result != ISC_R_SUCCESS) + goto free_mutex; + + /* + * Increment the reference count to indicate that we may + * (soon) have events outstanding. + */ + rl->refs++; + + ISC_EVENT_INIT(&rl->shutdownevent, + sizeof(isc_event_t), + 0, NULL, ISC_RATELIMITEREVENT_SHUTDOWN, + ratelimiter_shutdowncomplete, rl, rl, NULL, NULL); + + *ratelimiterp = rl; + return (ISC_R_SUCCESS); + +free_mutex: + DESTROYLOCK(&rl->lock); +free_mem: + isc_mem_put(mctx, rl, sizeof(*rl)); + return (result); +} + +isc_result_t +isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(rl != NULL); + REQUIRE(interval != NULL); + + LOCK(&rl->lock); + rl->interval = *interval; + /* + * If the timer is currently running, change its rate. + */ + if (rl->state == isc_ratelimiter_ratelimited) { + result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, + &rl->interval, false); + } + UNLOCK(&rl->lock); + return (result); +} + +void +isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, uint32_t pertic) { + + REQUIRE(rl != NULL); + + if (pertic == 0) + pertic = 1; + rl->pertic = pertic; +} + +void +isc_ratelimiter_setpushpop(isc_ratelimiter_t *rl, bool pushpop) { + + REQUIRE(rl != NULL); + + rl->pushpop = pushpop; +} + +isc_result_t +isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task, + isc_event_t **eventp) +{ + isc_result_t result = ISC_R_SUCCESS; + isc_event_t *ev; + + REQUIRE(rl != NULL); + REQUIRE(task != NULL); + REQUIRE(eventp != NULL && *eventp != NULL); + ev = *eventp; + REQUIRE(ev->ev_sender == NULL); + + LOCK(&rl->lock); + if (rl->state == isc_ratelimiter_ratelimited || + rl->state == isc_ratelimiter_stalled) { + ev->ev_sender = task; + *eventp = NULL; + if (rl->pushpop) + ISC_LIST_PREPEND(rl->pending, ev, ev_ratelink); + else + ISC_LIST_APPEND(rl->pending, ev, ev_ratelink); + } else if (rl->state == isc_ratelimiter_idle) { + result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, + &rl->interval, false); + if (result == ISC_R_SUCCESS) { + ev->ev_sender = task; + rl->state = isc_ratelimiter_ratelimited; + } + } else { + INSIST(rl->state == isc_ratelimiter_shuttingdown); + result = ISC_R_SHUTTINGDOWN; + } + UNLOCK(&rl->lock); + if (*eventp != NULL && result == ISC_R_SUCCESS) + isc_task_send(task, eventp); + return (result); +} + +isc_result_t +isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(rl != NULL); + REQUIRE(event != NULL); + + LOCK(&rl->lock); + if (ISC_LINK_LINKED(event, ev_ratelink)) { + ISC_LIST_UNLINK(rl->pending, event, ev_ratelink); + event->ev_sender = NULL; + } else + result = ISC_R_NOTFOUND; + UNLOCK(&rl->lock); + return (result); +} + +static void +ratelimiter_tick(isc_task_t *task, isc_event_t *event) { + isc_result_t result = ISC_R_SUCCESS; + isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; + isc_event_t *p; + uint32_t pertic; + + UNUSED(task); + + isc_event_free(&event); + + pertic = rl->pertic; + while (pertic != 0) { + pertic--; + LOCK(&rl->lock); + p = ISC_LIST_HEAD(rl->pending); + if (p != NULL) { + /* + * There is work to do. Let's do it after unlocking. + */ + ISC_LIST_UNLINK(rl->pending, p, ev_ratelink); + } else { + /* + * No work left to do. Stop the timer so that we don't + * waste resources by having it fire periodically. + */ + result = isc_timer_reset(rl->timer, + isc_timertype_inactive, + NULL, NULL, false); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + rl->state = isc_ratelimiter_idle; + pertic = 0; /* Force the loop to exit. */ + } + UNLOCK(&rl->lock); + if (p != NULL) { + isc_task_t *evtask = p->ev_sender; + isc_task_send(evtask, &p); + } + INSIST(p == NULL); + } +} + +void +isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) { + isc_event_t *ev; + isc_task_t *task; + + REQUIRE(rl != NULL); + + LOCK(&rl->lock); + rl->state = isc_ratelimiter_shuttingdown; + (void)isc_timer_reset(rl->timer, isc_timertype_inactive, + NULL, NULL, false); + while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) { + ISC_LIST_UNLINK(rl->pending, ev, ev_ratelink); + ev->ev_attributes |= ISC_EVENTATTR_CANCELED; + task = ev->ev_sender; + isc_task_send(task, &ev); + } + isc_timer_detach(&rl->timer); + + /* + * Send an event to our task. The delivery of this event + * indicates that no more timer events will be delivered. + */ + ev = &rl->shutdownevent; + isc_task_send(rl->task, &ev); + + UNLOCK(&rl->lock); +} + +static void +ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event) { + isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; + + UNUSED(task); + + isc_ratelimiter_detach(&rl); +} + +static void +ratelimiter_free(isc_ratelimiter_t *rl) { + DESTROYLOCK(&rl->lock); + isc_mem_put(rl->mctx, rl, sizeof(*rl)); +} + +void +isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) { + + REQUIRE(source != NULL); + REQUIRE(target != NULL && *target == NULL); + + LOCK(&source->lock); + REQUIRE(source->refs > 0); + source->refs++; + INSIST(source->refs > 0); + UNLOCK(&source->lock); + *target = source; +} + +void +isc_ratelimiter_detach(isc_ratelimiter_t **rlp) { + isc_ratelimiter_t *rl; + bool free_now = false; + + REQUIRE(rlp != NULL && *rlp != NULL); + + rl = *rlp; + + LOCK(&rl->lock); + REQUIRE(rl->refs > 0); + rl->refs--; + if (rl->refs == 0) + free_now = true; + UNLOCK(&rl->lock); + + if (free_now) + ratelimiter_free(rl); + + *rlp = NULL; +} + +isc_result_t +isc_ratelimiter_stall(isc_ratelimiter_t *rl) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(rl != NULL); + + LOCK(&rl->lock); + switch (rl->state) { + case isc_ratelimiter_shuttingdown: + result = ISC_R_SHUTTINGDOWN; + break; + case isc_ratelimiter_ratelimited: + result = isc_timer_reset(rl->timer, isc_timertype_inactive, + NULL, NULL, false); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + /* FALLTHROUGH */ + case isc_ratelimiter_idle: + case isc_ratelimiter_stalled: + rl->state = isc_ratelimiter_stalled; + break; + } + UNLOCK(&rl->lock); + return (result); +} + +isc_result_t +isc_ratelimiter_release(isc_ratelimiter_t *rl) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(rl != NULL); + + LOCK(&rl->lock); + switch (rl->state) { + case isc_ratelimiter_shuttingdown: + result = ISC_R_SHUTTINGDOWN; + break; + case isc_ratelimiter_stalled: + if (!ISC_LIST_EMPTY(rl->pending)) { + result = isc_timer_reset(rl->timer, + isc_timertype_ticker, NULL, + &rl->interval, false); + if (result == ISC_R_SUCCESS) + rl->state = isc_ratelimiter_ratelimited; + } else + rl->state = isc_ratelimiter_idle; + break; + case isc_ratelimiter_ratelimited: + case isc_ratelimiter_idle: + break; + } + UNLOCK(&rl->lock); + return (result); +} diff --git a/lib/isc/refcount.c b/lib/isc/refcount.c new file mode 100644 index 0000000..033af7b --- /dev/null +++ b/lib/isc/refcount.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <stddef.h> + +#include <isc/mutex.h> +#include <isc/refcount.h> +#include <isc/result.h> +#include <isc/util.h> + +isc_result_t +isc_refcount_init(isc_refcount_t *ref, unsigned int n) { + REQUIRE(ref != NULL); + + ref->refs = n; +#if defined(ISC_PLATFORM_USETHREADS) && !defined(ISC_REFCOUNT_HAVEATOMIC) + return (isc_mutex_init(&ref->lock)); +#else + return (ISC_R_SUCCESS); +#endif +} diff --git a/lib/isc/regex.c b/lib/isc/regex.c new file mode 100644 index 0000000..63261bb --- /dev/null +++ b/lib/isc/regex.c @@ -0,0 +1,368 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/file.h> +#include <isc/print.h> +#include <isc/regex.h> +#include <isc/string.h> + +#if VALREGEX_REPORT_REASON +#define FAIL(x) do { reason = (x); goto error; } while(0) +#else +#define FAIL(x) goto error +#endif + +/* + * Validate the regular expression 'C' locale. + */ +int +isc_regex_validate(const char *c) { + enum { + none, parse_bracket, parse_bound, + parse_ce, parse_ec, parse_cc + } state = none; + /* Well known character classes. */ + const char *cc[] = { + ":alnum:", ":digit:", ":punct:", ":alpha:", ":graph:", + ":space:", ":blank:", ":lower:", ":upper:", ":cntrl:", + ":print:", ":xdigit:" + }; + bool seen_comma = false; + bool seen_high = false; + bool seen_char = false; + bool seen_ec = false; + bool seen_ce = false; + bool have_atom = false; + int group = 0; + int range = 0; + int sub = 0; + bool empty_ok = false; + bool neg = false; + bool was_multiple = false; + unsigned int low = 0; + unsigned int high = 0; + const char *ccname = NULL; + int range_start = 0; +#if VALREGEX_REPORT_REASON + const char *reason = ""; +#endif + + if (c == NULL || *c == 0) + FAIL("empty string"); + + while (c != NULL && *c != 0) { + switch (state) { + case none: + switch (*c) { + case '\\': /* make literal */ + ++c; + switch (*c) { + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if ((*c - '0') > sub) + FAIL("bad back reference"); + have_atom = true; + was_multiple = false; + break; + case 0: + FAIL("escaped end-of-string"); + default: + goto literal; + } + ++c; + break; + case '[': /* bracket start */ + ++c; + neg = false; + was_multiple = false; + seen_char = false; + state = parse_bracket; + break; + case '{': /* bound start */ + switch (c[1]) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + if (!have_atom) + FAIL("no atom"); + if (was_multiple) + FAIL("was multiple"); + seen_comma = false; + seen_high = false; + low = high = 0; + state = parse_bound; + break; + default: + goto literal; + } + ++c; + have_atom = true; + was_multiple = true; + break; + case '}': + goto literal; + case '(': /* group start */ + have_atom = false; + was_multiple = false; + empty_ok = true; + ++group; + ++sub; + ++c; + break; + case ')': /* group end */ + if (group && !have_atom && !empty_ok) + FAIL("empty alternative"); + have_atom = true; + was_multiple = false; + if (group != 0) + --group; + ++c; + break; + case '|': /* alternative seperator */ + if (!have_atom) + FAIL("no atom"); + have_atom = false; + empty_ok = false; + was_multiple = false; + ++c; + break; + case '^': + case '$': + have_atom = true; + was_multiple = true; + ++c; + break; + case '+': + case '*': + case '?': + if (was_multiple) + FAIL("was multiple"); + if (!have_atom) + FAIL("no atom"); + have_atom = true; + was_multiple = true; + ++c; + break; + case '.': + default: + literal: + have_atom = true; + was_multiple = false; + ++c; + break; + } + break; + case parse_bound: + switch (*c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (!seen_comma) { + low = low * 10 + *c - '0'; + if (low > 255) + FAIL("lower bound too big"); + } else { + seen_high = true; + high = high * 10 + *c - '0'; + if (high > 255) + FAIL("upper bound too big"); + } + ++c; + break; + case ',': + if (seen_comma) + FAIL("multiple commas"); + seen_comma = true; + ++c; + break; + default: + case '{': + FAIL("non digit/comma"); + case '}': + if (seen_high && low > high) + FAIL("bad parse bound"); + seen_comma = false; + state = none; + ++c; + break; + } + break; + case parse_bracket: + switch (*c) { + case '^': + if (seen_char || neg) goto inside; + neg = true; + ++c; + break; + case '-': + if (range == 2) goto inside; + if (!seen_char) goto inside; + if (range == 1) + FAIL("bad range"); + range = 2; + ++c; + break; + case '[': + ++c; + switch (*c) { + case '.': /* collating element */ + if (range != 0) --range; + ++c; + state = parse_ce; + seen_ce = false; + break; + case '=': /* equivalence class */ + if (range == 2) + FAIL("equivalence class in range"); + ++c; + state = parse_ec; + seen_ec = false; + break; + case ':': /* character class */ + if (range == 2) + FAIL("character class in range"); + ccname = c; + ++c; + state = parse_cc; + break; + } + seen_char = true; + break; + case ']': + if (!c[1] && !seen_char) + FAIL("unfinished brace"); + if (!seen_char) + goto inside; + ++c; + range = 0; + have_atom = true; + state = none; + break; + default: + inside: + seen_char = true; + if (range == 2 && (*c & 0xff) < range_start) + FAIL("out of order range"); + if (range != 0) + --range; + range_start = *c & 0xff; + ++c; + break; + }; + break; + case parse_ce: + switch (*c) { + case '.': + ++c; + switch (*c) { + case ']': + if (!seen_ce) + FAIL("empty ce"); + ++c; + state = parse_bracket; + break; + default: + if (seen_ce) + range_start = 256; + else + range_start = '.'; + seen_ce = true; + break; + } + break; + default: + if (seen_ce) + range_start = 256; + else + range_start = *c; + seen_ce = true; + ++c; + break; + } + break; + case parse_ec: + switch (*c) { + case '=': + ++c; + switch (*c) { + case ']': + if (!seen_ec) + FAIL("no ec"); + ++c; + state = parse_bracket; + break; + default: + seen_ec = true; + break; + } + break; + default: + seen_ec = true; + ++c; + break; + } + break; + case parse_cc: + switch (*c) { + case ':': + ++c; + switch (*c) { + case ']': { + unsigned int i; + bool found = false; + for (i = 0; + i < sizeof(cc)/sizeof(*cc); + i++) + { + unsigned int len; + len = strlen(cc[i]); + if (len != + (unsigned int)(c - ccname)) + continue; + if (strncmp(cc[i], ccname, len)) + continue; + found = true; + } + if (!found) + FAIL("unknown cc"); + ++c; + state = parse_bracket; + break; + } + default: + break; + } + break; + default: + ++c; + break; + } + break; + } + } + if (group != 0) + FAIL("group open"); + if (state != none) + FAIL("incomplete"); + if (!have_atom) + FAIL("no atom"); + return (sub); + + error: +#if VALREGEX_REPORT_REASON + fprintf(stderr, "%s\n", reason); +#endif + return (-1); +} diff --git a/lib/isc/region.c b/lib/isc/region.c new file mode 100644 index 0000000..2f85842 --- /dev/null +++ b/lib/isc/region.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdlib.h> +#include <string.h> + +#include <isc/region.h> +#include <isc/util.h> + +int +isc_region_compare(isc_region_t *r1, isc_region_t *r2) { + unsigned int l; + int result; + + REQUIRE(r1 != NULL); + REQUIRE(r2 != NULL); + + l = (r1->length < r2->length) ? r1->length : r2->length; + + if ((result = memcmp(r1->base, r2->base, l)) != 0) + return ((result < 0) ? -1 : 1); + else + return ((r1->length == r2->length) ? 0 : + (r1->length < r2->length) ? -1 : 1); +} diff --git a/lib/isc/result.c b/lib/isc/result.c new file mode 100644 index 0000000..a707c32 --- /dev/null +++ b/lib/isc/result.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <stddef.h> +#include <stdlib.h> + +#include <isc/lib.h> +#include <isc/msgs.h> +#include <isc/mutex.h> +#include <isc/once.h> +#include <isc/resultclass.h> +#include <isc/util.h> + +typedef struct resulttable { + unsigned int base; + unsigned int last; + const char ** text; + isc_msgcat_t * msgcat; + int set; + ISC_LINK(struct resulttable) link; +} resulttable; + +typedef ISC_LIST(resulttable) resulttable_list_t; + +static const char *description[ISC_R_NRESULTS] = { + "success", /*%< 0 */ + "out of memory", /*%< 1 */ + "timed out", /*%< 2 */ + "no available threads", /*%< 3 */ + "address not available", /*%< 4 */ + "address in use", /*%< 5 */ + "permission denied", /*%< 6 */ + "no pending connections", /*%< 7 */ + "network unreachable", /*%< 8 */ + "host unreachable", /*%< 9 */ + "network down", /*%< 10 */ + "host down", /*%< 11 */ + "connection refused", /*%< 12 */ + "not enough free resources", /*%< 13 */ + "end of file", /*%< 14 */ + "socket already bound", /*%< 15 */ + "reload", /*%< 16 */ + "lock busy", /*%< 17 */ + "already exists", /*%< 18 */ + "ran out of space", /*%< 19 */ + "operation canceled", /*%< 20 */ + "socket is not bound", /*%< 21 */ + "shutting down", /*%< 22 */ + "not found", /*%< 23 */ + "unexpected end of input", /*%< 24 */ + "failure", /*%< 25 */ + "I/O error", /*%< 26 */ + "not implemented", /*%< 27 */ + "unbalanced parentheses", /*%< 28 */ + "no more", /*%< 29 */ + "invalid file", /*%< 30 */ + "bad base64 encoding", /*%< 31 */ + "unexpected token", /*%< 32 */ + "quota reached", /*%< 33 */ + "unexpected error", /*%< 34 */ + "already running", /*%< 35 */ + "ignore", /*%< 36 */ + "address mask not contiguous", /*%< 37 */ + "file not found", /*%< 38 */ + "file already exists", /*%< 39 */ + "socket is not connected", /*%< 40 */ + "out of range", /*%< 41 */ + "out of entropy", /*%< 42 */ + "invalid use of multicast address", /*%< 43 */ + "not a file", /*%< 44 */ + "not a directory", /*%< 45 */ + "queue is full", /*%< 46 */ + "address family mismatch", /*%< 47 */ + "address family not supported", /*%< 48 */ + "bad hex encoding", /*%< 49 */ + "too many open files", /*%< 50 */ + "not blocking", /*%< 51 */ + "unbalanced quotes", /*%< 52 */ + "operation in progress", /*%< 53 */ + "connection reset", /*%< 54 */ + "soft quota reached", /*%< 55 */ + "not a valid number", /*%< 56 */ + "disabled", /*%< 57 */ + "max size", /*%< 58 */ + "invalid address format", /*%< 59 */ + "bad base32 encoding", /*%< 60 */ + "unset", /*%< 61 */ + "multiple", /*%< 62 */ + "would block", /*%< 63 */ +}; + +static const char *identifier[ISC_R_NRESULTS] = { + "ISC_R_SUCCESS", + "ISC_R_NOMEMORY", + "ISC_R_TIMEDOUT", + "ISC_R_NOTHREADS", + "ISC_R_ADDRNOTAVAIL", + "ISC_R_ADDRINUSE", + "ISC_R_NOPERM", + "ISC_R_NOCONN", + "ISC_R_NETUNREACH", + "ISC_R_HOSTUNREACH", + "ISC_R_NETDOWN", + "ISC_R_HOSTDOWN", + "ISC_R_CONNREFUSED", + "ISC_R_NORESOURCES", + "ISC_R_EOF", + "ISC_R_BOUND", + "ISC_R_RELOAD", + "ISC_R_LOCKBUSY", + "ISC_R_EXISTS", + "ISC_R_NOSPACE", + "ISC_R_CANCELED", + "ISC_R_NOTBOUND", + "ISC_R_SHUTTINGDOWN", + "ISC_R_NOTFOUND", + "ISC_R_UNEXPECTEDEND", + "ISC_R_FAILURE", + "ISC_R_IOERROR", + "ISC_R_NOTIMPLEMENTED", + "ISC_R_UNBALANCED", + "ISC_R_NOMORE", + "ISC_R_INVALIDFILE", + "ISC_R_BADBASE64", + "ISC_R_UNEXPECTEDTOKEN", + "ISC_R_QUOTA", + "ISC_R_UNEXPECTED", + "ISC_R_ALREADYRUNNING", + "ISC_R_IGNORE", + "ISC_R_MASKNONCONTIG", + "ISC_R_FILENOTFOUND", + "ISC_R_FILEEXISTS", + "ISC_R_NOTCONNECTED", + "ISC_R_RANGE", + "ISC_R_NOENTROPY", + "ISC_R_MULTICAST", + "ISC_R_NOTFILE", + "ISC_R_NOTDIRECTORY", + "ISC_R_QUEUEFULL", + "ISC_R_FAMILYMISMATCH", + "ISC_R_FAMILYNOSUPPORT", + "ISC_R_BADHEX", + "ISC_R_TOOMANYOPENFILES", + "ISC_R_NOTBLOCKING", + "ISC_R_UNBALANCEDQUOTES", + "ISC_R_INPROGRESS", + "ISC_R_CONNECTIONRESET", + "ISC_R_SOFTQUOTA", + "ISC_R_BADNUMBER", + "ISC_R_DISABLED", + "ISC_R_MAXSIZE", + "ISC_R_BADADDRESSFORM", + "ISC_R_BADBASE32", + "ISC_R_UNSET", + "ISC_R_MULTIPLE", + "ISC_R_WOULDBLOCK", +}; + +#define ISC_RESULT_RESULTSET 2 +#define ISC_RESULT_UNAVAILABLESET 3 + +static isc_once_t once = ISC_ONCE_INIT; +static resulttable_list_t description_tables; +static resulttable_list_t identifier_tables; +static isc_mutex_t lock; + +static isc_result_t +register_table(resulttable_list_t *tables, unsigned int base, + unsigned int nresults, const char **text, + isc_msgcat_t *msgcat, int set) +{ + resulttable *table; + + REQUIRE(base % ISC_RESULTCLASS_SIZE == 0); + REQUIRE(nresults <= ISC_RESULTCLASS_SIZE); + REQUIRE(text != NULL); + + /* + * We use malloc() here because we we want to be able to use + * isc_result_totext() even if there is no memory context. + */ + table = malloc(sizeof(*table)); + if (table == NULL) + return (ISC_R_NOMEMORY); + table->base = base; + table->last = base + nresults - 1; + table->text = text; + table->msgcat = msgcat; + table->set = set; + ISC_LINK_INIT(table, link); + + LOCK(&lock); + + ISC_LIST_APPEND(*tables, table, link); + + UNLOCK(&lock); + + return (ISC_R_SUCCESS); +} + +static void +initialize_action(void) { + isc_result_t result; + + RUNTIME_CHECK(isc_mutex_init(&lock) == ISC_R_SUCCESS); + ISC_LIST_INIT(description_tables); + ISC_LIST_INIT(identifier_tables); + + result = register_table(&description_tables, + ISC_RESULTCLASS_ISC, ISC_R_NRESULTS, + description, isc_msgcat, ISC_RESULT_RESULTSET); + if (result != ISC_R_SUCCESS) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "register_table() %s: %u", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + result); + + result = register_table(&identifier_tables, + ISC_RESULTCLASS_ISC, ISC_R_NRESULTS, + identifier, isc_msgcat, ISC_RESULT_RESULTSET); + if (result != ISC_R_SUCCESS) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "register_table() %s: %u", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + result); +} + +static void +initialize(void) { + isc_lib_initmsgcat(); + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); +} + +static const char * +isc_result_tomany_helper(resulttable_list_t *tables, isc_result_t result) { + resulttable *table; + const char *text, *default_text; + int index; + + initialize(); + + LOCK(&lock); + + text = NULL; + for (table = ISC_LIST_HEAD(*tables); + table != NULL; + table = ISC_LIST_NEXT(table, link)) { + if (result >= table->base && result <= table->last) { + index = (int)(result - table->base); + default_text = table->text[index]; + /* + * Note: we use 'index + 1' as the message number + * instead of index because isc_msgcat_get() requires + * the message number to be > 0. + */ + text = isc_msgcat_get(table->msgcat, table->set, + index + 1, default_text); + break; + } + } + if (text == NULL) + text = isc_msgcat_get(isc_msgcat, ISC_RESULT_UNAVAILABLESET, + 1, "(result code text not available)"); + + UNLOCK(&lock); + + return (text); +} + +const char * +isc_result_totext(isc_result_t result) { + return (isc_result_tomany_helper(&description_tables, result)); +} + +const char * +isc_result_toid(isc_result_t result) { + return (isc_result_tomany_helper(&identifier_tables, result)); +} + +isc_result_t +isc_result_register(unsigned int base, unsigned int nresults, + const char **text, isc_msgcat_t *msgcat, int set) +{ + initialize(); + + return (register_table(&description_tables, base, nresults, text, + msgcat, set)); +} + +isc_result_t +isc_result_registerids(unsigned int base, unsigned int nresults, + const char **ids, isc_msgcat_t *msgcat, int set) +{ + initialize(); + + return (register_table(&identifier_tables, base, nresults, ids, + msgcat, set)); +} diff --git a/lib/isc/rwlock.c b/lib/isc/rwlock.c new file mode 100644 index 0000000..9533c0f --- /dev/null +++ b/lib/isc/rwlock.c @@ -0,0 +1,982 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> +#include <stddef.h> +#include <inttypes.h> + +#include <isc/atomic.h> +#include <isc/magic.h> +#include <isc/msgs.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/rwlock.h> +#include <isc/util.h> + +#define RWLOCK_MAGIC ISC_MAGIC('R', 'W', 'L', 'k') +#define VALID_RWLOCK(rwl) ISC_MAGIC_VALID(rwl, RWLOCK_MAGIC) + +#ifdef ISC_PLATFORM_USETHREADS + +#ifndef RWLOCK_DEFAULT_READ_QUOTA +#define RWLOCK_DEFAULT_READ_QUOTA 4 +#endif + +#ifndef RWLOCK_DEFAULT_WRITE_QUOTA +#define RWLOCK_DEFAULT_WRITE_QUOTA 4 +#endif + +#ifndef RWLOCK_MAX_ADAPTIVE_COUNT +#define RWLOCK_MAX_ADAPTIVE_COUNT 100 +#endif + +#if defined(ISC_RWLOCK_USEATOMIC) +static isc_result_t +isc__rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type); +#endif + +#ifdef ISC_RWLOCK_TRACE +#include <stdio.h> /* Required for fprintf/stderr. */ +#include <isc/thread.h> /* Required for isc_thread_self(). */ + +static void +print_lock(const char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) { +#if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG) + fprintf(stderr, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_PRINTLOCK2, + "rwlock %p thread %lu %s(%s): " + "write_requests=%u, write_completions=%u, " + "cnt_and_flag=0x%x, readers_waiting=%u, " + "write_granted=%u, write_quota=%u\n"), + rwl, isc_thread_self(), operation, + (type == isc_rwlocktype_read ? + isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_READ, "read") : + isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_WRITE, "write")), + rwl->write_requests, rwl->write_completions, + rwl->cnt_and_flag, rwl->readers_waiting, + rwl->write_granted, rwl->write_quota); +#else + fprintf(stderr, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_PRINTLOCK, + "rwlock %p thread %lu %s(%s): %s, %u active, " + "%u granted, %u rwaiting, %u wwaiting\n"), + rwl, isc_thread_self(), operation, + (type == isc_rwlocktype_read ? + isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_READ, "read") : + isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_WRITE, "write")), + (rwl->type == isc_rwlocktype_read ? + isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_READING, "reading") : + isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_WRITING, "writing")), + rwl->active, rwl->granted, + rwl->readers_waiting, rwl->writers_waiting); +#endif +} +#endif /* ISC_RWLOCK_TRACE */ + +isc_result_t +isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota, + unsigned int write_quota) +{ + isc_result_t result; + + REQUIRE(rwl != NULL); + + /* + * In case there's trouble initializing, we zero magic now. If all + * goes well, we'll set it to RWLOCK_MAGIC. + */ + rwl->magic = 0; + + rwl->spins = 0; +#if defined(ISC_RWLOCK_USEATOMIC) + rwl->write_requests = 0; + rwl->write_completions = 0; + rwl->cnt_and_flag = 0; + rwl->readers_waiting = 0; + rwl->write_granted = 0; + if (read_quota != 0) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "read quota is not supported"); + } + if (write_quota == 0) + write_quota = RWLOCK_DEFAULT_WRITE_QUOTA; + rwl->write_quota = write_quota; +#else + rwl->type = isc_rwlocktype_read; + rwl->original = isc_rwlocktype_none; + rwl->active = 0; + rwl->granted = 0; + rwl->readers_waiting = 0; + rwl->writers_waiting = 0; + if (read_quota == 0) + read_quota = RWLOCK_DEFAULT_READ_QUOTA; + rwl->read_quota = read_quota; + if (write_quota == 0) + write_quota = RWLOCK_DEFAULT_WRITE_QUOTA; + rwl->write_quota = write_quota; +#endif + + result = isc_mutex_init(&rwl->lock); + if (result != ISC_R_SUCCESS) + return (result); + + result = isc_condition_init(&rwl->readable); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init(readable) %s: %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + goto destroy_lock; + } + result = isc_condition_init(&rwl->writeable); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init(writeable) %s: %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + goto destroy_rcond; + } + + rwl->magic = RWLOCK_MAGIC; + + return (ISC_R_SUCCESS); + + destroy_rcond: + (void)isc_condition_destroy(&rwl->readable); + destroy_lock: + DESTROYLOCK(&rwl->lock); + + return (result); +} + +void +isc_rwlock_destroy(isc_rwlock_t *rwl) { + REQUIRE(VALID_RWLOCK(rwl)); + +#if defined(ISC_RWLOCK_USEATOMIC) + REQUIRE(rwl->write_requests == rwl->write_completions && + rwl->cnt_and_flag == 0 && rwl->readers_waiting == 0); +#else + LOCK(&rwl->lock); + REQUIRE(rwl->active == 0 && + rwl->readers_waiting == 0 && + rwl->writers_waiting == 0); + UNLOCK(&rwl->lock); +#endif + + rwl->magic = 0; + (void)isc_condition_destroy(&rwl->readable); + (void)isc_condition_destroy(&rwl->writeable); + DESTROYLOCK(&rwl->lock); +} + +#if defined(ISC_RWLOCK_USEATOMIC) + +/* + * When some architecture-dependent atomic operations are available, + * rwlock can be more efficient than the generic algorithm defined below. + * The basic algorithm is described in the following URL: + * http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/rw.html + * + * The key is to use the following integer variables modified atomically: + * write_requests, write_completions, and cnt_and_flag. + * + * write_requests and write_completions act as a waiting queue for writers + * in order to ensure the FIFO order. Both variables begin with the initial + * value of 0. When a new writer tries to get a write lock, it increments + * write_requests and gets the previous value of the variable as a "ticket". + * When write_completions reaches the ticket number, the new writer can start + * writing. When the writer completes its work, it increments + * write_completions so that another new writer can start working. If the + * write_requests is not equal to write_completions, it means a writer is now + * working or waiting. In this case, a new readers cannot start reading, or + * in other words, this algorithm basically prefers writers. + * + * cnt_and_flag is a "lock" shared by all readers and writers. This integer + * variable is a kind of structure with two members: writer_flag (1 bit) and + * reader_count (31 bits). The writer_flag shows whether a writer is working, + * and the reader_count shows the number of readers currently working or almost + * ready for working. A writer who has the current "ticket" tries to get the + * lock by exclusively setting the writer_flag to 1, provided that the whole + * 32-bit is 0 (meaning no readers or writers working). On the other hand, + * a new reader tries to increment the "reader_count" field provided that + * the writer_flag is 0 (meaning there is no writer working). + * + * If some of the above operations fail, the reader or the writer sleeps + * until the related condition changes. When a working reader or writer + * completes its work, some readers or writers are sleeping, and the condition + * that suspended the reader or writer has changed, it wakes up the sleeping + * readers or writers. + * + * As already noted, this algorithm basically prefers writers. In order to + * prevent readers from starving, however, the algorithm also introduces the + * "writer quota" (Q). When Q consecutive writers have completed their work, + * suspending readers, the last writer will wake up the readers, even if a new + * writer is waiting. + * + * Implementation specific note: due to the combination of atomic operations + * and a mutex lock, ordering between the atomic operation and locks can be + * very sensitive in some cases. In particular, it is generally very important + * to check the atomic variable that requires a reader or writer to sleep after + * locking the mutex and before actually sleeping; otherwise, it could be very + * likely to cause a deadlock. For example, assume "var" is a variable + * atomically modified, then the corresponding code would be: + * if (var == need_sleep) { + * LOCK(lock); + * if (var == need_sleep) + * WAIT(cond, lock); + * UNLOCK(lock); + * } + * The second check is important, since "var" is protected by the atomic + * operation, not by the mutex, and can be changed just before sleeping. + * (The first "if" could be omitted, but this is also important in order to + * make the code efficient by avoiding the use of the mutex unless it is + * really necessary.) + */ + +#define WRITER_ACTIVE 0x1 +#define READER_INCR 0x2 + +static isc_result_t +isc__rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + int32_t cntflag; + + REQUIRE(VALID_RWLOCK(rwl)); + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_PRELOCK, "prelock"), rwl, type); +#endif + + if (type == isc_rwlocktype_read) { + if (rwl->write_requests != rwl->write_completions) { + /* there is a waiting or active writer */ + LOCK(&rwl->lock); + if (rwl->write_requests != rwl->write_completions) { + rwl->readers_waiting++; + WAIT(&rwl->readable, &rwl->lock); + rwl->readers_waiting--; + } + UNLOCK(&rwl->lock); + } + +#if defined(ISC_RWLOCK_USESTDATOMIC) + cntflag = atomic_fetch_add_explicit(&rwl->cnt_and_flag, + READER_INCR, + memory_order_relaxed); +#else + cntflag = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR); +#endif + POST(cntflag); + while (1) { + if ((rwl->cnt_and_flag & WRITER_ACTIVE) == 0) + break; + + /* A writer is still working */ + LOCK(&rwl->lock); + rwl->readers_waiting++; + if ((rwl->cnt_and_flag & WRITER_ACTIVE) != 0) + WAIT(&rwl->readable, &rwl->lock); + rwl->readers_waiting--; + UNLOCK(&rwl->lock); + + /* + * Typically, the reader should be able to get a lock + * at this stage: + * (1) there should have been no pending writer when + * the reader was trying to increment the + * counter; otherwise, the writer should be in + * the waiting queue, preventing the reader from + * proceeding to this point. + * (2) once the reader increments the counter, no + * more writer can get a lock. + * Still, it is possible another writer can work at + * this point, e.g. in the following scenario: + * A previous writer unlocks the writer lock. + * This reader proceeds to point (1). + * A new writer appears, and gets a new lock before + * the reader increments the counter. + * The reader then increments the counter. + * The previous writer notices there is a waiting + * reader who is almost ready, and wakes it up. + * So, the reader needs to confirm whether it can now + * read explicitly (thus we loop). Note that this is + * not an infinite process, since the reader has + * incremented the counter at this point. + */ + } + + /* + * If we are temporarily preferred to writers due to the writer + * quota, reset the condition (race among readers doesn't + * matter). + */ + rwl->write_granted = 0; + } else { + int32_t prev_writer; + + /* enter the waiting queue, and wait for our turn */ +#if defined(ISC_RWLOCK_USESTDATOMIC) + prev_writer = atomic_fetch_add_explicit(&rwl->write_requests, 1, + memory_order_relaxed); +#else + prev_writer = isc_atomic_xadd(&rwl->write_requests, 1); +#endif + while (rwl->write_completions != prev_writer) { + LOCK(&rwl->lock); + if (rwl->write_completions != prev_writer) { + WAIT(&rwl->writeable, &rwl->lock); + UNLOCK(&rwl->lock); + continue; + } + UNLOCK(&rwl->lock); + break; + } + + while (1) { +#if defined(ISC_RWLOCK_USESTDATOMIC) + int_fast32_t cntflag2 = 0; + atomic_compare_exchange_strong_explicit + (&rwl->cnt_and_flag, &cntflag2, WRITER_ACTIVE, + memory_order_relaxed, memory_order_relaxed); +#else + int32_t cntflag2; + cntflag2 = isc_atomic_cmpxchg(&rwl->cnt_and_flag, 0, + WRITER_ACTIVE); +#endif + + if (cntflag2 == 0) + break; + + /* Another active reader or writer is working. */ + LOCK(&rwl->lock); + if (rwl->cnt_and_flag != 0) + WAIT(&rwl->writeable, &rwl->lock); + UNLOCK(&rwl->lock); + } + + INSIST((rwl->cnt_and_flag & WRITER_ACTIVE) != 0); + rwl->write_granted++; + } + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_POSTLOCK, "postlock"), rwl, type); +#endif + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + int32_t cnt = 0; + int32_t max_cnt = rwl->spins * 2 + 10; + isc_result_t result = ISC_R_SUCCESS; + + if (max_cnt > RWLOCK_MAX_ADAPTIVE_COUNT) + max_cnt = RWLOCK_MAX_ADAPTIVE_COUNT; + + do { + if (cnt++ >= max_cnt) { + result = isc__rwlock_lock(rwl, type); + break; + } +#ifdef ISC_PLATFORM_BUSYWAITNOP + ISC_PLATFORM_BUSYWAITNOP; +#endif + } while (isc_rwlock_trylock(rwl, type) != ISC_R_SUCCESS); + + rwl->spins += (cnt - rwl->spins) / 8; + + return (result); +} + +isc_result_t +isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + int32_t cntflag; + + REQUIRE(VALID_RWLOCK(rwl)); + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_PRELOCK, "prelock"), rwl, type); +#endif + + if (type == isc_rwlocktype_read) { + /* If a writer is waiting or working, we fail. */ + if (rwl->write_requests != rwl->write_completions) + return (ISC_R_LOCKBUSY); + + /* Otherwise, be ready for reading. */ +#if defined(ISC_RWLOCK_USESTDATOMIC) + cntflag = atomic_fetch_add_explicit(&rwl->cnt_and_flag, + READER_INCR, + memory_order_relaxed); +#else + cntflag = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR); +#endif + if ((cntflag & WRITER_ACTIVE) != 0) { + /* + * A writer is working. We lose, and cancel the read + * request. + */ +#if defined(ISC_RWLOCK_USESTDATOMIC) + cntflag = atomic_fetch_sub_explicit + (&rwl->cnt_and_flag, READER_INCR, + memory_order_relaxed); +#else + cntflag = isc_atomic_xadd(&rwl->cnt_and_flag, + -READER_INCR); +#endif + /* + * If no other readers are waiting and we've suspended + * new writers in this short period, wake them up. + */ + if (cntflag == READER_INCR && + rwl->write_completions != rwl->write_requests) { + LOCK(&rwl->lock); + BROADCAST(&rwl->writeable); + UNLOCK(&rwl->lock); + } + + return (ISC_R_LOCKBUSY); + } + } else { + /* Try locking without entering the waiting queue. */ +#if defined(ISC_RWLOCK_USESTDATOMIC) + int_fast32_t zero = 0; + if (!atomic_compare_exchange_strong_explicit + (&rwl->cnt_and_flag, &zero, WRITER_ACTIVE, + memory_order_relaxed, memory_order_relaxed)) + return (ISC_R_LOCKBUSY); +#else + cntflag = isc_atomic_cmpxchg(&rwl->cnt_and_flag, 0, + WRITER_ACTIVE); + if (cntflag != 0) + return (ISC_R_LOCKBUSY); +#endif + + /* + * XXXJT: jump into the queue, possibly breaking the writer + * order. + */ +#if defined(ISC_RWLOCK_USESTDATOMIC) + atomic_fetch_sub_explicit(&rwl->write_completions, 1, + memory_order_relaxed); +#else + (void)isc_atomic_xadd(&rwl->write_completions, -1); +#endif + + rwl->write_granted++; + } + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_POSTLOCK, "postlock"), rwl, type); +#endif + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_rwlock_tryupgrade(isc_rwlock_t *rwl) { + REQUIRE(VALID_RWLOCK(rwl)); + +#if defined(ISC_RWLOCK_USESTDATOMIC) + { + int_fast32_t reader_incr = READER_INCR; + + /* Try to acquire write access. */ + atomic_compare_exchange_strong_explicit + (&rwl->cnt_and_flag, &reader_incr, WRITER_ACTIVE, + memory_order_relaxed, memory_order_relaxed); + /* + * There must have been no writer, and there must have + * been at least one reader. + */ + INSIST((reader_incr & WRITER_ACTIVE) == 0 && + (reader_incr & ~WRITER_ACTIVE) != 0); + + if (reader_incr == READER_INCR) { + /* + * We are the only reader and have been upgraded. + * Now jump into the head of the writer waiting queue. + */ + atomic_fetch_sub_explicit(&rwl->write_completions, 1, + memory_order_relaxed); + } else + return (ISC_R_LOCKBUSY); + + } +#else + { + int32_t prevcnt; + + /* Try to acquire write access. */ + prevcnt = isc_atomic_cmpxchg(&rwl->cnt_and_flag, + READER_INCR, WRITER_ACTIVE); + /* + * There must have been no writer, and there must have + * been at least one reader. + */ + INSIST((prevcnt & WRITER_ACTIVE) == 0 && + (prevcnt & ~WRITER_ACTIVE) != 0); + + if (prevcnt == READER_INCR) { + /* + * We are the only reader and have been upgraded. + * Now jump into the head of the writer waiting queue. + */ + (void)isc_atomic_xadd(&rwl->write_completions, -1); + } else + return (ISC_R_LOCKBUSY); + } +#endif + + return (ISC_R_SUCCESS); +} + +void +isc_rwlock_downgrade(isc_rwlock_t *rwl) { + int32_t prev_readers; + + REQUIRE(VALID_RWLOCK(rwl)); + +#if defined(ISC_RWLOCK_USESTDATOMIC) + { + /* Become an active reader. */ + prev_readers = atomic_fetch_add_explicit(&rwl->cnt_and_flag, + READER_INCR, + memory_order_relaxed); + /* We must have been a writer. */ + INSIST((prev_readers & WRITER_ACTIVE) != 0); + + /* Complete write */ + atomic_fetch_sub_explicit(&rwl->cnt_and_flag, WRITER_ACTIVE, + memory_order_relaxed); + atomic_fetch_add_explicit(&rwl->write_completions, 1, + memory_order_relaxed); + } +#else + { + /* Become an active reader. */ + prev_readers = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR); + /* We must have been a writer. */ + INSIST((prev_readers & WRITER_ACTIVE) != 0); + + /* Complete write */ + (void)isc_atomic_xadd(&rwl->cnt_and_flag, -WRITER_ACTIVE); + (void)isc_atomic_xadd(&rwl->write_completions, 1); + } +#endif + + /* Resume other readers */ + LOCK(&rwl->lock); + if (rwl->readers_waiting > 0) + BROADCAST(&rwl->readable); + UNLOCK(&rwl->lock); +} + +isc_result_t +isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + int32_t prev_cnt; + + REQUIRE(VALID_RWLOCK(rwl)); + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_PREUNLOCK, "preunlock"), rwl, type); +#endif + + if (type == isc_rwlocktype_read) { +#if defined(ISC_RWLOCK_USESTDATOMIC) + prev_cnt = atomic_fetch_sub_explicit(&rwl->cnt_and_flag, + READER_INCR, + memory_order_relaxed); +#else + prev_cnt = isc_atomic_xadd(&rwl->cnt_and_flag, -READER_INCR); +#endif + /* + * If we're the last reader and any writers are waiting, wake + * them up. We need to wake up all of them to ensure the + * FIFO order. + */ + if (prev_cnt == READER_INCR && + rwl->write_completions != rwl->write_requests) { + LOCK(&rwl->lock); + BROADCAST(&rwl->writeable); + UNLOCK(&rwl->lock); + } + } else { + bool wakeup_writers = true; + + /* + * Reset the flag, and (implicitly) tell other writers + * we are done. + */ +#if defined(ISC_RWLOCK_USESTDATOMIC) + atomic_fetch_sub_explicit(&rwl->cnt_and_flag, WRITER_ACTIVE, + memory_order_relaxed); + atomic_fetch_add_explicit(&rwl->write_completions, 1, + memory_order_relaxed); +#else + (void)isc_atomic_xadd(&rwl->cnt_and_flag, -WRITER_ACTIVE); + (void)isc_atomic_xadd(&rwl->write_completions, 1); +#endif + + if (rwl->write_granted >= rwl->write_quota || + rwl->write_requests == rwl->write_completions || + (rwl->cnt_and_flag & ~WRITER_ACTIVE) != 0) { + /* + * We have passed the write quota, no writer is + * waiting, or some readers are almost ready, pending + * possible writers. Note that the last case can + * happen even if write_requests != write_completions + * (which means a new writer in the queue), so we need + * to catch the case explicitly. + */ + LOCK(&rwl->lock); + if (rwl->readers_waiting > 0) { + wakeup_writers = false; + BROADCAST(&rwl->readable); + } + UNLOCK(&rwl->lock); + } + + if (rwl->write_requests != rwl->write_completions && + wakeup_writers) { + LOCK(&rwl->lock); + BROADCAST(&rwl->writeable); + UNLOCK(&rwl->lock); + } + } + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_POSTUNLOCK, "postunlock"), + rwl, type); +#endif + + return (ISC_R_SUCCESS); +} + +#else /* ISC_RWLOCK_USEATOMIC */ + +static isc_result_t +doit(isc_rwlock_t *rwl, isc_rwlocktype_t type, bool nonblock) { + bool skip = false; + bool done = false; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(VALID_RWLOCK(rwl)); + + LOCK(&rwl->lock); + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_PRELOCK, "prelock"), rwl, type); +#endif + + if (type == isc_rwlocktype_read) { + if (rwl->readers_waiting != 0) + skip = true; + while (!done) { + if (!skip && + ((rwl->active == 0 || + (rwl->type == isc_rwlocktype_read && + (rwl->writers_waiting == 0 || + rwl->granted < rwl->read_quota))))) + { + rwl->type = isc_rwlocktype_read; + rwl->active++; + rwl->granted++; + done = true; + } else if (nonblock) { + result = ISC_R_LOCKBUSY; + done = true; + } else { + skip = false; + rwl->readers_waiting++; + WAIT(&rwl->readable, &rwl->lock); + rwl->readers_waiting--; + } + } + } else { + if (rwl->writers_waiting != 0) + skip = true; + while (!done) { + if (!skip && rwl->active == 0) { + rwl->type = isc_rwlocktype_write; + rwl->active = 1; + rwl->granted++; + done = true; + } else if (nonblock) { + result = ISC_R_LOCKBUSY; + done = true; + } else { + skip = false; + rwl->writers_waiting++; + WAIT(&rwl->writeable, &rwl->lock); + rwl->writers_waiting--; + } + } + } + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_POSTLOCK, "postlock"), rwl, type); +#endif + + UNLOCK(&rwl->lock); + + return (result); +} + +isc_result_t +isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + int32_t cnt = 0; + int32_t max_cnt = rwl->spins * 2 + 10; + isc_result_t result = ISC_R_SUCCESS; + + if (max_cnt > RWLOCK_MAX_ADAPTIVE_COUNT) + max_cnt = RWLOCK_MAX_ADAPTIVE_COUNT; + + do { + if (cnt++ >= max_cnt) { + result = doit(rwl, type, false); + break; + } +#ifdef ISC_PLATFORM_BUSYWAITNOP + ISC_PLATFORM_BUSYWAITNOP; +#endif + } while (doit(rwl, type, true) != ISC_R_SUCCESS); + + rwl->spins += (cnt - rwl->spins) / 8; + + return (result); +} + +isc_result_t +isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + return (doit(rwl, type, true)); +} + +isc_result_t +isc_rwlock_tryupgrade(isc_rwlock_t *rwl) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(VALID_RWLOCK(rwl)); + LOCK(&rwl->lock); + REQUIRE(rwl->type == isc_rwlocktype_read); + REQUIRE(rwl->active != 0); + + /* If we are the only reader then succeed. */ + if (rwl->active == 1) { + rwl->original = (rwl->original == isc_rwlocktype_none) ? + isc_rwlocktype_read : isc_rwlocktype_none; + rwl->type = isc_rwlocktype_write; + } else + result = ISC_R_LOCKBUSY; + + UNLOCK(&rwl->lock); + return (result); +} + +void +isc_rwlock_downgrade(isc_rwlock_t *rwl) { + + REQUIRE(VALID_RWLOCK(rwl)); + LOCK(&rwl->lock); + REQUIRE(rwl->type == isc_rwlocktype_write); + REQUIRE(rwl->active == 1); + + rwl->type = isc_rwlocktype_read; + rwl->original = (rwl->original == isc_rwlocktype_none) ? + isc_rwlocktype_write : isc_rwlocktype_none; + /* + * Resume processing any read request that were blocked when + * we upgraded. + */ + if (rwl->original == isc_rwlocktype_none && + (rwl->writers_waiting == 0 || rwl->granted < rwl->read_quota) && + rwl->readers_waiting > 0) + BROADCAST(&rwl->readable); + + UNLOCK(&rwl->lock); +} + +isc_result_t +isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + + REQUIRE(VALID_RWLOCK(rwl)); + LOCK(&rwl->lock); + REQUIRE(rwl->type == type); + + UNUSED(type); + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_PREUNLOCK, "preunlock"), rwl, type); +#endif + + INSIST(rwl->active > 0); + rwl->active--; + if (rwl->active == 0) { + if (rwl->original != isc_rwlocktype_none) { + rwl->type = rwl->original; + rwl->original = isc_rwlocktype_none; + } + if (rwl->type == isc_rwlocktype_read) { + rwl->granted = 0; + if (rwl->writers_waiting > 0) { + rwl->type = isc_rwlocktype_write; + SIGNAL(&rwl->writeable); + } else if (rwl->readers_waiting > 0) { + /* Does this case ever happen? */ + BROADCAST(&rwl->readable); + } + } else { + if (rwl->readers_waiting > 0) { + if (rwl->writers_waiting > 0 && + rwl->granted < rwl->write_quota) { + SIGNAL(&rwl->writeable); + } else { + rwl->granted = 0; + rwl->type = isc_rwlocktype_read; + BROADCAST(&rwl->readable); + } + } else if (rwl->writers_waiting > 0) { + rwl->granted = 0; + SIGNAL(&rwl->writeable); + } else { + rwl->granted = 0; + } + } + } + INSIST(rwl->original == isc_rwlocktype_none); + +#ifdef ISC_RWLOCK_TRACE + print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK, + ISC_MSG_POSTUNLOCK, "postunlock"), + rwl, type); +#endif + + UNLOCK(&rwl->lock); + + return (ISC_R_SUCCESS); +} + +#endif /* ISC_RWLOCK_USEATOMIC */ +#else /* ISC_PLATFORM_USETHREADS */ + +isc_result_t +isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota, + unsigned int write_quota) +{ + REQUIRE(rwl != NULL); + + UNUSED(read_quota); + UNUSED(write_quota); + + rwl->type = isc_rwlocktype_read; + rwl->active = 0; + rwl->magic = RWLOCK_MAGIC; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + REQUIRE(VALID_RWLOCK(rwl)); + + if (type == isc_rwlocktype_read) { + if (rwl->type != isc_rwlocktype_read && rwl->active != 0) + return (ISC_R_LOCKBUSY); + rwl->type = isc_rwlocktype_read; + rwl->active++; + } else { + if (rwl->active != 0) + return (ISC_R_LOCKBUSY); + rwl->type = isc_rwlocktype_write; + rwl->active = 1; + } + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + return (isc_rwlock_lock(rwl, type)); +} + +isc_result_t +isc_rwlock_tryupgrade(isc_rwlock_t *rwl) { + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(VALID_RWLOCK(rwl)); + REQUIRE(rwl->type == isc_rwlocktype_read); + REQUIRE(rwl->active != 0); + + /* If we are the only reader then succeed. */ + if (rwl->active == 1) + rwl->type = isc_rwlocktype_write; + else + result = ISC_R_LOCKBUSY; + return (result); +} + +void +isc_rwlock_downgrade(isc_rwlock_t *rwl) { + + REQUIRE(VALID_RWLOCK(rwl)); + REQUIRE(rwl->type == isc_rwlocktype_write); + REQUIRE(rwl->active == 1); + + rwl->type = isc_rwlocktype_read; +} + +isc_result_t +isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) { + REQUIRE(VALID_RWLOCK(rwl)); + REQUIRE(rwl->type == type); + + UNUSED(type); + + INSIST(rwl->active > 0); + rwl->active--; + + return (ISC_R_SUCCESS); +} + +void +isc_rwlock_destroy(isc_rwlock_t *rwl) { + REQUIRE(rwl != NULL); + REQUIRE(rwl->active == 0); + rwl->magic = 0; +} + +#endif /* ISC_PLATFORM_USETHREADS */ diff --git a/lib/isc/safe.c b/lib/isc/safe.c new file mode 100644 index 0000000..7a464b6 --- /dev/null +++ b/lib/isc/safe.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/safe.h> +#include <isc/string.h> +#include <isc/util.h> + +#ifdef WIN32 +#include <windows.h> +#endif + +#ifdef _MSC_VER +#pragma optimize("", off) +#endif + +bool +isc_safe_memequal(const void *s1, const void *s2, size_t n) { + uint8_t acc = 0; + + if (n != 0U) { + const uint8_t *p1 = s1, *p2 = s2; + + do { + acc |= *p1++ ^ *p2++; + } while (--n != 0U); + } + return (acc == 0); +} + + +int +isc_safe_memcompare(const void *b1, const void *b2, size_t len) { + const unsigned char *p1 = b1, *p2 = b2; + size_t i; + int res = 0, done = 0; + + for (i = 0; i < len; i++) { + /* lt is -1 if p1[i] < p2[i]; else 0. */ + int lt = (p1[i] - p2[i]) >> CHAR_BIT; + + /* gt is -1 if p1[i] > p2[i]; else 0. */ + int gt = (p2[i] - p1[i]) >> CHAR_BIT; + + /* cmp is 1 if p1[i] > p2[i]; -1 if p1[i] < p2[i]; else 0. */ + int cmp = lt - gt; + + /* set res = cmp if !done. */ + res |= cmp & ~done; + + /* set done if p1[i] != p2[i]. */ + done |= lt | gt; + } + + return (res); +} + +void +isc_safe_memwipe(void *ptr, size_t len) { + if (ISC_UNLIKELY(ptr == NULL || len == 0)) + return; + +#ifdef WIN32 + SecureZeroMemory(ptr, len); +#elif HAVE_EXPLICIT_BZERO + explicit_bzero(ptr, len); +#else + memset(ptr, 0, len); +#endif +} diff --git a/lib/isc/serial.c b/lib/isc/serial.c new file mode 100644 index 0000000..860a0c6 --- /dev/null +++ b/lib/isc/serial.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/serial.h> + +bool +isc_serial_lt(uint32_t a, uint32_t b) { + /* + * Undefined => false + */ + if (a == (b ^ 0x80000000U)) + return (false); + return (((int32_t)(a - b) < 0) ? true : false); +} + +bool +isc_serial_gt(uint32_t a, uint32_t b) { + return (((int32_t)(a - b) > 0) ? true : false); +} + +bool +isc_serial_le(uint32_t a, uint32_t b) { + return ((a == b) ? true : isc_serial_lt(a, b)); +} + +bool +isc_serial_ge(uint32_t a, uint32_t b) { + return ((a == b) ? true : isc_serial_gt(a, b)); +} + +bool +isc_serial_eq(uint32_t a, uint32_t b) { + return ((a == b) ? true : false); +} + +bool +isc_serial_ne(uint32_t a, uint32_t b) { + return ((a != b) ? true : false); +} diff --git a/lib/isc/sha1.c b/lib/isc/sha1.c new file mode 100644 index 0000000..c0c89b4 --- /dev/null +++ b/lib/isc/sha1.c @@ -0,0 +1,456 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id$ */ + +/* $NetBSD: sha1.c,v 1.5 2000/01/22 22:19:14 mycroft Exp $ */ +/* $OpenBSD: sha1.c,v 1.9 1997/07/23 21:12:32 kstailey Exp $ */ + +/*! \file + * SHA-1 in C + * \author By Steve Reid <steve@edmweb.com> + * 100% Public Domain + * \verbatim + * Test Vectors (from FIPS PUB 180-1) + * "abc" + * A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + * 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 + * A million repetitions of "a" + * 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + * \endverbatim + */ + +#include "config.h" + +#include <stdbool.h> + +#include <isc/assertions.h> +#include <isc/platform.h> +#include <isc/safe.h> +#include <isc/sha1.h> +#include <isc/string.h> +#include <isc/types.h> +#include <isc/util.h> + +#if PKCS11CRYPTO +#include <pk11/internal.h> +#include <pk11/pk11.h> +#endif + +#ifdef ISC_PLATFORM_OPENSSLHASH +#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) +#define EVP_MD_CTX_new() &(context->_ctx) +#define EVP_MD_CTX_free(ptr) EVP_MD_CTX_cleanup(ptr) +#endif + +void +isc_sha1_init(isc_sha1_t *context) +{ + INSIST(context != NULL); + + context->ctx = EVP_MD_CTX_new(); + RUNTIME_CHECK(context->ctx != NULL); + if (EVP_DigestInit(context->ctx, EVP_sha1()) != 1) { + FATAL_ERROR(__FILE__, __LINE__, "Cannot initialize SHA1."); + } +} + +void +isc_sha1_invalidate(isc_sha1_t *context) { + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +void +isc_sha1_update(isc_sha1_t *context, const unsigned char *data, + unsigned int len) +{ + INSIST(context != 0); + INSIST(context->ctx != 0); + INSIST(data != 0); + + RUNTIME_CHECK(EVP_DigestUpdate(context->ctx, + (const void *) data, + (size_t) len) == 1); +} + +void +isc_sha1_final(isc_sha1_t *context, unsigned char *digest) { + INSIST(digest != 0); + INSIST(context != 0); + INSIST(context->ctx != 0); + + RUNTIME_CHECK(EVP_DigestFinal(context->ctx, digest, NULL) == 1); + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +#elif PKCS11CRYPTO + +void +isc_sha1_init(isc_sha1_t *ctx) { + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA_1, NULL, 0 }; + + RUNTIME_CHECK(pk11_get_session(ctx, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + PK11_FATALCHECK(pkcs_C_DigestInit, (ctx->session, &mech)); +} + +void +isc_sha1_invalidate(isc_sha1_t *ctx) { + CK_BYTE garbage[ISC_SHA1_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA1_DIGESTLENGTH; + + if (ctx->handle == NULL) + return; + (void) pkcs_C_DigestFinal(ctx->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + pk11_return_session(ctx); +} + +void +isc_sha1_update(isc_sha1_t *ctx, const unsigned char *buf, unsigned int len) { + CK_RV rv; + CK_BYTE_PTR pPart; + + DE_CONST(buf, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (ctx->session, pPart, (CK_ULONG) len)); +} + +void +isc_sha1_final(isc_sha1_t *ctx, unsigned char *digest) { + CK_RV rv; + CK_ULONG len = ISC_SHA1_DIGESTLENGTH; + + PK11_FATALCHECK(pkcs_C_DigestFinal, + (ctx->session, (CK_BYTE_PTR) digest, &len)); + pk11_return_session(ctx); +} + +#else + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/*@{*/ +/*! + * blk0() and blk() perform the initial expand. + * I got the idea of expanding during the round function from SSLeay + */ +#if !defined(WORDS_BIGENDIAN) +# define blk0(i) \ + (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) \ + | (rol(block->l[i], 8) & 0x00FF00FF)) +#else +# define blk0(i) block->l[i] +#endif +#define blk(i) \ + (block->l[i & 15] = rol(block->l[(i + 13) & 15] \ + ^ block->l[(i + 8) & 15] \ + ^ block->l[(i + 2) & 15] \ + ^ block->l[i & 15], 1)) + +/*@}*/ +/*@{*/ +/*! + * (R0+R1), R2, R3, R4 are the different operations (rounds) used in SHA1 + */ +#define R0(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ + w = rol(w, 30); +#define R3(v,w,x,y,z,i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w = rol(w, 30); + +/*@}*/ + +typedef union { + unsigned char c[64]; + unsigned int l[16]; +} CHAR64LONG16; + +#ifdef __sparc_v9__ +static void do_R01(uint32_t *a, uint32_t *b, uint32_t *c, + uint32_t *d, uint32_t *e, CHAR64LONG16 *); +static void do_R2(uint32_t *a, uint32_t *b, uint32_t *c, + uint32_t *d, uint32_t *e, CHAR64LONG16 *); +static void do_R3(uint32_t *a, uint32_t *b, uint32_t *c, + uint32_t *d, uint32_t *e, CHAR64LONG16 *); +static void do_R4(uint32_t *a, uint32_t *b, uint32_t *c, + uint32_t *d, uint32_t *e, CHAR64LONG16 *); + +#define nR0(v,w,x,y,z,i) R0(*v,*w,*x,*y,*z,i) +#define nR1(v,w,x,y,z,i) R1(*v,*w,*x,*y,*z,i) +#define nR2(v,w,x,y,z,i) R2(*v,*w,*x,*y,*z,i) +#define nR3(v,w,x,y,z,i) R3(*v,*w,*x,*y,*z,i) +#define nR4(v,w,x,y,z,i) R4(*v,*w,*x,*y,*z,i) + +static void +do_R01(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, + uint32_t *e, CHAR64LONG16 *block) +{ + nR0(a,b,c,d,e, 0); nR0(e,a,b,c,d, 1); nR0(d,e,a,b,c, 2); + nR0(c,d,e,a,b, 3); nR0(b,c,d,e,a, 4); nR0(a,b,c,d,e, 5); + nR0(e,a,b,c,d, 6); nR0(d,e,a,b,c, 7); nR0(c,d,e,a,b, 8); + nR0(b,c,d,e,a, 9); nR0(a,b,c,d,e,10); nR0(e,a,b,c,d,11); + nR0(d,e,a,b,c,12); nR0(c,d,e,a,b,13); nR0(b,c,d,e,a,14); + nR0(a,b,c,d,e,15); nR1(e,a,b,c,d,16); nR1(d,e,a,b,c,17); + nR1(c,d,e,a,b,18); nR1(b,c,d,e,a,19); +} + +static void +do_R2(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, + uint32_t *e, CHAR64LONG16 *block) +{ + nR2(a,b,c,d,e,20); nR2(e,a,b,c,d,21); nR2(d,e,a,b,c,22); + nR2(c,d,e,a,b,23); nR2(b,c,d,e,a,24); nR2(a,b,c,d,e,25); + nR2(e,a,b,c,d,26); nR2(d,e,a,b,c,27); nR2(c,d,e,a,b,28); + nR2(b,c,d,e,a,29); nR2(a,b,c,d,e,30); nR2(e,a,b,c,d,31); + nR2(d,e,a,b,c,32); nR2(c,d,e,a,b,33); nR2(b,c,d,e,a,34); + nR2(a,b,c,d,e,35); nR2(e,a,b,c,d,36); nR2(d,e,a,b,c,37); + nR2(c,d,e,a,b,38); nR2(b,c,d,e,a,39); +} + +static void +do_R3(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, + uint32_t *e, CHAR64LONG16 *block) +{ + nR3(a,b,c,d,e,40); nR3(e,a,b,c,d,41); nR3(d,e,a,b,c,42); + nR3(c,d,e,a,b,43); nR3(b,c,d,e,a,44); nR3(a,b,c,d,e,45); + nR3(e,a,b,c,d,46); nR3(d,e,a,b,c,47); nR3(c,d,e,a,b,48); + nR3(b,c,d,e,a,49); nR3(a,b,c,d,e,50); nR3(e,a,b,c,d,51); + nR3(d,e,a,b,c,52); nR3(c,d,e,a,b,53); nR3(b,c,d,e,a,54); + nR3(a,b,c,d,e,55); nR3(e,a,b,c,d,56); nR3(d,e,a,b,c,57); + nR3(c,d,e,a,b,58); nR3(b,c,d,e,a,59); +} + +static void +do_R4(uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, + uint32_t *e, CHAR64LONG16 *block) +{ + nR4(a,b,c,d,e,60); nR4(e,a,b,c,d,61); nR4(d,e,a,b,c,62); + nR4(c,d,e,a,b,63); nR4(b,c,d,e,a,64); nR4(a,b,c,d,e,65); + nR4(e,a,b,c,d,66); nR4(d,e,a,b,c,67); nR4(c,d,e,a,b,68); + nR4(b,c,d,e,a,69); nR4(a,b,c,d,e,70); nR4(e,a,b,c,d,71); + nR4(d,e,a,b,c,72); nR4(c,d,e,a,b,73); nR4(b,c,d,e,a,74); + nR4(a,b,c,d,e,75); nR4(e,a,b,c,d,76); nR4(d,e,a,b,c,77); + nR4(c,d,e,a,b,78); nR4(b,c,d,e,a,79); +} +#endif + +/*! + * Hash a single 512-bit block. This is the core of the algorithm. + */ +static void +transform(uint32_t state[5], const unsigned char buffer[64]) { + uint32_t a, b, c, d, e; + CHAR64LONG16 *block; + CHAR64LONG16 workspace; + + INSIST(buffer != NULL); + INSIST(state != NULL); + + block = &workspace; + (void)memmove(block, buffer, 64); + + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + +#ifdef __sparc_v9__ + do_R01(&a, &b, &c, &d, &e, block); + do_R2(&a, &b, &c, &d, &e, block); + do_R3(&a, &b, &c, &d, &e, block); + do_R4(&a, &b, &c, &d, &e, block); +#else + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); +#endif + + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + + /* Wipe variables */ + a = b = c = d = e = 0; + /* Avoid compiler warnings */ + POST(a); POST(b); POST(c); POST(d); POST(e); +} + + +/*! + * isc_sha1_init - Initialize new context + */ +void +isc_sha1_init(isc_sha1_t *context) +{ + INSIST(context != NULL); + + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = 0; + context->count[1] = 0; +} + +void +isc_sha1_invalidate(isc_sha1_t *context) { + isc_safe_memwipe(context, sizeof(*context)); +} + +/*! + * Run your data through this. + */ +void +isc_sha1_update(isc_sha1_t *context, const unsigned char *data, + unsigned int len) +{ + unsigned int i, j; + + INSIST(context != 0); + INSIST(data != 0); + + j = context->count[0]; + if ((context->count[0] += len << 3) < j) + context->count[1] += (len >> 29) + 1; + j = (j >> 3) & 63; + if ((j + len) > 63) { + (void)memmove(&context->buffer[j], data, (i = 64 - j)); + transform(context->state, context->buffer); + for (; i + 63 < len; i += 64) + transform(context->state, &data[i]); + j = 0; + } else { + i = 0; + } + + (void)memmove(&context->buffer[j], &data[i], len - i); +} + + +/*! + * Add padding and return the message digest. + */ + +static const unsigned char final_200 = 128; +static const unsigned char final_0 = 0; + +void +isc_sha1_final(isc_sha1_t *context, unsigned char *digest) { + unsigned int i; + unsigned char finalcount[8]; + + INSIST(digest != 0); + INSIST(context != 0); + + for (i = 0; i < 8; i++) { + /* Endian independent */ + finalcount[i] = (unsigned char) + ((context->count[(i >= 4 ? 0 : 1)] + >> ((3 - (i & 3)) * 8)) & 255); + } + + isc_sha1_update(context, &final_200, 1); + while ((context->count[0] & 504) != 448) + isc_sha1_update(context, &final_0, 1); + /* The next Update should cause a transform() */ + isc_sha1_update(context, finalcount, 8); + + if (digest) { + for (i = 0; i < 20; i++) + digest[i] = (unsigned char) + ((context->state[i >> 2] + >> ((3 - (i & 3)) * 8)) & 255); + } + + isc_safe_memwipe(context, sizeof(*context)); +} +#endif + +/* + * Check for SHA-1 support; if it does not work, raise a fatal error. + * + * Use "a" as the test vector. + * + * Standard use is testing false and result true. + * Testing use is testing true and result false; + */ +bool +isc_sha1_check(bool testing) { + isc_sha1_t ctx; + unsigned char input = 'a'; + unsigned char digest[ISC_SHA1_DIGESTLENGTH]; + unsigned char expected[] = { + 0x86, 0xf7, 0xe4, 0x37, 0xfa, 0xa5, 0xa7, 0xfc, + 0xe1, 0x5d, 0x1d, 0xdc, 0xb9, 0xea, 0xea, 0xea, + 0x37, 0x76, 0x67, 0xb8 + }; + + INSIST(sizeof(expected) == ISC_SHA1_DIGESTLENGTH); + + /* + * Introduce a fault for testing. + */ + if (testing) { + input ^= 0x01; + } + + /* + * These functions do not return anything; any failure will be fatal. + */ + isc_sha1_init(&ctx); + isc_sha1_update(&ctx, &input, 1U); + isc_sha1_final(&ctx, digest); + + /* + * Must return true in standard case, should return false for testing. + */ + return (memcmp(digest, expected, ISC_SHA1_DIGESTLENGTH) == 0); +} diff --git a/lib/isc/sha2.c b/lib/isc/sha2.c new file mode 100644 index 0000000..8e502bf --- /dev/null +++ b/lib/isc/sha2.c @@ -0,0 +1,1767 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id$ */ + +/* $FreeBSD: src/sys/crypto/sha2/sha2.c,v 1.2.2.2 2002/03/05 08:36:47 ume Exp $ */ +/* $KAME: sha2.c,v 1.8 2001/11/08 01:07:52 itojun Exp $ */ + +/* + * sha2.c + * + * Version 1.0.0beta1 + * + * Written by Aaron D. Gifford <me@aarongifford.com> + * + * Copyright 2000 Aaron D. Gifford. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTOR(S) ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTOR(S) BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + + +#include <config.h> + +#include <inttypes.h> + +#include <isc/assertions.h> +#include <isc/platform.h> +#include <isc/safe.h> +#include <isc/sha2.h> +#include <isc/string.h> +#include <isc/util.h> + +#if PKCS11CRYPTO +#include <pk11/internal.h> +#include <pk11/pk11.h> +#endif + +#if defined(ISC_PLATFORM_OPENSSLHASH) && !defined(LIBRESSL_VERSION_NUMBER) +#if OPENSSL_VERSION_NUMBER < 0x10100000L +#define EVP_MD_CTX_new() &(context->_ctx) +#define EVP_MD_CTX_free(ptr) EVP_MD_CTX_cleanup(ptr) +#define EVP_MD_CTX_reset(c) EVP_MD_CTX_cleanup(c) +#endif + +void +isc_sha224_init(isc_sha224_t *context) { + if (context == (isc_sha224_t *)0) { + return; + } + context->ctx = EVP_MD_CTX_new(); + RUNTIME_CHECK(context->ctx != NULL); + if (EVP_DigestInit(context->ctx, EVP_sha224()) != 1) { + FATAL_ERROR(__FILE__, __LINE__, "Cannot initialize SHA224."); + } +} + +void +isc_sha224_invalidate(isc_sha224_t *context) { + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +void +isc_sha224_update(isc_sha224_t *context, const uint8_t* data, size_t len) { + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha224_t *)0); + REQUIRE(context->ctx != (EVP_MD_CTX *)0); + REQUIRE(data != (uint8_t*)0); + + RUNTIME_CHECK(EVP_DigestUpdate(context->ctx, + (const void *) data, len) == 1); +} + +void +isc_sha224_final(uint8_t digest[], isc_sha224_t *context) { + /* Sanity check: */ + REQUIRE(context != (isc_sha224_t *)0); + REQUIRE(context->ctx != (EVP_MD_CTX *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) + RUNTIME_CHECK(EVP_DigestFinal(context->ctx, + digest, NULL) == 1); + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +void +isc_sha256_init(isc_sha256_t *context) { + if (context == (isc_sha256_t *)0) { + return; + } + context->ctx = EVP_MD_CTX_new(); + RUNTIME_CHECK(context->ctx != NULL); + if (EVP_DigestInit(context->ctx, EVP_sha256()) != 1) { + FATAL_ERROR(__FILE__, __LINE__, "Cannot initialize SHA256."); + } +} + +void +isc_sha256_invalidate(isc_sha256_t *context) { + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +void +isc_sha256_update(isc_sha256_t *context, const uint8_t *data, size_t len) { + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha256_t *)0); + REQUIRE(context->ctx != (EVP_MD_CTX *)0); + REQUIRE(data != (uint8_t*)0); + + RUNTIME_CHECK(EVP_DigestUpdate(context->ctx, + (const void *) data, len) == 1); +} + +void +isc_sha256_final(uint8_t digest[], isc_sha256_t *context) { + /* Sanity check: */ + REQUIRE(context != (isc_sha256_t *)0); + REQUIRE(context->ctx != (EVP_MD_CTX *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) + RUNTIME_CHECK(EVP_DigestFinal(context->ctx, + digest, NULL) == 1); + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +void +isc_sha512_init(isc_sha512_t *context) { + if (context == (isc_sha512_t *)0) { + return; + } + context->ctx = EVP_MD_CTX_new(); + RUNTIME_CHECK(context->ctx != NULL); + if (EVP_DigestInit(context->ctx, EVP_sha512()) != 1) { + FATAL_ERROR(__FILE__, __LINE__, "Cannot initialize SHA512."); + } +} + +void +isc_sha512_invalidate(isc_sha512_t *context) { + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +void isc_sha512_update(isc_sha512_t *context, const uint8_t *data, size_t len) { + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha512_t *)0); + REQUIRE(context->ctx != (EVP_MD_CTX *)0); + REQUIRE(data != (uint8_t*)0); + + RUNTIME_CHECK(EVP_DigestUpdate(context->ctx, + (const void *) data, len) == 1); +} + +void isc_sha512_final(uint8_t digest[], isc_sha512_t *context) { + /* Sanity check: */ + REQUIRE(context != (isc_sha512_t *)0); + REQUIRE(context->ctx != (EVP_MD_CTX *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) + RUNTIME_CHECK(EVP_DigestFinal(context->ctx, + digest, NULL) == 1); + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +void +isc_sha384_init(isc_sha384_t *context) { + if (context == (isc_sha384_t *)0) { + return; + } + context->ctx = EVP_MD_CTX_new(); + RUNTIME_CHECK(context->ctx != NULL); + if (EVP_DigestInit(context->ctx, EVP_sha384()) != 1) { + FATAL_ERROR(__FILE__, __LINE__, "Cannot initialize SHA384."); + } +} + +void +isc_sha384_invalidate(isc_sha384_t *context) { + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +void +isc_sha384_update(isc_sha384_t *context, const uint8_t* data, size_t len) { + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha512_t *)0); + REQUIRE(context->ctx != (EVP_MD_CTX *)0); + REQUIRE(data != (uint8_t*)0); + + RUNTIME_CHECK(EVP_DigestUpdate(context->ctx, + (const void *) data, len) == 1); +} + +void +isc_sha384_final(uint8_t digest[], isc_sha384_t *context) { + /* Sanity check: */ + REQUIRE(context != (isc_sha384_t *)0); + REQUIRE(context->ctx != (EVP_MD_CTX *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) + RUNTIME_CHECK(EVP_DigestFinal(context->ctx, + digest, NULL) == 1); + EVP_MD_CTX_free(context->ctx); + context->ctx = NULL; +} + +#elif PKCS11CRYPTO + +void +isc_sha224_init(isc_sha224_t *context) { + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA224, NULL, 0 }; + + if (context == (isc_sha224_t *)0) { + return; + } + RUNTIME_CHECK(pk11_get_session(context, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + PK11_FATALCHECK(pkcs_C_DigestInit, (context->session, &mech)); +} + +void +isc_sha224_invalidate(isc_sha224_t *context) { + CK_BYTE garbage[ISC_SHA224_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA224_DIGESTLENGTH; + + if (context->handle == NULL) + return; + (void) pkcs_C_DigestFinal(context->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + pk11_return_session(context); +} + +void +isc_sha224_update(isc_sha224_t *context, const uint8_t* data, size_t len) { + CK_RV rv; + CK_BYTE_PTR pPart; + + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha224_t *)0 && data != (uint8_t*)0); + + DE_CONST(data, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (context->session, pPart, (CK_ULONG) len)); +} + +void +isc_sha224_final(uint8_t digest[], isc_sha224_t *context) { + CK_RV rv; + CK_ULONG len = ISC_SHA224_DIGESTLENGTH; + + /* Sanity check: */ + REQUIRE(context != (isc_sha224_t *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) { + PK11_FATALCHECK(pkcs_C_DigestFinal, + (context->session, + (CK_BYTE_PTR) digest, + &len)); + } else { + CK_BYTE garbage[ISC_SHA224_DIGESTLENGTH]; + + (void) pkcs_C_DigestFinal(context->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + } + pk11_return_session(context); +} + +void +isc_sha256_init(isc_sha256_t *context) { + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA256, NULL, 0 }; + + if (context == (isc_sha256_t *)0) { + return; + } + RUNTIME_CHECK(pk11_get_session(context, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + PK11_FATALCHECK(pkcs_C_DigestInit, (context->session, &mech)); +} + +void +isc_sha256_invalidate(isc_sha256_t *context) { + CK_BYTE garbage[ISC_SHA256_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA256_DIGESTLENGTH; + + if (context->handle == NULL) + return; + (void) pkcs_C_DigestFinal(context->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + pk11_return_session(context); +} + +void +isc_sha256_update(isc_sha256_t *context, const uint8_t* data, size_t len) { + CK_RV rv; + CK_BYTE_PTR pPart; + + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha256_t *)0 && data != (uint8_t*)0); + + DE_CONST(data, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (context->session, pPart, (CK_ULONG) len)); +} + +void +isc_sha256_final(uint8_t digest[], isc_sha256_t *context) { + CK_RV rv; + CK_ULONG len = ISC_SHA256_DIGESTLENGTH; + + /* Sanity check: */ + REQUIRE(context != (isc_sha256_t *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) { + PK11_FATALCHECK(pkcs_C_DigestFinal, + (context->session, + (CK_BYTE_PTR) digest, + &len)); + } else { + CK_BYTE garbage[ISC_SHA256_DIGESTLENGTH]; + + (void) pkcs_C_DigestFinal(context->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + } + pk11_return_session(context); +} + +void +isc_sha512_init(isc_sha512_t *context) { + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA512, NULL, 0 }; + + if (context == (isc_sha512_t *)0) { + return; + } + RUNTIME_CHECK(pk11_get_session(context, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + PK11_FATALCHECK(pkcs_C_DigestInit, (context->session, &mech)); +} + +void +isc_sha512_invalidate(isc_sha512_t *context) { + CK_BYTE garbage[ISC_SHA512_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA512_DIGESTLENGTH; + + if (context->handle == NULL) + return; + (void) pkcs_C_DigestFinal(context->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + pk11_return_session(context); +} + +void +isc_sha512_update(isc_sha512_t *context, const uint8_t* data, size_t len) { + CK_RV rv; + CK_BYTE_PTR pPart; + + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha512_t *)0 && data != (uint8_t*)0); + + DE_CONST(data, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (context->session, pPart, (CK_ULONG) len)); +} + +void +isc_sha512_final(uint8_t digest[], isc_sha512_t *context) { + CK_RV rv; + CK_ULONG len = ISC_SHA512_DIGESTLENGTH; + + /* Sanity check: */ + REQUIRE(context != (isc_sha512_t *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) { + PK11_FATALCHECK(pkcs_C_DigestFinal, + (context->session, + (CK_BYTE_PTR) digest, + &len)); + } else { + CK_BYTE garbage[ISC_SHA512_DIGESTLENGTH]; + + (void) pkcs_C_DigestFinal(context->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + } + pk11_return_session(context); +} + +void +isc_sha384_init(isc_sha384_t *context) { + CK_RV rv; + CK_MECHANISM mech = { CKM_SHA384, NULL, 0 }; + + if (context == (isc_sha384_t *)0) { + return; + } + RUNTIME_CHECK(pk11_get_session(context, OP_DIGEST, true, false, + false, NULL, 0) == ISC_R_SUCCESS); + PK11_FATALCHECK(pkcs_C_DigestInit, (context->session, &mech)); +} + +void +isc_sha384_invalidate(isc_sha384_t *context) { + CK_BYTE garbage[ISC_SHA384_DIGESTLENGTH]; + CK_ULONG len = ISC_SHA384_DIGESTLENGTH; + + if (context->handle == NULL) + return; + (void) pkcs_C_DigestFinal(context->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + pk11_return_session(context); +} + +void +isc_sha384_update(isc_sha384_t *context, const uint8_t* data, size_t len) { + CK_RV rv; + CK_BYTE_PTR pPart; + + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha384_t *)0 && data != (uint8_t*)0); + + DE_CONST(data, pPart); + PK11_FATALCHECK(pkcs_C_DigestUpdate, + (context->session, pPart, (CK_ULONG) len)); +} + +void +isc_sha384_final(uint8_t digest[], isc_sha384_t *context) { + CK_RV rv; + CK_ULONG len = ISC_SHA384_DIGESTLENGTH; + + /* Sanity check: */ + REQUIRE(context != (isc_sha384_t *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) { + PK11_FATALCHECK(pkcs_C_DigestFinal, + (context->session, + (CK_BYTE_PTR) digest, + &len)); + } else { + CK_BYTE garbage[ISC_SHA384_DIGESTLENGTH]; + + (void) pkcs_C_DigestFinal(context->session, garbage, &len); + isc_safe_memwipe(garbage, sizeof(garbage)); + } + pk11_return_session(context); +} + +#else + +/* + * UNROLLED TRANSFORM LOOP NOTE: + * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform + * loop version for the hash transform rounds (defined using macros + * later in this file). Either define on the command line, for example: + * + * cc -DISC_SHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c + * + * or define below: + * + * \#define ISC_SHA2_UNROLL_TRANSFORM + * + */ + +/*** SHA-256/384/512 Machine Architecture Definitions *****************/ +/* + * BYTE_ORDER NOTE: + * + * Please make sure that your system defines BYTE_ORDER. If your + * architecture is little-endian, make sure it also defines + * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are + * equivalent. + * + * If your system does not define the above, then you can do so by + * hand like this: + * + * \#define LITTLE_ENDIAN 1234 + * \#define BIG_ENDIAN 4321 + * + * And for little-endian machines, add: + * + * \#define BYTE_ORDER LITTLE_ENDIAN + * + * Or for big-endian machines: + * + * \#define BYTE_ORDER BIG_ENDIAN + * + * The FreeBSD machine this was written on defines BYTE_ORDER + * appropriately by including <sys/types.h> (which in turn includes + * <machine/endian.h> where the appropriate definitions are actually + * made). + */ +#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) +#ifndef BYTE_ORDER +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifdef WORDS_BIGENDIAN +#define BYTE_ORDER BIG_ENDIAN +#else +#define BYTE_ORDER LITTLE_ENDIAN +#endif +#else +#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN +#endif +#endif + +/*** SHA-256/384/512 Various Length Definitions ***********************/ +/* NOTE: Most of these are in sha2.h */ +#define ISC_SHA256_SHORT_BLOCK_LENGTH (ISC_SHA256_BLOCK_LENGTH - 8) +#define ISC_SHA384_SHORT_BLOCK_LENGTH (ISC_SHA384_BLOCK_LENGTH - 16) +#define ISC_SHA512_SHORT_BLOCK_LENGTH (ISC_SHA512_BLOCK_LENGTH - 16) + + +/*** ENDIAN REVERSAL MACROS *******************************************/ +#if BYTE_ORDER == LITTLE_ENDIAN +#define REVERSE32(w,x) { \ + uint32_t tmp = (w); \ + tmp = (tmp >> 16) | (tmp << 16); \ + (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ +} +#ifdef WIN32 +#define REVERSE64(w,x) { \ + uint64_t tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00UL) >> 8) | \ + ((tmp & 0x00ff00ff00ff00ffUL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000UL) >> 16) | \ + ((tmp & 0x0000ffff0000ffffUL) << 16); \ +} +#else +#define REVERSE64(w,x) { \ + uint64_t tmp = (w); \ + tmp = (tmp >> 32) | (tmp << 32); \ + tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ + ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ + (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ + ((tmp & 0x0000ffff0000ffffULL) << 16); \ +} +#endif +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +/* + * Macro for incrementally adding the unsigned 64-bit integer n to the + * unsigned 128-bit integer (represented using a two-element array of + * 64-bit words): + */ +#define ADDINC128(w,n) { \ + (w)[0] += (uint64_t)(n); \ + if ((w)[0] < (n)) { \ + (w)[1]++; \ + } \ +} + +/*** THE SIX LOGICAL FUNCTIONS ****************************************/ +/* + * Bit shifting and rotation (used by the six SHA-XYZ logical functions: + * + * NOTE: The naming of R and S appears backwards here (R is a SHIFT and + * S is a ROTATION) because the SHA-256/384/512 description document + * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this + * same "backwards" definition. + */ +/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ +#define R(b,x) ((x) >> (b)) +/* 32-bit Rotate-right (used in SHA-256): */ +#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) +/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ +#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) + +/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ +#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) +#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) + +/* Four of six logical functions used in SHA-256: */ +#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) +#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) +#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) +#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) + +/* Four of six logical functions used in SHA-384 and SHA-512: */ +#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) +#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) +#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) +#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) + +/*** INTERNAL FUNCTION PROTOTYPES *************************************/ +/* NOTE: These should not be accessed directly from outside this + * library -- they are intended for private internal visibility/use + * only. + */ +void isc_sha512_last(isc_sha512_t *); +void isc_sha256_transform(isc_sha256_t *, const uint32_t*); +void isc_sha512_transform(isc_sha512_t *, const uint64_t*); + + +/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ +/* Hash constant words K for SHA-224 and SHA-256: */ +static const uint32_t K256[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, + 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, + 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, + 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, + 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, + 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, + 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, + 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, + 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, + 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, + 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, + 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, + 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +/* Initial hash value H for SHA-224: */ +static const uint32_t sha224_initial_hash_value[8] = { + 0xc1059ed8UL, + 0x367cd507UL, + 0x3070dd17UL, + 0xf70e5939UL, + 0xffc00b31UL, + 0x68581511UL, + 0x64f98fa7UL, + 0xbefa4fa4UL +}; + +/* Initial hash value H for SHA-256: */ +static const uint32_t sha256_initial_hash_value[8] = { + 0x6a09e667UL, + 0xbb67ae85UL, + 0x3c6ef372UL, + 0xa54ff53aUL, + 0x510e527fUL, + 0x9b05688cUL, + 0x1f83d9abUL, + 0x5be0cd19UL +}; + +#ifdef WIN32 +/* Hash constant words K for SHA-384 and SHA-512: */ +static const uint64_t K512[80] = { + 0x428a2f98d728ae22UL, 0x7137449123ef65cdUL, + 0xb5c0fbcfec4d3b2fUL, 0xe9b5dba58189dbbcUL, + 0x3956c25bf348b538UL, 0x59f111f1b605d019UL, + 0x923f82a4af194f9bUL, 0xab1c5ed5da6d8118UL, + 0xd807aa98a3030242UL, 0x12835b0145706fbeUL, + 0x243185be4ee4b28cUL, 0x550c7dc3d5ffb4e2UL, + 0x72be5d74f27b896fUL, 0x80deb1fe3b1696b1UL, + 0x9bdc06a725c71235UL, 0xc19bf174cf692694UL, + 0xe49b69c19ef14ad2UL, 0xefbe4786384f25e3UL, + 0x0fc19dc68b8cd5b5UL, 0x240ca1cc77ac9c65UL, + 0x2de92c6f592b0275UL, 0x4a7484aa6ea6e483UL, + 0x5cb0a9dcbd41fbd4UL, 0x76f988da831153b5UL, + 0x983e5152ee66dfabUL, 0xa831c66d2db43210UL, + 0xb00327c898fb213fUL, 0xbf597fc7beef0ee4UL, + 0xc6e00bf33da88fc2UL, 0xd5a79147930aa725UL, + 0x06ca6351e003826fUL, 0x142929670a0e6e70UL, + 0x27b70a8546d22ffcUL, 0x2e1b21385c26c926UL, + 0x4d2c6dfc5ac42aedUL, 0x53380d139d95b3dfUL, + 0x650a73548baf63deUL, 0x766a0abb3c77b2a8UL, + 0x81c2c92e47edaee6UL, 0x92722c851482353bUL, + 0xa2bfe8a14cf10364UL, 0xa81a664bbc423001UL, + 0xc24b8b70d0f89791UL, 0xc76c51a30654be30UL, + 0xd192e819d6ef5218UL, 0xd69906245565a910UL, + 0xf40e35855771202aUL, 0x106aa07032bbd1b8UL, + 0x19a4c116b8d2d0c8UL, 0x1e376c085141ab53UL, + 0x2748774cdf8eeb99UL, 0x34b0bcb5e19b48a8UL, + 0x391c0cb3c5c95a63UL, 0x4ed8aa4ae3418acbUL, + 0x5b9cca4f7763e373UL, 0x682e6ff3d6b2b8a3UL, + 0x748f82ee5defb2fcUL, 0x78a5636f43172f60UL, + 0x84c87814a1f0ab72UL, 0x8cc702081a6439ecUL, + 0x90befffa23631e28UL, 0xa4506cebde82bde9UL, + 0xbef9a3f7b2c67915UL, 0xc67178f2e372532bUL, + 0xca273eceea26619cUL, 0xd186b8c721c0c207UL, + 0xeada7dd6cde0eb1eUL, 0xf57d4f7fee6ed178UL, + 0x06f067aa72176fbaUL, 0x0a637dc5a2c898a6UL, + 0x113f9804bef90daeUL, 0x1b710b35131c471bUL, + 0x28db77f523047d84UL, 0x32caab7b40c72493UL, + 0x3c9ebe0a15c9bebcUL, 0x431d67c49c100d4cUL, + 0x4cc5d4becb3e42b6UL, 0x597f299cfc657e2aUL, + 0x5fcb6fab3ad6faecUL, 0x6c44198c4a475817UL +}; + +/* Initial hash value H for SHA-384: */ +static const uint64_t sha384_initial_hash_value[8] = { + 0xcbbb9d5dc1059ed8UL, + 0x629a292a367cd507UL, + 0x9159015a3070dd17UL, + 0x152fecd8f70e5939UL, + 0x67332667ffc00b31UL, + 0x8eb44a8768581511UL, + 0xdb0c2e0d64f98fa7UL, + 0x47b5481dbefa4fa4UL +}; + +/* Initial hash value H for SHA-512: */ +static const uint64_t sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908U, + 0xbb67ae8584caa73bUL, + 0x3c6ef372fe94f82bUL, + 0xa54ff53a5f1d36f1UL, + 0x510e527fade682d1UL, + 0x9b05688c2b3e6c1fUL, + 0x1f83d9abfb41bd6bUL, + 0x5be0cd19137e2179UL +}; +#else +/* Hash constant words K for SHA-384 and SHA-512: */ +static const uint64_t K512[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/* Initial hash value H for SHA-384: */ +static const uint64_t sha384_initial_hash_value[8] = { + 0xcbbb9d5dc1059ed8ULL, + 0x629a292a367cd507ULL, + 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, + 0x67332667ffc00b31ULL, + 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, + 0x47b5481dbefa4fa4ULL +}; + +/* Initial hash value H for SHA-512: */ +static const uint64_t sha512_initial_hash_value[8] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL +}; +#endif + + +/*** SHA-224: *********************************************************/ +void +isc_sha224_init(isc_sha224_t *context) { + if (context == (isc_sha256_t *)0) { + return; + } + memmove(context->state, sha224_initial_hash_value, + ISC_SHA256_DIGESTLENGTH); + memset(context->buffer, 0, ISC_SHA256_BLOCK_LENGTH); + context->bitcount = 0; +} + +void +isc_sha224_invalidate(isc_sha224_t *context) { + isc_safe_memwipe(context, sizeof(*context)); +} + +void +isc_sha224_update(isc_sha224_t *context, const uint8_t* data, size_t len) { + isc_sha256_update((isc_sha256_t *)context, data, len); +} + +void +isc_sha224_final(uint8_t digest[], isc_sha224_t *context) { + uint8_t sha256_digest[ISC_SHA256_DIGESTLENGTH]; + isc_sha256_final(sha256_digest, (isc_sha256_t *)context); + memmove(digest, sha256_digest, ISC_SHA224_DIGESTLENGTH); + isc_safe_memwipe(sha256_digest, sizeof(sha256_digest)); +} + +/*** SHA-256: *********************************************************/ +void +isc_sha256_init(isc_sha256_t *context) { + if (context == (isc_sha256_t *)0) { + return; + } + memmove(context->state, sha256_initial_hash_value, + ISC_SHA256_DIGESTLENGTH); + memset(context->buffer, 0, ISC_SHA256_BLOCK_LENGTH); + context->bitcount = 0; +} + +void +isc_sha256_invalidate(isc_sha256_t *context) { + isc_safe_memwipe(context, sizeof(*context)); +} + +#ifdef ISC_SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-256 round macros: */ + +#if BYTE_ORDER == LITTLE_ENDIAN + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + REVERSE32(*data++, W256[j]); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + W256[j]; \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + + +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ + K256[j] + (W256[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND256(a,b,c,d,e,f,g,h) \ + s0 = W256[(j+1)&0x0f]; \ + s0 = sigma0_256(s0); \ + s1 = W256[(j+14)&0x0f]; \ + s1 = sigma1_256(s1); \ + T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ + j++ + +void isc_sha256_transform(isc_sha256_t *context, const uint32_t* data) { + uint32_t a, b, c, d, e, f, g, h, s0, s1; + uint32_t T1, *W256; + int j; + + W256 = (uint32_t*)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + /* Rounds 0 to 15 (unrolled): */ + ROUND256_0_TO_15(a,b,c,d,e,f,g,h); + ROUND256_0_TO_15(h,a,b,c,d,e,f,g); + ROUND256_0_TO_15(g,h,a,b,c,d,e,f); + ROUND256_0_TO_15(f,g,h,a,b,c,d,e); + ROUND256_0_TO_15(e,f,g,h,a,b,c,d); + ROUND256_0_TO_15(d,e,f,g,h,a,b,c); + ROUND256_0_TO_15(c,d,e,f,g,h,a,b); + ROUND256_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds to 64: */ + do { + ROUND256(a,b,c,d,e,f,g,h); + ROUND256(h,a,b,c,d,e,f,g); + ROUND256(g,h,a,b,c,d,e,f); + ROUND256(f,g,h,a,b,c,d,e); + ROUND256(e,f,g,h,a,b,c,d); + ROUND256(d,e,f,g,h,a,b,c); + ROUND256(c,d,e,f,g,h,a,b); + ROUND256(b,c,d,e,f,g,h,a); + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; + /* Avoid compiler warnings */ + POST(a); POST(b); POST(c); POST(d); POST(e); POST(f); + POST(g); POST(h); POST(T1); +} + +#else /* ISC_SHA2_UNROLL_TRANSFORM */ + +void +isc_sha256_transform(isc_sha256_t *context, const uint32_t* data) { + uint32_t a, b, c, d, e, f, g, h, s0, s1; + uint32_t T1, T2, *W256; + int j; + + W256 = (uint32_t*)context->buffer; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { +#if BYTE_ORDER == LITTLE_ENDIAN + /* Copy data while converting to host byte order */ + REVERSE32(*data++,W256[j]); + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + /* Apply the SHA-256 compression function to update a..h with copy */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W256[(j+1)&0x0f]; + s0 = sigma0_256(s0); + s1 = W256[(j+14)&0x0f]; + s1 = sigma1_256(s1); + + /* Apply the SHA-256 compression function to update a..h */ + T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); + T2 = Sigma0_256(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 64); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; + /* Avoid compiler warnings */ + POST(a); POST(b); POST(c); POST(d); POST(e); POST(f); + POST(g); POST(h); POST(T1); POST(T2); +} + +#endif /* ISC_SHA2_UNROLL_TRANSFORM */ + +void +isc_sha256_update(isc_sha256_t *context, const uint8_t *data, size_t len) { + unsigned int freespace, usedspace; + + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha256_t *)0 && data != (uint8_t*)0); + + usedspace = (unsigned int)((context->bitcount >> 3) % + ISC_SHA256_BLOCK_LENGTH); + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = ISC_SHA256_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + memmove(&context->buffer[usedspace], data, freespace); + context->bitcount += freespace << 3; + len -= freespace; + data += freespace; + isc_sha256_transform(context, + (uint32_t*)context->buffer); + } else { + /* The buffer is not yet full */ + memmove(&context->buffer[usedspace], data, len); + context->bitcount += len << 3; + /* Clean up: */ + usedspace = freespace = 0; + /* Avoid compiler warnings: */ + POST(usedspace); POST(freespace); + return; + } + } + while (len >= ISC_SHA256_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + memmove(context->buffer, data, ISC_SHA256_BLOCK_LENGTH); + isc_sha256_transform(context, (uint32_t*)context->buffer); + context->bitcount += ISC_SHA256_BLOCK_LENGTH << 3; + len -= ISC_SHA256_BLOCK_LENGTH; + data += ISC_SHA256_BLOCK_LENGTH; + } + if (len > 0U) { + /* There's left-overs, so save 'em */ + memmove(context->buffer, data, len); + context->bitcount += len << 3; + } + /* Clean up: */ + usedspace = freespace = 0; + /* Avoid compiler warnings: */ + POST(usedspace); POST(freespace); +} + +void +isc_sha256_final(uint8_t digest[], isc_sha256_t *context) { + uint32_t *d = (uint32_t*)digest; + unsigned int usedspace; + + /* Sanity check: */ + REQUIRE(context != (isc_sha256_t *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) { + usedspace = (unsigned int)((context->bitcount >> 3) % + ISC_SHA256_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + REVERSE64(context->bitcount,context->bitcount); +#endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= ISC_SHA256_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + memset(&context->buffer[usedspace], 0, + ISC_SHA256_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < ISC_SHA256_BLOCK_LENGTH) { + memset(&context->buffer[usedspace], 0, + ISC_SHA256_BLOCK_LENGTH - + usedspace); + } + /* Do second-to-last transform: */ + isc_sha256_transform(context, + (uint32_t*)context->buffer); + + /* And set-up for the last transform: */ + memset(context->buffer, 0, + ISC_SHA256_SHORT_BLOCK_LENGTH); + } + } else { + /* Set-up for the last transform: */ + memset(context->buffer, 0, ISC_SHA256_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Set the bit count: */ + *(uint64_t*)&context->buffer[ISC_SHA256_SHORT_BLOCK_LENGTH] = context->bitcount; + + /* Final transform: */ + isc_sha256_transform(context, (uint32_t*)context->buffer); + +#if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE32(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + memmove(d, context->state, ISC_SHA256_DIGESTLENGTH); +#endif + } + + /* Clean up state data: */ + isc_safe_memwipe(context, sizeof(*context)); + usedspace = 0; + POST(usedspace); +} + +/*** SHA-512: *********************************************************/ +void +isc_sha512_init(isc_sha512_t *context) { + if (context == (isc_sha512_t *)0) { + return; + } + memmove(context->state, sha512_initial_hash_value, + ISC_SHA512_DIGESTLENGTH); + memset(context->buffer, 0, ISC_SHA512_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +void +isc_sha512_invalidate(isc_sha512_t *context) { + isc_safe_memwipe(context, sizeof(*context)); +} + +#ifdef ISC_SHA2_UNROLL_TRANSFORM + +/* Unrolled SHA-512 round macros: */ +#if BYTE_ORDER == LITTLE_ENDIAN + +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ + REVERSE64(*data++, W512[j]); \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ + K512[j] + W512[j]; \ + (d) += T1, \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \ + j++ + + +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ + K512[j] + (W512[j] = *data++); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + +#define ROUND512(a,b,c,d,e,f,g,h) \ + s0 = W512[(j+1)&0x0f]; \ + s0 = sigma0_512(s0); \ + s1 = W512[(j+14)&0x0f]; \ + s1 = sigma1_512(s1); \ + T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \ + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ + (d) += T1; \ + (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ + j++ + +void isc_sha512_transform(isc_sha512_t *context, const uint64_t* data) { + uint64_t a, b, c, d, e, f, g, h, s0, s1; + uint64_t T1, *W512 = (uint64_t*)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { + ROUND512_0_TO_15(a,b,c,d,e,f,g,h); + ROUND512_0_TO_15(h,a,b,c,d,e,f,g); + ROUND512_0_TO_15(g,h,a,b,c,d,e,f); + ROUND512_0_TO_15(f,g,h,a,b,c,d,e); + ROUND512_0_TO_15(e,f,g,h,a,b,c,d); + ROUND512_0_TO_15(d,e,f,g,h,a,b,c); + ROUND512_0_TO_15(c,d,e,f,g,h,a,b); + ROUND512_0_TO_15(b,c,d,e,f,g,h,a); + } while (j < 16); + + /* Now for the remaining rounds up to 79: */ + do { + ROUND512(a,b,c,d,e,f,g,h); + ROUND512(h,a,b,c,d,e,f,g); + ROUND512(g,h,a,b,c,d,e,f); + ROUND512(f,g,h,a,b,c,d,e); + ROUND512(e,f,g,h,a,b,c,d); + ROUND512(d,e,f,g,h,a,b,c); + ROUND512(c,d,e,f,g,h,a,b); + ROUND512(b,c,d,e,f,g,h,a); + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = 0; + /* Avoid compiler warnings */ + POST(a); POST(b); POST(c); POST(d); POST(e); POST(f); + POST(g); POST(h); POST(T1); +} + +#else /* ISC_SHA2_UNROLL_TRANSFORM */ + +void +isc_sha512_transform(isc_sha512_t *context, const uint64_t* data) { + uint64_t a, b, c, d, e, f, g, h, s0, s1; + uint64_t T1, T2, *W512 = (uint64_t*)context->buffer; + int j; + + /* Initialize registers with the prev. intermediate value */ + a = context->state[0]; + b = context->state[1]; + c = context->state[2]; + d = context->state[3]; + e = context->state[4]; + f = context->state[5]; + g = context->state[6]; + h = context->state[7]; + + j = 0; + do { +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert TO host byte order */ + REVERSE64(*data++, W512[j]); + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; +#else /* BYTE_ORDER == LITTLE_ENDIAN */ + /* Apply the SHA-512 compression function to update a..h with copy */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++); +#endif /* BYTE_ORDER == LITTLE_ENDIAN */ + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 16); + + do { + /* Part of the message block expansion: */ + s0 = W512[(j+1)&0x0f]; + s0 = sigma0_512(s0); + s1 = W512[(j+14)&0x0f]; + s1 = sigma1_512(s1); + + /* Apply the SHA-512 compression function to update a..h */ + T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); + T2 = Sigma0_512(a) + Maj(a, b, c); + h = g; + g = f; + f = e; + e = d + T1; + d = c; + c = b; + b = a; + a = T1 + T2; + + j++; + } while (j < 80); + + /* Compute the current intermediate hash value */ + context->state[0] += a; + context->state[1] += b; + context->state[2] += c; + context->state[3] += d; + context->state[4] += e; + context->state[5] += f; + context->state[6] += g; + context->state[7] += h; + + /* Clean up */ + a = b = c = d = e = f = g = h = T1 = T2 = 0; + /* Avoid compiler warnings */ + POST(a); POST(b); POST(c); POST(d); POST(e); POST(f); + POST(g); POST(h); POST(T1); POST(T2); +} + +#endif /* ISC_SHA2_UNROLL_TRANSFORM */ + +void isc_sha512_update(isc_sha512_t *context, const uint8_t *data, size_t len) { + unsigned int freespace, usedspace; + + if (len == 0U) { + /* Calling with no data is valid - we do nothing */ + return; + } + + /* Sanity check: */ + REQUIRE(context != (isc_sha512_t *)0 && data != (uint8_t*)0); + + usedspace = (unsigned int)((context->bitcount[0] >> 3) % + ISC_SHA512_BLOCK_LENGTH); + if (usedspace > 0) { + /* Calculate how much free space is available in the buffer */ + freespace = ISC_SHA512_BLOCK_LENGTH - usedspace; + + if (len >= freespace) { + /* Fill the buffer completely and process it */ + memmove(&context->buffer[usedspace], data, freespace); + ADDINC128(context->bitcount, freespace << 3); + len -= freespace; + data += freespace; + isc_sha512_transform(context, + (uint64_t*)context->buffer); + } else { + /* The buffer is not yet full */ + memmove(&context->buffer[usedspace], data, len); + ADDINC128(context->bitcount, len << 3); + /* Clean up: */ + usedspace = freespace = 0; + /* Avoid compiler warnings: */ + POST(usedspace); POST(freespace); + return; + } + } + while (len >= ISC_SHA512_BLOCK_LENGTH) { + /* Process as many complete blocks as we can */ + memmove(context->buffer, data, ISC_SHA512_BLOCK_LENGTH); + isc_sha512_transform(context, (uint64_t*)context->buffer); + ADDINC128(context->bitcount, ISC_SHA512_BLOCK_LENGTH << 3); + len -= ISC_SHA512_BLOCK_LENGTH; + data += ISC_SHA512_BLOCK_LENGTH; + } + if (len > 0U) { + /* There's left-overs, so save 'em */ + memmove(context->buffer, data, len); + ADDINC128(context->bitcount, len << 3); + } + /* Clean up: */ + usedspace = freespace = 0; + /* Avoid compiler warnings: */ + POST(usedspace); POST(freespace); +} + +void isc_sha512_last(isc_sha512_t *context) { + unsigned int usedspace; + + usedspace = (unsigned int)((context->bitcount[0] >> 3) % + ISC_SHA512_BLOCK_LENGTH); +#if BYTE_ORDER == LITTLE_ENDIAN + /* Convert FROM host byte order */ + REVERSE64(context->bitcount[0],context->bitcount[0]); + REVERSE64(context->bitcount[1],context->bitcount[1]); +#endif + if (usedspace > 0) { + /* Begin padding with a 1 bit: */ + context->buffer[usedspace++] = 0x80; + + if (usedspace <= ISC_SHA512_SHORT_BLOCK_LENGTH) { + /* Set-up for the last transform: */ + memset(&context->buffer[usedspace], 0, + ISC_SHA512_SHORT_BLOCK_LENGTH - usedspace); + } else { + if (usedspace < ISC_SHA512_BLOCK_LENGTH) { + memset(&context->buffer[usedspace], 0, + ISC_SHA512_BLOCK_LENGTH - usedspace); + } + /* Do second-to-last transform: */ + isc_sha512_transform(context, + (uint64_t*)context->buffer); + + /* And set-up for the last transform: */ + memset(context->buffer, 0, ISC_SHA512_BLOCK_LENGTH - 2); + } + } else { + /* Prepare for final transform: */ + memset(context->buffer, 0, ISC_SHA512_SHORT_BLOCK_LENGTH); + + /* Begin padding with a 1 bit: */ + *context->buffer = 0x80; + } + /* Store the length of input data (in bits): */ + *(uint64_t*)&context->buffer[ISC_SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1]; + *(uint64_t*)&context->buffer[ISC_SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0]; + + /* Final transform: */ + isc_sha512_transform(context, (uint64_t*)context->buffer); +} + +void isc_sha512_final(uint8_t digest[], isc_sha512_t *context) { + uint64_t *d = (uint64_t*)digest; + + /* Sanity check: */ + REQUIRE(context != (isc_sha512_t *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) { + isc_sha512_last(context); + + /* Save the hash data for output: */ +#if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 8; j++) { + REVERSE64(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + memmove(d, context->state, ISC_SHA512_DIGESTLENGTH); +#endif + } + + /* Zero out state data */ + isc_safe_memwipe(context, sizeof(*context)); +} + + +/*** SHA-384: *********************************************************/ +void +isc_sha384_init(isc_sha384_t *context) { + if (context == (isc_sha384_t *)0) { + return; + } + memmove(context->state, sha384_initial_hash_value, + ISC_SHA512_DIGESTLENGTH); + memset(context->buffer, 0, ISC_SHA384_BLOCK_LENGTH); + context->bitcount[0] = context->bitcount[1] = 0; +} + +void +isc_sha384_invalidate(isc_sha384_t *context) { + isc_safe_memwipe(context, sizeof(*context)); +} + +void +isc_sha384_update(isc_sha384_t *context, const uint8_t* data, size_t len) { + isc_sha512_update((isc_sha512_t *)context, data, len); +} + +void +isc_sha384_final(uint8_t digest[], isc_sha384_t *context) { + uint64_t *d = (uint64_t*)digest; + + /* Sanity check: */ + REQUIRE(context != (isc_sha384_t *)0); + + /* If no digest buffer is passed, we don't bother doing this: */ + if (digest != (uint8_t*)0) { + isc_sha512_last((isc_sha512_t *)context); + + /* Save the hash data for output: */ +#if BYTE_ORDER == LITTLE_ENDIAN + { + /* Convert TO host byte order */ + int j; + for (j = 0; j < 6; j++) { + REVERSE64(context->state[j],context->state[j]); + *d++ = context->state[j]; + } + } +#else + memmove(d, context->state, ISC_SHA384_DIGESTLENGTH); +#endif + } + + /* Zero out state data */ + isc_safe_memwipe(context, sizeof(*context)); +} +#endif /* !ISC_PLATFORM_OPENSSLHASH */ + +/* + * Constant used by SHA256/384/512_End() functions for converting the + * digest to a readable hexadecimal character string: + */ +static const char *sha2_hex_digits = "0123456789abcdef"; + +char * +isc_sha224_end(isc_sha224_t *context, char buffer[]) { + uint8_t digest[ISC_SHA224_DIGESTLENGTH], *d = digest; + unsigned int i; + + /* Sanity check: */ + REQUIRE(context != (isc_sha224_t *)0); + + if (buffer != (char*)0) { + isc_sha224_final(digest, context); + + for (i = 0; i < ISC_SHA224_DIGESTLENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { +#if defined(ISC_PLATFORM_OPENSSLHASH) && !defined(LIBRESSL_VERSION_NUMBER) + EVP_MD_CTX_reset(context->ctx); +#elif PKCS11CRYPTO + pk11_return_session(context); +#else + isc_safe_memwipe(context, sizeof(*context)); +#endif + } + isc_safe_memwipe(digest, sizeof(digest)); + return buffer; +} + +char * +isc_sha224_data(const uint8_t *data, size_t len, + char digest[ISC_SHA224_DIGESTSTRINGLENGTH]) +{ + isc_sha224_t context; + + isc_sha224_init(&context); + isc_sha224_update(&context, data, len); + return (isc_sha224_end(&context, digest)); +} + +char * +isc_sha256_end(isc_sha256_t *context, char buffer[]) { + uint8_t digest[ISC_SHA256_DIGESTLENGTH], *d = digest; + unsigned int i; + + /* Sanity check: */ + REQUIRE(context != (isc_sha256_t *)0); + + if (buffer != (char*)0) { + isc_sha256_final(digest, context); + + for (i = 0; i < ISC_SHA256_DIGESTLENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { +#if defined(ISC_PLATFORM_OPENSSLHASH) && !defined(LIBRESSL_VERSION_NUMBER) + EVP_MD_CTX_reset(context->ctx); +#elif PKCS11CRYPTO + pk11_return_session(context); +#else + isc_safe_memwipe(context, sizeof(*context)); +#endif + } + isc_safe_memwipe(digest, sizeof(digest)); + return buffer; +} + +char * +isc_sha256_data(const uint8_t* data, size_t len, + char digest[ISC_SHA256_DIGESTSTRINGLENGTH]) +{ + isc_sha256_t context; + + isc_sha256_init(&context); + isc_sha256_update(&context, data, len); + return (isc_sha256_end(&context, digest)); +} + +char * +isc_sha512_end(isc_sha512_t *context, char buffer[]) { + uint8_t digest[ISC_SHA512_DIGESTLENGTH], *d = digest; + unsigned int i; + + /* Sanity check: */ + REQUIRE(context != (isc_sha512_t *)0); + + if (buffer != (char*)0) { + isc_sha512_final(digest, context); + + for (i = 0; i < ISC_SHA512_DIGESTLENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { +#if defined(ISC_PLATFORM_OPENSSLHASH) && !defined(LIBRESSL_VERSION_NUMBER) + EVP_MD_CTX_reset(context->ctx); +#elif PKCS11CRYPTO + pk11_return_session(context); +#else + isc_safe_memwipe(context, sizeof(*context)); +#endif + } + isc_safe_memwipe(digest, sizeof(digest)); + return buffer; +} + +char * +isc_sha512_data(const uint8_t *data, size_t len, + char digest[ISC_SHA512_DIGESTSTRINGLENGTH]) +{ + isc_sha512_t context; + + isc_sha512_init(&context); + isc_sha512_update(&context, data, len); + return (isc_sha512_end(&context, digest)); +} + +char * +isc_sha384_end(isc_sha384_t *context, char buffer[]) { + uint8_t digest[ISC_SHA384_DIGESTLENGTH], *d = digest; + unsigned int i; + + /* Sanity check: */ + REQUIRE(context != (isc_sha384_t *)0); + + if (buffer != (char*)0) { + isc_sha384_final(digest, context); + + for (i = 0; i < ISC_SHA384_DIGESTLENGTH; i++) { + *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; + *buffer++ = sha2_hex_digits[*d & 0x0f]; + d++; + } + *buffer = (char)0; + } else { +#if defined(ISC_PLATFORM_OPENSSLHASH) && !defined(LIBRESSL_VERSION_NUMBER) + EVP_MD_CTX_reset(context->ctx); +#elif PKCS11CRYPTO + pk11_return_session(context); +#else + isc_safe_memwipe(context, sizeof(*context)); +#endif + } + isc_safe_memwipe(digest, sizeof(digest)); + return buffer; +} + +char * +isc_sha384_data(const uint8_t *data, size_t len, + char digest[ISC_SHA384_DIGESTSTRINGLENGTH]) +{ + isc_sha384_t context; + + isc_sha384_init(&context); + isc_sha384_update(&context, data, len); + return (isc_sha384_end(&context, digest)); +} diff --git a/lib/isc/sockaddr.c b/lib/isc/sockaddr.c new file mode 100644 index 0000000..6a97e69 --- /dev/null +++ b/lib/isc/sockaddr.c @@ -0,0 +1,507 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> +#include <stdio.h> + +#include <isc/buffer.h> +#include <isc/hash.h> +#include <isc/msgs.h> +#include <isc/netaddr.h> +#include <isc/print.h> +#include <isc/region.h> +#include <isc/sockaddr.h> +#include <isc/string.h> +#include <isc/util.h> + +bool +isc_sockaddr_equal(const isc_sockaddr_t *a, const isc_sockaddr_t *b) { + return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR| + ISC_SOCKADDR_CMPPORT| + ISC_SOCKADDR_CMPSCOPE)); +} + +bool +isc_sockaddr_eqaddr(const isc_sockaddr_t *a, const isc_sockaddr_t *b) { + return (isc_sockaddr_compare(a, b, ISC_SOCKADDR_CMPADDR| + ISC_SOCKADDR_CMPSCOPE)); +} + +bool +isc_sockaddr_compare(const isc_sockaddr_t *a, const isc_sockaddr_t *b, + unsigned int flags) +{ + REQUIRE(a != NULL && b != NULL); + + if (a->length != b->length) + return (false); + + /* + * We don't just memcmp because the sin_zero field isn't always + * zero. + */ + + if (a->type.sa.sa_family != b->type.sa.sa_family) + return (false); + switch (a->type.sa.sa_family) { + case AF_INET: + if ((flags & ISC_SOCKADDR_CMPADDR) != 0 && + memcmp(&a->type.sin.sin_addr, &b->type.sin.sin_addr, + sizeof(a->type.sin.sin_addr)) != 0) + return (false); + if ((flags & ISC_SOCKADDR_CMPPORT) != 0 && + a->type.sin.sin_port != b->type.sin.sin_port) + return (false); + break; + case AF_INET6: + if ((flags & ISC_SOCKADDR_CMPADDR) != 0 && + memcmp(&a->type.sin6.sin6_addr, &b->type.sin6.sin6_addr, + sizeof(a->type.sin6.sin6_addr)) != 0) + return (false); +#ifdef ISC_PLATFORM_HAVESCOPEID + /* + * If ISC_SOCKADDR_CMPSCOPEZERO is set then don't return + * false if one of the scopes in zero. + */ + if ((flags & ISC_SOCKADDR_CMPSCOPE) != 0 && + a->type.sin6.sin6_scope_id != b->type.sin6.sin6_scope_id && + ((flags & ISC_SOCKADDR_CMPSCOPEZERO) == 0 || + (a->type.sin6.sin6_scope_id != 0 && + b->type.sin6.sin6_scope_id != 0))) + return (false); +#endif + if ((flags & ISC_SOCKADDR_CMPPORT) != 0 && + a->type.sin6.sin6_port != b->type.sin6.sin6_port) + return (false); + break; + default: + if (memcmp(&a->type, &b->type, a->length) != 0) + return (false); + } + return (true); +} + +bool +isc_sockaddr_eqaddrprefix(const isc_sockaddr_t *a, const isc_sockaddr_t *b, + unsigned int prefixlen) +{ + isc_netaddr_t na, nb; + isc_netaddr_fromsockaddr(&na, a); + isc_netaddr_fromsockaddr(&nb, b); + return (isc_netaddr_eqprefix(&na, &nb, prefixlen)); +} + +isc_result_t +isc_sockaddr_totext(const isc_sockaddr_t *sockaddr, isc_buffer_t *target) { + isc_result_t result; + isc_netaddr_t netaddr; + char pbuf[sizeof("65000")]; + unsigned int plen; + isc_region_t avail; + + REQUIRE(sockaddr != NULL); + + /* + * Do the port first, giving us the opportunity to check for + * unsupported address families before calling + * isc_netaddr_fromsockaddr(). + */ + switch (sockaddr->type.sa.sa_family) { + case AF_INET: + snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sockaddr->type.sin.sin_port)); + break; + case AF_INET6: + snprintf(pbuf, sizeof(pbuf), "%u", ntohs(sockaddr->type.sin6.sin6_port)); + break; +#ifdef ISC_PLAFORM_HAVESYSUNH + case AF_UNIX: + plen = strlen(sockaddr->type.sunix.sun_path); + if (plen >= isc_buffer_availablelength(target)) + return (ISC_R_NOSPACE); + + isc_buffer_putmem(target, sockaddr->type.sunix.sun_path, plen); + + /* + * Null terminate after used region. + */ + isc_buffer_availableregion(target, &avail); + INSIST(avail.length >= 1); + avail.base[0] = '\0'; + + return (ISC_R_SUCCESS); +#endif + default: + return (ISC_R_FAILURE); + } + + plen = strlen(pbuf); + INSIST(plen < sizeof(pbuf)); + + isc_netaddr_fromsockaddr(&netaddr, sockaddr); + result = isc_netaddr_totext(&netaddr, target); + if (result != ISC_R_SUCCESS) + return (result); + + if (1 + plen + 1 > isc_buffer_availablelength(target)) + return (ISC_R_NOSPACE); + + isc_buffer_putmem(target, (const unsigned char *)"#", 1); + isc_buffer_putmem(target, (const unsigned char *)pbuf, plen); + + /* + * Null terminate after used region. + */ + isc_buffer_availableregion(target, &avail); + INSIST(avail.length >= 1); + avail.base[0] = '\0'; + + return (ISC_R_SUCCESS); +} + +void +isc_sockaddr_format(const isc_sockaddr_t *sa, char *array, unsigned int size) { + isc_result_t result; + isc_buffer_t buf; + + if (size == 0U) + return; + + isc_buffer_init(&buf, array, size); + result = isc_sockaddr_totext(sa, &buf); + if (result != ISC_R_SUCCESS) { + /* + * The message is the same as in netaddr.c. + */ + snprintf(array, size, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_NETADDR, + ISC_MSG_UNKNOWNADDR, + "<unknown address, family %u>"), + sa->type.sa.sa_family); + array[size - 1] = '\0'; + } +} + +unsigned int +isc_sockaddr_hash(const isc_sockaddr_t *sockaddr, bool address_only) { + unsigned int length = 0; + const unsigned char *s = NULL; + unsigned int h = 0; + unsigned int p = 0; + const struct in6_addr *in6; + + REQUIRE(sockaddr != NULL); + + switch (sockaddr->type.sa.sa_family) { + case AF_INET: + s = (const unsigned char *)&sockaddr->type.sin.sin_addr; + p = ntohs(sockaddr->type.sin.sin_port); + length = sizeof(sockaddr->type.sin.sin_addr.s_addr); + break; + case AF_INET6: + in6 = &sockaddr->type.sin6.sin6_addr; + s = (const unsigned char *)in6; + if (IN6_IS_ADDR_V4MAPPED(in6)) { + s += 12; + length = sizeof(sockaddr->type.sin.sin_addr.s_addr); + } else + length = sizeof(sockaddr->type.sin6.sin6_addr); + p = ntohs(sockaddr->type.sin6.sin6_port); + break; + default: + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_SOCKADDR, + ISC_MSG_UNKNOWNFAMILY, + "unknown address family: %d"), + (int)sockaddr->type.sa.sa_family); + s = (const unsigned char *)&sockaddr->type; + length = sockaddr->length; + p = 0; + } + + h = isc_hash_function(s, length, true, NULL); + if (!address_only) + h = isc_hash_function(&p, sizeof(p), true, &h); + + return (h); +} + +void +isc_sockaddr_any(isc_sockaddr_t *sockaddr) +{ + memset(sockaddr, 0, sizeof(*sockaddr)); + sockaddr->type.sin.sin_family = AF_INET; +#ifdef ISC_PLATFORM_HAVESALEN + sockaddr->type.sin.sin_len = sizeof(sockaddr->type.sin); +#endif + sockaddr->type.sin.sin_addr.s_addr = INADDR_ANY; + sockaddr->type.sin.sin_port = 0; + sockaddr->length = sizeof(sockaddr->type.sin); + ISC_LINK_INIT(sockaddr, link); +} + +void +isc_sockaddr_any6(isc_sockaddr_t *sockaddr) +{ + memset(sockaddr, 0, sizeof(*sockaddr)); + sockaddr->type.sin6.sin6_family = AF_INET6; +#ifdef ISC_PLATFORM_HAVESALEN + sockaddr->type.sin6.sin6_len = sizeof(sockaddr->type.sin6); +#endif + sockaddr->type.sin6.sin6_addr = in6addr_any; + sockaddr->type.sin6.sin6_port = 0; + sockaddr->length = sizeof(sockaddr->type.sin6); + ISC_LINK_INIT(sockaddr, link); +} + +void +isc_sockaddr_fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina, + in_port_t port) +{ + memset(sockaddr, 0, sizeof(*sockaddr)); + sockaddr->type.sin.sin_family = AF_INET; +#ifdef ISC_PLATFORM_HAVESALEN + sockaddr->type.sin.sin_len = sizeof(sockaddr->type.sin); +#endif + sockaddr->type.sin.sin_addr = *ina; + sockaddr->type.sin.sin_port = htons(port); + sockaddr->length = sizeof(sockaddr->type.sin); + ISC_LINK_INIT(sockaddr, link); +} + +void +isc_sockaddr_anyofpf(isc_sockaddr_t *sockaddr, int pf) { + switch (pf) { + case AF_INET: + isc_sockaddr_any(sockaddr); + break; + case AF_INET6: + isc_sockaddr_any6(sockaddr); + break; + default: + INSIST(0); + } +} + +void +isc_sockaddr_fromin6(isc_sockaddr_t *sockaddr, const struct in6_addr *ina6, + in_port_t port) +{ + memset(sockaddr, 0, sizeof(*sockaddr)); + sockaddr->type.sin6.sin6_family = AF_INET6; +#ifdef ISC_PLATFORM_HAVESALEN + sockaddr->type.sin6.sin6_len = sizeof(sockaddr->type.sin6); +#endif + sockaddr->type.sin6.sin6_addr = *ina6; + sockaddr->type.sin6.sin6_port = htons(port); + sockaddr->length = sizeof(sockaddr->type.sin6); + ISC_LINK_INIT(sockaddr, link); +} + +void +isc_sockaddr_v6fromin(isc_sockaddr_t *sockaddr, const struct in_addr *ina, + in_port_t port) +{ + memset(sockaddr, 0, sizeof(*sockaddr)); + sockaddr->type.sin6.sin6_family = AF_INET6; +#ifdef ISC_PLATFORM_HAVESALEN + sockaddr->type.sin6.sin6_len = sizeof(sockaddr->type.sin6); +#endif + sockaddr->type.sin6.sin6_addr.s6_addr[10] = 0xff; + sockaddr->type.sin6.sin6_addr.s6_addr[11] = 0xff; + memmove(&sockaddr->type.sin6.sin6_addr.s6_addr[12], ina, 4); + sockaddr->type.sin6.sin6_port = htons(port); + sockaddr->length = sizeof(sockaddr->type.sin6); + ISC_LINK_INIT(sockaddr, link); +} + +int +isc_sockaddr_pf(const isc_sockaddr_t *sockaddr) { + + /* + * Get the protocol family of 'sockaddr'. + */ + +#if (AF_INET == PF_INET && AF_INET6 == PF_INET6) + /* + * Assume that PF_xxx == AF_xxx for all AF and PF. + */ + return (sockaddr->type.sa.sa_family); +#else + switch (sockaddr->type.sa.sa_family) { + case AF_INET: + return (PF_INET); + case AF_INET6: + return (PF_INET6); + default: + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKADDR, + ISC_MSG_UNKNOWNFAMILY, + "unknown address family: %d"), + (int)sockaddr->type.sa.sa_family); + } +#endif +} + +void +isc_sockaddr_fromnetaddr(isc_sockaddr_t *sockaddr, const isc_netaddr_t *na, + in_port_t port) +{ + memset(sockaddr, 0, sizeof(*sockaddr)); + sockaddr->type.sin.sin_family = na->family; + switch (na->family) { + case AF_INET: + sockaddr->length = sizeof(sockaddr->type.sin); +#ifdef ISC_PLATFORM_HAVESALEN + sockaddr->type.sin.sin_len = sizeof(sockaddr->type.sin); +#endif + sockaddr->type.sin.sin_addr = na->type.in; + sockaddr->type.sin.sin_port = htons(port); + break; + case AF_INET6: + sockaddr->length = sizeof(sockaddr->type.sin6); +#ifdef ISC_PLATFORM_HAVESALEN + sockaddr->type.sin6.sin6_len = sizeof(sockaddr->type.sin6); +#endif + memmove(&sockaddr->type.sin6.sin6_addr, &na->type.in6, 16); +#ifdef ISC_PLATFORM_HAVESCOPEID + sockaddr->type.sin6.sin6_scope_id = isc_netaddr_getzone(na); +#endif + sockaddr->type.sin6.sin6_port = htons(port); + break; + default: + INSIST(0); + } + ISC_LINK_INIT(sockaddr, link); +} + +void +isc_sockaddr_setport(isc_sockaddr_t *sockaddr, in_port_t port) { + switch (sockaddr->type.sa.sa_family) { + case AF_INET: + sockaddr->type.sin.sin_port = htons(port); + break; + case AF_INET6: + sockaddr->type.sin6.sin6_port = htons(port); + break; + default: + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKADDR, + ISC_MSG_UNKNOWNFAMILY, + "unknown address family: %d"), + (int)sockaddr->type.sa.sa_family); + } +} + +in_port_t +isc_sockaddr_getport(const isc_sockaddr_t *sockaddr) { + in_port_t port = 0; + + switch (sockaddr->type.sa.sa_family) { + case AF_INET: + port = ntohs(sockaddr->type.sin.sin_port); + break; + case AF_INET6: + port = ntohs(sockaddr->type.sin6.sin6_port); + break; + default: + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKADDR, + ISC_MSG_UNKNOWNFAMILY, + "unknown address family: %d"), + (int)sockaddr->type.sa.sa_family); + } + + return (port); +} + +bool +isc_sockaddr_ismulticast(const isc_sockaddr_t *sockaddr) { + isc_netaddr_t netaddr; + + if (sockaddr->type.sa.sa_family == AF_INET || + sockaddr->type.sa.sa_family == AF_INET6) { + isc_netaddr_fromsockaddr(&netaddr, sockaddr); + return (isc_netaddr_ismulticast(&netaddr)); + } + return (false); +} + +bool +isc_sockaddr_isexperimental(const isc_sockaddr_t *sockaddr) { + isc_netaddr_t netaddr; + + if (sockaddr->type.sa.sa_family == AF_INET) { + isc_netaddr_fromsockaddr(&netaddr, sockaddr); + return (isc_netaddr_isexperimental(&netaddr)); + } + return (false); +} + +bool +isc_sockaddr_issitelocal(const isc_sockaddr_t *sockaddr) { + isc_netaddr_t netaddr; + + if (sockaddr->type.sa.sa_family == AF_INET6) { + isc_netaddr_fromsockaddr(&netaddr, sockaddr); + return (isc_netaddr_issitelocal(&netaddr)); + } + return (false); +} + +bool +isc_sockaddr_islinklocal(const isc_sockaddr_t *sockaddr) { + isc_netaddr_t netaddr; + + if (sockaddr->type.sa.sa_family == AF_INET6) { + isc_netaddr_fromsockaddr(&netaddr, sockaddr); + return (isc_netaddr_islinklocal(&netaddr)); + } + return (false); +} + +bool +isc_sockaddr_isnetzero(const isc_sockaddr_t *sockaddr) { + isc_netaddr_t netaddr; + + if (sockaddr->type.sa.sa_family == AF_INET) { + isc_netaddr_fromsockaddr(&netaddr, sockaddr); + return (isc_netaddr_isnetzero(&netaddr)); + } + return (false); +} + +isc_result_t +isc_sockaddr_frompath(isc_sockaddr_t *sockaddr, const char *path) { +#ifdef ISC_PLATFORM_HAVESYSUNH + if (strlen(path) >= sizeof(sockaddr->type.sunix.sun_path)) + return (ISC_R_NOSPACE); + memset(sockaddr, 0, sizeof(*sockaddr)); + sockaddr->length = sizeof(sockaddr->type.sunix); + sockaddr->type.sunix.sun_family = AF_UNIX; +#ifdef ISC_PLATFORM_HAVESALEN + sockaddr->type.sunix.sun_len = + (unsigned char)sizeof(sockaddr->type.sunix); +#endif + strlcpy(sockaddr->type.sunix.sun_path, path, + sizeof(sockaddr->type.sunix.sun_path)); + return (ISC_R_SUCCESS); +#else + UNUSED(sockaddr); + UNUSED(path); + return (ISC_R_NOTIMPLEMENTED); +#endif +} diff --git a/lib/isc/socket_api.c b/lib/isc/socket_api.c new file mode 100644 index 0000000..5de98a9 --- /dev/null +++ b/lib/isc/socket_api.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/app.h> +#include <isc/magic.h> +#include <isc/mutex.h> +#include <isc/once.h> +#include <isc/socket.h> +#include <isc/util.h> + +static isc_mutex_t createlock; +static isc_once_t once = ISC_ONCE_INIT; +static isc_socketmgrcreatefunc_t socketmgr_createfunc = NULL; + +static void +initialize(void) { + RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS); +} + +isc_result_t +isc_socket_register(isc_socketmgrcreatefunc_t createfunc) { + isc_result_t result = ISC_R_SUCCESS; + + RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS); + + LOCK(&createlock); + if (socketmgr_createfunc == NULL) + socketmgr_createfunc = createfunc; + else + result = ISC_R_EXISTS; + UNLOCK(&createlock); + + return (result); +} + +isc_result_t +isc_socketmgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, + isc_socketmgr_t **managerp) +{ + isc_result_t result; + + LOCK(&createlock); + + REQUIRE(socketmgr_createfunc != NULL); + result = (*socketmgr_createfunc)(mctx, managerp); + + UNLOCK(&createlock); + + if (result == ISC_R_SUCCESS) + isc_appctx_setsocketmgr(actx, *managerp); + + return (result); +} + +isc_result_t +isc_socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp) { + isc_result_t result; + + if (isc_bind9) + return (isc__socketmgr_create(mctx, managerp)); + + LOCK(&createlock); + + REQUIRE(socketmgr_createfunc != NULL); + result = (*socketmgr_createfunc)(mctx, managerp); + + UNLOCK(&createlock); + + return (result); +} + +void +isc_socketmgr_destroy(isc_socketmgr_t **managerp) { + REQUIRE(managerp != NULL && ISCAPI_SOCKETMGR_VALID(*managerp)); + + if (isc_bind9) + isc__socketmgr_destroy(managerp); + else + (*managerp)->methods->destroy(managerp); + + ENSURE(*managerp == NULL); +} + +isc_result_t +isc_socket_create(isc_socketmgr_t *manager, int pf, isc_sockettype_t type, + isc_socket_t **socketp) +{ + REQUIRE(ISCAPI_SOCKETMGR_VALID(manager)); + + if (isc_bind9) + return (isc__socket_create(manager, pf, type, socketp)); + + return (manager->methods->socketcreate(manager, pf, type, socketp)); +} + +void +isc_socket_attach(isc_socket_t *sock, isc_socket_t **socketp) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + REQUIRE(socketp != NULL && *socketp == NULL); + + if (isc_bind9) + isc__socket_attach(sock, socketp); + else + sock->methods->attach(sock, socketp); + + ENSURE(*socketp == sock); +} + +void +isc_socket_detach(isc_socket_t **socketp) { + REQUIRE(socketp != NULL && ISCAPI_SOCKET_VALID(*socketp)); + + if (isc_bind9) + isc__socket_detach(socketp); + else + (*socketp)->methods->detach(socketp); + + ENSURE(*socketp == NULL); +} + +isc_result_t +isc_socket_bind(isc_socket_t *sock, isc_sockaddr_t *sockaddr, + unsigned int options) +{ + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + return (isc__socket_bind(sock, sockaddr, options)); + + return (sock->methods->bind(sock, sockaddr, options)); +} + +isc_result_t +isc_socket_sendto(isc_socket_t *sock, isc_region_t *region, isc_task_t *task, + isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo) +{ + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + return (isc__socket_sendto(sock, region, task, + action, arg, address, pktinfo)); + + return (sock->methods->sendto(sock, region, task, action, arg, address, + pktinfo)); +} + +isc_result_t +isc_socket_connect(isc_socket_t *sock, isc_sockaddr_t *addr, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + return (isc__socket_connect(sock, addr, task, action, arg)); + + return (sock->methods->connect(sock, addr, task, action, arg)); +} + +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) +{ + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + return (isc__socket_recv(sock, region, minimum, + task, action, arg)); + + return (sock->methods->recv(sock, region, minimum, task, action, arg)); +} + +void +isc_socket_cancel(isc_socket_t *sock, isc_task_t *task, unsigned int how) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + isc__socket_cancel(sock, task, how); + else + sock->methods->cancel(sock, task, how); +} + +isc_result_t +isc_socket_getsockname(isc_socket_t *sock, isc_sockaddr_t *addressp) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + return (isc__socket_getsockname(sock, addressp)); + + return (sock->methods->getsockname(sock, addressp)); +} + +void +isc_socket_ipv6only(isc_socket_t *sock, bool yes) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + isc__socket_ipv6only(sock, yes); + else + sock->methods->ipv6only(sock, yes); +} + +void +isc_socket_dscp(isc_socket_t *sock, isc_dscp_t dscp) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + sock->methods->dscp(sock, dscp); +} + +isc_sockettype_t +isc_socket_gettype(isc_socket_t *sock) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + return (isc__socket_gettype(sock)); + + return (sock->methods->gettype(sock)); +} + +void +isc_socket_setname(isc_socket_t *sock, const char *name, void *tag) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + UNUSED(sock); /* in case REQUIRE() is empty */ + UNUSED(name); + UNUSED(tag); +} + +isc_result_t +isc_socket_fdwatchcreate(isc_socketmgr_t *manager, int fd, int flags, + isc_sockfdwatch_t callback, void *cbarg, + isc_task_t *task, isc_socket_t **socketp) +{ + REQUIRE(ISCAPI_SOCKETMGR_VALID(manager)); + + if (isc_bind9) + return (isc__socket_fdwatchcreate(manager, fd, flags, + callback, cbarg, + task, socketp)); + + return (manager->methods->fdwatchcreate(manager, fd, flags, + callback, cbarg, task, + socketp)); +} + +isc_result_t +isc_socket_fdwatchpoke(isc_socket_t *sock, int flags) +{ + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + return (isc__socket_fdwatchpoke(sock, flags)); + + return (sock->methods->fdwatchpoke(sock, flags)); +} + +isc_result_t +isc_socket_dup(isc_socket_t *sock, isc_socket_t **socketp) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + REQUIRE(socketp != NULL && *socketp == NULL); + + if (isc_bind9) + return (isc__socket_dup(sock, socketp)); + + return (sock->methods->dup(sock, socketp)); +} + +int +isc_socket_getfd(isc_socket_t *sock) { + REQUIRE(ISCAPI_SOCKET_VALID(sock)); + + if (isc_bind9) + return (isc__socket_getfd(sock)); + + return (sock->methods->getfd(sock)); +} + +isc_result_t +isc_socket_open(isc_socket_t *sock) { + return (isc__socket_open(sock)); +} + +isc_result_t +isc_socket_close(isc_socket_t *sock) { + return (isc__socket_close(sock)); +} + +isc_result_t +isc_socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp, + unsigned int maxsocks) +{ + return (isc__socketmgr_create2(mctx, managerp, maxsocks)); +} + +isc_result_t +isc_socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, + unsigned int minimum, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + return (isc__socket_recvv(sock, buflist, minimum, task, action, arg)); +} + +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) +{ + return (isc__socket_recv2(sock, region, minimum, task, event, flags)); +} + +isc_result_t +isc_socket_send(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, isc_taskaction_t action, void *arg) +{ + return (isc__socket_send(sock, region, task, action, arg)); +} + +isc_result_t +isc_socket_sendv(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg) +{ + return (isc__socket_sendv(sock, buflist, task, action, arg)); +} + +isc_result_t +isc_socket_sendtov(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo) +{ + return (isc__socket_sendtov(sock, buflist, task, action, arg, + address, pktinfo)); +} + +isc_result_t +isc_socket_sendtov2(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + unsigned int flags) +{ + return (isc__socket_sendtov2(sock, buflist, task, action, arg, + address, pktinfo, flags)); +} + +isc_result_t +isc_socket_sendto2(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + isc_socketevent_t *event, unsigned int flags) +{ + return (isc__socket_sendto2(sock, region, task, address, pktinfo, + event, flags)); +} + +void +isc_socket_cleanunix(isc_sockaddr_t *sockaddr, bool active) { + isc__socket_cleanunix(sockaddr, active); +} + +isc_result_t +isc_socket_permunix(isc_sockaddr_t *sockaddr, uint32_t perm, + uint32_t owner, uint32_t group) +{ + return (isc__socket_permunix(sockaddr, perm, owner, group)); +} + +isc_result_t +isc_socket_filter(isc_socket_t *sock, const char *filter) { + return (isc__socket_filter(sock, filter)); +} + +isc_result_t +isc_socket_listen(isc_socket_t *sock, unsigned int backlog) { + return (isc__socket_listen(sock, backlog)); +} + +isc_result_t +isc_socket_accept(isc_socket_t *sock, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + return (isc__socket_accept(sock, task, action, arg)); +} + +isc_result_t +isc_socket_getpeername(isc_socket_t *sock, isc_sockaddr_t *addressp) { + return (isc__socket_getpeername(sock, addressp)); +} diff --git a/lib/isc/sparc64/Makefile.in b/lib/isc/sparc64/Makefile.in new file mode 100644 index 0000000..419cf9f --- /dev/null +++ b/lib/isc/sparc64/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = include +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/sparc64/include/Makefile.in b/lib/isc/sparc64/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/sparc64/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/sparc64/include/isc/Makefile.in b/lib/isc/sparc64/include/isc/Makefile.in new file mode 100644 index 0000000..6075633 --- /dev/null +++ b/lib/isc/sparc64/include/isc/Makefile.in @@ -0,0 +1,29 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = atomic.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done diff --git a/lib/isc/sparc64/include/isc/atomic.h b/lib/isc/sparc64/include/isc/atomic.h new file mode 100644 index 0000000..21ec67e --- /dev/null +++ b/lib/isc/sparc64/include/isc/atomic.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/* + * This code was written based on FreeBSD's kernel source whose copyright + * follows: + */ + +/*- + * Copyright (c) 1998 Doug Rabson. + * Copyright (c) 2001 Jake Burkholder. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: FreeBSD: src/sys/i386/include/atomic.h,v 1.20 2001/02/11 + * $FreeBSD: src/sys/sparc64/include/atomic.h,v 1.8 2004/05/22 00:52:16 marius Exp $ + */ + +#ifndef ISC_ATOMIC_H +#define ISC_ATOMIC_H 1 + +#include <inttypes.h> + +#include <isc/platform.h> +#include <isc/types.h> + +#define ASI_P 0x80 /* Primary Address Space Identifier */ + +#ifdef ISC_PLATFORM_USEGCCASM + +/* + * This routine atomically increments the value stored in 'p' by 'val', and + * returns the previous value. + */ +static inline int32_t +isc_atomic_xadd(int32_t *p, int32_t val) { + int32_t prev, swapped; + + for (prev = *(volatile int32_t *)p; ; prev = swapped) { + swapped = prev + val; + __asm__ volatile( + "casa [%2] %3, %4, %0" + : "+r"(swapped), "=m"(*p) + : "r"(p), "n"(ASI_P), "r"(prev), "m"(*p)); + if (swapped == prev) + break; + } + + return (prev); +} + +/* + * This routine atomically stores the value 'val' in 'p'. + */ +static inline void +isc_atomic_store(int32_t *p, int32_t val) { + int32_t prev, swapped; + + for (prev = *(volatile int32_t *)p; ; prev = swapped) { + swapped = val; + __asm__ volatile( + "casa [%2] %3, %4, %0" + : "+r"(swapped), "=m"(*p) + : "r"(p), "n"(ASI_P), "r"(prev), "m"(*p)); + if (swapped == prev) + break; + } +} + +/* + * This routine atomically replaces the value in 'p' with 'val', if the + * original value is equal to 'cmpval'. The original value is returned in any + * case. + */ +static inline int32_t +isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val) { + int32_t temp = val; + + __asm__ volatile( + "casa [%2] %3, %4, %0" + : "+r"(temp), "=m"(*p) + : "r"(p), "n"(ASI_P), "r"(cmpval), "m"(*p)); + + return (temp); +} + +#else /* ISC_PLATFORM_USEGCCASM */ + +#error "unsupported compiler. disable atomic ops by --disable-atomic" + +#endif /* ISC_PLATFORM_USEGCCASM */ + +#endif /* ISC_ATOMIC_H */ diff --git a/lib/isc/stats.c b/lib/isc/stats.c new file mode 100644 index 0000000..196099e --- /dev/null +++ b/lib/isc/stats.c @@ -0,0 +1,454 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <string.h> + +#include <isc/atomic.h> +#include <isc/buffer.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/rwlock.h> +#include <isc/stats.h> +#include <isc/util.h> + +#if defined(ISC_PLATFORM_HAVESTDATOMIC) +#if defined(__cplusplus) +#include <isc/stdatomic.h> +#else +#include <stdatomic.h> +#endif +#endif + +#define ISC_STATS_MAGIC ISC_MAGIC('S', 't', 'a', 't') +#define ISC_STATS_VALID(x) ISC_MAGIC_VALID(x, ISC_STATS_MAGIC) + +/*% + * Local macro confirming prescence of 64-bit + * increment and store operations, just to make + * the later macros simpler + */ +#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_LONG_LOCK_FREE)) || \ + (defined(ISC_PLATFORM_HAVEXADDQ) && defined(ISC_PLATFORM_HAVEATOMICSTOREQ)) +#define ISC_STATS_HAVEATOMICQ 1 +#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_LONG_LOCK_FREE)) +#define ISC_STATS_HAVESTDATOMICQ 1 +#endif +#else +#define ISC_STATS_HAVEATOMICQ 0 +#endif + +/*% + * Only lock the counters if 64-bit atomic operations are + * not available but cheap atomic lock operations are. + * On a modern 64-bit system this should never be the case. + * + * Normal locks are too expensive to be used whenever a counter + * is updated. + */ +#if !ISC_STATS_HAVEATOMICQ && defined(ISC_RWLOCK_HAVEATOMIC) +#define ISC_STATS_LOCKCOUNTERS 1 +#else +#define ISC_STATS_LOCKCOUNTERS 0 +#endif + +/*% + * If 64-bit atomic operations are not available but + * 32-bit operations are then split the counter into two, + * using the atomic operations to try to ensure that any carry + * from the low word is correctly carried into the high word. + * + * Otherwise, just rely on standard 64-bit data types + * and operations + */ +#if !ISC_STATS_HAVEATOMICQ && ((defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) || defined(ISC_PLATFORM_HAVEXADD)) +#define ISC_STATS_USEMULTIFIELDS 1 +#if (defined(ISC_PLATFORM_HAVESTDATOMIC) && defined(ATOMIC_INT_LOCK_FREE)) +#define ISC_STATS_HAVESTDATOMIC 1 +#endif +#else +#define ISC_STATS_USEMULTIFIELDS 0 +#endif + +#if ISC_STATS_USEMULTIFIELDS +typedef struct { +#if defined(ISC_STATS_HAVESTDATOMIC) + atomic_int_fast32_t hi; + atomic_int_fast32_t lo; +#else + uint32_t hi; + uint32_t lo; +#endif +} isc_stat_t; +#else +#if defined(ISC_STATS_HAVESTDATOMICQ) +typedef atomic_int_fast64_t isc_stat_t; +#else +typedef uint64_t isc_stat_t; +#endif +#endif + +struct isc_stats { + /*% Unlocked */ + unsigned int magic; + isc_mem_t *mctx; + int ncounters; + + isc_mutex_t lock; + unsigned int references; /* locked by lock */ + + /*% + * Locked by counterlock or unlocked if efficient rwlock is not + * available. + */ +#if ISC_STATS_LOCKCOUNTERS + isc_rwlock_t counterlock; +#endif + isc_stat_t *counters; + + /*% + * We don't want to lock the counters while we are dumping, so we first + * copy the current counter values into a local array. This buffer + * will be used as the copy destination. It's allocated on creation + * of the stats structure so that the dump operation won't fail due + * to memory allocation failure. + * XXX: this approach is weird for non-threaded build because the + * additional memory and the copy overhead could be avoided. We prefer + * simplicity here, however, under the assumption that this function + * should be only rarely called. + */ + uint64_t *copiedcounters; +}; + +static isc_result_t +create_stats(isc_mem_t *mctx, int ncounters, isc_stats_t **statsp) { + isc_stats_t *stats; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(statsp != NULL && *statsp == NULL); + + stats = isc_mem_get(mctx, sizeof(*stats)); + if (stats == NULL) + return (ISC_R_NOMEMORY); + + result = isc_mutex_init(&stats->lock); + if (result != ISC_R_SUCCESS) + goto clean_stats; + + stats->counters = isc_mem_get(mctx, sizeof(isc_stat_t) * ncounters); + if (stats->counters == NULL) { + result = ISC_R_NOMEMORY; + goto clean_mutex; + } + stats->copiedcounters = isc_mem_get(mctx, + sizeof(uint64_t) * ncounters); + if (stats->copiedcounters == NULL) { + result = ISC_R_NOMEMORY; + goto clean_counters; + } + +#if ISC_STATS_LOCKCOUNTERS + result = isc_rwlock_init(&stats->counterlock, 0, 0); + if (result != ISC_R_SUCCESS) + goto clean_copiedcounters; +#endif + + stats->references = 1; + memset(stats->counters, 0, sizeof(isc_stat_t) * ncounters); + stats->mctx = NULL; + isc_mem_attach(mctx, &stats->mctx); + stats->ncounters = ncounters; + stats->magic = ISC_STATS_MAGIC; + + *statsp = stats; + + return (result); + +clean_counters: + isc_mem_put(mctx, stats->counters, sizeof(isc_stat_t) * ncounters); + +#if ISC_STATS_LOCKCOUNTERS +clean_copiedcounters: + isc_mem_put(mctx, stats->copiedcounters, + sizeof(isc_stat_t) * ncounters); +#endif + +clean_mutex: + DESTROYLOCK(&stats->lock); + +clean_stats: + isc_mem_put(mctx, stats, sizeof(*stats)); + + return (result); +} + +void +isc_stats_attach(isc_stats_t *stats, isc_stats_t **statsp) { + REQUIRE(ISC_STATS_VALID(stats)); + REQUIRE(statsp != NULL && *statsp == NULL); + + LOCK(&stats->lock); + stats->references++; + UNLOCK(&stats->lock); + + *statsp = stats; +} + +void +isc_stats_detach(isc_stats_t **statsp) { + isc_stats_t *stats; + + REQUIRE(statsp != NULL && ISC_STATS_VALID(*statsp)); + + stats = *statsp; + *statsp = NULL; + + LOCK(&stats->lock); + stats->references--; + + if (stats->references == 0) { + isc_mem_put(stats->mctx, stats->copiedcounters, + sizeof(isc_stat_t) * stats->ncounters); + isc_mem_put(stats->mctx, stats->counters, + sizeof(isc_stat_t) * stats->ncounters); + UNLOCK(&stats->lock); + DESTROYLOCK(&stats->lock); +#if ISC_STATS_LOCKCOUNTERS + isc_rwlock_destroy(&stats->counterlock); +#endif + isc_mem_putanddetach(&stats->mctx, stats, sizeof(*stats)); + return; + } + + UNLOCK(&stats->lock); +} + +int +isc_stats_ncounters(isc_stats_t *stats) { + REQUIRE(ISC_STATS_VALID(stats)); + + return (stats->ncounters); +} + +static inline void +incrementcounter(isc_stats_t *stats, int counter) { + int32_t prev; + +#if ISC_STATS_LOCKCOUNTERS + /* + * We use a "read" lock to prevent other threads from reading the + * counter while we "writing" a counter field. The write access itself + * is protected by the atomic operation. + */ + isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read); +#endif + +#if ISC_STATS_USEMULTIFIELDS +#if defined(ISC_STATS_HAVESTDATOMIC) + prev = atomic_fetch_add_explicit(&stats->counters[counter].lo, 1, + memory_order_relaxed); +#else + prev = isc_atomic_xadd((int32_t *)&stats->counters[counter].lo, 1); +#endif + /* + * If the lower 32-bit field overflows, increment the higher field. + * Note that it's *theoretically* possible that the lower field + * overlaps again before the higher field is incremented. It doesn't + * matter, however, because we don't read the value until + * isc_stats_copy() is called where the whole process is protected + * by the write (exclusive) lock. + */ + if (prev == (int32_t)0xffffffff) { +#if defined(ISC_STATS_HAVESTDATOMIC) + atomic_fetch_add_explicit(&stats->counters[counter].hi, 1, + memory_order_relaxed); +#else + isc_atomic_xadd((int32_t *)&stats->counters[counter].hi, 1); +#endif + } +#elif ISC_STATS_HAVEATOMICQ + UNUSED(prev); +#if defined(ISC_STATS_HAVESTDATOMICQ) + atomic_fetch_add_explicit(&stats->counters[counter], 1, + memory_order_relaxed); +#else + isc_atomic_xaddq((int64_t *)&stats->counters[counter], 1); +#endif +#else + UNUSED(prev); + stats->counters[counter]++; +#endif + +#if ISC_STATS_LOCKCOUNTERS + isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read); +#endif +} + +static inline void +decrementcounter(isc_stats_t *stats, int counter) { + int32_t prev; + +#if ISC_STATS_LOCKCOUNTERS + isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_read); +#endif + +#if ISC_STATS_USEMULTIFIELDS +#if defined(ISC_STATS_HAVESTDATOMIC) + prev = atomic_fetch_sub_explicit(&stats->counters[counter].lo, 1, + memory_order_relaxed); +#else + prev = isc_atomic_xadd((int32_t *)&stats->counters[counter].lo, -1); +#endif + if (prev == 0) { +#if defined(ISC_STATS_HAVESTDATOMIC) + atomic_fetch_sub_explicit(&stats->counters[counter].hi, 1, + memory_order_relaxed); +#else + isc_atomic_xadd((int32_t *)&stats->counters[counter].hi, + -1); +#endif + } +#elif ISC_STATS_HAVEATOMICQ + UNUSED(prev); +#if defined(ISC_STATS_HAVESTDATOMICQ) + atomic_fetch_sub_explicit(&stats->counters[counter], 1, + memory_order_relaxed); +#else + isc_atomic_xaddq((int64_t *)&stats->counters[counter], -1); +#endif +#else + UNUSED(prev); + stats->counters[counter]--; +#endif + +#if ISC_STATS_LOCKCOUNTERS + isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_read); +#endif +} + +static void +copy_counters(isc_stats_t *stats) { + int i; + +#if ISC_STATS_LOCKCOUNTERS + /* + * We use a "write" lock before "reading" the statistics counters as + * an exclusive lock. + */ + isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_write); +#endif + + for (i = 0; i < stats->ncounters; i++) { +#if ISC_STATS_USEMULTIFIELDS + stats->copiedcounters[i] = + (uint64_t)(stats->counters[i].hi) << 32 | + stats->counters[i].lo; +#elif ISC_STATS_HAVEATOMICQ +#if defined(ISC_STATS_HAVESTDATOMICQ) + stats->copiedcounters[i] = + atomic_load_explicit(&stats->counters[i], + memory_order_relaxed); +#else + /* use xaddq(..., 0) as an atomic load */ + stats->copiedcounters[i] = + (uint64_t)isc_atomic_xaddq((int64_t *)&stats->counters[i], 0); +#endif +#else + stats->copiedcounters[i] = stats->counters[i]; +#endif + } + +#if ISC_STATS_LOCKCOUNTERS + isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_write); +#endif +} + +isc_result_t +isc_stats_create(isc_mem_t *mctx, isc_stats_t **statsp, int ncounters) { + REQUIRE(statsp != NULL && *statsp == NULL); + + return (create_stats(mctx, ncounters, statsp)); +} + +void +isc_stats_increment(isc_stats_t *stats, isc_statscounter_t counter) { + REQUIRE(ISC_STATS_VALID(stats)); + REQUIRE(counter < stats->ncounters); + + incrementcounter(stats, (int)counter); +} + +void +isc_stats_decrement(isc_stats_t *stats, isc_statscounter_t counter) { + REQUIRE(ISC_STATS_VALID(stats)); + REQUIRE(counter < stats->ncounters); + + decrementcounter(stats, (int)counter); +} + +void +isc_stats_dump(isc_stats_t *stats, isc_stats_dumper_t dump_fn, + void *arg, unsigned int options) +{ + int i; + + REQUIRE(ISC_STATS_VALID(stats)); + + copy_counters(stats); + + for (i = 0; i < stats->ncounters; i++) { + if ((options & ISC_STATSDUMP_VERBOSE) == 0 && + stats->copiedcounters[i] == 0) + continue; + dump_fn((isc_statscounter_t)i, stats->copiedcounters[i], arg); + } +} + +void +isc_stats_set(isc_stats_t *stats, uint64_t val, + isc_statscounter_t counter) +{ + REQUIRE(ISC_STATS_VALID(stats)); + REQUIRE(counter < stats->ncounters); + +#if ISC_STATS_LOCKCOUNTERS + /* + * We use a "write" lock before "reading" the statistics counters as + * an exclusive lock. + */ + isc_rwlock_lock(&stats->counterlock, isc_rwlocktype_write); +#endif + +#if ISC_STATS_USEMULTIFIELDS + stats->counters[counter].hi = (uint32_t)((val >> 32) & 0xffffffff); + stats->counters[counter].lo = (uint32_t)(val & 0xffffffff); +#elif ISC_STATS_HAVEATOMICQ +#if defined(ISC_STATS_HAVESTDATOMICQ) + atomic_store_explicit(&stats->counters[counter], val, + memory_order_relaxed); +#else + isc_atomic_storeq((int64_t *)&stats->counters[counter], val); +#endif +#else + stats->counters[counter] = val; +#endif + +#if ISC_STATS_LOCKCOUNTERS + isc_rwlock_unlock(&stats->counterlock, isc_rwlocktype_write); +#endif +} diff --git a/lib/isc/string.c b/lib/isc/string.c new file mode 100644 index 0000000..6728cfb --- /dev/null +++ b/lib/isc/string.c @@ -0,0 +1,313 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*! \file */ + +#include <config.h> + +#include <ctype.h> + +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/region.h> +#include <isc/string.h> +#include <isc/util.h> + +static const char digits[] = "0123456789abcdefghijklmnoprstuvwxyz"; + +uint64_t +isc_string_touint64(char *source, char **end, int base) { + uint64_t tmp; + uint64_t overflow; + char *s = source; + const char *o; + char c; + + if ((base < 0) || (base == 1) || (base > 36)) { + *end = source; + return (0); + } + + while (*s != 0 && isascii(*s&0xff) && isspace(*s&0xff)) + s++; + if (*s == '+' /* || *s == '-' */) + s++; + if (base == 0) { + if (*s == '0' && (*(s+1) == 'X' || *(s+1) == 'x')) { + s += 2; + base = 16; + } else if (*s == '0') + base = 8; + else + base = 10; + } + if (*s == 0) { + *end = source; + return (0); + } + overflow = ~0; + overflow /= base; + tmp = 0; + + while ((c = *s) != 0) { + c = tolower(c&0xff); + /* end ? */ + if ((o = strchr(digits, c)) == NULL) { + *end = s; + return (tmp); + } + /* end ? */ + if ((o - digits) >= base) { + *end = s; + return (tmp); + } + /* overflow ? */ + if (tmp > overflow) { + *end = source; + return (0); + } + tmp *= base; + /* overflow ? */ + if ((tmp + (o - digits)) < tmp) { + *end = source; + return (0); + } + tmp += o - digits; + s++; + } + *end = s; + return (tmp); +} + +isc_result_t +isc_string_copy(char *target, size_t size, const char *source) { + REQUIRE(size > 0U); + + if (strlcpy(target, source, size) >= size) { + memset(target, ISC_STRING_MAGIC, size); + return (ISC_R_NOSPACE); + } + + ENSURE(strlen(target) < size); + + return (ISC_R_SUCCESS); +} + +void +isc_string_copy_truncate(char *target, size_t size, const char *source) { + REQUIRE(size > 0U); + + strlcpy(target, source, size); + + ENSURE(strlen(target) < size); +} + +isc_result_t +isc_string_append(char *target, size_t size, const char *source) { + REQUIRE(size > 0U); + REQUIRE(strlen(target) < size); + + if (strlcat(target, source, size) >= size) { + memset(target, ISC_STRING_MAGIC, size); + return (ISC_R_NOSPACE); + } + + ENSURE(strlen(target) < size); + + return (ISC_R_SUCCESS); +} + +void +isc_string_append_truncate(char *target, size_t size, const char *source) { + REQUIRE(size > 0U); + REQUIRE(strlen(target) < size); + + strlcat(target, source, size); + + ENSURE(strlen(target) < size); +} + +isc_result_t +isc_string_printf(char *target, size_t size, const char *format, ...) { + va_list args; + size_t n; + + REQUIRE(size > 0U); + + va_start(args, format); + n = vsnprintf(target, size, format, args); + va_end(args); + + if (n >= size) { + memset(target, ISC_STRING_MAGIC, size); + return (ISC_R_NOSPACE); + } + + ENSURE(strlen(target) < size); + + return (ISC_R_SUCCESS); +} + +void +isc_string_printf_truncate(char *target, size_t size, const char *format, ...) +{ + va_list args; + + REQUIRE(size > 0U); + + va_start(args, format); + /* check return code? */ + (void)vsnprintf(target, size, format, args); + va_end(args); + + ENSURE(strlen(target) < size); +} + +char * +isc_string_regiondup(isc_mem_t *mctx, const isc_region_t *source) { + char *target; + + REQUIRE(mctx != NULL); + REQUIRE(source != NULL); + + target = (char *) isc_mem_allocate(mctx, source->length + 1); + if (target != NULL) { + memmove(source->base, target, source->length); + target[source->length] = '\0'; + } + + return (target); +} + +char * +isc_string_separate(char **stringp, const char *delim) { + char *string = *stringp; + char *s; + const char *d; + char sc, dc; + + if (string == NULL) + return (NULL); + + for (s = string; (sc = *s) != '\0'; s++) + for (d = delim; (dc = *d) != '\0'; d++) + if (sc == dc) { + *s++ = '\0'; + *stringp = s; + return (string); + } + *stringp = NULL; + return (string); +} + +size_t +isc_string_strlcpy(char *dst, const char *src, size_t size) +{ + char *d = dst; + const char *s = src; + size_t n = size; + + /* Copy as many bytes as will fit */ + if (n != 0U && --n != 0U) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0U); + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0U) { + if (size != 0U) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} + +size_t +isc_string_strlcat(char *dst, const char *src, size_t size) +{ + char *d = dst; + const char *s = src; + size_t n = size; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0U && *d != '\0') + d++; + dlen = d - dst; + n = size - dlen; + + if (n == 0U) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1U) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} + +char * +isc_string_strcasestr(const char *str, const char *search) { + char c, sc, *s; + size_t len; + + if ((c = *search++) != 0) { + c = tolower((unsigned char) c); + len = strlen(search); + do { + do { + if ((sc = *str++) == 0) + return (NULL); + } while ((char) tolower((unsigned char) sc) != c); + } while (strncasecmp(str, search, len) != 0); + str--; + } + DE_CONST(str, s); + return (s); + +} diff --git a/lib/isc/strtoul.c b/lib/isc/strtoul.c new file mode 100644 index 0000000..acc541a --- /dev/null +++ b/lib/isc/strtoul.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +/*! \file */ +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strtoul.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include <limits.h> +#include <ctype.h> +#include <errno.h> + +#include <isc/stdlib.h> +#include <isc/util.h> + +/*! + * Convert a string to an unsigned long integer. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ +unsigned long +isc_strtoul(const char *nptr, char **endptr, int base) { + const char *s = nptr; + unsigned long acc; + unsigned char c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; + cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) { + if (!isascii(c)) + break; + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULONG_MAX; + errno = ERANGE; + } else if (neg) + acc = -acc; + if (endptr != 0) + DE_CONST(any ? s - 1 : nptr, *endptr); + return (acc); +} diff --git a/lib/isc/symtab.c b/lib/isc/symtab.c new file mode 100644 index 0000000..b6466d3 --- /dev/null +++ b/lib/isc/symtab.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <ctype.h> +#include <stdbool.h> + +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/string.h> +#include <isc/symtab.h> +#include <isc/util.h> + +typedef struct elt { + char * key; + unsigned int type; + isc_symvalue_t value; + LINK(struct elt) link; +} elt_t; + +typedef LIST(elt_t) eltlist_t; + +#define SYMTAB_MAGIC ISC_MAGIC('S', 'y', 'm', 'T') +#define VALID_SYMTAB(st) ISC_MAGIC_VALID(st, SYMTAB_MAGIC) + +struct isc_symtab { + /* Unlocked. */ + unsigned int magic; + isc_mem_t * mctx; + unsigned int size; + unsigned int count; + unsigned int maxload; + eltlist_t * table; + isc_symtabaction_t undefine_action; + void * undefine_arg; + bool case_sensitive; +}; + +isc_result_t +isc_symtab_create(isc_mem_t *mctx, unsigned int size, + isc_symtabaction_t undefine_action, + void *undefine_arg, + bool case_sensitive, + isc_symtab_t **symtabp) +{ + isc_symtab_t *symtab; + unsigned int i; + + REQUIRE(mctx != NULL); + REQUIRE(symtabp != NULL && *symtabp == NULL); + REQUIRE(size > 0); /* Should be prime. */ + + symtab = (isc_symtab_t *)isc_mem_get(mctx, sizeof(*symtab)); + if (symtab == NULL) + return (ISC_R_NOMEMORY); + + symtab->mctx = NULL; + isc_mem_attach(mctx, &symtab->mctx); + symtab->table = (eltlist_t *)isc_mem_get(mctx, + size * sizeof(eltlist_t)); + if (symtab->table == NULL) { + isc_mem_putanddetach(&symtab->mctx, symtab, sizeof(*symtab)); + return (ISC_R_NOMEMORY); + } + for (i = 0; i < size; i++) + INIT_LIST(symtab->table[i]); + symtab->size = size; + symtab->count = 0; + symtab->maxload = size * 3 / 4; + symtab->undefine_action = undefine_action; + symtab->undefine_arg = undefine_arg; + symtab->case_sensitive = case_sensitive; + symtab->magic = SYMTAB_MAGIC; + + *symtabp = symtab; + + return (ISC_R_SUCCESS); +} + +void +isc_symtab_destroy(isc_symtab_t **symtabp) { + isc_symtab_t *symtab; + unsigned int i; + elt_t *elt, *nelt; + + REQUIRE(symtabp != NULL); + symtab = *symtabp; + REQUIRE(VALID_SYMTAB(symtab)); + + for (i = 0; i < symtab->size; i++) { + for (elt = HEAD(symtab->table[i]); elt != NULL; elt = nelt) { + nelt = NEXT(elt, link); + if (symtab->undefine_action != NULL) + (symtab->undefine_action)(elt->key, + elt->type, + elt->value, + symtab->undefine_arg); + isc_mem_put(symtab->mctx, elt, sizeof(*elt)); + } + } + isc_mem_put(symtab->mctx, symtab->table, + symtab->size * sizeof(eltlist_t)); + symtab->magic = 0; + isc_mem_putanddetach(&symtab->mctx, symtab, sizeof(*symtab)); + + *symtabp = NULL; +} + +static inline unsigned int +hash(const char *key, bool case_sensitive) { + const char *s; + unsigned int h = 0; + int c; + + /* + * This hash function is similar to the one Ousterhout + * uses in Tcl. + */ + + if (case_sensitive) { + for (s = key; *s != '\0'; s++) { + h += (h << 3) + *s; + } + } else { + for (s = key; *s != '\0'; s++) { + c = *s; + c = tolower((unsigned char)c); + h += (h << 3) + c; + } + } + + return (h); +} + +#define FIND(s, k, t, b, e) \ + b = hash((k), (s)->case_sensitive) % (s)->size; \ + if ((s)->case_sensitive) { \ + for (e = HEAD((s)->table[b]); e != NULL; e = NEXT(e, link)) { \ + if (((t) == 0 || e->type == (t)) && \ + strcmp(e->key, (k)) == 0) \ + break; \ + } \ + } else { \ + for (e = HEAD((s)->table[b]); e != NULL; e = NEXT(e, link)) { \ + if (((t) == 0 || e->type == (t)) && \ + strcasecmp(e->key, (k)) == 0) \ + break; \ + } \ + } + +isc_result_t +isc_symtab_lookup(isc_symtab_t *symtab, const char *key, unsigned int type, + isc_symvalue_t *value) +{ + unsigned int bucket; + elt_t *elt; + + REQUIRE(VALID_SYMTAB(symtab)); + REQUIRE(key != NULL); + + FIND(symtab, key, type, bucket, elt); + + if (elt == NULL) + return (ISC_R_NOTFOUND); + + if (value != NULL) + *value = elt->value; + + return (ISC_R_SUCCESS); +} + +static void +grow_table(isc_symtab_t *symtab) { + eltlist_t *newtable; + unsigned int i, newsize, newmax; + + REQUIRE(symtab != NULL); + + newsize = symtab->size * 2; + newmax = newsize * 3 / 4; + INSIST(newsize > 0U && newmax > 0U); + + newtable = isc_mem_get(symtab->mctx, newsize * sizeof(eltlist_t)); + if (newtable == NULL) + return; + + for (i = 0; i < newsize; i++) + INIT_LIST(newtable[i]); + + for (i = 0; i < symtab->size; i++) { + elt_t *elt, *nelt; + + for (elt = HEAD(symtab->table[i]); elt != NULL; elt = nelt) { + unsigned int hv; + + nelt = NEXT(elt, link); + + UNLINK(symtab->table[i], elt, link); + hv = hash(elt->key, symtab->case_sensitive); + APPEND(newtable[hv % newsize], elt, link); + } + } + + isc_mem_put(symtab->mctx, symtab->table, + symtab->size * sizeof(eltlist_t)); + + symtab->table = newtable; + symtab->size = newsize; + symtab->maxload = newmax; +} + +isc_result_t +isc_symtab_define(isc_symtab_t *symtab, const char *key, unsigned int type, + isc_symvalue_t value, isc_symexists_t exists_policy) +{ + unsigned int bucket; + elt_t *elt; + + REQUIRE(VALID_SYMTAB(symtab)); + REQUIRE(key != NULL); + REQUIRE(type != 0); + + FIND(symtab, key, type, bucket, elt); + + if (exists_policy != isc_symexists_add && elt != NULL) { + if (exists_policy == isc_symexists_reject) + return (ISC_R_EXISTS); + INSIST(exists_policy == isc_symexists_replace); + UNLINK(symtab->table[bucket], elt, link); + if (symtab->undefine_action != NULL) + (symtab->undefine_action)(elt->key, elt->type, + elt->value, + symtab->undefine_arg); + } else { + elt = (elt_t *)isc_mem_get(symtab->mctx, sizeof(*elt)); + if (elt == NULL) + return (ISC_R_NOMEMORY); + ISC_LINK_INIT(elt, link); + symtab->count++; + } + + /* + * Though the "key" can be const coming in, it is not stored as const + * so that the calling program can easily have writable access to + * it in its undefine_action function. In the event that it *was* + * truly const coming in and then the caller modified it anyway ... + * well, don't do that! + */ + DE_CONST(key, elt->key); + elt->type = type; + elt->value = value; + + /* + * We prepend so that the most recent definition will be found. + */ + PREPEND(symtab->table[bucket], elt, link); + + if (symtab->count > symtab->maxload) + grow_table(symtab); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_symtab_undefine(isc_symtab_t *symtab, const char *key, unsigned int type) { + unsigned int bucket; + elt_t *elt; + + REQUIRE(VALID_SYMTAB(symtab)); + REQUIRE(key != NULL); + + FIND(symtab, key, type, bucket, elt); + + if (elt == NULL) + return (ISC_R_NOTFOUND); + + if (symtab->undefine_action != NULL) + (symtab->undefine_action)(elt->key, elt->type, + elt->value, symtab->undefine_arg); + UNLINK(symtab->table[bucket], elt, link); + isc_mem_put(symtab->mctx, elt, sizeof(*elt)); + symtab->count--; + + return (ISC_R_SUCCESS); +} + +unsigned int +isc_symtab_count(isc_symtab_t *symtab) { + REQUIRE(VALID_SYMTAB(symtab)); + return (symtab->count); +} diff --git a/lib/isc/task.c b/lib/isc/task.c new file mode 100644 index 0000000..f9f1eed --- /dev/null +++ b/lib/isc/task.c @@ -0,0 +1,2327 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +/* + * XXXRTH Need to document the states a task can be in, and the rules + * for changing states. + */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/app.h> +#include <isc/condition.h> +#include <isc/event.h> +#include <isc/json.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/once.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/thread.h> +#include <isc/time.h> +#include <isc/util.h> +#include <isc/xml.h> + +#ifdef OPENSSL_LEAKS +#include <openssl/err.h> +#endif + +/*% + * For BIND9 internal applications: + * when built with threads we use multiple worker threads shared by the whole + * application. + * when built without threads we share a single global task manager and use + * an integrated event loop for socket, timer, and other generic task events. + * For generic library: + * we don't use either of them: an application can have multiple task managers + * whether or not it's threaded, and if the application is threaded each thread + * is expected to have a separate manager; no "worker threads" are shared by + * the application threads. + */ +#ifdef ISC_PLATFORM_USETHREADS +#define USE_WORKER_THREADS +#else +#define USE_SHARED_MANAGER +#endif /* ISC_PLATFORM_USETHREADS */ + +#include "task_p.h" + +#ifdef ISC_TASK_TRACE +#define XTRACE(m) fprintf(stderr, "task %p thread %lu: %s\n", \ + task, isc_thread_self(), (m)) +#define XTTRACE(t, m) fprintf(stderr, "task %p thread %lu: %s\n", \ + (t), isc_thread_self(), (m)) +#define XTHREADTRACE(m) fprintf(stderr, "thread %lu: %s\n", \ + isc_thread_self(), (m)) +#else +#define XTRACE(m) +#define XTTRACE(t, m) +#define XTHREADTRACE(m) +#endif + +/*** + *** Types. + ***/ + +typedef enum { + task_state_idle, task_state_ready, task_state_running, + task_state_done +} task_state_t; + +#if defined(HAVE_LIBXML2) || defined(HAVE_JSON) +static const char *statenames[] = { + "idle", "ready", "running", "done", +}; +#endif + +#define TASK_MAGIC ISC_MAGIC('T', 'A', 'S', 'K') +#define VALID_TASK(t) ISC_MAGIC_VALID(t, TASK_MAGIC) + +typedef struct isc__task isc__task_t; +typedef struct isc__taskmgr isc__taskmgr_t; + +struct isc__task { + /* Not locked. */ + isc_task_t common; + isc__taskmgr_t * manager; + isc_mutex_t lock; + /* Locked by task lock. */ + task_state_t state; + unsigned int references; + isc_eventlist_t events; + isc_eventlist_t on_shutdown; + unsigned int nevents; + unsigned int quantum; + unsigned int flags; + isc_stdtime_t now; + isc_time_t tnow; + char name[16]; + void * tag; + /* Locked by task manager lock. */ + LINK(isc__task_t) link; + LINK(isc__task_t) ready_link; + LINK(isc__task_t) ready_priority_link; +}; + +#define TASK_F_SHUTTINGDOWN 0x01 +#define TASK_F_PRIVILEGED 0x02 + +#define TASK_SHUTTINGDOWN(t) (((t)->flags & TASK_F_SHUTTINGDOWN) \ + != 0) + +#define TASK_MANAGER_MAGIC ISC_MAGIC('T', 'S', 'K', 'M') +#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TASK_MANAGER_MAGIC) + +typedef ISC_LIST(isc__task_t) isc__tasklist_t; + +struct isc__taskmgr { + /* Not locked. */ + isc_taskmgr_t common; + isc_mem_t * mctx; + isc_mutex_t lock; +#ifdef ISC_PLATFORM_USETHREADS + unsigned int workers; + isc_thread_t * threads; +#endif /* ISC_PLATFORM_USETHREADS */ + /* Locked by task manager lock. */ + unsigned int default_quantum; + LIST(isc__task_t) tasks; + isc__tasklist_t ready_tasks; + isc__tasklist_t ready_priority_tasks; + isc_taskmgrmode_t mode; +#ifdef ISC_PLATFORM_USETHREADS + isc_condition_t work_available; + isc_condition_t exclusive_granted; + isc_condition_t paused; +#endif /* ISC_PLATFORM_USETHREADS */ + unsigned int tasks_running; + unsigned int tasks_ready; + bool pause_requested; + bool exclusive_requested; + bool exiting; + + /* + * Multiple threads can read/write 'excl' at the same time, so we need + * to protect the access. We can't use 'lock' since isc_task_detach() + * will try to acquire it. + */ + isc_mutex_t excl_lock; + isc__task_t *excl; +#ifdef USE_SHARED_MANAGER + unsigned int refs; +#endif /* ISC_PLATFORM_USETHREADS */ +}; + +#define DEFAULT_TASKMGR_QUANTUM 10 +#define DEFAULT_DEFAULT_QUANTUM 5 +#define FINISHED(m) ((m)->exiting && EMPTY((m)->tasks)) + +#ifdef USE_SHARED_MANAGER +static isc__taskmgr_t *taskmgr = NULL; +#endif /* USE_SHARED_MANAGER */ + +/*% + * The following are intended for internal use (indicated by "isc__" + * prefix) but are not declared as static, allowing direct access from + * unit tests etc. + */ + +isc_result_t +isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, + isc_task_t **taskp); +void +isc__task_attach(isc_task_t *source0, isc_task_t **targetp); +void +isc__task_detach(isc_task_t **taskp); +void +isc__task_send(isc_task_t *task0, isc_event_t **eventp); +void +isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp); +unsigned int +isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag); +unsigned int +isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag); +bool +isc_task_purgeevent(isc_task_t *task0, isc_event_t *event); +unsigned int +isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag, + isc_eventlist_t *events); +unsigned int +isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag, isc_eventlist_t *events); +isc_result_t +isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action, + void *arg); +void +isc__task_shutdown(isc_task_t *task0); +void +isc__task_destroy(isc_task_t **taskp); +void +isc__task_setname(isc_task_t *task0, const char *name, void *tag); +const char * +isc__task_getname(isc_task_t *task0); +void * +isc__task_gettag(isc_task_t *task0); +void +isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t); +void +isc__task_getcurrenttimex(isc_task_t *task0, isc_time_t *t); +isc_result_t +isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, + unsigned int default_quantum, isc_taskmgr_t **managerp); +void +isc__taskmgr_destroy(isc_taskmgr_t **managerp); +void +isc_taskmgr_setexcltask(isc_taskmgr_t *mgr0, isc_task_t *task0); +isc_result_t +isc_taskmgr_excltask(isc_taskmgr_t *mgr0, isc_task_t **taskp); +isc_result_t +isc__task_beginexclusive(isc_task_t *task); +void +isc__task_endexclusive(isc_task_t *task0); +void +isc__task_setprivilege(isc_task_t *task0, bool priv); +bool +isc__task_privilege(isc_task_t *task0); +void +isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode); +isc_taskmgrmode_t +isc__taskmgr_mode(isc_taskmgr_t *manager0); + +static inline bool +empty_readyq(isc__taskmgr_t *manager); + +static inline isc__task_t * +pop_readyq(isc__taskmgr_t *manager); + +static inline void +push_readyq(isc__taskmgr_t *manager, isc__task_t *task); + +static struct isc__taskmethods { + isc_taskmethods_t methods; + + /*% + * The following are defined just for avoiding unused static functions. + */ + void *purgeevent, *unsendrange, *getname, *gettag, + *getcurrenttime, *getcurrenttimex; +} taskmethods = { + { + isc__task_attach, + isc__task_detach, + isc__task_destroy, + isc__task_send, + isc__task_sendanddetach, + isc__task_unsend, + isc__task_onshutdown, + isc__task_shutdown, + isc__task_setname, + isc__task_purge, + isc__task_purgerange, + isc__task_beginexclusive, + isc__task_endexclusive, + isc__task_setprivilege, + isc__task_privilege + }, + (void *)isc_task_purgeevent, + (void *)isc__task_unsendrange, + (void *)isc__task_getname, + (void *)isc__task_gettag, + (void *)isc__task_getcurrenttime, + (void *)isc__task_getcurrenttimex +}; + +static isc_taskmgrmethods_t taskmgrmethods = { + isc__taskmgr_destroy, + isc__taskmgr_setmode, + isc__taskmgr_mode, + isc__task_create, + isc_taskmgr_setexcltask, + isc_taskmgr_excltask +}; + +/*** + *** Tasks. + ***/ + +static void +task_finished(isc__task_t *task) { + isc__taskmgr_t *manager = task->manager; + + REQUIRE(EMPTY(task->events)); + REQUIRE(task->nevents == 0); + REQUIRE(EMPTY(task->on_shutdown)); + REQUIRE(task->references == 0); + REQUIRE(task->state == task_state_done); + + XTRACE("task_finished"); + + LOCK(&manager->lock); + UNLINK(manager->tasks, task, link); +#ifdef USE_WORKER_THREADS + if (FINISHED(manager)) { + /* + * All tasks have completed and the + * task manager is exiting. Wake up + * any idle worker threads so they + * can exit. + */ + BROADCAST(&manager->work_available); + } +#endif /* USE_WORKER_THREADS */ + UNLOCK(&manager->lock); + + DESTROYLOCK(&task->lock); + task->common.impmagic = 0; + task->common.magic = 0; + isc_mem_put(manager->mctx, task, sizeof(*task)); +} + +isc_result_t +isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, + isc_task_t **taskp) +{ + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + isc__task_t *task; + bool exiting; + isc_result_t result; + + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(taskp != NULL && *taskp == NULL); + + task = isc_mem_get(manager->mctx, sizeof(*task)); + if (task == NULL) + return (ISC_R_NOMEMORY); + XTRACE("isc_task_create"); + task->manager = manager; + result = isc_mutex_init(&task->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(manager->mctx, task, sizeof(*task)); + return (result); + } + task->state = task_state_idle; + task->references = 1; + INIT_LIST(task->events); + INIT_LIST(task->on_shutdown); + task->nevents = 0; + task->quantum = quantum; + task->flags = 0; + task->now = 0; + isc_time_settoepoch(&task->tnow); + memset(task->name, 0, sizeof(task->name)); + task->tag = NULL; + INIT_LINK(task, link); + INIT_LINK(task, ready_link); + INIT_LINK(task, ready_priority_link); + + exiting = false; + LOCK(&manager->lock); + if (!manager->exiting) { + if (task->quantum == 0) + task->quantum = manager->default_quantum; + APPEND(manager->tasks, task, link); + } else + exiting = true; + UNLOCK(&manager->lock); + + if (exiting) { + DESTROYLOCK(&task->lock); + isc_mem_put(manager->mctx, task, sizeof(*task)); + return (ISC_R_SHUTTINGDOWN); + } + + task->common.methods = (isc_taskmethods_t *)&taskmethods; + task->common.magic = ISCAPI_TASK_MAGIC; + task->common.impmagic = TASK_MAGIC; + *taskp = (isc_task_t *)task; + + return (ISC_R_SUCCESS); +} + +void +isc__task_attach(isc_task_t *source0, isc_task_t **targetp) { + isc__task_t *source = (isc__task_t *)source0; + + /* + * Attach *targetp to source. + */ + + REQUIRE(VALID_TASK(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + XTTRACE(source, "isc_task_attach"); + + LOCK(&source->lock); + source->references++; + UNLOCK(&source->lock); + + *targetp = (isc_task_t *)source; +} + +static inline bool +task_shutdown(isc__task_t *task) { + bool was_idle = false; + isc_event_t *event, *prev; + + /* + * Caller must be holding the task's lock. + */ + + XTRACE("task_shutdown"); + + if (! TASK_SHUTTINGDOWN(task)) { + XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_SHUTTINGDOWN, "shutting down")); + task->flags |= TASK_F_SHUTTINGDOWN; + if (task->state == task_state_idle) { + INSIST(EMPTY(task->events)); + task->state = task_state_ready; + was_idle = true; + } + INSIST(task->state == task_state_ready || + task->state == task_state_running); + + /* + * Note that we post shutdown events LIFO. + */ + for (event = TAIL(task->on_shutdown); + event != NULL; + event = prev) { + prev = PREV(event, ev_link); + DEQUEUE(task->on_shutdown, event, ev_link); + ENQUEUE(task->events, event, ev_link); + task->nevents++; + } + } + + return (was_idle); +} + +/* + * Moves a task onto the appropriate run queue. + * + * Caller must NOT hold manager lock. + */ +static inline void +task_ready(isc__task_t *task) { + isc__taskmgr_t *manager = task->manager; +#ifdef USE_WORKER_THREADS + bool has_privilege = isc__task_privilege((isc_task_t *) task); +#endif /* USE_WORKER_THREADS */ + + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(task->state == task_state_ready); + + XTRACE("task_ready"); + + LOCK(&manager->lock); + push_readyq(manager, task); +#ifdef USE_WORKER_THREADS + if (manager->mode == isc_taskmgrmode_normal || has_privilege) + SIGNAL(&manager->work_available); +#endif /* USE_WORKER_THREADS */ + UNLOCK(&manager->lock); +} + +static inline bool +task_detach(isc__task_t *task) { + + /* + * Caller must be holding the task lock. + */ + + REQUIRE(task->references > 0); + + XTRACE("detach"); + + task->references--; + if (task->references == 0 && task->state == task_state_idle) { + INSIST(EMPTY(task->events)); + /* + * There are no references to this task, and no + * pending events. We could try to optimize and + * either initiate shutdown or clean up the task, + * depending on its state, but it's easier to just + * make the task ready and allow run() or the event + * loop to deal with shutting down and termination. + */ + task->state = task_state_ready; + return (true); + } + + return (false); +} + +void +isc__task_detach(isc_task_t **taskp) { + isc__task_t *task; + bool was_idle; + + /* + * Detach *taskp from its task. + */ + + REQUIRE(taskp != NULL); + task = (isc__task_t *)*taskp; + REQUIRE(VALID_TASK(task)); + + XTRACE("isc_task_detach"); + + LOCK(&task->lock); + was_idle = task_detach(task); + UNLOCK(&task->lock); + + if (was_idle) + task_ready(task); + + *taskp = NULL; +} + +static inline bool +task_send(isc__task_t *task, isc_event_t **eventp) { + bool was_idle = false; + isc_event_t *event; + + /* + * Caller must be holding the task lock. + */ + + REQUIRE(eventp != NULL); + event = *eventp; + REQUIRE(event != NULL); + REQUIRE(event->ev_type > 0); + REQUIRE(task->state != task_state_done); + REQUIRE(!ISC_LINK_LINKED(event, ev_ratelink)); + + XTRACE("task_send"); + + if (task->state == task_state_idle) { + was_idle = true; + INSIST(EMPTY(task->events)); + task->state = task_state_ready; + } + INSIST(task->state == task_state_ready || + task->state == task_state_running); + ENQUEUE(task->events, event, ev_link); + task->nevents++; + *eventp = NULL; + + return (was_idle); +} + +void +isc__task_send(isc_task_t *task0, isc_event_t **eventp) { + isc__task_t *task = (isc__task_t *)task0; + bool was_idle; + + /* + * Send '*event' to 'task'. + */ + + REQUIRE(VALID_TASK(task)); + + XTRACE("isc_task_send"); + + /* + * We're trying hard to hold locks for as short a time as possible. + * We're also trying to hold as few locks as possible. This is why + * some processing is deferred until after the lock is released. + */ + LOCK(&task->lock); + was_idle = task_send(task, eventp); + UNLOCK(&task->lock); + + if (was_idle) { + /* + * We need to add this task to the ready queue. + * + * We've waited until now to do it because making a task + * ready requires locking the manager. If we tried to do + * this while holding the task lock, we could deadlock. + * + * We've changed the state to ready, so no one else will + * be trying to add this task to the ready queue. The + * only way to leave the ready state is by executing the + * task. It thus doesn't matter if events are added, + * removed, or a shutdown is started in the interval + * between the time we released the task lock, and the time + * we add the task to the ready queue. + */ + task_ready(task); + } +} + +void +isc__task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { + bool idle1, idle2; + isc__task_t *task; + + /* + * Send '*event' to '*taskp' and then detach '*taskp' from its + * task. + */ + + REQUIRE(taskp != NULL); + task = (isc__task_t *)*taskp; + REQUIRE(VALID_TASK(task)); + + XTRACE("isc_task_sendanddetach"); + + LOCK(&task->lock); + idle1 = task_send(task, eventp); + idle2 = task_detach(task); + UNLOCK(&task->lock); + + /* + * If idle1, then idle2 shouldn't be true as well since we're holding + * the task lock, and thus the task cannot switch from ready back to + * idle. + */ + INSIST(!(idle1 && idle2)); + + if (idle1 || idle2) + task_ready(task); + + *taskp = NULL; +} + +#define PURGE_OK(event) (((event)->ev_attributes & ISC_EVENTATTR_NOPURGE) == 0) + +static unsigned int +dequeue_events(isc__task_t *task, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag, + isc_eventlist_t *events, bool purging) +{ + isc_event_t *event, *next_event; + unsigned int count = 0; + + REQUIRE(VALID_TASK(task)); + REQUIRE(last >= first); + + XTRACE("dequeue_events"); + + /* + * Events matching 'sender', whose type is >= first and <= last, and + * whose tag is 'tag' will be dequeued. If 'purging', matching events + * which are marked as unpurgable will not be dequeued. + * + * sender == NULL means "any sender", and tag == NULL means "any tag". + */ + + LOCK(&task->lock); + + for (event = HEAD(task->events); event != NULL; event = next_event) { + next_event = NEXT(event, ev_link); + if (event->ev_type >= first && event->ev_type <= last && + (sender == NULL || event->ev_sender == sender) && + (tag == NULL || event->ev_tag == tag) && + (!purging || PURGE_OK(event))) { + DEQUEUE(task->events, event, ev_link); + task->nevents--; + ENQUEUE(*events, event, ev_link); + count++; + } + } + + UNLOCK(&task->lock); + + return (count); +} + +unsigned int +isc__task_purgerange(isc_task_t *task0, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag) +{ + isc__task_t *task = (isc__task_t *)task0; + unsigned int count; + isc_eventlist_t events; + isc_event_t *event, *next_event; + + /* + * Purge events from a task's event queue. + */ + + XTRACE("isc_task_purgerange"); + + ISC_LIST_INIT(events); + + count = dequeue_events(task, sender, first, last, tag, &events, + true); + + for (event = HEAD(events); event != NULL; event = next_event) { + next_event = NEXT(event, ev_link); + ISC_LIST_UNLINK(events, event, ev_link); + isc_event_free(&event); + } + + /* + * Note that purging never changes the state of the task. + */ + + return (count); +} + +unsigned int +isc__task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag) +{ + /* + * Purge events from a task's event queue. + */ + + XTRACE("isc_task_purge"); + + return (isc__task_purgerange(task, sender, type, type, tag)); +} + +bool +isc_task_purgeevent(isc_task_t *task0, isc_event_t *event) { + isc__task_t *task = (isc__task_t *)task0; + isc_event_t *curr_event, *next_event; + + /* + * Purge 'event' from a task's event queue. + * + * XXXRTH: WARNING: This method may be removed before beta. + */ + + REQUIRE(VALID_TASK(task)); + + /* + * If 'event' is on the task's event queue, it will be purged, + * unless it is marked as unpurgeable. 'event' does not have to be + * on the task's event queue; in fact, it can even be an invalid + * pointer. Purging only occurs if the event is actually on the task's + * event queue. + * + * Purging never changes the state of the task. + */ + + LOCK(&task->lock); + for (curr_event = HEAD(task->events); + curr_event != NULL; + curr_event = next_event) { + next_event = NEXT(curr_event, ev_link); + if (curr_event == event && PURGE_OK(event)) { + DEQUEUE(task->events, curr_event, ev_link); + task->nevents--; + break; + } + } + UNLOCK(&task->lock); + + if (curr_event == NULL) + return (false); + + isc_event_free(&curr_event); + + return (true); +} + +unsigned int +isc__task_unsendrange(isc_task_t *task, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag, + isc_eventlist_t *events) +{ + /* + * Remove events from a task's event queue. + */ + + XTRACE("isc_task_unsendrange"); + + return (dequeue_events((isc__task_t *)task, sender, first, + last, tag, events, false)); +} + +unsigned int +isc__task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag, isc_eventlist_t *events) +{ + /* + * Remove events from a task's event queue. + */ + + XTRACE("isc_task_unsend"); + + return (dequeue_events((isc__task_t *)task, sender, type, + type, tag, events, false)); +} + +isc_result_t +isc__task_onshutdown(isc_task_t *task0, isc_taskaction_t action, + void *arg) +{ + isc__task_t *task = (isc__task_t *)task0; + bool disallowed = false; + isc_result_t result = ISC_R_SUCCESS; + isc_event_t *event; + + /* + * Send a shutdown event with action 'action' and argument 'arg' when + * 'task' is shutdown. + */ + + REQUIRE(VALID_TASK(task)); + REQUIRE(action != NULL); + + event = isc_event_allocate(task->manager->mctx, + NULL, + ISC_TASKEVENT_SHUTDOWN, + action, + arg, + sizeof(*event)); + if (event == NULL) + return (ISC_R_NOMEMORY); + + LOCK(&task->lock); + if (TASK_SHUTTINGDOWN(task)) { + disallowed = true; + result = ISC_R_SHUTTINGDOWN; + } else + ENQUEUE(task->on_shutdown, event, ev_link); + UNLOCK(&task->lock); + + if (disallowed) + isc_mem_put(task->manager->mctx, event, sizeof(*event)); + + return (result); +} + +void +isc__task_shutdown(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; + bool was_idle; + + /* + * Shutdown 'task'. + */ + + REQUIRE(VALID_TASK(task)); + + LOCK(&task->lock); + was_idle = task_shutdown(task); + UNLOCK(&task->lock); + + if (was_idle) + task_ready(task); +} + +void +isc__task_destroy(isc_task_t **taskp) { + + /* + * Destroy '*taskp'. + */ + + REQUIRE(taskp != NULL); + + isc_task_shutdown(*taskp); + isc_task_detach(taskp); +} + +void +isc__task_setname(isc_task_t *task0, const char *name, void *tag) { + isc__task_t *task = (isc__task_t *)task0; + + /* + * Name 'task'. + */ + + REQUIRE(VALID_TASK(task)); + + LOCK(&task->lock); + strlcpy(task->name, name, sizeof(task->name)); + task->tag = tag; + UNLOCK(&task->lock); +} + +const char * +isc__task_getname(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; + + REQUIRE(VALID_TASK(task)); + + return (task->name); +} + +void * +isc__task_gettag(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; + + REQUIRE(VALID_TASK(task)); + + return (task->tag); +} + +void +isc__task_getcurrenttime(isc_task_t *task0, isc_stdtime_t *t) { + isc__task_t *task = (isc__task_t *)task0; + + REQUIRE(VALID_TASK(task)); + REQUIRE(t != NULL); + + LOCK(&task->lock); + *t = task->now; + UNLOCK(&task->lock); +} + +void +isc__task_getcurrenttimex(isc_task_t *task0, isc_time_t *t) { + isc__task_t *task = (isc__task_t *)task0; + + REQUIRE(VALID_TASK(task)); + REQUIRE(t != NULL); + + LOCK(&task->lock); + *t = task->tnow; + UNLOCK(&task->lock); +} + +/*** + *** Task Manager. + ***/ + +/* + * Return true if the current ready list for the manager, which is + * either ready_tasks or the ready_priority_tasks, depending on whether + * the manager is currently in normal or privileged execution mode. + * + * Caller must hold the task manager lock. + */ +static inline bool +empty_readyq(isc__taskmgr_t *manager) { + isc__tasklist_t queue; + + if (manager->mode == isc_taskmgrmode_normal) + queue = manager->ready_tasks; + else + queue = manager->ready_priority_tasks; + + return (EMPTY(queue)); +} + +/* + * Dequeue and return a pointer to the first task on the current ready + * list for the manager. + * If the task is privileged, dequeue it from the other ready list + * as well. + * + * Caller must hold the task manager lock. + */ +static inline isc__task_t * +pop_readyq(isc__taskmgr_t *manager) { + isc__task_t *task; + + if (manager->mode == isc_taskmgrmode_normal) + task = HEAD(manager->ready_tasks); + else + task = HEAD(manager->ready_priority_tasks); + + if (task != NULL) { + DEQUEUE(manager->ready_tasks, task, ready_link); + if (ISC_LINK_LINKED(task, ready_priority_link)) + DEQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + } + + return (task); +} + +/* + * Push 'task' onto the ready_tasks queue. If 'task' has the privilege + * flag set, then also push it onto the ready_priority_tasks queue. + * + * Caller must hold the task manager lock. + */ +static inline void +push_readyq(isc__taskmgr_t *manager, isc__task_t *task) { + ENQUEUE(manager->ready_tasks, task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) + ENQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + manager->tasks_ready++; +} + +static void +dispatch(isc__taskmgr_t *manager) { + isc__task_t *task; +#ifndef USE_WORKER_THREADS + unsigned int total_dispatch_count = 0; + isc__tasklist_t new_ready_tasks; + isc__tasklist_t new_priority_tasks; + unsigned int tasks_ready = 0; +#endif /* USE_WORKER_THREADS */ + + REQUIRE(VALID_MANAGER(manager)); + + /* + * Again we're trying to hold the lock for as short a time as possible + * and to do as little locking and unlocking as possible. + * + * In both while loops, the appropriate lock must be held before the + * while body starts. Code which acquired the lock at the top of + * the loop would be more readable, but would result in a lot of + * extra locking. Compare: + * + * Straightforward: + * + * LOCK(); + * ... + * UNLOCK(); + * while (expression) { + * LOCK(); + * ... + * UNLOCK(); + * + * Unlocked part here... + * + * LOCK(); + * ... + * UNLOCK(); + * } + * + * Note how if the loop continues we unlock and then immediately lock. + * For N iterations of the loop, this code does 2N+1 locks and 2N+1 + * unlocks. Also note that the lock is not held when the while + * condition is tested, which may or may not be important, depending + * on the expression. + * + * As written: + * + * LOCK(); + * while (expression) { + * ... + * UNLOCK(); + * + * Unlocked part here... + * + * LOCK(); + * ... + * } + * UNLOCK(); + * + * For N iterations of the loop, this code does N+1 locks and N+1 + * unlocks. The while expression is always protected by the lock. + */ + +#ifndef USE_WORKER_THREADS + ISC_LIST_INIT(new_ready_tasks); + ISC_LIST_INIT(new_priority_tasks); +#endif + LOCK(&manager->lock); + + while (!FINISHED(manager)) { +#ifdef USE_WORKER_THREADS + /* + * For reasons similar to those given in the comment in + * isc_task_send() above, it is safe for us to dequeue + * the task while only holding the manager lock, and then + * change the task to running state while only holding the + * task lock. + * + * If a pause has been requested, don't do any work + * until it's been released. + */ + while ((empty_readyq(manager) || manager->pause_requested || + manager->exclusive_requested) && !FINISHED(manager)) + { + XTHREADTRACE(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_WAIT, "wait")); + WAIT(&manager->work_available, &manager->lock); + XTHREADTRACE(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_TASK, + ISC_MSG_AWAKE, "awake")); + } +#else /* USE_WORKER_THREADS */ + if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM || + empty_readyq(manager)) + break; +#endif /* USE_WORKER_THREADS */ + XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, + ISC_MSG_WORKING, "working")); + + task = pop_readyq(manager); + if (task != NULL) { + unsigned int dispatch_count = 0; + bool done = false; + bool requeue = false; + bool finished = false; + isc_event_t *event; + + INSIST(VALID_TASK(task)); + + /* + * Note we only unlock the manager lock if we actually + * have a task to do. We must reacquire the manager + * lock before exiting the 'if (task != NULL)' block. + */ + manager->tasks_ready--; + manager->tasks_running++; + UNLOCK(&manager->lock); + + LOCK(&task->lock); + INSIST(task->state == task_state_ready); + task->state = task_state_running; + XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_RUNNING, "running")); + TIME_NOW(&task->tnow); + task->now = isc_time_seconds(&task->tnow); + do { + if (!EMPTY(task->events)) { + event = HEAD(task->events); + DEQUEUE(task->events, event, ev_link); + task->nevents--; + + /* + * Execute the event action. + */ + XTRACE(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_TASK, + ISC_MSG_EXECUTE, + "execute action")); + if (event->ev_action != NULL) { + UNLOCK(&task->lock); + (event->ev_action)( + (isc_task_t *)task, + event); + LOCK(&task->lock); + } + dispatch_count++; +#ifndef USE_WORKER_THREADS + total_dispatch_count++; +#endif /* USE_WORKER_THREADS */ + } + + if (task->references == 0 && + EMPTY(task->events) && + !TASK_SHUTTINGDOWN(task)) { + bool was_idle; + + /* + * There are no references and no + * pending events for this task, + * which means it will not become + * runnable again via an external + * action (such as sending an event + * or detaching). + * + * We initiate shutdown to prevent + * it from becoming a zombie. + * + * We do this here instead of in + * the "if EMPTY(task->events)" block + * below because: + * + * If we post no shutdown events, + * we want the task to finish. + * + * If we did post shutdown events, + * will still want the task's + * quantum to be applied. + */ + was_idle = task_shutdown(task); + INSIST(!was_idle); + } + + if (EMPTY(task->events)) { + /* + * Nothing else to do for this task + * right now. + */ + XTRACE(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_TASK, + ISC_MSG_EMPTY, + "empty")); + if (task->references == 0 && + TASK_SHUTTINGDOWN(task)) { + /* + * The task is done. + */ + XTRACE(isc_msgcat_get( + isc_msgcat, + ISC_MSGSET_TASK, + ISC_MSG_DONE, + "done")); + finished = true; + task->state = task_state_done; + } else + task->state = task_state_idle; + done = true; + } else if (dispatch_count >= task->quantum) { + /* + * Our quantum has expired, but + * there is more work to be done. + * We'll requeue it to the ready + * queue later. + * + * We don't check quantum until + * dispatching at least one event, + * so the minimum quantum is one. + */ + XTRACE(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_TASK, + ISC_MSG_QUANTUM, + "quantum")); + task->state = task_state_ready; + requeue = true; + done = true; + } + } while (!done); + UNLOCK(&task->lock); + + if (finished) + task_finished(task); + + LOCK(&manager->lock); + manager->tasks_running--; +#ifdef USE_WORKER_THREADS + if (manager->exclusive_requested && + manager->tasks_running == 1) { + SIGNAL(&manager->exclusive_granted); + } else if (manager->pause_requested && + manager->tasks_running == 0) { + SIGNAL(&manager->paused); + } +#endif /* USE_WORKER_THREADS */ + if (requeue) { + /* + * We know we're awake, so we don't have + * to wakeup any sleeping threads if the + * ready queue is empty before we requeue. + * + * A possible optimization if the queue is + * empty is to 'goto' the 'if (task != NULL)' + * block, avoiding the ENQUEUE of the task + * and the subsequent immediate DEQUEUE + * (since it is the only executable task). + * We don't do this because then we'd be + * skipping the exit_requested check. The + * cost of ENQUEUE is low anyway, especially + * when you consider that we'd have to do + * an extra EMPTY check to see if we could + * do the optimization. If the ready queue + * were usually nonempty, the 'optimization' + * might even hurt rather than help. + */ +#ifdef USE_WORKER_THREADS + push_readyq(manager, task); +#else + ENQUEUE(new_ready_tasks, task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) + ENQUEUE(new_priority_tasks, task, + ready_priority_link); + tasks_ready++; +#endif + } + } + +#ifdef USE_WORKER_THREADS + /* + * If we are in privileged execution mode and there are no + * tasks remaining on the current ready queue, then + * we're stuck. Automatically drop privileges at that + * point and continue with the regular ready queue. + */ + if (manager->tasks_running == 0 && empty_readyq(manager)) { + manager->mode = isc_taskmgrmode_normal; + if (!empty_readyq(manager)) + BROADCAST(&manager->work_available); + } +#endif + } + +#ifndef USE_WORKER_THREADS + ISC_LIST_APPENDLIST(manager->ready_tasks, new_ready_tasks, ready_link); + ISC_LIST_APPENDLIST(manager->ready_priority_tasks, new_priority_tasks, + ready_priority_link); + manager->tasks_ready += tasks_ready; + if (empty_readyq(manager)) + manager->mode = isc_taskmgrmode_normal; +#endif + + UNLOCK(&manager->lock); +} + +#ifdef USE_WORKER_THREADS +static isc_threadresult_t +#ifdef _WIN32 +WINAPI +#endif +run(void *uap) { + isc__taskmgr_t *manager = uap; + + XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_STARTING, "starting")); + + dispatch(manager); + + XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_EXITING, "exiting")); + +#ifdef OPENSSL_LEAKS + ERR_remove_state(0); +#endif + + return ((isc_threadresult_t)0); +} +#endif /* USE_WORKER_THREADS */ + +static void +manager_free(isc__taskmgr_t *manager) { + isc_mem_t *mctx; + +#ifdef USE_WORKER_THREADS + (void)isc_condition_destroy(&manager->exclusive_granted); + (void)isc_condition_destroy(&manager->work_available); + (void)isc_condition_destroy(&manager->paused); + isc_mem_free(manager->mctx, manager->threads); +#endif /* USE_WORKER_THREADS */ + DESTROYLOCK(&manager->lock); + DESTROYLOCK(&manager->excl_lock); + manager->common.impmagic = 0; + manager->common.magic = 0; + mctx = manager->mctx; + isc_mem_put(mctx, manager, sizeof(*manager)); + isc_mem_detach(&mctx); + +#ifdef USE_SHARED_MANAGER + taskmgr = NULL; +#endif /* USE_SHARED_MANAGER */ +} + +isc_result_t +isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, + unsigned int default_quantum, isc_taskmgr_t **managerp) +{ + isc_result_t result; + unsigned int i, started = 0; + isc__taskmgr_t *manager; + + /* + * Create a new task manager. + */ + + REQUIRE(workers > 0); + REQUIRE(managerp != NULL && *managerp == NULL); + +#ifndef USE_WORKER_THREADS + UNUSED(i); + UNUSED(started); +#endif + +#ifdef USE_SHARED_MANAGER + if (taskmgr != NULL) { + if (taskmgr->refs == 0) + return (ISC_R_SHUTTINGDOWN); + taskmgr->refs++; + *managerp = (isc_taskmgr_t *)taskmgr; + return (ISC_R_SUCCESS); + } +#endif /* USE_SHARED_MANAGER */ + + manager = isc_mem_get(mctx, sizeof(*manager)); + if (manager == NULL) + return (ISC_R_NOMEMORY); + manager->common.methods = &taskmgrmethods; + manager->common.impmagic = TASK_MANAGER_MAGIC; + manager->common.magic = ISCAPI_TASKMGR_MAGIC; + manager->mode = isc_taskmgrmode_normal; + manager->mctx = NULL; + result = isc_mutex_init(&manager->lock); + if (result != ISC_R_SUCCESS) + goto cleanup_mgr; + result = isc_mutex_init(&manager->excl_lock); + if (result != ISC_R_SUCCESS) { + DESTROYLOCK(&manager->lock); + goto cleanup_mgr; + } + +#ifdef USE_WORKER_THREADS + manager->workers = 0; + manager->threads = isc_mem_allocate(mctx, + workers * sizeof(isc_thread_t)); + if (manager->threads == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_lock; + } + if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + result = ISC_R_UNEXPECTED; + goto cleanup_threads; + } + if (isc_condition_init(&manager->exclusive_granted) != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + result = ISC_R_UNEXPECTED; + goto cleanup_workavailable; + } + if (isc_condition_init(&manager->paused) != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + result = ISC_R_UNEXPECTED; + goto cleanup_exclusivegranted; + } +#endif /* USE_WORKER_THREADS */ + if (default_quantum == 0) + default_quantum = DEFAULT_DEFAULT_QUANTUM; + manager->default_quantum = default_quantum; + INIT_LIST(manager->tasks); + INIT_LIST(manager->ready_tasks); + INIT_LIST(manager->ready_priority_tasks); + manager->tasks_running = 0; + manager->tasks_ready = 0; + manager->exclusive_requested = false; + manager->pause_requested = false; + manager->exiting = false; + manager->excl = NULL; + + isc_mem_attach(mctx, &manager->mctx); + +#ifdef USE_WORKER_THREADS + LOCK(&manager->lock); + /* + * Start workers. + */ + for (i = 0; i < workers; i++) { + if (isc_thread_create(run, manager, + &manager->threads[manager->workers]) == + ISC_R_SUCCESS) { + char name[16]; /* thread name limit on Linux */ + snprintf(name, sizeof(name), "isc-worker%04u", i); + isc_thread_setname(manager->threads[manager->workers], + name); + manager->workers++; + started++; + } + } + UNLOCK(&manager->lock); + + if (started == 0) { + manager_free(manager); + return (ISC_R_NOTHREADS); + } + isc_thread_setconcurrency(workers); +#endif /* USE_WORKER_THREADS */ +#ifdef USE_SHARED_MANAGER + manager->refs = 1; + taskmgr = manager; +#endif /* USE_SHARED_MANAGER */ + + *managerp = (isc_taskmgr_t *)manager; + + return (ISC_R_SUCCESS); + +#ifdef USE_WORKER_THREADS + cleanup_exclusivegranted: + (void)isc_condition_destroy(&manager->exclusive_granted); + cleanup_workavailable: + (void)isc_condition_destroy(&manager->work_available); + cleanup_threads: + isc_mem_free(mctx, manager->threads); + cleanup_lock: + DESTROYLOCK(&manager->lock); +#endif + cleanup_mgr: + isc_mem_put(mctx, manager, sizeof(*manager)); + return (result); +} + +void +isc__taskmgr_destroy(isc_taskmgr_t **managerp) { + isc__taskmgr_t *manager; + isc__task_t *task; + unsigned int i; + + /* + * Destroy '*managerp'. + */ + + REQUIRE(managerp != NULL); + manager = (isc__taskmgr_t *)*managerp; + REQUIRE(VALID_MANAGER(manager)); + +#ifndef USE_WORKER_THREADS + UNUSED(i); +#endif /* USE_WORKER_THREADS */ + +#ifdef USE_SHARED_MANAGER + manager->refs--; + if (manager->refs > 0) { + *managerp = NULL; + return; + } +#endif + + XTHREADTRACE("isc_taskmgr_destroy"); + /* + * Only one non-worker thread may ever call this routine. + * If a worker thread wants to initiate shutdown of the + * task manager, it should ask some non-worker thread to call + * isc_taskmgr_destroy(), e.g. by signalling a condition variable + * that the startup thread is sleeping on. + */ + + /* + * Detach the exclusive task before acquiring the manager lock + */ + LOCK(&manager->excl_lock); + if (manager->excl != NULL) + isc__task_detach((isc_task_t **) &manager->excl); + UNLOCK(&manager->excl_lock); + + /* + * Unlike elsewhere, we're going to hold this lock a long time. + * We need to do so, because otherwise the list of tasks could + * change while we were traversing it. + * + * This is also the only function where we will hold both the + * task manager lock and a task lock at the same time. + */ + + LOCK(&manager->lock); + + /* + * Make sure we only get called once. + */ + INSIST(!manager->exiting); + manager->exiting = true; + + /* + * If privileged mode was on, turn it off. + */ + manager->mode = isc_taskmgrmode_normal; + + /* + * Post shutdown event(s) to every task (if they haven't already been + * posted). + */ + for (task = HEAD(manager->tasks); + task != NULL; + task = NEXT(task, link)) { + LOCK(&task->lock); + if (task_shutdown(task)) + push_readyq(manager, task); + UNLOCK(&task->lock); + } +#ifdef USE_WORKER_THREADS + /* + * Wake up any sleeping workers. This ensures we get work done if + * there's work left to do, and if there are already no tasks left + * it will cause the workers to see manager->exiting. + */ + BROADCAST(&manager->work_available); + UNLOCK(&manager->lock); + + /* + * Wait for all the worker threads to exit. + */ + for (i = 0; i < manager->workers; i++) + (void)isc_thread_join(manager->threads[i], NULL); +#else /* USE_WORKER_THREADS */ + /* + * Dispatch the shutdown events. + */ + UNLOCK(&manager->lock); + while (isc__taskmgr_ready((isc_taskmgr_t *)manager)) + (void)isc__taskmgr_dispatch((isc_taskmgr_t *)manager); + if (!ISC_LIST_EMPTY(manager->tasks)) + isc_mem_printallactive(stderr); + INSIST(ISC_LIST_EMPTY(manager->tasks)); +#ifdef USE_SHARED_MANAGER + taskmgr = NULL; +#endif +#endif /* USE_WORKER_THREADS */ + + manager_free(manager); + + *managerp = NULL; +} + +void +isc__taskmgr_setmode(isc_taskmgr_t *manager0, isc_taskmgrmode_t mode) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + + LOCK(&manager->lock); + manager->mode = mode; + UNLOCK(&manager->lock); +} + +isc_taskmgrmode_t +isc__taskmgr_mode(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + isc_taskmgrmode_t mode; + LOCK(&manager->lock); + mode = manager->mode; + UNLOCK(&manager->lock); + return (mode); +} + +#ifndef USE_WORKER_THREADS +bool +isc__taskmgr_ready(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + bool is_ready; + +#ifdef USE_SHARED_MANAGER + if (manager == NULL) + manager = taskmgr; +#endif + if (manager == NULL) + return (false); + + LOCK(&manager->lock); + is_ready = !empty_readyq(manager); + UNLOCK(&manager->lock); + + return (is_ready); +} + +isc_result_t +isc__taskmgr_dispatch(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + +#ifdef USE_SHARED_MANAGER + if (manager == NULL) + manager = taskmgr; +#endif + if (manager == NULL) + return (ISC_R_NOTFOUND); + + dispatch(manager); + + return (ISC_R_SUCCESS); +} + +#else +void +isc__taskmgr_pause(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + manager->pause_requested = true; + LOCK(&manager->lock); + while (manager->tasks_running > 0) { + WAIT(&manager->paused, &manager->lock); + } + UNLOCK(&manager->lock); +} + +void +isc__taskmgr_resume(isc_taskmgr_t *manager0) { + isc__taskmgr_t *manager = (isc__taskmgr_t *)manager0; + + LOCK(&manager->lock); + if (manager->pause_requested) { + manager->pause_requested = false; + BROADCAST(&manager->work_available); + } + UNLOCK(&manager->lock); +} +#endif /* USE_WORKER_THREADS */ + +void +isc_taskmgr_setexcltask(isc_taskmgr_t *mgr0, isc_task_t *task0) { + isc__taskmgr_t *mgr = (isc__taskmgr_t *) mgr0; + isc__task_t *task = (isc__task_t *) task0; + + REQUIRE(VALID_MANAGER(mgr)); + REQUIRE(VALID_TASK(task)); + LOCK(&mgr->excl_lock); + if (mgr->excl != NULL) + isc__task_detach((isc_task_t **) &mgr->excl); + isc__task_attach(task0, (isc_task_t **) &mgr->excl); + UNLOCK(&mgr->excl_lock); +} + +isc_result_t +isc_taskmgr_excltask(isc_taskmgr_t *mgr0, isc_task_t **taskp) { + isc__taskmgr_t *mgr = (isc__taskmgr_t *) mgr0; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(VALID_MANAGER(mgr)); + REQUIRE(taskp != NULL && *taskp == NULL); + + LOCK(&mgr->excl_lock); + if (mgr->excl != NULL) + isc__task_attach((isc_task_t *) mgr->excl, taskp); + else + result = ISC_R_NOTFOUND; + UNLOCK(&mgr->excl_lock); + + return (result); +} + +isc_result_t +isc__task_beginexclusive(isc_task_t *task0) { +#ifdef USE_WORKER_THREADS + isc__task_t *task = (isc__task_t *)task0; + isc__taskmgr_t *manager = task->manager; + + REQUIRE(task->state == task_state_running); +/* + * TODO REQUIRE(task == task->manager->excl); + * it should be here, it fails on shutdown server->task + */ + + LOCK(&manager->lock); + if (manager->exclusive_requested) { + UNLOCK(&manager->lock); + return (ISC_R_LOCKBUSY); + } + manager->exclusive_requested = true; + while (manager->tasks_running > 1) { + WAIT(&manager->exclusive_granted, &manager->lock); + } + UNLOCK(&manager->lock); +#else + UNUSED(task0); +#endif + return (ISC_R_SUCCESS); +} + +void +isc__task_endexclusive(isc_task_t *task0) { +#ifdef USE_WORKER_THREADS + isc__task_t *task = (isc__task_t *)task0; + isc__taskmgr_t *manager = task->manager; + + REQUIRE(task->state == task_state_running); + LOCK(&manager->lock); + REQUIRE(manager->exclusive_requested); + manager->exclusive_requested = false; + BROADCAST(&manager->work_available); + UNLOCK(&manager->lock); +#else + UNUSED(task0); +#endif +} + +void +isc__task_setprivilege(isc_task_t *task0, bool priv) { + isc__task_t *task = (isc__task_t *)task0; + isc__taskmgr_t *manager = task->manager; + bool oldpriv; + + LOCK(&task->lock); + oldpriv = (task->flags & TASK_F_PRIVILEGED); + if (priv) + task->flags |= TASK_F_PRIVILEGED; + else + task->flags &= ~TASK_F_PRIVILEGED; + UNLOCK(&task->lock); + + if (priv == oldpriv) + return; + + LOCK(&manager->lock); + if (priv && ISC_LINK_LINKED(task, ready_link)) + ENQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + else if (!priv && ISC_LINK_LINKED(task, ready_priority_link)) + DEQUEUE(manager->ready_priority_tasks, task, + ready_priority_link); + UNLOCK(&manager->lock); +} + +bool +isc__task_privilege(isc_task_t *task0) { + isc__task_t *task = (isc__task_t *)task0; + bool priv; + + LOCK(&task->lock); + priv = (task->flags & TASK_F_PRIVILEGED); + UNLOCK(&task->lock); + return (priv); +} + +isc_result_t +isc__task_register(void) { + return (isc_task_register(isc__taskmgr_create)); +} + +bool +isc_task_exiting(isc_task_t *t) { + isc__task_t *task = (isc__task_t *)t; + + REQUIRE(VALID_TASK(task)); + return (TASK_SHUTTINGDOWN(task)); +} + + +#ifdef HAVE_LIBXML2 +#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0) +int +isc_taskmgr_renderxml(isc_taskmgr_t *mgr0, xmlTextWriterPtr writer) { + isc__taskmgr_t *mgr = (isc__taskmgr_t *)mgr0; + isc__task_t *task = NULL; + int xmlrc; + + LOCK(&mgr->lock); + + /* + * Write out the thread-model, and some details about each depending + * on which type is enabled. + */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "thread-model")); +#ifdef ISC_PLATFORM_USETHREADS + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "threaded")); + TRY0(xmlTextWriterEndElement(writer)); /* type */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "worker-threads")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->workers)); + TRY0(xmlTextWriterEndElement(writer)); /* worker-threads */ +#else /* ISC_PLATFORM_USETHREADS */ + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type")); + TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "non-threaded")); + TRY0(xmlTextWriterEndElement(writer)); /* type */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->refs)); + TRY0(xmlTextWriterEndElement(writer)); /* references */ +#endif /* ISC_PLATFORM_USETHREADS */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "default-quantum")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", + mgr->default_quantum)); + TRY0(xmlTextWriterEndElement(writer)); /* default-quantum */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-running")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->tasks_running)); + TRY0(xmlTextWriterEndElement(writer)); /* tasks-running */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks-ready")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->tasks_ready)); + TRY0(xmlTextWriterEndElement(writer)); /* tasks-ready */ + + TRY0(xmlTextWriterEndElement(writer)); /* thread-model */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tasks")); + task = ISC_LIST_HEAD(mgr->tasks); + while (task != NULL) { + LOCK(&task->lock); + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "task")); + + if (task->name[0] != 0) { + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "name")); + TRY0(xmlTextWriterWriteFormatString(writer, "%s", + task->name)); + TRY0(xmlTextWriterEndElement(writer)); /* name */ + } + + TRY0(xmlTextWriterStartElement(writer, + ISC_XMLCHAR "references")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", + task->references)); + TRY0(xmlTextWriterEndElement(writer)); /* references */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "id")); + TRY0(xmlTextWriterWriteFormatString(writer, "%p", task)); + TRY0(xmlTextWriterEndElement(writer)); /* id */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "state")); + TRY0(xmlTextWriterWriteFormatString(writer, "%s", + statenames[task->state])); + TRY0(xmlTextWriterEndElement(writer)); /* state */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "quantum")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", + task->quantum)); + TRY0(xmlTextWriterEndElement(writer)); /* quantum */ + + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "events")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", + task->nevents)); + TRY0(xmlTextWriterEndElement(writer)); /* events */ + + TRY0(xmlTextWriterEndElement(writer)); + + UNLOCK(&task->lock); + task = ISC_LIST_NEXT(task, link); + } + TRY0(xmlTextWriterEndElement(writer)); /* tasks */ + + error: + if (task != NULL) + UNLOCK(&task->lock); + UNLOCK(&mgr->lock); + + return (xmlrc); +} +#endif /* HAVE_LIBXML2 */ + +#ifdef HAVE_JSON +#define CHECKMEM(m) do { \ + if (m == NULL) { \ + result = ISC_R_NOMEMORY;\ + goto error;\ + } \ +} while(0) + +isc_result_t +isc_taskmgr_renderjson(isc_taskmgr_t *mgr0, json_object *tasks) { + isc_result_t result = ISC_R_SUCCESS; + isc__taskmgr_t *mgr = (isc__taskmgr_t *)mgr0; + isc__task_t *task = NULL; + json_object *obj = NULL, *array = NULL, *taskobj = NULL; + + LOCK(&mgr->lock); + + /* + * Write out the thread-model, and some details about each depending + * on which type is enabled. + */ +#ifdef ISC_PLATFORM_USETHREADS + obj = json_object_new_string("threaded"); + CHECKMEM(obj); + json_object_object_add(tasks, "thread-model", obj); + + obj = json_object_new_int(mgr->workers); + CHECKMEM(obj); + json_object_object_add(tasks, "worker-threads", obj); +#else /* ISC_PLATFORM_USETHREADS */ + obj = json_object_new_string("non-threaded"); + CHECKMEM(obj); + json_object_object_add(tasks, "thread-model", obj); + + obj = json_object_new_int(mgr->refs); + CHECKMEM(obj); + json_object_object_add(tasks, "references", obj); +#endif /* ISC_PLATFORM_USETHREADS */ + + obj = json_object_new_int(mgr->default_quantum); + CHECKMEM(obj); + json_object_object_add(tasks, "default-quantum", obj); + + obj = json_object_new_int(mgr->tasks_running); + CHECKMEM(obj); + json_object_object_add(tasks, "tasks-running", obj); + + obj = json_object_new_int(mgr->tasks_ready); + CHECKMEM(obj); + json_object_object_add(tasks, "tasks-ready", obj); + + array = json_object_new_array(); + CHECKMEM(array); + + for (task = ISC_LIST_HEAD(mgr->tasks); + task != NULL; + task = ISC_LIST_NEXT(task, link)) + { + char buf[255]; + + LOCK(&task->lock); + + taskobj = json_object_new_object(); + CHECKMEM(taskobj); + json_object_array_add(array, taskobj); + + snprintf(buf, sizeof(buf), "%p", task); + obj = json_object_new_string(buf); + CHECKMEM(obj); + json_object_object_add(taskobj, "id", obj); + + if (task->name[0] != 0) { + obj = json_object_new_string(task->name); + CHECKMEM(obj); + json_object_object_add(taskobj, "name", obj); + } + + obj = json_object_new_int(task->references); + CHECKMEM(obj); + json_object_object_add(taskobj, "references", obj); + + obj = json_object_new_string(statenames[task->state]); + CHECKMEM(obj); + json_object_object_add(taskobj, "state", obj); + + obj = json_object_new_int(task->quantum); + CHECKMEM(obj); + json_object_object_add(taskobj, "quantum", obj); + + obj = json_object_new_int(task->nevents); + CHECKMEM(obj); + json_object_object_add(taskobj, "events", obj); + + UNLOCK(&task->lock); + } + + json_object_object_add(tasks, "tasks", array); + array = NULL; + result = ISC_R_SUCCESS; + + error: + if (array != NULL) + json_object_put(array); + + if (task != NULL) + UNLOCK(&task->lock); + UNLOCK(&mgr->lock); + + return (result); +} +#endif + + +static isc_mutex_t createlock; +static isc_once_t once = ISC_ONCE_INIT; +static isc_taskmgrcreatefunc_t taskmgr_createfunc = NULL; + +static void +initialize(void) { + RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS); +} + +isc_result_t +isc_task_register(isc_taskmgrcreatefunc_t createfunc) { + isc_result_t result = ISC_R_SUCCESS; + + RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS); + + LOCK(&createlock); + if (taskmgr_createfunc == NULL) + taskmgr_createfunc = createfunc; + else + result = ISC_R_EXISTS; + UNLOCK(&createlock); + + return (result); +} + +isc_result_t +isc_taskmgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, + unsigned int workers, unsigned int default_quantum, + isc_taskmgr_t **managerp) +{ + isc_result_t result; + + LOCK(&createlock); + + REQUIRE(taskmgr_createfunc != NULL); + result = (*taskmgr_createfunc)(mctx, workers, default_quantum, + managerp); + + UNLOCK(&createlock); + + if (result == ISC_R_SUCCESS) + isc_appctx_settaskmgr(actx, *managerp); + + return (result); +} + +isc_result_t +isc_taskmgr_create(isc_mem_t *mctx, unsigned int workers, + unsigned int default_quantum, isc_taskmgr_t **managerp) +{ + isc_result_t result; + + if (isc_bind9) + return (isc__taskmgr_create(mctx, workers, + default_quantum, managerp)); + LOCK(&createlock); + + REQUIRE(taskmgr_createfunc != NULL); + result = (*taskmgr_createfunc)(mctx, workers, default_quantum, + managerp); + + UNLOCK(&createlock); + + return (result); +} + +void +isc_taskmgr_destroy(isc_taskmgr_t **managerp) { + REQUIRE(managerp != NULL && ISCAPI_TASKMGR_VALID(*managerp)); + + if (isc_bind9) + isc__taskmgr_destroy(managerp); + else + (*managerp)->methods->destroy(managerp); + + ENSURE(*managerp == NULL); +} + +void +isc_taskmgr_setmode(isc_taskmgr_t *manager, isc_taskmgrmode_t mode) { + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + + if (isc_bind9) + isc__taskmgr_setmode(manager, mode); + else + manager->methods->setmode(manager, mode); +} + +isc_taskmgrmode_t +isc_taskmgr_mode(isc_taskmgr_t *manager) { + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + + if (isc_bind9) + return (isc__taskmgr_mode(manager)); + + return (manager->methods->mode(manager)); +} + +isc_result_t +isc_task_create(isc_taskmgr_t *manager, unsigned int quantum, + isc_task_t **taskp) +{ + REQUIRE(ISCAPI_TASKMGR_VALID(manager)); + REQUIRE(taskp != NULL && *taskp == NULL); + + if (isc_bind9) + return (isc__task_create(manager, quantum, taskp)); + + return (manager->methods->taskcreate(manager, quantum, taskp)); +} + +void +isc_task_attach(isc_task_t *source, isc_task_t **targetp) { + REQUIRE(ISCAPI_TASK_VALID(source)); + REQUIRE(targetp != NULL && *targetp == NULL); + + if (isc_bind9) + isc__task_attach(source, targetp); + else + source->methods->attach(source, targetp); + + ENSURE(*targetp == source); +} + +void +isc_task_detach(isc_task_t **taskp) { + REQUIRE(taskp != NULL && ISCAPI_TASK_VALID(*taskp)); + + if (isc_bind9) + isc__task_detach(taskp); + else + (*taskp)->methods->detach(taskp); + + ENSURE(*taskp == NULL); +} + +void +isc_task_send(isc_task_t *task, isc_event_t **eventp) { + REQUIRE(ISCAPI_TASK_VALID(task)); + REQUIRE(eventp != NULL && *eventp != NULL); + + if (isc_bind9) + isc__task_send(task, eventp); + else { + task->methods->send(task, eventp); + ENSURE(*eventp == NULL); + } +} + +void +isc_task_sendanddetach(isc_task_t **taskp, isc_event_t **eventp) { + REQUIRE(taskp != NULL && ISCAPI_TASK_VALID(*taskp)); + REQUIRE(eventp != NULL && *eventp != NULL); + + if (isc_bind9) + isc__task_sendanddetach(taskp, eventp); + else { + (*taskp)->methods->sendanddetach(taskp, eventp); + ENSURE(*eventp == NULL); + } + + ENSURE(*taskp == NULL); +} + +unsigned int +isc_task_unsend(isc_task_t *task, void *sender, isc_eventtype_t type, + void *tag, isc_eventlist_t *events) +{ + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_unsend(task, sender, type, tag, events)); + + return (task->methods->unsend(task, sender, type, tag, events)); +} + +isc_result_t +isc_task_onshutdown(isc_task_t *task, isc_taskaction_t action, void *arg) +{ + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_onshutdown(task, action, arg)); + + return (task->methods->onshutdown(task, action, arg)); +} + +void +isc_task_shutdown(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + isc__task_shutdown(task); + else + task->methods->shutdown(task); +} + +void +isc_task_destroy(isc_task_t **taskp) { + if (!isc_bind9) + return; + + isc__task_destroy(taskp); +} + +void +isc_task_setname(isc_task_t *task, const char *name, void *tag) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + isc__task_setname(task, name, tag); + else + task->methods->setname(task, name, tag); +} + +unsigned int +isc_task_purge(isc_task_t *task, void *sender, isc_eventtype_t type, void *tag) +{ + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_purge(task, sender, type, tag)); + + return (task->methods->purgeevents(task, sender, type, tag)); +} + +isc_result_t +isc_task_beginexclusive(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_beginexclusive(task)); + + return (task->methods->beginexclusive(task)); +} + +void +isc_task_endexclusive(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + isc__task_endexclusive(task); + else + task->methods->endexclusive(task); +} + +void +isc_task_setprivilege(isc_task_t *task, bool priv) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + isc__task_setprivilege(task, priv); + else + task->methods->setprivilege(task, priv); +} + +bool +isc_task_privilege(isc_task_t *task) { + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_privilege(task)); + + return (task->methods->privilege(task)); +} + +void +isc_task_getcurrenttime(isc_task_t *task, isc_stdtime_t *t) { + if (!isc_bind9) + return; + + isc__task_getcurrenttime(task, t); +} + +void +isc_task_getcurrenttimex(isc_task_t *task, isc_time_t *t) { + if (!isc_bind9) + return; + + isc__task_getcurrenttimex(task, t); +} + +/*% + * This is necessary for libisc's internal timer implementation. Other + * implementation might skip implementing this. + */ +unsigned int +isc_task_purgerange(isc_task_t *task, void *sender, isc_eventtype_t first, + isc_eventtype_t last, void *tag) +{ + REQUIRE(ISCAPI_TASK_VALID(task)); + + if (isc_bind9) + return (isc__task_purgerange(task, sender, first, last, tag)); + + return (task->methods->purgerange(task, sender, first, last, tag)); +} diff --git a/lib/isc/task_p.h b/lib/isc/task_p.h new file mode 100644 index 0000000..1fa3bb1 --- /dev/null +++ b/lib/isc/task_p.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id$ */ + +#ifndef ISC_TASK_P_H +#define ISC_TASK_P_H + +/*! \file */ + +#if defined(ISC_PLATFORM_USETHREADS) +void +isc__taskmgr_pause(isc_taskmgr_t *taskmgr); + +void +isc__taskmgr_resume(isc_taskmgr_t *taskmgr); +#else +bool +isc__taskmgr_ready(isc_taskmgr_t *taskmgr); + +isc_result_t +isc__taskmgr_dispatch(isc_taskmgr_t *taskmgr); +#endif /* !ISC_PLATFORM_USETHREADS */ + +#endif /* ISC_TASK_P_H */ diff --git a/lib/isc/taskpool.c b/lib/isc/taskpool.c new file mode 100644 index 0000000..5dfc1d1 --- /dev/null +++ b/lib/isc/taskpool.c @@ -0,0 +1,182 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/mem.h> +#include <isc/random.h> +#include <isc/taskpool.h> +#include <isc/util.h> + +/*** + *** Types. + ***/ + +struct isc_taskpool { + isc_mem_t * mctx; + isc_taskmgr_t * tmgr; + unsigned int ntasks; + unsigned int quantum; + isc_task_t ** tasks; +}; + +/*** + *** Functions. + ***/ + +static isc_result_t +alloc_pool(isc_taskmgr_t *tmgr, isc_mem_t *mctx, unsigned int ntasks, + unsigned int quantum, isc_taskpool_t **poolp) +{ + isc_taskpool_t *pool; + unsigned int i; + + pool = isc_mem_get(mctx, sizeof(*pool)); + if (pool == NULL) + return (ISC_R_NOMEMORY); + + pool->mctx = NULL; + isc_mem_attach(mctx, &pool->mctx); + pool->ntasks = ntasks; + pool->quantum = quantum; + pool->tmgr = tmgr; + pool->tasks = isc_mem_get(mctx, ntasks * sizeof(isc_task_t *)); + if (pool->tasks == NULL) { + isc_mem_putanddetach(&pool->mctx, pool, sizeof(*pool)); + return (ISC_R_NOMEMORY); + } + for (i = 0; i < ntasks; i++) + pool->tasks[i] = NULL; + + *poolp = pool; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_taskpool_create(isc_taskmgr_t *tmgr, isc_mem_t *mctx, + unsigned int ntasks, unsigned int quantum, + isc_taskpool_t **poolp) +{ + unsigned int i; + isc_taskpool_t *pool = NULL; + isc_result_t result; + + INSIST(ntasks > 0); + + /* Allocate the pool structure */ + result = alloc_pool(tmgr, mctx, ntasks, quantum, &pool); + if (result != ISC_R_SUCCESS) + return (result); + + /* Create the tasks */ + for (i = 0; i < ntasks; i++) { + result = isc_task_create(tmgr, quantum, &pool->tasks[i]); + if (result != ISC_R_SUCCESS) { + isc_taskpool_destroy(&pool); + return (result); + } + isc_task_setname(pool->tasks[i], "taskpool", NULL); + } + + *poolp = pool; + return (ISC_R_SUCCESS); +} + +void +isc_taskpool_gettask(isc_taskpool_t *pool, isc_task_t **targetp) { + uint32_t i; + isc_random_get(&i); + isc_task_attach(pool->tasks[i % pool->ntasks], targetp); +} + +int +isc_taskpool_size(isc_taskpool_t *pool) { + REQUIRE(pool != NULL); + return (pool->ntasks); +} + +isc_result_t +isc_taskpool_expand(isc_taskpool_t **sourcep, unsigned int size, + isc_taskpool_t **targetp) +{ + isc_result_t result; + isc_taskpool_t *pool; + + REQUIRE(sourcep != NULL && *sourcep != NULL); + REQUIRE(targetp != NULL && *targetp == NULL); + + pool = *sourcep; + if (size > pool->ntasks) { + isc_taskpool_t *newpool = NULL; + unsigned int i; + + /* Allocate a new pool structure */ + result = alloc_pool(pool->tmgr, pool->mctx, size, + pool->quantum, &newpool); + if (result != ISC_R_SUCCESS) + return (result); + + /* Copy over the tasks from the old pool */ + for (i = 0; i < pool->ntasks; i++) { + newpool->tasks[i] = pool->tasks[i]; + pool->tasks[i] = NULL; + } + + /* Create new tasks */ + for (i = pool->ntasks; i < size; i++) { + result = isc_task_create(pool->tmgr, pool->quantum, + &newpool->tasks[i]); + if (result != ISC_R_SUCCESS) { + isc_taskpool_destroy(&newpool); + return (result); + } + isc_task_setname(newpool->tasks[i], "taskpool", NULL); + } + + isc_taskpool_destroy(&pool); + pool = newpool; + } + + *sourcep = NULL; + *targetp = pool; + return (ISC_R_SUCCESS); +} + +void +isc_taskpool_destroy(isc_taskpool_t **poolp) { + unsigned int i; + isc_taskpool_t *pool = *poolp; + for (i = 0; i < pool->ntasks; i++) { + if (pool->tasks[i] != NULL) + isc_task_detach(&pool->tasks[i]); + } + isc_mem_put(pool->mctx, pool->tasks, + pool->ntasks * sizeof(isc_task_t *)); + isc_mem_putanddetach(&pool->mctx, pool, sizeof(*pool)); + *poolp = NULL; +} + +void +isc_taskpool_setprivilege(isc_taskpool_t *pool, bool priv) { + unsigned int i; + + REQUIRE(pool != NULL); + + for (i = 0; i < pool->ntasks; i++) { + if (pool->tasks[i] != NULL) + isc_task_setprivilege(pool->tasks[i], priv); + } +} diff --git a/lib/isc/tests/Atffile b/lib/isc/tests/Atffile new file mode 100644 index 0000000..8681844 --- /dev/null +++ b/lib/isc/tests/Atffile @@ -0,0 +1,33 @@ +Content-Type: application/X-atf-atffile; version="1" + +prop: test-suite = bind9 + +tp: aes_test +tp: atomic_test +tp: buffer_test +tp: counter_test +tp: errno_test +tp: file_test +tp: hash_test +tp: heap_test +tp: ht_test +tp: inet_ntop_test +tp: lex_test +tp: mem_test +tp: netaddr_test +tp: parse_test +tp: pool_test +tp: print_test +tp: queue_test +tp: radix_test +tp: random_test +tp: regex_test +tp: result_test +tp: safe_test +tp: sockaddr_test +tp: socket_test +tp: symtab_test +tp: task_test +tp: taskpool_test +tp: time_test +tp: timer_test diff --git a/lib/isc/tests/Kyuafile b/lib/isc/tests/Kyuafile new file mode 100644 index 0000000..1c510c1 --- /dev/null +++ b/lib/isc/tests/Kyuafile @@ -0,0 +1,32 @@ +syntax(2) +test_suite('bind9') + +atf_test_program{name='aes_test'} +atf_test_program{name='atomic_test'} +atf_test_program{name='buffer_test'} +atf_test_program{name='counter_test'} +atf_test_program{name='errno_test'} +atf_test_program{name='file_test'} +atf_test_program{name='hash_test'} +atf_test_program{name='heap_test'} +atf_test_program{name='ht_test'} +atf_test_program{name='inet_ntop_test'} +atf_test_program{name='lex_test'} +atf_test_program{name='mem_test'} +atf_test_program{name='netaddr_test'} +atf_test_program{name='parse_test'} +atf_test_program{name='pool_test'} +atf_test_program{name='print_test'} +atf_test_program{name='queue_test'} +atf_test_program{name='radix_test'} +atf_test_program{name='random_test'} +atf_test_program{name='regex_test'} +atf_test_program{name='result_test'} +atf_test_program{name='safe_test'} +atf_test_program{name='sockaddr_test'} +atf_test_program{name='socket_test'} +atf_test_program{name='symtab_test'} +atf_test_program{name='task_test'} +atf_test_program{name='taskpool_test'} +atf_test_program{name='time_test'} +atf_test_program{name='timer_test'} diff --git a/lib/isc/tests/Makefile.in b/lib/isc/tests/Makefile.in new file mode 100644 index 0000000..add8068 --- /dev/null +++ b/lib/isc/tests/Makefile.in @@ -0,0 +1,177 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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@ + +# Attempt to disable parallel processing. +.NOTPARALLEL: +.NO_PARALLEL: + +VERSION=@BIND9_VERSION@ + +@BIND9_MAKE_INCLUDES@ + +CINCLUDES = -I. -Iinclude ${ISC_INCLUDES} @ISC_OPENSSL_INC@ +CDEFINES = @CRYPTO@ -DTESTS="\"${top_builddir}/lib/isc/tests/\"" + +ISCLIBS = ../libisc.@A@ @ISC_OPENSSL_LIBS@ +ISCDEPLIBS = ../libisc.@A@ + +LIBS = @LIBS@ @ATFLIBS@ + +OBJS = isctest.@O@ +SRCS = isctest.c aes_test.c atomic_test.c buffer_test.c \ + counter_test.c errno_test.c file_test.c hash_test.c \ + heap_test.c ht_test.c inet_ntop_test.c lex_test.c \ + mem_test.c netaddr_test.c parse_test.c pool_test.c \ + print_test.c queue_test.c radix_test.c random_test.c \ + regex_test.c result_test.c safe_test.c sockaddr_test.c \ + socket_test.c socket_test.c symtab_test.c task_test.c \ + taskpool_test.c time_test.c timer_test.c + +SUBDIRS = +TARGETS = aes_test@EXEEXT@ atomic_test@EXEEXT@ buffer_test@EXEEXT@ \ + counter_test@EXEEXT@ errno_test@EXEEXT@ file_test@EXEEXT@ \ + hash_test@EXEEXT@ heap_test@EXEEXT@ ht_test@EXEEXT@ \ + inet_ntop_test@EXEEXT@ lex_test@EXEEXT@ mem_test@EXEEXT@ \ + netaddr_test@EXEEXT@ parse_test@EXEEXT@ pool_test@EXEEXT@ \ + print_test@EXEEXT@ queue_test@EXEEXT@ radix_test@EXEEXT@ \ + random_test@EXEEXT@ regex_test@EXEEXT@ result_test@EXEEXT@ \ + safe_test@EXEEXT@ sockaddr_test@EXEEXT@ socket_test@EXEEXT@ \ + socket_test@EXEEXT@ symtab_test@EXEEXT@ task_test@EXEEXT@ \ + taskpool_test@EXEEXT@ time_test@EXEEXT@ timer_test@EXEEXT@ + +@BIND9_MAKE_RULES@ + +atomic_test@EXEEXT@: atomic_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + atomic_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +aes_test@EXEEXT@: aes_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + aes_test.@O@ ${ISCLIBS} ${LIBS} + +buffer_test@EXEEXT@: buffer_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + buffer_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +counter_test@EXEEXT@: counter_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + counter_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +errno_test@EXEEXT@: errno_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + errno_test.@O@ ${ISCLIBS} ${LIBS} + +file_test@EXEEXT@: file_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + file_test.@O@ ${ISCLIBS} ${LIBS} + +hash_test@EXEEXT@: hash_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + hash_test.@O@ ${ISCLIBS} ${LIBS} + +heap_test@EXEEXT@: heap_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + heap_test.@O@ ${ISCLIBS} ${LIBS} + +ht_test@EXEEXT@: ht_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + ht_test.@O@ ${ISCLIBS} ${LIBS} + +inet_ntop_test.c.@O@: ${top_srcdir}/lib/isc/ntop_test.c +inet_ntop_test@EXEEXT@: inet_ntop_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + inet_ntop_test.@O@ ${ISCLIBS} ${LIBS} + +lex_test@EXEEXT@: lex_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + lex_test.@O@ ${ISCLIBS} ${LIBS} + +parse_test@EXEEXT@: parse_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + parse_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +pool_test@EXEEXT@: pool_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + pool_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +print_test.@O@: ${top_srcdir}/lib/isc/print.c +print_test@EXEEXT@: print_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + print_test.@O@ ${ISCLIBS} ${LIBS} + +queue_test@EXEEXT@: queue_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + queue_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +radix_test@EXEEXT@: radix_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + radix_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +random_test@EXEEXT@: random_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + random_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +regex_test@EXEEXT@: regex_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + regex_test.@O@ ${ISCLIBS} ${LIBS} + +mem_test@EXEEXT@: mem_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + mem_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +netaddr_test@EXEEXT@: netaddr_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + netaddr_test.@O@ ${ISCLIBS} ${LIBS} + +result_test@EXEEXT@: result_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + result_test.@O@ ${ISCLIBS} ${LIBS} + +safe_test@EXEEXT@: safe_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + safe_test.@O@ ${ISCLIBS} ${LIBS} + +sockaddr_test@EXEEXT@: sockaddr_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + sockaddr_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +socket_test@EXEEXT@: socket_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + socket_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +symtab_test@EXEEXT@: symtab_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + symtab_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +task_test@EXEEXT@: task_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + task_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +taskpool_test@EXEEXT@: taskpool_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + taskpool_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +time_test@EXEEXT@: time_test.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + time_test.@O@ ${ISCLIBS} ${LIBS} + +timer_test@EXEEXT@: timer_test.@O@ isctest.@O@ ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \ + timer_test.@O@ isctest.@O@ ${ISCLIBS} ${LIBS} + +unit:: + sh ${top_builddir}/unit/unittest.sh + +clean distclean:: + rm -f ${TARGETS} + rm -f atf.out diff --git a/lib/isc/tests/aes_test.c b/lib/isc/tests/aes_test.c new file mode 100644 index 0000000..9c15b52 --- /dev/null +++ b/lib/isc/tests/aes_test.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/* ! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <stdio.h> +#include <string.h> + +#include <isc/aes.h> +#include <isc/buffer.h> +#include <isc/hex.h> +#include <isc/platform.h> +#include <isc/region.h> +#include <isc/string.h> +#include <isc/util.h> + +#ifdef ISC_PLATFORM_WANTAES + +/* + * Test data from NIST KAT + */ + +isc_result_t +tohexstr(unsigned char *d, char *out); + +size_t +fromhexstr(const char *in, unsigned char *d); + +unsigned char plaintext[3 * ISC_AES_BLOCK_LENGTH]; +unsigned char ciphertext[ISC_AES_BLOCK_LENGTH]; +char str[2 * ISC_AES_BLOCK_LENGTH + 1]; +unsigned char key[ISC_AES256_KEYLENGTH + 1]; +size_t len; + +isc_result_t +tohexstr(unsigned char *d, char *out) { + isc_buffer_t b; + isc_region_t r; + + isc_buffer_init(&b, out, 2 * ISC_AES_BLOCK_LENGTH + 1); + r.base = d; + r.length = ISC_AES_BLOCK_LENGTH; + return (isc_hex_totext(&r, 0, "", &b)); +} + +size_t +fromhexstr(const char *in, unsigned char *d) +{ + isc_buffer_t b; + isc_result_t ret; + + isc_buffer_init(&b, d, ISC_AES256_KEYLENGTH + 1); + ret = isc_hex_decodestring(in, &b); + if (ret != ISC_R_SUCCESS) + return 0; + return isc_buffer_usedlength(&b); +} + +typedef struct aes_testcase { + const char *key; + const char *input; + const char *result; +} aes_testcase_t; + + +ATF_TC(isc_aes128); +ATF_TC_HEAD(isc_aes128, tc) { + atf_tc_set_md_var(tc, "descr", "AES 128 test vectors"); +} +ATF_TC_BODY(isc_aes128, tc) { + UNUSED(tc); + + aes_testcase_t testcases[] = { + /* Test 1 (KAT ECBVarTxt128 #3) */ + { + "00000000000000000000000000000000", + "F0000000000000000000000000000000", + "96D9FD5CC4F07441727DF0F33E401A36" + }, + /* Test 2 (KAT ECBVarTxt128 #123) */ + { + "00000000000000000000000000000000", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0", + "F9B0FDA0C4A898F5B9E6F661C4CE4D07" + }, + /* Test 3 (KAT ECBVarKey128 #3) */ + { + "F0000000000000000000000000000000", + "00000000000000000000000000000000", + "970014D634E2B7650777E8E84D03CCD8" + }, + /* Test 4 (KAT ECBVarKey128 #123) */ + { + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0", + "00000000000000000000000000000000", + "41C78C135ED9E98C096640647265DA1E" + }, + /* Test 5 (KAT ECBGFSbox128 #3) */ + { + "00000000000000000000000000000000", + "6A118A874519E64E9963798A503F1D35", + "DC43BE40BE0E53712F7E2BF5CA707209" + }, + /* Test 6 (KAT ECBKeySbox128 #3) */ + { + "B6364AC4E1DE1E285EAF144A2415F7A0", + "00000000000000000000000000000000", + "5D9B05578FC944B3CF1CCF0E746CD581" + }, + { NULL, NULL, NULL } + }; + + aes_testcase_t *testcase = testcases; + + while (testcase->key != NULL) { + len = fromhexstr(testcase->key, key); + ATF_CHECK_EQ(len, ISC_AES128_KEYLENGTH); + len = fromhexstr(testcase->input, plaintext); + ATF_CHECK_EQ(len, ISC_AES_BLOCK_LENGTH); + isc_aes128_crypt(key, plaintext, ciphertext); + ATF_CHECK(tohexstr(ciphertext, str) == ISC_R_SUCCESS); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} + +ATF_TC(isc_aes192); +ATF_TC_HEAD(isc_aes192, tc) { + atf_tc_set_md_var(tc, "descr", "AES 192 test vectors"); +} +ATF_TC_BODY(isc_aes192, tc) { + UNUSED(tc); + + aes_testcase_t testcases[] = { + /* Test 1 (KAT ECBVarTxt192 #3) */ + { + "000000000000000000000000000000000000000000000000", + "F0000000000000000000000000000000", + "2A560364CE529EFC21788779568D5555" + }, + /* Test 2 (KAT ECBVarTxt192 #123) */ + { + "000000000000000000000000000000000000000000000000", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0", + "2AABB999F43693175AF65C6C612C46FB" + }, + /* Test 3 (KAT ECBVarKey192 #3) */ + { + "F00000000000000000000000000000000000000000000000", + "00000000000000000000000000000000", + "180B09F267C45145DB2F826C2582D35C" + }, + /* Test 4 (KAT ECBVarKey192 #187) */ + { + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0", + "00000000000000000000000000000000", + "EACF1E6C4224EFB38900B185AB1DFD42" + }, + /* Test 5 (KAT ECBGFSbox192 #3) */ + { + "000000000000000000000000000000000000000000000000", + "51719783D3185A535BD75ADC65071CE1", + "4F354592FF7C8847D2D0870CA9481B7C" + }, + /* Test 6 (KAT ECBKeySbox192 #3) */ + { + "CD62376D5EBB414917F0C78F05266433DC9192A1EC943300", + "00000000000000000000000000000000", + "7F6C25FF41858561BB62F36492E93C29" + }, + { NULL, NULL, NULL } + }; + + aes_testcase_t *testcase = testcases; + + while (testcase->key != NULL) { + len = fromhexstr(testcase->key, key); + ATF_CHECK_EQ(len, ISC_AES192_KEYLENGTH); + len = fromhexstr(testcase->input, plaintext); + ATF_CHECK_EQ(len, ISC_AES_BLOCK_LENGTH); + isc_aes192_crypt(key, plaintext, ciphertext); + ATF_CHECK(tohexstr(ciphertext, str) == ISC_R_SUCCESS); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} + +ATF_TC(isc_aes256); +ATF_TC_HEAD(isc_aes256, tc) { + atf_tc_set_md_var(tc, "descr", "AES 256 test vectors"); +} +ATF_TC_BODY(isc_aes256, tc) { + UNUSED(tc); + + aes_testcase_t testcases[] = { + /* Test 1 (KAT ECBVarTxt256 #3) */ + { + "00000000000000000000000000000000" + "00000000000000000000000000000000", + "F0000000000000000000000000000000", + "7F2C5ECE07A98D8BEE13C51177395FF7" + }, + /* Test 2 (KAT ECBVarTxt256 #123) */ + { + "00000000000000000000000000000000" + "00000000000000000000000000000000", + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0", + "7240E524BC51D8C4D440B1BE55D1062C" + }, + /* Test 3 (KAT ECBVarKey256 #3) */ + { + "F0000000000000000000000000000000" + "00000000000000000000000000000000", + "00000000000000000000000000000000", + "1C777679D50037C79491A94DA76A9A35" + }, + /* Test 4 (KAT ECBVarKey256 #251) */ + { + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0", + "00000000000000000000000000000000", + "03720371A04962EAEA0A852E69972858" + }, + /* Test 5 (KAT ECBGFSbox256 #3) */ + { + "00000000000000000000000000000000" + "00000000000000000000000000000000", + "8A560769D605868AD80D819BDBA03771", + "38F2C7AE10612415D27CA190D27DA8B4" + }, + /* Test 6 (KAT ECBKeySbox256 #3) */ + { + "984CA75F4EE8D706F46C2D98C0BF4A45" + "F5B00D791C2DFEB191B5ED8E420FD627", + "00000000000000000000000000000000", + "4307456A9E67813B452E15FA8FFFE398" + }, + { NULL, NULL, NULL } + }; + + aes_testcase_t *testcase = testcases; + + while (testcase->key != NULL) { + len = fromhexstr(testcase->key, key); + ATF_CHECK_EQ(len, ISC_AES256_KEYLENGTH); + len = fromhexstr(testcase->input, plaintext); + ATF_CHECK_EQ(len, ISC_AES_BLOCK_LENGTH); + isc_aes256_crypt(key, plaintext, ciphertext); + ATF_CHECK(tohexstr(ciphertext, str) == ISC_R_SUCCESS); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} +#else +ATF_TC(untested); +ATF_TC_HEAD(untested, tc) { + atf_tc_set_md_var(tc, "descr", "skipping aes test"); +} +ATF_TC_BODY(untested, tc) { + UNUSED(tc); + atf_tc_skip("AES not available"); +} +#endif + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { +#ifdef ISC_PLATFORM_WANTAES + ATF_TP_ADD_TC(tp, isc_aes128); + ATF_TP_ADD_TC(tp, isc_aes192); + ATF_TP_ADD_TC(tp, isc_aes256); +#else + ATF_TP_ADD_TC(tp, untested); +#endif + return (atf_no_error()); +} + diff --git a/lib/isc/tests/atomic_test.c b/lib/isc/tests/atomic_test.c new file mode 100644 index 0000000..8013010 --- /dev/null +++ b/lib/isc/tests/atomic_test.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <inttypes.h> +#include <stdlib.h> + +#include <atf-c.h> + +#include <isc/atomic.h> +#include <isc/print.h> +#include <isc/result.h> + +#include "isctest.h" + +#define TASKS 32 +#define ITERATIONS 1000 +#define COUNTS_PER_ITERATION 1000 +#define INCREMENT_64 (int64_t)0x0000000010000000 +#define EXPECTED_COUNT_32 (TASKS * ITERATIONS * COUNTS_PER_ITERATION) +#define EXPECTED_COUNT_64 (TASKS * ITERATIONS * COUNTS_PER_ITERATION * INCREMENT_64) + +typedef struct { + uint32_t iteration; +} counter_t; + +counter_t counters[TASKS]; + +#if defined(ISC_PLATFORM_HAVEXADD) +static int32_t counter_32; + +static void +do_xadd(isc_task_t *task, isc_event_t *ev) { + counter_t *state = (counter_t *)ev->ev_arg; + int i; + + for (i = 0 ; i < COUNTS_PER_ITERATION ; i++) { + isc_atomic_xadd(&counter_32, 1); + } + + state->iteration++; + if (state->iteration < ITERATIONS) { + isc_task_send(task, &ev); + } else { + isc_event_free(&ev); + } +} + +ATF_TC(atomic_xadd); +ATF_TC_HEAD(atomic_xadd, tc) { + atf_tc_set_md_var(tc, "descr", "atomic XADD"); +} +ATF_TC_BODY(atomic_xadd, tc) { + isc_result_t result; + isc_task_t *tasks[TASKS]; + isc_event_t *event = NULL; + int i; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + memset(counters, 0, sizeof(counters)); + counter_32 = 0; + + /* + * Create our tasks, and allocate an event to get the counters going. + */ + for (i = 0 ; i < TASKS ; i++) { + tasks[i] = NULL; + ATF_REQUIRE_EQ(isc_task_create(taskmgr, 0, &tasks[i]), + ISC_R_SUCCESS); + event = isc_event_allocate(mctx, NULL, 1000, do_xadd, + &counters[i], + sizeof(struct isc_event)); + ATF_REQUIRE(event != NULL); + isc_task_sendanddetach(&tasks[i], &event); + } + + isc_test_end(); + + printf("32-bit counter %d, expected %d\n", + counter_32, EXPECTED_COUNT_32); + + ATF_CHECK_EQ(counter_32, EXPECTED_COUNT_32); + counter_32 = 0; +} +#endif + +#if defined(ISC_PLATFORM_HAVEXADDQ) +static int64_t counter_64; + +static void +do_xaddq(isc_task_t *task, isc_event_t *ev) { + counter_t *state = (counter_t *)ev->ev_arg; + int i; + + for (i = 0 ; i < COUNTS_PER_ITERATION ; i++) { + isc_atomic_xaddq(&counter_64, INCREMENT_64); + } + + state->iteration++; + if (state->iteration < ITERATIONS) { + isc_task_send(task, &ev); + } else { + isc_event_free(&ev); + } +} + +ATF_TC(atomic_xaddq); +ATF_TC_HEAD(atomic_xaddq, tc) { + atf_tc_set_md_var(tc, "descr", "atomic XADDQ"); +} +ATF_TC_BODY(atomic_xaddq, tc) { + isc_result_t result; + isc_task_t *tasks[TASKS]; + isc_event_t *event = NULL; + int i; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + memset(counters, 0, sizeof(counters)); + counter_64 = 0; + + /* + * Create our tasks, and allocate an event to get the counters going. + */ + for (i = 0 ; i < TASKS ; i++) { + tasks[i] = NULL; + ATF_REQUIRE_EQ(isc_task_create(taskmgr, 0, &tasks[i]), + ISC_R_SUCCESS); + event = isc_event_allocate(mctx, NULL, 1000, do_xaddq, + &counters[i], + sizeof(struct isc_event)); + ATF_REQUIRE(event != NULL); + isc_task_sendanddetach(&tasks[i], &event); + } + + isc_test_end(); + + printf("64-bit counter %" PRId64 ", " + "expected %" PRId64 "\n", + counter_64, EXPECTED_COUNT_64); + + ATF_CHECK_EQ(counter_64, EXPECTED_COUNT_64); + counter_32 = 0; +} +#endif + +#if defined(ISC_PLATFORM_HAVEATOMICSTORE) +static int32_t store_32; + +static void +do_store(isc_task_t *task, isc_event_t *ev) { + counter_t *state = (counter_t *)ev->ev_arg; + int i; + uint32_t r; + uint32_t val; + + r = random() % 256; + val = (r << 24) | (r << 16) | (r << 8) | r; + + for (i = 0 ; i < COUNTS_PER_ITERATION ; i++) { + isc_atomic_store(&store_32, val); + } + + state->iteration++; + if (state->iteration < ITERATIONS) { + isc_task_send(task, &ev); + } else { + isc_event_free(&ev); + } +} + +ATF_TC(atomic_store); +ATF_TC_HEAD(atomic_store, tc) { + atf_tc_set_md_var(tc, "descr", "atomic STORE"); +} +ATF_TC_BODY(atomic_store, tc) { + isc_result_t result; + isc_task_t *tasks[TASKS]; + isc_event_t *event = NULL; + uint32_t val; + uint32_t r; + int i; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + memset(counters, 0, sizeof(counters)); + store_32 = 0; + + /* + * Create our tasks, and allocate an event to get the counters + * going. + */ + for (i = 0 ; i < TASKS ; i++) { + tasks[i] = NULL; + ATF_REQUIRE_EQ(isc_task_create(taskmgr, 0, &tasks[i]), + ISC_R_SUCCESS); + event = isc_event_allocate(mctx, NULL, 1000, do_store, + &counters[i], + sizeof(struct isc_event)); + ATF_REQUIRE(event != NULL); + isc_task_sendanddetach(&tasks[i], &event); + } + + isc_test_end(); + + r = store_32 & 0xff; + val = (r << 24) | (r << 16) | (r << 8) | r; + + printf("32-bit store 0x%x, expected 0x%x\n", + (uint32_t) store_32, val); + + ATF_CHECK_EQ((uint32_t) store_32, val); + store_32 = 0; +} +#endif + +#if defined(ISC_PLATFORM_HAVEATOMICSTOREQ) +static int64_t store_64; + +static void +do_storeq(isc_task_t *task, isc_event_t *ev) { + counter_t *state = (counter_t *)ev->ev_arg; + int i; + uint8_t r; + uint64_t val; + + r = random() % 256; + val = (((uint64_t) r << 24) | + ((uint64_t) r << 16) | + ((uint64_t) r << 8) | + (uint64_t) r); + val |= ((uint64_t) val << 32); + + for (i = 0 ; i < COUNTS_PER_ITERATION ; i++) { + isc_atomic_storeq(&store_64, val); + } + + state->iteration++; + if (state->iteration < ITERATIONS) { + isc_task_send(task, &ev); + } else { + isc_event_free(&ev); + } +} + +ATF_TC(atomic_storeq); +ATF_TC_HEAD(atomic_storeq, tc) { + atf_tc_set_md_var(tc, "descr", "atomic STOREQ"); +} +ATF_TC_BODY(atomic_storeq, tc) { + isc_result_t result; + isc_task_t *tasks[TASKS]; + isc_event_t *event = NULL; + uint64_t val; + uint32_t r; + int i; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + memset(counters, 0, sizeof(counters)); + store_64 = 0; + + /* + * Create our tasks, and allocate an event to get the counters + * going. + */ + for (i = 0 ; i < TASKS ; i++) { + tasks[i] = NULL; + ATF_REQUIRE_EQ(isc_task_create(taskmgr, 0, &tasks[i]), + ISC_R_SUCCESS); + event = isc_event_allocate(mctx, NULL, 1000, do_storeq, + &counters[i], + sizeof(struct isc_event)); + ATF_REQUIRE(event != NULL); + isc_task_sendanddetach(&tasks[i], &event); + } + + isc_test_end(); + + r = store_64 & 0xff; + val = (((uint64_t) r << 24) | + ((uint64_t) r << 16) | + ((uint64_t) r << 8) | + (uint64_t) r); + val |= ((uint64_t) val << 32); + + printf("64-bit store 0x%" PRIx64 ", " + "expected 0x%" PRIx64 "\n", + (uint64_t) store_64, val); + + ATF_CHECK_EQ((uint64_t) store_64, val); + store_64 = 0; +} +#endif + +#if !defined(ISC_PLATFORM_HAVEXADD) && \ + !defined(ISC_PLATFORM_HAVEXADDQ) && \ + !defined(ISC_PLATFORM_HAVEATOMICSTOREQ) +ATF_TC(untested); +ATF_TC_HEAD(untested, tc) { + atf_tc_set_md_var(tc, "descr", "skipping aes test"); +} +ATF_TC_BODY(untested, tc) { + UNUSED(tc); + atf_tc_skip("AES not available"); +} +#endif /* !HAVEXADD, !HAVEXADDQ, !HAVEATOMICSTOREQ */ + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { +#if defined(ISC_PLATFORM_HAVEXADD) + ATF_TP_ADD_TC(tp, atomic_xadd); +#endif +#if defined(ISC_PLATFORM_HAVEXADDQ) + ATF_TP_ADD_TC(tp, atomic_xaddq); +#endif +#ifdef ISC_PLATFORM_HAVEATOMICSTORE + ATF_TP_ADD_TC(tp, atomic_store); +#endif +#if defined(ISC_PLATFORM_HAVEATOMICSTOREQ) + ATF_TP_ADD_TC(tp, atomic_storeq); +#endif +#if !defined(ISC_PLATFORM_HAVEXADD) && \ + !defined(ISC_PLATFORM_HAVEXADDQ) && \ + !defined(ISC_PLATFORM_HAVEATOMICSTOREQ) + ATF_TP_ADD_TC(tp, untested); +#endif + + return (atf_no_error()); +} diff --git a/lib/isc/tests/buffer_test.c b/lib/isc/tests/buffer_test.c new file mode 100644 index 0000000..a2d76cc --- /dev/null +++ b/lib/isc/tests/buffer_test.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#include <atf-c.h> + +#include "isctest.h" + +#include <isc/buffer.h> +#include <isc/result.h> + +ATF_TC(isc_buffer_reserve); +ATF_TC_HEAD(isc_buffer_reserve, tc) { + atf_tc_set_md_var(tc, "descr", "reserve space in dynamic buffers"); +} + +ATF_TC_BODY(isc_buffer_reserve, tc) { + isc_result_t result; + isc_buffer_t *b; + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + b = NULL; + result = isc_buffer_allocate(mctx, &b, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_EQ(b->length, 1024); + + /* + * 1024 bytes should already be available, so this call does + * nothing. + */ + result = isc_buffer_reserve(&b, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_REQUIRE(b != NULL); + ATF_CHECK_EQ(b->length, 1024); + + /* + * This call should grow it to 2048 bytes as only 1024 bytes are + * available in the buffer. + */ + result = isc_buffer_reserve(&b, 1025); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_REQUIRE(b != NULL); + ATF_CHECK_EQ(b->length, 2048); + + /* + * 2048 bytes should already be available, so this call does + * nothing. + */ + result = isc_buffer_reserve(&b, 2000); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_REQUIRE(b != NULL); + ATF_CHECK_EQ(b->length, 2048); + + /* + * This call should grow it to 4096 bytes as only 2048 bytes are + * available in the buffer. + */ + result = isc_buffer_reserve(&b, 3000); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_REQUIRE(b != NULL); + ATF_CHECK_EQ(b->length, 4096); + + /* Consume some of the buffer so we can run the next test. */ + isc_buffer_add(b, 4096); + + /* + * This call should fail and leave buffer untouched. + */ + result = isc_buffer_reserve(&b, UINT_MAX); + ATF_CHECK_EQ(result, ISC_R_NOMEMORY); + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_REQUIRE(b != NULL); + ATF_CHECK_EQ(b->length, 4096); + + isc_buffer_free(&b); + + isc_test_end(); +} + +ATF_TC(isc_buffer_reallocate); +ATF_TC_HEAD(isc_buffer_reallocate, tc) { + atf_tc_set_md_var(tc, "descr", "reallocate dynamic buffers"); +} + +ATF_TC_BODY(isc_buffer_reallocate, tc) { + isc_result_t result; + isc_buffer_t *b; + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + b = NULL; + result = isc_buffer_allocate(mctx, &b, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE(b != NULL); + ATF_CHECK_EQ(b->length, 1024); + + result = isc_buffer_reallocate(&b, 512); + ATF_CHECK_EQ(result, ISC_R_NOSPACE); + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_REQUIRE(b != NULL); + ATF_CHECK_EQ(b->length, 1024); + + result = isc_buffer_reallocate(&b, 1536); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(ISC_BUFFER_VALID(b)); + ATF_REQUIRE(b != NULL); + ATF_CHECK_EQ(b->length, 1536); + + isc_buffer_free(&b); + + isc_test_end(); +} + +ATF_TC(isc_buffer_dynamic); +ATF_TC_HEAD(isc_buffer_dynamic, tc) { + atf_tc_set_md_var(tc, "descr", "dynamic buffer automatic reallocation"); +} + +ATF_TC_BODY(isc_buffer_dynamic, tc) { + isc_result_t result; + isc_buffer_t *b; + size_t last_length = 10; + int i; + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + b = NULL; + result = isc_buffer_allocate(mctx, &b, last_length); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE(b != NULL); + ATF_CHECK_EQ(b->length, last_length); + + isc_buffer_setautorealloc(b, true); + + isc_buffer_putuint8(b, 1); + + for (i = 0; i < 1000; i++) { + isc_buffer_putstr(b, "thisisa24charslongstring"); + } + ATF_CHECK(b->length-last_length >= 1000*24); + last_length+=1000*24; + + for (i = 0; i < 10000; i++) { + isc_buffer_putuint8(b, 1); + } + + ATF_CHECK(b->length-last_length >= 10000*1); + last_length += 10000*1; + + for (i = 0; i < 10000; i++) { + isc_buffer_putuint16(b, 1); + } + + ATF_CHECK(b->length-last_length >= 10000*2); + + last_length += 10000*2; + for (i = 0; i < 10000; i++) { + isc_buffer_putuint24(b, 1); + } + ATF_CHECK(b->length-last_length >= 10000*3); + + last_length+=10000*3; + + for (i = 0; i < 10000; i++) { + isc_buffer_putuint32(b, 1); + } + ATF_CHECK(b->length-last_length >= 10000*4); + + + isc_buffer_free(&b); + + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_buffer_reserve); + ATF_TP_ADD_TC(tp, isc_buffer_reallocate); + ATF_TP_ADD_TC(tp, isc_buffer_dynamic); + return (atf_no_error()); +} diff --git a/lib/isc/tests/counter_test.c b/lib/isc/tests/counter_test.c new file mode 100644 index 0000000..ddcdae3 --- /dev/null +++ b/lib/isc/tests/counter_test.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> +#include <stdlib.h> + +#include <atf-c.h> + +#include <isc/counter.h> +#include <isc/result.h> + +#include "isctest.h" + +ATF_TC(isc_counter); +ATF_TC_HEAD(isc_counter, tc) { + atf_tc_set_md_var(tc, "descr", "isc counter object"); +} +ATF_TC_BODY(isc_counter, tc) { + isc_result_t result; + isc_counter_t *counter = NULL; + int i; + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_counter_create(mctx, 0, &counter); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + for (i = 0; i < 10; i++) { + result = isc_counter_increment(counter); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + } + + ATF_CHECK_EQ(isc_counter_used(counter), 10); + + isc_counter_setlimit(counter, 15); + for (i = 0; i < 10; i++) { + result = isc_counter_increment(counter); + if (result != ISC_R_SUCCESS) + break; + } + + ATF_CHECK_EQ(isc_counter_used(counter), 15); + + isc_counter_detach(&counter); + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_counter); + return (atf_no_error()); +} + diff --git a/lib/isc/tests/errno_test.c b/lib/isc/tests/errno_test.c new file mode 100644 index 0000000..e45bdd8 --- /dev/null +++ b/lib/isc/tests/errno_test.c @@ -0,0 +1,105 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <stdio.h> +#include <sys/errno.h> + +#include <atf-c.h> + +#include <isc/errno.h> +#include <isc/result.h> + +typedef struct { + int err; + isc_result_t result; +} testpair_t; + +testpair_t testpair[] = { + { EPERM, ISC_R_NOPERM }, + { ENOENT, ISC_R_FILENOTFOUND }, + { EIO, ISC_R_IOERROR }, + { EBADF, ISC_R_INVALIDFILE }, + { ENOMEM, ISC_R_NOMEMORY }, + { EACCES, ISC_R_NOPERM }, + { EEXIST, ISC_R_FILEEXISTS }, + { ENOTDIR, ISC_R_INVALIDFILE }, + { EINVAL, ISC_R_INVALIDFILE }, + { ENFILE, ISC_R_TOOMANYOPENFILES }, + { EMFILE, ISC_R_TOOMANYOPENFILES }, + { EPIPE, ISC_R_CONNECTIONRESET }, + { ENAMETOOLONG, ISC_R_INVALIDFILE }, + { ELOOP, ISC_R_INVALIDFILE }, +#ifdef EOVERFLOW + { EOVERFLOW, ISC_R_RANGE }, +#endif +#ifdef EAFNOSUPPORT + { EAFNOSUPPORT, ISC_R_FAMILYNOSUPPORT }, +#endif +#ifdef EADDRINUSE + { EADDRINUSE, ISC_R_ADDRINUSE }, +#endif + { EADDRNOTAVAIL, ISC_R_ADDRNOTAVAIL }, +#ifdef ENETDOWN + { ENETDOWN, ISC_R_NETDOWN }, +#endif +#ifdef ENETUNREACH + { ENETUNREACH, ISC_R_NETUNREACH }, +#endif +#ifdef ECONNABORTED + { ECONNABORTED, ISC_R_CONNECTIONRESET }, +#endif +#ifdef ECONNRESET + { ECONNRESET, ISC_R_CONNECTIONRESET }, +#endif +#ifdef ENOBUFS + { ENOBUFS, ISC_R_NORESOURCES }, +#endif +#ifdef ENOTCONN + { ENOTCONN, ISC_R_NOTCONNECTED }, +#endif +#ifdef ETIMEDOUT + { ETIMEDOUT, ISC_R_TIMEDOUT }, +#endif + { ECONNREFUSED, ISC_R_CONNREFUSED }, +#ifdef EHOSTDOWN + { EHOSTDOWN, ISC_R_HOSTDOWN }, +#endif +#ifdef EHOSTUNREACH + { EHOSTUNREACH, ISC_R_HOSTUNREACH }, +#endif + { 0, ISC_R_UNEXPECTED } +}; + +ATF_TC(isc_errno_toresult); +ATF_TC_HEAD(isc_errno_toresult, tc) { + atf_tc_set_md_var(tc, "descr", "convert errno to ISC result"); +} +ATF_TC_BODY(isc_errno_toresult, tc) { + isc_result_t result, expect; + size_t i; + + for (i = 0; i < sizeof(testpair)/sizeof(testpair[0]); i++) { + result = isc_errno_toresult(testpair[i].err); + expect = testpair[i].result; + ATF_CHECK(result == expect); + } +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_errno_toresult); + return (atf_no_error()); +} + diff --git a/lib/isc/tests/file_test.c b/lib/isc/tests/file_test.c new file mode 100644 index 0000000..3dadb52 --- /dev/null +++ b/lib/isc/tests/file_test.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include <isc/file.h> +#include <isc/result.h> + +ATF_TC(isc_file_sanitize); +ATF_TC_HEAD(isc_file_sanitize, tc) { + atf_tc_set_md_var(tc, "descr", "sanitized filenames"); +} + +#define NAME "internal" +#define SHA "3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f" +#define TRUNC_SHA "3bed2cb3a3acf7b6" + +#define BAD1 "in/internal" +#define BADHASH1 "8bbb97a888791399" + +#define BAD2 "Internal" +#define BADHASH2 "2ea1842b445b0c81" + +#define F(x) "testdata/file/" x ".test" + +static void +touch(const char *filename) { + int fd; + + unlink(filename); + fd = creat(filename, 0644); + if (fd != -1) + close(fd); +} + +ATF_TC_BODY(isc_file_sanitize, tc) { + isc_result_t result; + char buf[1024]; + + ATF_CHECK(chdir(TESTS) != -1); + + result = isc_file_sanitize("testdata/file", NAME, "test", buf, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(strcmp(buf, F(NAME)) == 0); + + touch(F(TRUNC_SHA)); + result = isc_file_sanitize("testdata/file", NAME, "test", buf, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(strcmp(buf, F(TRUNC_SHA)) == 0); + + touch(F(SHA)); + result = isc_file_sanitize("testdata/file", NAME, "test", buf, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(strcmp(buf, F(SHA)) == 0); + + result = isc_file_sanitize("testdata/file", BAD1, "test", buf, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(strcmp(buf, F(BADHASH1)) == 0); + + result = isc_file_sanitize("testdata/file", BAD2, "test", buf, 1024); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK(strcmp(buf, F(BADHASH2)) == 0); + + unlink(F(TRUNC_SHA)); + unlink(F(SHA)); +} + +ATF_TC(isc_file_template); +ATF_TC_HEAD(isc_file_template, tc) { + atf_tc_set_md_var(tc, "descr", "file template"); +} + +ATF_TC_BODY(isc_file_template, tc) { + isc_result_t result; + char buf[1024]; + + ATF_CHECK(chdir(TESTS) != -1); + + result = isc_file_template("/absolute/path", "file-XXXXXXXX", + buf, sizeof(buf)); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(buf, "/absolute/file-XXXXXXXX"); + + result = isc_file_template("relative/path", "file-XXXXXXXX", + buf, sizeof(buf)); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(buf, "relative/file-XXXXXXXX"); + + result = isc_file_template("/trailing/slash/", "file-XXXXXXXX", + buf, sizeof(buf)); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(buf, "/trailing/slash/file-XXXXXXXX"); + + result = isc_file_template("relative/trailing/slash/", "file-XXXXXXXX", + buf, sizeof(buf)); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(buf, "relative/trailing/slash/file-XXXXXXXX"); + + result = isc_file_template("/", "file-XXXXXXXX", buf, sizeof(buf)); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(buf, "/file-XXXXXXXX"); + + result = isc_file_template("noslash", "file-XXXXXXXX", + buf, sizeof(buf)); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(buf, "file-XXXXXXXX"); + + result = isc_file_template(NULL, "file-XXXXXXXX", buf, sizeof(buf)); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(buf, "file-XXXXXXXX"); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_file_sanitize); + ATF_TP_ADD_TC(tp, isc_file_template); + return (atf_no_error()); +} + diff --git a/lib/isc/tests/hash_test.c b/lib/isc/tests/hash_test.c new file mode 100644 index 0000000..8f12342 --- /dev/null +++ b/lib/isc/tests/hash_test.c @@ -0,0 +1,2038 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* ! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> + +#include <isc/hash.h> + +#include <isc/crc64.h> +#include <isc/hmacmd5.h> +#include <isc/hmacsha.h> +#include <isc/md5.h> +#include <isc/sha1.h> +#include <isc/util.h> +#include <isc/print.h> +#include <isc/string.h> + +#include <pk11/site.h> + +/* + * Test data from RFC6234 + */ + +unsigned char digest[ISC_SHA512_DIGESTLENGTH]; +unsigned char buffer[1024]; +const char *s; +char str[2 * ISC_SHA512_DIGESTLENGTH + 3]; +unsigned char key[20]; + +/* + * Precondition: a hexadecimal number in *d, the length of that number in len, + * and a pointer to a character array to put the output (*out). + * Postcondition: A String representation of the given hexadecimal number is + * placed into the array *out + * + * 'out' MUST point to an array of at least len * 2 + 1 + * + * Return values: ISC_R_SUCCESS if the operation is sucessful + */ +static isc_result_t +tohexstr(unsigned char *d, unsigned int len, char *out, size_t out_size) { + char c_ret[] = "AA"; + unsigned int i; + + out[0] = '\0'; + strlcat(out, "0x", out_size); + for (i = 0; i < len; i++) { + snprintf(c_ret, sizeof(c_ret), "%02X", d[i]); + strlcat(out, c_ret, out_size); + } + return (ISC_R_SUCCESS); +} + + +#define TEST_INPUT(x) (x), sizeof(x)-1 + +typedef struct hash_testcase { + const char *input; + size_t input_len; + const char *result; + int repeats; +} hash_testcase_t; + +typedef struct hash_test_key { + const char *key; + const int len; +} hash_test_key_t; + +/* non-hmac tests */ + +ATF_TC(isc_sha1); +ATF_TC_HEAD(isc_sha1, tc) { + atf_tc_set_md_var(tc, "descr", "sha1 examples from RFC4634"); +} +ATF_TC_BODY(isc_sha1, tc) { + isc_sha1_t sha1; + int i; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("abc"), + "0xA9993E364706816ABA3E25717850C26C9CD0D89D", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("abcdbcdecdefdefgefghfghighijhijkijk" + "ljklmklmnlmnomnopnopq"), + "0x84983E441C3BD26EBAAE4AA1F95129E5E54670F1", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("a") /* times 1000000 */, + "0x34AA973CD4C4DAA4F61EEB2BDBAD27316534016F", + 1000000 + }, + /* Test 4 -- exact multiple of 512 bits */ + { + TEST_INPUT("01234567012345670123456701234567"), + "0xDEA356A2CDDD90C7A7ECEDC5EBB563934F460452", + 20 /* 20 times */ + }, +#if 0 + /* Test 5 -- optional feature, not implemented */ + { + TEST_INPUT(""), + /* "extrabits": 0x98 , "numberextrabits": 5 */ + "0x29826B003B906E660EFF4027CE98AF3531AC75BA", + 1 + }, +#endif + /* Test 6 */ + { + TEST_INPUT("\x5e"), + "0x5E6F80A34A9798CAFC6A5DB96CC57BA4C4DB59C2", + 1 + }, +#if 0 + /* Test 7 -- optional feature, not implemented */ + { + TEST_INPUT("\x49\xb2\xae\xc2\x59\x4b\xbe\x3a" + "\x3b\x11\x75\x42\xd9\x4a\xc8"), + /* "extrabits": 0x80, "numberextrabits": 3 */ + "0x6239781E03729919C01955B3FFA8ACB60B988340", 1 }, +#endif + /* Test 8 */ + { + TEST_INPUT("\x9a\x7d\xfd\xf1\xec\xea\xd0\x6e\xd6\x46" + "\xaa\x55\xfe\x75\x71\x46"), + "0x82ABFF6605DBE1C17DEF12A394FA22A82B544A35", + 1 + }, +#if 0 + /* Test 9 -- optional feature, not implemented */ + { + TEST_INPUT("\x65\xf9\x32\x99\x5b\xa4\xce\x2c\xb1\xb4" + "\xa2\xe7\x1a\xe7\x02\x20\xaa\xce\xc8\x96" + "\x2d\xd4\x49\x9c\xbd\x7c\x88\x7a\x94\xea" + "\xaa\x10\x1e\xa5\xaa\xbc\x52\x9b\x4e\x7e" + "\x43\x66\x5a\x5a\xf2\xcd\x03\xfe\x67\x8e" + "\xa6\xa5\x00\x5b\xba\x3b\x08\x22\x04\xc2" + "\x8b\x91\x09\xf4\x69\xda\xc9\x2a\xaa\xb3" + "\xaa\x7c\x11\xa1\xb3\x2a"), + /* "extrabits": 0xE0 , "numberextrabits": 3 */ + "0x8C5B2A5DDAE5A97FC7F9D85661C672ADBF7933D4", + 1 + }, +#endif + /* Test 10 */ + { + TEST_INPUT("\xf7\x8f\x92\x14\x1b\xcd\x17\x0a\xe8\x9b" + "\x4f\xba\x15\xa1\xd5\x9f\x3f\xd8\x4d\x22" + "\x3c\x92\x51\xbd\xac\xbb\xae\x61\xd0\x5e" + "\xd1\x15\xa0\x6a\x7c\xe1\x17\xb7\xbe\xea" + "\xd2\x44\x21\xde\xd9\xc3\x25\x92\xbd\x57" + "\xed\xea\xe3\x9c\x39\xfa\x1f\xe8\x94\x6a" + "\x84\xd0\xcf\x1f\x7b\xee\xad\x17\x13\xe2" + "\xe0\x95\x98\x97\x34\x7f\x67\xc8\x0b\x04" + "\x00\xc2\x09\x81\x5d\x6b\x10\xa6\x83\x83" + "\x6f\xd5\x56\x2a\x56\xca\xb1\xa2\x8e\x81" + "\xb6\x57\x66\x54\x63\x1c\xf1\x65\x66\xb8" + "\x6e\x3b\x33\xa1\x08\xb0\x53\x07\xc0\x0a" + "\xff\x14\xa7\x68\xed\x73\x50\x60\x6a\x0f" + "\x85\xe6\xa9\x1d\x39\x6f\x5b\x5c\xbe\x57" + "\x7f\x9b\x38\x80\x7c\x7d\x52\x3d\x6d\x79" + "\x2f\x6e\xbc\x24\xa4\xec\xf2\xb3\xa4\x27" + "\xcd\xbb\xfb"), + "0xCB0082C8F197D260991BA6A460E76E202BAD27B3", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + while (testcase->input != NULL && testcase->result != NULL) { + isc_sha1_init(&sha1); + for(i = 0; i < testcase->repeats; i++) { + isc_sha1_update(&sha1, + (const uint8_t *) testcase->input, + testcase->input_len); + } + isc_sha1_final(&sha1, digest); + tohexstr(digest, ISC_SHA1_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} + +ATF_TC(isc_sha224); +ATF_TC_HEAD(isc_sha224, tc) { + atf_tc_set_md_var(tc, "descr", "sha224 examples from RFC4634"); +} +ATF_TC_BODY(isc_sha224, tc) { + isc_sha224_t sha224; + int i; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("abc"), + "0x23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7" + "E36C9DA7", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("abcdbcdecdefdefgefghfghighijhijkijklj" + "klmklmnlmnomnopnopq"), + "0x75388B16512776CC5DBA5DA1FD890150B0C6455CB4F58B" + "1952522525", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("a"), + "0x20794655980C91D8BBB4C1EA97618A4BF03F42581948B2" + "EE4EE7AD67", + 1000000 + }, + /* Test 4 */ + { + TEST_INPUT("01234567012345670123456701234567"), + "0x567F69F168CD7844E65259CE658FE7AADFA25216E68ECA" + "0EB7AB8262", + 20 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 6 */ + { + TEST_INPUT("\x07"), + "0x00ECD5F138422B8AD74C9799FD826C531BAD2FCABC7450" + "BEE2AA8C2A", + 1 + }, +#if 0 + /* Test 7 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 8 */ + { + TEST_INPUT("\x18\x80\x40\x05\xdd\x4f\xbd\x15\x56\x29" + "\x9d\x6f\x9d\x93\xdf\x62"), + "0xDF90D78AA78821C99B40BA4C966921ACCD8FFB1E98AC38" + "8E56191DB1", + 1 + }, +#if 0 + /* Test 9 */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 10 */ + { + TEST_INPUT("\x55\xb2\x10\x07\x9c\x61\xb5\x3a\xdd\x52" + "\x06\x22\xd1\xac\x97\xd5\xcd\xbe\x8c\xb3" + "\x3a\xa0\xae\x34\x45\x17\xbe\xe4\xd7\xba" + "\x09\xab\xc8\x53\x3c\x52\x50\x88\x7a\x43" + "\xbe\xbb\xac\x90\x6c\x2e\x18\x37\xf2\x6b" + "\x36\xa5\x9a\xe3\xbe\x78\x14\xd5\x06\x89" + "\x6b\x71\x8b\x2a\x38\x3e\xcd\xac\x16\xb9" + "\x61\x25\x55\x3f\x41\x6f\xf3\x2c\x66\x74" + "\xc7\x45\x99\xa9\x00\x53\x86\xd9\xce\x11" + "\x12\x24\x5f\x48\xee\x47\x0d\x39\x6c\x1e" + "\xd6\x3b\x92\x67\x0c\xa5\x6e\xc8\x4d\xee" + "\xa8\x14\xb6\x13\x5e\xca\x54\x39\x2b\xde" + "\xdb\x94\x89\xbc\x9b\x87\x5a\x8b\xaf\x0d" + "\xc1\xae\x78\x57\x36\x91\x4a\xb7\xda\xa2" + "\x64\xbc\x07\x9d\x26\x9f\x2c\x0d\x7e\xdd" + "\xd8\x10\xa4\x26\x14\x5a\x07\x76\xf6\x7c" + "\x87\x82\x73"), + "0x0B31894EC8937AD9B91BDFBCBA294D9ADEFAA18E09305E" + "9F20D5C3A4", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + while (testcase->input != NULL && testcase->result != NULL) { + isc_sha224_init(&sha224); + for(i = 0; i < testcase->repeats; i++) { + isc_sha224_update(&sha224, + (const uint8_t *) testcase->input, + testcase->input_len); + } + isc_sha224_final(digest, &sha224); + /* + *API inconsistency BUG HERE + * in order to be consistant with the other isc_hash_final + * functions the call should be + * isc_sha224_final(&sha224, digest); + */ + tohexstr(digest, ISC_SHA224_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} + +ATF_TC(isc_sha256); +ATF_TC_HEAD(isc_sha256, tc) { + atf_tc_set_md_var(tc, "descr", "sha224 examples from RFC4634"); +} +ATF_TC_BODY(isc_sha256, tc) { + isc_sha256_t sha256; + int i; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("abc"), + "0xBA7816BF8F01CFEA414140DE5DAE2223B00361A396177A" + "9CB410FF61F20015AD", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("abcdbcdecdefdefgefghfghighijhijkijkljk" + "lmklmnlmnomnopnopq"), + "0x248D6A61D20638B8E5C026930C3E6039A33CE45964FF21" + "67F6ECEDD419DB06C1", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("a"), + "0xCDC76E5C9914FB9281A1C7E284D73E67F1809A48A49720" + "0E046D39CCC7112CD0", + 1000000 }, + /* Test 4 */ + { + TEST_INPUT("01234567012345670123456701234567"), + "0x594847328451BDFA85056225462CC1D867D877FB388DF0" + "CE35F25AB5562BFBB5", + 20 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 6 */ + { + TEST_INPUT("\x19"), + "0x68AA2E2EE5DFF96E3355E6C7EE373E3D6A4E17F75F9518" + "D843709C0C9BC3E3D4", + 1 + }, +#if 0 + /* Test 7 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 8 */ + { + TEST_INPUT("\xe3\xd7\x25\x70\xdc\xdd\x78\x7c\xe3" + "\x88\x7a\xb2\xcd\x68\x46\x52"), + "0x175EE69B02BA9B58E2B0A5FD13819CEA573F3940A94F82" + "5128CF4209BEABB4E8", + 1 + }, +#if 0 + /* Test 9 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 10 */ + { + TEST_INPUT("\x83\x26\x75\x4e\x22\x77\x37\x2f\x4f\xc1" + "\x2b\x20\x52\x7a\xfe\xf0\x4d\x8a\x05\x69" + "\x71\xb1\x1a\xd5\x71\x23\xa7\xc1\x37\x76" + "\x00\x00\xd7\xbe\xf6\xf3\xc1\xf7\xa9\x08" + "\x3a\xa3\x9d\x81\x0d\xb3\x10\x77\x7d\xab" + "\x8b\x1e\x7f\x02\xb8\x4a\x26\xc7\x73\x32" + "\x5f\x8b\x23\x74\xde\x7a\x4b\x5a\x58\xcb" + "\x5c\x5c\xf3\x5b\xce\xe6\xfb\x94\x6e\x5b" + "\xd6\x94\xfa\x59\x3a\x8b\xeb\x3f\x9d\x65" + "\x92\xec\xed\xaa\x66\xca\x82\xa2\x9d\x0c" + "\x51\xbc\xf9\x33\x62\x30\xe5\xd7\x84\xe4" + "\xc0\xa4\x3f\x8d\x79\xa3\x0a\x16\x5c\xba" + "\xbe\x45\x2b\x77\x4b\x9c\x71\x09\xa9\x7d" + "\x13\x8f\x12\x92\x28\x96\x6f\x6c\x0a\xdc" + "\x10\x6a\xad\x5a\x9f\xdd\x30\x82\x57\x69" + "\xb2\xc6\x71\xaf\x67\x59\xdf\x28\xeb\x39" + "\x3d\x54\xd6"), + "0x97DBCA7DF46D62C8A422C941DD7E835B8AD3361763F7E9" + "B2D95F4F0DA6E1CCBC", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + while (testcase->input != NULL && testcase->result != NULL) { + isc_sha256_init(&sha256); + for(i = 0; i < testcase->repeats; i++) { + isc_sha256_update(&sha256, + (const uint8_t *) testcase->input, + testcase->input_len); + } + isc_sha256_final(digest, &sha256); + /* + *API inconsistency BUG HERE + * in order to be consistant with the other isc_hash_final + * functions the call should be + * isc_sha224_final(&sha224, digest); + */ + tohexstr(digest, ISC_SHA256_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} + +ATF_TC(isc_sha384); +ATF_TC_HEAD(isc_sha384, tc) { + atf_tc_set_md_var(tc, "descr", "sha224 examples from RFC4634"); +} +ATF_TC_BODY(isc_sha384, tc) { + isc_sha384_t sha384; + int i; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("abc"), + "0xCB00753F45A35E8BB5A03D699AC65007272C32AB0EDED1" + "631A8B605A43FF5BED8086072BA1E7CC2358BAEC" + "A134C825A7", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("abcdefghbcdefghicdefghijdefghijkefghijkl" + "fghijklmghijklmnhijklmnoijklmnopjklmnopq" + "klmnopqrlmnopqrsmnopqrstnopqrstu"), + "0x09330C33F71147E83D192FC782CD1B4753111B173B3B05" + "D22FA08086E3B0F712FCC7C71A557E2DB966C3E9" + "FA91746039", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("a"), + "0x9D0E1809716474CB086E834E310A4A1CED149E9C00F248" + "527972CEC5704C2A5B07B8B3DC38ECC4EBAE97DD" + "D87F3D8985", + 1000000 + }, + /* Test 4 */ + { + TEST_INPUT("01234567012345670123456701234567"), + "0x2FC64A4F500DDB6828F6A3430B8DD72A368EB7F3A8322A" + "70BC84275B9C0B3AB00D27A5CC3C2D224AA6B61A" + "0D79FB4596", + 20 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 6 */ + { TEST_INPUT("\xb9"), + "0xBC8089A19007C0B14195F4ECC74094FEC64F01F9092928" + "2C2FB392881578208AD466828B1C6C283D2722CF" + "0AD1AB6938", + 1 + }, +#if 0 + /* Test 7 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 8 */ + { + TEST_INPUT("\xa4\x1c\x49\x77\x79\xc0\x37\x5f\xf1" + "\x0a\x7f\x4e\x08\x59\x17\x39"), + "0xC9A68443A005812256B8EC76B00516F0DBB74FAB26D665" + "913F194B6FFB0E91EA9967566B58109CBC675CC2" + "08E4C823F7", + 1 + }, +#if 0 + /* Test 9 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 10 */ + { + TEST_INPUT("\x39\x96\x69\xe2\x8f\x6b\x9c\x6d\xbc\xbb" + "\x69\x12\xec\x10\xff\xcf\x74\x79\x03\x49" + "\xb7\xdc\x8f\xbe\x4a\x8e\x7b\x3b\x56\x21" + "\xdb\x0f\x3e\x7d\xc8\x7f\x82\x32\x64\xbb" + "\xe4\x0d\x18\x11\xc9\xea\x20\x61\xe1\xc8" + "\x4a\xd1\x0a\x23\xfa\xc1\x72\x7e\x72\x02" + "\xfc\x3f\x50\x42\xe6\xbf\x58\xcb\xa8\xa2" + "\x74\x6e\x1f\x64\xf9\xb9\xea\x35\x2c\x71" + "\x15\x07\x05\x3c\xf4\xe5\x33\x9d\x52\x86" + "\x5f\x25\xcc\x22\xb5\xe8\x77\x84\xa1\x2f" + "\xc9\x61\xd6\x6c\xb6\xe8\x95\x73\x19\x9a" + "\x2c\xe6\x56\x5c\xbd\xf1\x3d\xca\x40\x38" + "\x32\xcf\xcb\x0e\x8b\x72\x11\xe8\x3a\xf3" + "\x2a\x11\xac\x17\x92\x9f\xf1\xc0\x73\xa5" + "\x1c\xc0\x27\xaa\xed\xef\xf8\x5a\xad\x7c" + "\x2b\x7c\x5a\x80\x3e\x24\x04\xd9\x6d\x2a" + "\x77\x35\x7b\xda\x1a\x6d\xae\xed\x17\x15" + "\x1c\xb9\xbc\x51\x25\xa4\x22\xe9\x41\xde" + "\x0c\xa0\xfc\x50\x11\xc2\x3e\xcf\xfe\xfd" + "\xd0\x96\x76\x71\x1c\xf3\xdb\x0a\x34\x40" + "\x72\x0e\x16\x15\xc1\xf2\x2f\xbc\x3c\x72" + "\x1d\xe5\x21\xe1\xb9\x9b\xa1\xbd\x55\x77" + "\x40\x86\x42\x14\x7e\xd0\x96"), + "0x4F440DB1E6EDD2899FA335F09515AA025EE177A79F4B4A" + "AF38E42B5C4DE660F5DE8FB2A5B2FBD2A3CBFFD2" + "0CFF1288C0", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + while (testcase->input != NULL && testcase->result != NULL) { + isc_sha384_init(&sha384); + for(i = 0; i < testcase->repeats; i++) { + isc_sha384_update(&sha384, + (const uint8_t *) testcase->input, + testcase->input_len); + } + isc_sha384_final(digest, &sha384); + /* + *API inconsistency BUG HERE + * in order to be consistant with the other isc_hash_final + * functions the call should be + * isc_sha224_final(&sha224, digest); + */ + tohexstr(digest, ISC_SHA384_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} + +ATF_TC(isc_sha512); +ATF_TC_HEAD(isc_sha512, tc) { + atf_tc_set_md_var(tc, "descr", "sha224 examples from RFC4634"); +} +ATF_TC_BODY(isc_sha512, tc) { + isc_sha512_t sha512; + int i; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("abc"), + "0xDDAF35A193617ABACC417349AE20413112E6FA4E89A97E" + "A20A9EEEE64B55D39A2192992A274FC1A836BA3C" + "23A3FEEBBD454D4423643CE80E2A9AC94FA54CA49F", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("abcdefghbcdefghicdefghijdefghijkefghijkl" + "fghijklmghijklmnhijklmnoijklmnopjklmnopq" + "klmnopqrlmnopqrsmnopqrstnopqrstu"), + "0x8E959B75DAE313DA8CF4F72814FC143F8F7779C6EB9F7F" + "A17299AEADB6889018501D289E4900F7E4331B99" + "DEC4B5433AC7D329EEB6DD26545E96E55B874BE909", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("a"), + "0xE718483D0CE769644E2E42C7BC15B4638E1F98B13B2044" + "285632A803AFA973EBDE0FF244877EA60A4CB043" + "2CE577C31BEB009C5C2C49AA2E4EADB217AD8CC09B", + 1000000 + }, + /* Test 4 */ + { + TEST_INPUT("01234567012345670123456701234567"), + "0x89D05BA632C699C31231DED4FFC127D5A894DAD412C0E0" + "24DB872D1ABD2BA8141A0F85072A9BE1E2AA04CF" + "33C765CB510813A39CD5A84C4ACAA64D3F3FB7BAE9", + 20 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 6 */ + { + TEST_INPUT("\xD0"), + "0x9992202938E882E73E20F6B69E68A0A7149090423D93C8" + "1BAB3F21678D4ACEEEE50E4E8CAFADA4C85A54EA" + "8306826C4AD6E74CECE9631BFA8A549B4AB3FBBA15", + 1 + }, +#if 0 + /* Test 7 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 8 */ + { + TEST_INPUT("\x8d\x4e\x3c\x0e\x38\x89\x19\x14\x91\x81" + "\x6e\x9d\x98\xbf\xf0\xa0"), + "0xCB0B67A4B8712CD73C9AABC0B199E9269B20844AFB75AC" + "BDD1C153C9828924C3DDEDAAFE669C5FDD0BC66F" + "630F6773988213EB1B16F517AD0DE4B2F0C95C90F8", + 1 + }, +#if 0 + /* Test 9 -- unimplemented optional functionality */ + { + TEST_INPUT(""), + "0xXXX", + 1 + }, +#endif + /* Test 10 */ + { + TEST_INPUT("\xa5\x5f\x20\xc4\x11\xaa\xd1\x32\x80\x7a" + "\x50\x2d\x65\x82\x4e\x31\xa2\x30\x54\x32" + "\xaa\x3d\x06\xd3\xe2\x82\xa8\xd8\x4e\x0d" + "\xe1\xde\x69\x74\xbf\x49\x54\x69\xfc\x7f" + "\x33\x8f\x80\x54\xd5\x8c\x26\xc4\x93\x60" + "\xc3\xe8\x7a\xf5\x65\x23\xac\xf6\xd8\x9d" + "\x03\xe5\x6f\xf2\xf8\x68\x00\x2b\xc3\xe4" + "\x31\xed\xc4\x4d\xf2\xf0\x22\x3d\x4b\xb3" + "\xb2\x43\x58\x6e\x1a\x7d\x92\x49\x36\x69" + "\x4f\xcb\xba\xf8\x8d\x95\x19\xe4\xeb\x50" + "\xa6\x44\xf8\xe4\xf9\x5e\xb0\xea\x95\xbc" + "\x44\x65\xc8\x82\x1a\xac\xd2\xfe\x15\xab" + "\x49\x81\x16\x4b\xbb\x6d\xc3\x2f\x96\x90" + "\x87\xa1\x45\xb0\xd9\xcc\x9c\x67\xc2\x2b" + "\x76\x32\x99\x41\x9c\xc4\x12\x8b\xe9\xa0" + "\x77\xb3\xac\xe6\x34\x06\x4e\x6d\x99\x28" + "\x35\x13\xdc\x06\xe7\x51\x5d\x0d\x73\x13" + "\x2e\x9a\x0d\xc6\xd3\xb1\xf8\xb2\x46\xf1" + "\xa9\x8a\x3f\xc7\x29\x41\xb1\xe3\xbb\x20" + "\x98\xe8\xbf\x16\xf2\x68\xd6\x4f\x0b\x0f" + "\x47\x07\xfe\x1e\xa1\xa1\x79\x1b\xa2\xf3" + "\xc0\xc7\x58\xe5\xf5\x51\x86\x3a\x96\xc9" + "\x49\xad\x47\xd7\xfb\x40\xd2"), + "0xC665BEFB36DA189D78822D10528CBF3B12B3EEF7260399" + "09C1A16A270D48719377966B957A878E72058477" + "9A62825C18DA26415E49A7176A894E7510FD1451F5", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + while (testcase->input != NULL && testcase->result != NULL) { + isc_sha512_init(&sha512); + for(i = 0; i < testcase->repeats; i++) { + isc_sha512_update(&sha512, + (const uint8_t *) testcase->input, + testcase->input_len); + } + isc_sha512_final(digest, &sha512); + /* + *API inconsistency BUG HERE + * in order to be consistant with the other isc_hash_final + * functions the call should be + * isc_sha224_final(&sha224, digest); + */ + tohexstr(digest, ISC_SHA512_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} + +#ifndef PK11_MD5_DISABLE +ATF_TC(isc_md5); +ATF_TC_HEAD(isc_md5, tc) { + atf_tc_set_md_var(tc, "descr", "md5 example from RFC1321"); +} +ATF_TC_BODY(isc_md5, tc) { + isc_md5_t md5; + int i; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + { + TEST_INPUT(""), + "0xD41D8CD98F00B204E9800998ECF8427E", + 1 + }, + { + TEST_INPUT("a"), + "0x0CC175B9C0F1B6A831C399E269772661", + 1 + }, + { + TEST_INPUT("abc"), + "0x900150983CD24FB0D6963F7D28E17F72", + 1 + }, + { + TEST_INPUT("message digest"), + "0xF96B697D7CB7938D525A2F31AAF161D0", + 1 + }, + { + TEST_INPUT("abcdefghijklmnopqrstuvwxyz"), + "0xC3FCD3D76192E4007DFB496CCA67E13B", + 1 + }, + { + TEST_INPUT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm" + "nopqrstuvwxyz0123456789"), + "0xD174AB98D277D9F5A5611C2C9F419D9F", + 1 + }, + { + TEST_INPUT("123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890"), + "0x57EDF4A22BE3C955AC49DA2E2107B67A", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + while (testcase->input != NULL && testcase->result != NULL) { + isc_md5_init(&md5); + for(i = 0; i < testcase->repeats; i++) { + isc_md5_update(&md5, + (const uint8_t *) testcase->input, + testcase->input_len); + } + isc_md5_final(&md5, digest); + tohexstr(digest, ISC_MD5_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} +#endif + +/* HMAC-SHA1 test */ +ATF_TC(isc_hmacsha1); +ATF_TC_HEAD(isc_hmacsha1, tc) { + atf_tc_set_md_var(tc, "descr", "HMAC-SHA1 examples from RFC2104"); +} +ATF_TC_BODY(isc_hmacsha1, tc) { + isc_hmacsha1_t hmacsha1; + + UNUSED(tc); + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("\x48\x69\x20\x54\x68\x65\x72\x65"), + "0xB617318655057264E28BC0B6FB378C8EF146BE00", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61" + "\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20" + "\x6e\x6f\x74\x68\x69\x6e\x67\x3f"), + "0xEFFCDF6AE5EB2FA2D27416D5F184DF9C259A7C79", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"), + "0x125D7342B9AC11CD91A39AF48AA17B4F63F175D3", + 1 + }, + /* Test 4 */ + { + TEST_INPUT("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"), + "0x4C9007F4026250C6BC8414F9BF50C86C2D7235DA", + 1 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT("Test With Truncation"), + "0x4C1A03424B55E07FE7F27BE1", + 1 + }, +#endif + /* Test 6 */ + { + TEST_INPUT("Test Using Larger Than Block-Size Key - " + "Hash Key First"), + "0xAA4AE5E15272D00E95705637CE8A3B55ED402112", 1 }, + /* Test 7 */ + { + TEST_INPUT("Test Using Larger Than Block-Size Key and " + "Larger Than One Block-Size Data"), + "0xE8E99D0F45237D786D6BBAA7965C7808BBFF1A91", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + hash_test_key_t test_keys[] = { + /* Key 1 */ + { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 20 }, + /* Key 2 */ + { "Jefe", 4 }, + /* Key 3 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 20 }, + /* Key 4 */ + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19", 25 }, +#if 0 + /* Key 5 */ + { "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", 20 }, +#endif + /* Key 6 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 80 }, + /* Key 7 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 80 }, + { "", 0 } + }; + + hash_test_key_t *test_key = test_keys; + + while (testcase->input != NULL && testcase->result != NULL) { + memmove(buffer, test_key->key, test_key->len); + isc_hmacsha1_init(&hmacsha1, buffer, test_key->len); + isc_hmacsha1_update(&hmacsha1, + (const uint8_t *) testcase->input, + testcase->input_len); + isc_hmacsha1_sign(&hmacsha1, digest, ISC_SHA1_DIGESTLENGTH); + tohexstr(digest, ISC_SHA1_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + test_key++; + } +} + +/* HMAC-SHA224 test */ +ATF_TC(isc_hmacsha224); +ATF_TC_HEAD(isc_hmacsha224, tc) { + atf_tc_set_md_var(tc, "descr", "HMAC-SHA224 examples from RFC4634"); +} +ATF_TC_BODY(isc_hmacsha224, tc) { + isc_hmacsha224_t hmacsha224; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("\x48\x69\x20\x54\x68\x65\x72\x65"), + "0x896FB1128ABBDF196832107CD49DF33F47B4B1169912BA" + "4F53684B22", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61" + "\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20" + "\x6e\x6f\x74\x68\x69\x6e\x67\x3f"), + "0xA30E01098BC6DBBF45690F3A7E9E6D0F8BBEA2A39E61480" + "08FD05E44", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"), + "0x7FB3CB3588C6C1F6FFA9694D7D6AD2649365B0C1F65D69" + "D1EC8333EA", + 1 + }, + /* Test 4 */ + { + TEST_INPUT("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"), + "0x6C11506874013CAC6A2ABC1BB382627CEC6A90D86EFC01" + "2DE7AFEC5A", + 1 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT("Test With Truncation"), + "0x4C1A03424B55E07FE7F27BE1", + 1 + }, +#endif + /* Test 6 */ + { + TEST_INPUT("Test Using Larger Than Block-Size Key - " + "Hash Key First"), + "0x95E9A0DB962095ADAEBE9B2D6F0DBCE2D499F112F2D2B7" + "273FA6870E", + 1 + }, + /* Test 7 */ + { + TEST_INPUT("\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20" + "\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67" + "\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20" + "\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b" + "\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20" + "\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67" + "\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c" + "\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64" + "\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b" + "\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74" + "\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65" + "\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62" + "\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20" + "\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41" + "\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68" + "\x6d\x2e"), + "0x3A854166AC5D9F023F54D517D0B39DBD946770DB9C2B95" + "C9F6F565D1", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + hash_test_key_t test_keys[] = { + /* Key 1 */ + { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 20 }, + /* Key 2 */ + { "Jefe", 4 }, + /* Key 3 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 20 }, + /* Key 4 */ + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19", 25 }, +#if 0 + /* Key 5 */ + { "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", 20 }, +#endif + /* Key 6 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, + /* Key 7 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, + { "", 0 } + }; + + hash_test_key_t *test_key = test_keys; + + while (testcase->input != NULL && testcase->result != NULL) { + memmove(buffer, test_key->key, test_key->len); + isc_hmacsha224_init(&hmacsha224, buffer, test_key->len); + isc_hmacsha224_update(&hmacsha224, + (const uint8_t *) testcase->input, + testcase->input_len); + isc_hmacsha224_sign(&hmacsha224, digest, ISC_SHA224_DIGESTLENGTH); + tohexstr(digest, ISC_SHA224_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + test_key++; + } +} + +/* HMAC-SHA256 test */ +ATF_TC(isc_hmacsha256); +ATF_TC_HEAD(isc_hmacsha256, tc) { + atf_tc_set_md_var(tc, "descr", "HMAC-SHA256 examples from RFC4634"); +} +ATF_TC_BODY(isc_hmacsha256, tc) { + isc_hmacsha256_t hmacsha256; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("\x48\x69\x20\x54\x68\x65\x72\x65"), + "0xB0344C61D8DB38535CA8AFCEAF0BF12B881DC200C9833D" + "A726E9376C2E32CFF7", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61" + "\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20" + "\x6e\x6f\x74\x68\x69\x6e\x67\x3f"), + "0x5BDCC146BF60754E6A042426089575C75A003F089D2739" + "839DEC58B964EC3843", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"), + "0x773EA91E36800E46854DB8EBD09181A72959098B3EF8C1" + "22D9635514CED565FE", + 1 + }, + /* Test 4 */ + { + TEST_INPUT("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"), + "0x82558A389A443C0EA4CC819899F2083A85F0FAA3E578F8" + "077A2E3FF46729665B", + 1 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT("Test With Truncation"), + "0x4C1A03424B55E07FE7F27BE1", + 1 + }, +#endif + /* Test 6 */ + { + TEST_INPUT("Test Using Larger Than Block-Size Key - " + "Hash Key First"), + "0x60E431591EE0B67F0D8A26AACBF5B77F8E0BC6213728C5" + "140546040F0EE37F54", + 1 + }, + /* Test 7 */ + { + TEST_INPUT("\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20" + "\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67" + "\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20" + "\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b" + "\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20" + "\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67" + "\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c" + "\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64" + "\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b" + "\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74" + "\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65" + "\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62" + "\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20" + "\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41" + "\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68" + "\x6d\x2e"), + "0x9B09FFA71B942FCB27635FBCD5B0E944BFDC63644F0713" + "938A7F51535C3A35E2", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + hash_test_key_t test_keys[] = { + /* Key 1 */ + { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 20 }, + /* Key 2 */ + { "Jefe", 4 }, + /* Key 3 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 20 }, + /* Key 4 */ + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19", 25 }, +#if 0 + /* Key 5 */ + { "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", 20 }, +#endif + /* Key 6 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, + /* Key 7 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, + { "", 0 } + }; + + hash_test_key_t *test_key = test_keys; + + while (testcase->input != NULL && testcase->result != NULL) { + memmove(buffer, test_key->key, test_key->len); + isc_hmacsha256_init(&hmacsha256, buffer, test_key->len); + isc_hmacsha256_update(&hmacsha256, + (const uint8_t *) testcase->input, + testcase->input_len); + isc_hmacsha256_sign(&hmacsha256, digest, ISC_SHA256_DIGESTLENGTH); + tohexstr(digest, ISC_SHA256_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + test_key++; + } +} + +/* HMAC-SHA384 test */ +ATF_TC(isc_hmacsha384); +ATF_TC_HEAD(isc_hmacsha384, tc) { + atf_tc_set_md_var(tc, "descr", "HMAC-SHA384 examples from RFC4634"); +} +ATF_TC_BODY(isc_hmacsha384, tc) { + isc_hmacsha384_t hmacsha384; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("\x48\x69\x20\x54\x68\x65\x72\x65"), + "0xAFD03944D84895626B0825F4AB46907F15F9DADBE4101E" + "C682AA034C7CEBC59CFAEA9EA9076EDE7F4AF152" + "E8B2FA9CB6", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61" + "\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20" + "\x6e\x6f\x74\x68\x69\x6e\x67\x3f"), + "0xAF45D2E376484031617F78D2B58A6B1B9C7EF464F5A01B" + "47E42EC3736322445E8E2240CA5E69E2C78B3239" + "ECFAB21649", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"), + "0x88062608D3E6AD8A0AA2ACE014C8A86F0AA635D947AC9F" + "EBE83EF4E55966144B2A5AB39DC13814B94E3AB6" + "E101A34F27", + 1 + }, + /* Test 4 */ + { + TEST_INPUT("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"), + "0x3E8A69B7783C25851933AB6290AF6CA77A998148085000" + "9CC5577C6E1F573B4E6801DD23C4A7D679CCF8A3" + "86C674CFFB", + 1 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT("Test With Truncation"), + "0x4C1A03424B55E07FE7F27BE1", + 1 + }, +#endif + /* Test 6 */ + { + TEST_INPUT("Test Using Larger Than Block-Size Key - " + "Hash Key First"), + "0x4ECE084485813E9088D2C63A041BC5B44F9EF1012A2B58" + "8F3CD11F05033AC4C60C2EF6AB4030FE8296248D" + "F163F44952", + 1 + }, + /* Test 7 */ + { + TEST_INPUT("\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20" + "\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67" + "\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20" + "\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b" + "\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20" + "\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67" + "\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c" + "\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64" + "\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b" + "\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74" + "\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65" + "\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62" + "\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20" + "\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41" + "\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68" + "\x6d\x2e"), + "0x6617178E941F020D351E2F254E8FD32C602420FEB0B8FB" + "9ADCCEBB82461E99C5A678CC31E799176D3860E6" + "110C46523E", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + hash_test_key_t test_keys[] = { + /* Key 1 */ + { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 20 }, + /* Key 2 */ + { "Jefe", 4 }, + /* Key 3 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 20 }, + /* Key 4 */ + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19", 25 }, +#if 0 + /* Key 5 */ + { "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", 20 }, +#endif + /* Key 6 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, + /* Key 7 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, + { "", 0 } + }; + + hash_test_key_t *test_key = test_keys; + + while (testcase->input != NULL && testcase->result != NULL) { + memmove(buffer, test_key->key, test_key->len); + isc_hmacsha384_init(&hmacsha384, buffer, test_key->len); + isc_hmacsha384_update(&hmacsha384, + (const uint8_t *) testcase->input, + testcase->input_len); + isc_hmacsha384_sign(&hmacsha384, digest, ISC_SHA384_DIGESTLENGTH); + tohexstr(digest, ISC_SHA384_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + test_key++; + } +} + +/* HMAC-SHA512 test */ +ATF_TC(isc_hmacsha512); +ATF_TC_HEAD(isc_hmacsha512, tc) { + atf_tc_set_md_var(tc, "descr", "HMAC-SHA512 examples from RFC4634"); +} +ATF_TC_BODY(isc_hmacsha512, tc) { + isc_hmacsha512_t hmacsha512; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("\x48\x69\x20\x54\x68\x65\x72\x65"), + "0x87AA7CDEA5EF619D4FF0B4241A1D6CB02379F4E2CE4EC2" + "787AD0B30545E17CDEDAA833B7D6B8A702038B27" + "4EAEA3F4E4BE9D914EEB61F1702E696C203A126854", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61" + "\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20" + "\x6e\x6f\x74\x68\x69\x6e\x67\x3f"), + "0x164B7A7BFCF819E2E395FBE73B56E0A387BD64222E831F" + "D610270CD7EA2505549758BF75C05A994A6D034F" + "65F8F0E6FDCAEAB1A34D4A6B4B636E070A38BCE737", + 1 + }, + /* Test 3 */ + { + TEST_INPUT("\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"), + "0xFA73B0089D56A284EFB0F0756C890BE9B1B5DBDD8EE81A" + "3655F83E33B2279D39BF3E848279A722C806B485" + "A47E67C807B946A337BEE8942674278859E13292FB", + 1 + }, + /* Test 4 */ + { + TEST_INPUT("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"), + "0xB0BA465637458C6990E5A8C5F61D4AF7E576D97FF94B87" + "2DE76F8050361EE3DBA91CA5C11AA25EB4D67927" + "5CC5788063A5F19741120C4F2DE2ADEBEB10A298DD", + 1 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT("Test With Truncation"), + "0x4C1A03424B55E07FE7F27BE1", + 1 + }, +#endif + /* Test 6 */ + { + TEST_INPUT("Test Using Larger Than Block-Size Key - " + "Hash Key First"), + "0x80B24263C7C1A3EBB71493C1DD7BE8B49B46D1F41B4AEE" + "C1121B013783F8F3526B56D037E05F2598BD0FD2" + "215D6A1E5295E64F73F63F0AEC8B915A985D786598", + 1 + }, + /* Test 7 */ + { + TEST_INPUT("\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20" + "\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67" + "\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20" + "\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b" + "\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20" + "\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67" + "\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c" + "\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64" + "\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b" + "\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74" + "\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65" + "\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62" + "\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20" + "\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41" + "\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68" + "\x6d\x2e"), + "0xE37B6A775DC87DBAA4DFA9F96E5E3FFDDEBD71F8867289" + "865DF5A32D20CDC944B6022CAC3C4982B10D5EEB" + "55C3E4DE15134676FB6DE0446065C97440FA8C6A58", + 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + hash_test_key_t test_keys[] = { + /* Key 1 */ + { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 20 }, + /* Key 2 */ + { "Jefe", 4 }, + /* Key 3 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 20 }, + /* Key 4 */ + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19", 25 }, +#if 0 + /* Key 5 */ + { "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", 20 }, +#endif + /* Key 6 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, + /* Key 7 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, + { "", 0 } + }; + + hash_test_key_t *test_key = test_keys; + + while (testcase->input != NULL && testcase->result != NULL) { + memmove(buffer, test_key->key, test_key->len); + isc_hmacsha512_init(&hmacsha512, buffer, test_key->len); + isc_hmacsha512_update(&hmacsha512, + (const uint8_t *) testcase->input, + testcase->input_len); + isc_hmacsha512_sign(&hmacsha512, digest, ISC_SHA512_DIGESTLENGTH); + tohexstr(digest, ISC_SHA512_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + test_key++; + } +} + + +#ifndef PK11_MD5_DISABLE +/* HMAC-MD5 Test */ +ATF_TC(isc_hmacmd5); +ATF_TC_HEAD(isc_hmacmd5, tc) { + atf_tc_set_md_var(tc, "descr", "HMAC-MD5 examples from RFC2104"); +} +ATF_TC_BODY(isc_hmacmd5, tc) { + isc_hmacmd5_t hmacmd5; + + UNUSED(tc); + + /* + * These are the various test vectors. All of these are passed + * through the hash function and the results are compared to the + * result specified here. + */ + hash_testcase_t testcases[] = { + /* Test 1 */ + { + TEST_INPUT("\x48\x69\x20\x54\x68\x65\x72\x65"), + "0x9294727A3638BB1C13F48EF8158BFC9D", + 1 + }, + /* Test 2 */ + { + TEST_INPUT("\x77\x68\x61\x74\x20\x64\x6f\x20\x79" + "\x61\x20\x77\x61\x6e\x74\x20\x66\x6f" + "\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f"), + "0x750C783E6AB0B503EAA86E310A5DB738", 1 + }, + /* Test 3 */ + { + TEST_INPUT("\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD"), + "0x56BE34521D144C88DBB8C733F0E8B3F6", + 1 + }, + /* Test 4 */ + { + TEST_INPUT("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"), + "0x697EAF0ACA3A3AEA3A75164746FFAA79", + 1 + }, +#if 0 + /* Test 5 -- unimplemented optional functionality */ + { + TEST_INPUT("Test With Truncation"), + "0x4C1A03424B55E07FE7F27BE1", + 1 + }, + /* Test 6 -- unimplemented optional functionality */ + { + TEST_INPUT("Test Using Larger Than Block-Size Key - " + "Hash Key First"), + "0xAA4AE5E15272D00E95705637CE8A3B55ED402112", + 1 + }, + /* Test 7 -- unimplemented optional functionality */ + { + TEST_INPUT("Test Using Larger Than Block-Size Key and " + "Larger Than One Block-Size Data"), + "0xE8E99D0F45237D786D6BBAA7965C7808BBFF1A91", + 1 + }, +#endif + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + hash_test_key_t test_keys[] = { + /* Key 1 */ + { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b", 16 }, + /* Key 2 */ + { "Jefe", 4 }, + /* Key 3 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa", 16 }, + /* Key 4 */ + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a" + "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" + "\x15\x16\x17\x18\x19", 25 }, +#if 0 + /* Key 5 */ + { "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c" + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", 20 }, + /* Key 6 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, + /* Key 7 */ + { "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 131 }, +#endif + { "", 0 } + }; + + hash_test_key_t *test_key = test_keys; + + while (testcase->input != NULL && testcase->result != NULL) { + memmove(buffer, test_key->key, test_key->len); + isc_hmacmd5_init(&hmacmd5, buffer, test_key->len); + isc_hmacmd5_update(&hmacmd5, + (const uint8_t *) testcase->input, + testcase->input_len); + isc_hmacmd5_sign(&hmacmd5, digest); + tohexstr(digest, ISC_MD5_DIGESTLENGTH, str, sizeof(str)); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + test_key++; + } +} +#endif + +/* CRC64 Test */ +ATF_TC(isc_crc64); +ATF_TC_HEAD(isc_crc64, tc) { + atf_tc_set_md_var(tc, "descr", "64-bit cyclic redundancy check"); +} +ATF_TC_BODY(isc_crc64, tc) { + uint64_t crc; + int i; + + UNUSED(tc); + + hash_testcase_t testcases[] = { + { + TEST_INPUT(""), + "0x0000000000000000", 1 + }, + { + TEST_INPUT("a"), + "0xCE73F427ACC0A99A", 1 + }, + { + TEST_INPUT("abc"), + "0x048B813AF9F49702", 1 + }, + { + TEST_INPUT("message digest"), + "0x5273F9EA7A357BF4", 1 + }, + { + TEST_INPUT("abcdefghijklmnopqrstuvwxyz"), + "0x59F079F9218BAAA1", 1 + }, + { + TEST_INPUT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm" + "nopqrstuvwxyz0123456789"), + "0xA36DA8F71E78B6FB", 1 + }, + { + TEST_INPUT("123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890"), + "0x81E5EB73C8E7874A", 1 + }, + { NULL, 0, NULL, 1 } + }; + + hash_testcase_t *testcase = testcases; + + while (testcase->input != NULL && testcase->result != NULL) { + isc_crc64_init(&crc); + for(i = 0; i < testcase->repeats; i++) { + isc_crc64_update(&crc, + (const uint8_t *) testcase->input, + testcase->input_len); + } + isc_crc64_final(&crc); + snprintf(str, sizeof(str), + "0x%016" PRIX64, crc); + ATF_CHECK_STREQ(str, testcase->result); + + testcase++; + } +} + +ATF_TC(isc_hash_function); +ATF_TC_HEAD(isc_hash_function, tc) { + atf_tc_set_md_var(tc, "descr", "Hash function test"); +} +ATF_TC_BODY(isc_hash_function, tc) { + unsigned int h1; + unsigned int h2; + + UNUSED(tc); + + /* Incremental hashing */ + + h1 = isc_hash_function(NULL, 0, true, NULL); + h1 = isc_hash_function("This ", 5, true, &h1); + h1 = isc_hash_function("is ", 3, true, &h1); + h1 = isc_hash_function("a long test", 12, true, &h1); + + h2 = isc_hash_function("This is a long test", 20, + true, NULL); + + ATF_CHECK_EQ(h1, h2); + + /* Immutability of hash function */ + h1 = isc_hash_function(NULL, 0, true, NULL); + h2 = isc_hash_function(NULL, 0, true, NULL); + + ATF_CHECK_EQ(h1, h2); + + /* Hash function characteristics */ + h1 = isc_hash_function("Hello world", 12, true, NULL); + h2 = isc_hash_function("Hello world", 12, true, NULL); + + ATF_CHECK_EQ(h1, h2); + + /* Case */ + h1 = isc_hash_function("Hello world", 12, false, NULL); + h2 = isc_hash_function("heLLo WorLd", 12, false, NULL); + + ATF_CHECK_EQ(h1, h2); + + /* Unequal */ + h1 = isc_hash_function("Hello world", 12, true, NULL); + h2 = isc_hash_function("heLLo WorLd", 12, true, NULL); + + ATF_CHECK(h1 != h2); +} + +ATF_TC(isc_hash_function_reverse); +ATF_TC_HEAD(isc_hash_function_reverse, tc) { + atf_tc_set_md_var(tc, "descr", "Reverse hash function test"); +} +ATF_TC_BODY(isc_hash_function_reverse, tc) { + unsigned int h1; + unsigned int h2; + + UNUSED(tc); + + /* Incremental hashing */ + + h1 = isc_hash_function_reverse(NULL, 0, true, NULL); + h1 = isc_hash_function_reverse("\000", 1, true, &h1); + h1 = isc_hash_function_reverse("\003org", 4, true, &h1); + h1 = isc_hash_function_reverse("\007example", 8, true, &h1); + + h2 = isc_hash_function_reverse("\007example\003org\000", 13, + true, NULL); + + ATF_CHECK_EQ(h1, h2); + + /* Immutability of hash function */ + h1 = isc_hash_function_reverse(NULL, 0, true, NULL); + h2 = isc_hash_function_reverse(NULL, 0, true, NULL); + + ATF_CHECK_EQ(h1, h2); + + /* Hash function characteristics */ + h1 = isc_hash_function_reverse("Hello world", 12, true, NULL); + h2 = isc_hash_function_reverse("Hello world", 12, true, NULL); + + ATF_CHECK_EQ(h1, h2); + + /* Case */ + h1 = isc_hash_function_reverse("Hello world", 12, false, NULL); + h2 = isc_hash_function_reverse("heLLo WorLd", 12, false, NULL); + + ATF_CHECK_EQ(h1, h2); + + /* Unequal */ + h1 = isc_hash_function_reverse("Hello world", 12, true, NULL); + h2 = isc_hash_function_reverse("heLLo WorLd", 12, true, NULL); + + ATF_CHECK(h1 != h2); +} + +ATF_TC(isc_hash_initializer); +ATF_TC_HEAD(isc_hash_initializer, tc) { + atf_tc_set_md_var(tc, "descr", "Hash function initializer test"); +} +ATF_TC_BODY(isc_hash_initializer, tc) { + unsigned int h1; + unsigned int h2; + + UNUSED(tc); + + h1 = isc_hash_function("Hello world", 12, true, NULL); + h2 = isc_hash_function("Hello world", 12, true, NULL); + + ATF_CHECK_EQ(h1, h2); + + isc_hash_set_initializer(isc_hash_get_initializer()); + + /* Hash value must not change */ + h2 = isc_hash_function("Hello world", 12, true, NULL); + + ATF_CHECK_EQ(h1, h2); +} + +#ifndef PK11_MD5_DISABLE +ATF_TC(md5_check); +ATF_TC_HEAD(md5_check, tc) { + atf_tc_set_md_var(tc, "descr", "Startup MD5 check test"); +} +ATF_TC_BODY(md5_check, tc) { + UNUSED(tc); + + ATF_REQUIRE(isc_md5_check(false)); + ATF_CHECK(!isc_md5_check(true)); + + ATF_REQUIRE(isc_hmacmd5_check(0)); + ATF_CHECK(!isc_hmacmd5_check(1)); + ATF_CHECK(!isc_hmacmd5_check(2)); + ATF_CHECK(!isc_hmacmd5_check(3)); + ATF_CHECK(!isc_hmacmd5_check(4)); +} +#endif + +ATF_TC(sha1_check); +ATF_TC_HEAD(sha1_check, tc) { + atf_tc_set_md_var(tc, "descr", "Startup SHA-1 check test"); +} +ATF_TC_BODY(sha1_check, tc) { + UNUSED(tc); + + ATF_REQUIRE(isc_sha1_check(false)); + ATF_CHECK(!isc_sha1_check(true)); + + ATF_REQUIRE(isc_hmacsha1_check(0)); + ATF_CHECK(!isc_hmacsha1_check(1)); + ATF_CHECK(!isc_hmacsha1_check(2)); + ATF_CHECK(!isc_hmacsha1_check(3)); + ATF_CHECK(!isc_hmacsha1_check(4)); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + /* + * Tests of hash functions, including isc_hash and the + * various cryptographic hashes. + */ +#ifndef PK11_MD5_DISABLE + ATF_TP_ADD_TC(tp, md5_check); +#endif + ATF_TP_ADD_TC(tp, sha1_check); + + ATF_TP_ADD_TC(tp, isc_hash_function); + ATF_TP_ADD_TC(tp, isc_hash_function_reverse); + ATF_TP_ADD_TC(tp, isc_hash_initializer); +#ifndef PK11_MD5_DISABLE + ATF_TP_ADD_TC(tp, isc_hmacmd5); +#endif + ATF_TP_ADD_TC(tp, isc_hmacsha1); + ATF_TP_ADD_TC(tp, isc_hmacsha224); + ATF_TP_ADD_TC(tp, isc_hmacsha256); + ATF_TP_ADD_TC(tp, isc_hmacsha384); + ATF_TP_ADD_TC(tp, isc_hmacsha512); +#ifndef PK11_MD5_DISABLE + ATF_TP_ADD_TC(tp, isc_md5); +#endif + ATF_TP_ADD_TC(tp, isc_sha1); + ATF_TP_ADD_TC(tp, isc_sha224); + ATF_TP_ADD_TC(tp, isc_sha256); + ATF_TP_ADD_TC(tp, isc_sha384); + ATF_TP_ADD_TC(tp, isc_sha512); + ATF_TP_ADD_TC(tp, isc_crc64); + + return (atf_no_error()); +} diff --git a/lib/isc/tests/heap_test.c b/lib/isc/tests/heap_test.c new file mode 100644 index 0000000..c030573 --- /dev/null +++ b/lib/isc/tests/heap_test.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* ! \file */ + +#include <config.h> + +#include <stdbool.h> +#include <atf-c.h> + +#include <stdio.h> +#include <string.h> + +#include <isc/heap.h> +#include <isc/mem.h> + +#include <isc/util.h> + +struct e { + unsigned int value; + unsigned int index; +}; + +static bool +compare(void *p1, void *p2) { + struct e *e1 = p1; + struct e *e2 = p2; + + return (e1->value < e2->value); +} + +static void +idx(void *p, unsigned int i) { + struct e *e = p; + + e->index = i; +} + +ATF_TC(isc_heap_delete); +ATF_TC_HEAD(isc_heap_delete, tc) { + atf_tc_set_md_var(tc, "descr", "test isc_heap_delete"); +} +ATF_TC_BODY(isc_heap_delete, tc) { + isc_mem_t *mctx = NULL; + isc_heap_t *heap = NULL; + isc_result_t result; + struct e e1 = { 100, 0 }; + + UNUSED(tc); + + result = isc_mem_create(0, 0, &mctx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_heap_create(mctx, compare, idx, 0, &heap); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE(heap != NULL); + + isc_heap_insert(heap, &e1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(e1.index, 1); + + isc_heap_delete(heap, e1.index); + ATF_CHECK_EQ(e1.index, 0); + + isc_heap_destroy(&heap); + ATF_REQUIRE_EQ(heap, NULL); + + isc_mem_detach(&mctx); + ATF_REQUIRE_EQ(mctx, NULL); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_heap_delete); + + return (atf_no_error()); +} diff --git a/lib/isc/tests/ht_test.c b/lib/isc/tests/ht_test.c new file mode 100644 index 0000000..5356142 --- /dev/null +++ b/lib/isc/tests/ht_test.c @@ -0,0 +1,362 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* ! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <inttypes.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <isc/hash.h> +#include <isc/ht.h> +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/util.h> + +static void * +default_memalloc(void *arg, size_t size) { + UNUSED(arg); + if (size == 0U) + size = 1; + return (malloc(size)); +} + +static void +default_memfree(void *arg, void *ptr) { + UNUSED(arg); + free(ptr); +} + +static void test_ht_full(int bits, uintptr_t count) { + isc_ht_t *ht = NULL; + isc_result_t result; + isc_mem_t *mctx = NULL; + uintptr_t i; + + result = isc_mem_createx2(0, 0, default_memalloc, default_memfree, + NULL, &mctx, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_ht_init(&ht, mctx, bits); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE(ht != NULL); + + for (i = 1; i < count; i++) { + /* + * Note: snprintf() is followed with strlcat() + * to ensure we are always filling the 16 byte key. + */ + unsigned char key[16]; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); + result = isc_ht_add(ht, key, 16, (void *) i); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + } + + for (i = 1; i < count; i++) { + unsigned char key[16]; + void *f = NULL; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); + result = isc_ht_find(ht, key, 16, &f); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(i, (uintptr_t) f); + } + + for (i = 1; i < count; i++) { + unsigned char key[16]; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); + result = isc_ht_add(ht, key, 16, (void *) i); + ATF_REQUIRE_EQ(result, ISC_R_EXISTS); + } + + for (i = 1; i < count; i++) { + char key[64]; + /* + * Note: the key size is now strlen(key) which is bigger + * then the keys added above. + */ + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); + result = isc_ht_add(ht, (const unsigned char *) key, + strlen(key), (void *) i); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + } + + for (i = 1; i < count; i++) { + unsigned char key[16]; + void *f = NULL; + /* + * Note: case of KEY is now in capitals, + */ + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " KEY of a raw hashtable!!", sizeof(key)); + result = isc_ht_find(ht, key, 16, &f); + ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND); + ATF_REQUIRE_EQ(f, NULL); + } + + for (i = 1; i < count; i++) { + char key[64]; + void *f = NULL; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); + result = isc_ht_find(ht, (const unsigned char *) key, + strlen(key), &f); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(f, (void *) i); + } + + for (i = 1; i < count; i++) { + unsigned char key[16]; + void *f = NULL; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); + result = isc_ht_delete(ht, key, 16); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_ht_find(ht, key, 16, &f); + ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND); + ATF_REQUIRE_EQ(f, NULL); + } + + for (i = 1; i < count; i++) { + unsigned char key[16]; + /* + * Note: upper case KEY. + */ + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " KEY of a raw hashtable!!", sizeof(key)); + result = isc_ht_add(ht, key, 16, (void *) i); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + } + + for (i = 1; i < count; i++) { + char key[64]; + void *f = NULL; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); + result = isc_ht_delete(ht, (const unsigned char *) key, + strlen(key)); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_ht_find(ht, (const unsigned char *) key, + strlen(key), &f); + ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND); + ATF_REQUIRE_EQ(f, NULL); + } + + + for (i = 1; i < count; i++) { + unsigned char key[16]; + void *f = NULL; + /* + * Note: case of KEY is now in capitals, + */ + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " KEY of a raw hashtable!!", sizeof(key)); + result = isc_ht_find(ht, key, 16, &f); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(i, (uintptr_t) f); + } + + for (i = 1; i < count; i++) { + unsigned char key[16]; + void *f = NULL; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, " key of a raw hashtable!!", sizeof(key)); + result = isc_ht_find(ht, key, 16, &f); + ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND); + ATF_REQUIRE_EQ(f, NULL); + } + + isc_ht_destroy(&ht); + ATF_REQUIRE_EQ(ht, NULL); +} + +static void test_ht_iterator() { + isc_ht_t *ht = NULL; + isc_result_t result; + isc_mem_t *mctx = NULL; + isc_ht_iter_t * iter = NULL; + uintptr_t i; + void *v; + uintptr_t count = 10000; + uint32_t walked; + unsigned char key[16]; + unsigned char *tkey; + size_t tksize; + + result = isc_mem_createx2(0, 0, default_memalloc, default_memfree, + NULL, &mctx, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_ht_init(&ht, mctx, 16); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE(ht != NULL); + for (i = 1; i <= count; i++) { + /* + * Note that the string we're snprintfing is always > 16 bytes + * so we are always filling the key. + */ + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, "key of a raw hashtable!!", sizeof(key)); + result = isc_ht_add(ht, key, 16, (void *) i); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + } + + walked = 0; + result = isc_ht_iter_create(ht, &iter); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS; + result = isc_ht_iter_next(iter)) + { + isc_ht_iter_current(iter, &v); + isc_ht_iter_currentkey(iter, &tkey, &tksize); + ATF_REQUIRE_EQ(tksize, 16); + i = (uintptr_t)v; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, "key of a raw hashtable!!", sizeof(key)); + ATF_REQUIRE_EQ(memcmp(key, tkey, 16), 0); + walked++; + } + ATF_REQUIRE_EQ(walked, count); + ATF_REQUIRE_EQ(result, ISC_R_NOMORE); + + /* erase odd */ + walked = 0; + result = isc_ht_iter_first(iter); + while (result == ISC_R_SUCCESS) { + isc_ht_iter_current(iter, &v); + isc_ht_iter_currentkey(iter, &tkey, &tksize); + ATF_REQUIRE_EQ(tksize, 16); + i = (uintptr_t)v; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, "key of a raw hashtable!!", sizeof(key)); + ATF_REQUIRE_EQ(memcmp(key, tkey, 16), 0); + if ((uintptr_t)v % 2 == 0) { + result = isc_ht_iter_delcurrent_next(iter); + } else { + result = isc_ht_iter_next(iter); + } + walked++; + } + ATF_REQUIRE_EQ(result, ISC_R_NOMORE); + ATF_REQUIRE_EQ(walked, count); + + /* erase even */ + walked = 0; + result = isc_ht_iter_first(iter); + while (result == ISC_R_SUCCESS) { + isc_ht_iter_current(iter, &v); + isc_ht_iter_currentkey(iter, &tkey, &tksize); + ATF_REQUIRE_EQ(tksize, 16); + i = (uintptr_t)v; + snprintf((char *)key, sizeof(key), "%u", (unsigned int)i); + strlcat((char *)key, "key of a raw hashtable!!", sizeof(key)); + ATF_REQUIRE_EQ(memcmp(key, tkey, 16), 0); + if ((uintptr_t)v % 2 == 1) { + result = isc_ht_iter_delcurrent_next(iter); + } else { + result = isc_ht_iter_next(iter); + } + walked++; + } + ATF_REQUIRE_EQ(result, ISC_R_NOMORE); + ATF_REQUIRE_EQ(walked, count/2); + + walked = 0; + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS; + result = isc_ht_iter_next(iter)) + { + walked++; + } + + ATF_REQUIRE_EQ(result, ISC_R_NOMORE); + ATF_REQUIRE_EQ(walked, 0); + + isc_ht_destroy(&ht); + ATF_REQUIRE_EQ(ht, NULL); +} + +ATF_TC(isc_ht_20); +ATF_TC_HEAD(isc_ht_20, tc) { + atf_tc_set_md_var(tc, "descr", "20 bit, 200K elements test"); +} + +ATF_TC_BODY(isc_ht_20, tc) { + UNUSED(tc); + test_ht_full(20, 200000); +} + + +ATF_TC(isc_ht_8); +ATF_TC_HEAD(isc_ht_8, tc) { + atf_tc_set_md_var(tc, "descr", "8 bit, 20000 elements crowded test"); +} + +ATF_TC_BODY(isc_ht_8, tc) { + UNUSED(tc); + test_ht_full(8, 20000); +} + +ATF_TC(isc_ht_1); +ATF_TC_HEAD(isc_ht_1, tc) { + atf_tc_set_md_var(tc, "descr", "1 bit, 100 elements corner case test"); +} + +ATF_TC_BODY(isc_ht_1, tc) { + UNUSED(tc); + test_ht_full(1, 100); +} + +/* xxxwpk we should limit the size of hashtable, 32bit doesn't make sense */ +#if 0 +ATF_TC(isc_ht_32); +ATF_TC_HEAD(isc_ht_32, tc) { + atf_tc_set_md_var(tc, "descr", "32 bit, 10000 elements corner case test"); +} + +ATF_TC_BODY(isc_ht_32, tc) { + UNUSED(tc); + test_ht_full(32, 10000); +} +#endif + +ATF_TC(isc_ht_iterator); +ATF_TC_HEAD(isc_ht_iterator, tc) { + atf_tc_set_md_var(tc, "descr", "hashtable iterator"); +} + +ATF_TC_BODY(isc_ht_iterator, tc) { + UNUSED(tc); + test_ht_iterator(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_ht_20); + ATF_TP_ADD_TC(tp, isc_ht_8); + ATF_TP_ADD_TC(tp, isc_ht_1); +/* ATF_TP_ADD_TC(tp, isc_ht_32); */ + ATF_TP_ADD_TC(tp, isc_ht_iterator); + return (atf_no_error()); +} diff --git a/lib/isc/tests/inet_ntop_test.c b/lib/isc/tests/inet_ntop_test.c new file mode 100644 index 0000000..2bfe9de --- /dev/null +++ b/lib/isc/tests/inet_ntop_test.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <atf-c.h> + +/* + * Force the prototype for isc_net_ntop to be declared. + */ +#include <isc/platform.h> +#undef ISC_PLATFORM_NEEDNTOP +#define ISC_PLATFORM_NEEDNTOP +#include "../inet_ntop.c" + +ATF_TC(isc_net_ntop); +ATF_TC_HEAD(isc_net_ntop, tc) { + atf_tc_set_md_var(tc, "descr", "isc_net_ntop implementation"); +} +ATF_TC_BODY(isc_net_ntop, tc) { + char buf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + int r; + size_t i; + unsigned char abuf[16]; + struct { + int family; + const char * address; + } testdata[] = { + { AF_INET, "0.0.0.0" }, + { AF_INET, "0.1.0.0" }, + { AF_INET, "0.0.2.0" }, + { AF_INET, "0.0.0.3" }, + { AF_INET, "255.255.255.255" }, + { AF_INET6, "::" }, + { AF_INET6, "::1.2.3.4" }, + { AF_INET6, "::ffff:1.2.3.4" }, + { AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" } + }; + + for (i = 0; i < sizeof(testdata)/sizeof(testdata[0]); i++) { + r = inet_pton(testdata[i].family, testdata[i].address, abuf); + ATF_REQUIRE_EQ_MSG(r, 1, "%s", testdata[i].address); + isc_net_ntop(testdata[i].family, abuf, buf, sizeof(buf)); + ATF_CHECK_STREQ(buf, testdata[i].address); + } +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_net_ntop); + return (atf_no_error()); +} diff --git a/lib/isc/tests/isctest.c b/lib/isc/tests/isctest.c new file mode 100644 index 0000000..cf4ae97 --- /dev/null +++ b/lib/isc/tests/isctest.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <time.h> + +#include <isc/app.h> +#include <isc/buffer.h> +#include <isc/entropy.h> +#include <isc/hash.h> +#include <isc/mem.h> +#include <isc/os.h> +#include <isc/socket.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/timer.h> +#include <isc/util.h> + +#include "isctest.h" + +isc_mem_t *mctx = NULL; +isc_entropy_t *ectx = NULL; +isc_log_t *lctx = NULL; +isc_taskmgr_t *taskmgr = NULL; +isc_timermgr_t *timermgr = NULL; +isc_socketmgr_t *socketmgr = NULL; +isc_task_t *maintask = NULL; +int ncpus; + +static bool hash_active = false; + +/* + * Logging categories: this needs to match the list in bin/named/log.c. + */ +static isc_logcategory_t categories[] = { + { "", 0 }, + { "client", 0 }, + { "network", 0 }, + { "update", 0 }, + { "queries", 0 }, + { "unmatched", 0 }, + { "update-security", 0 }, + { "query-errors", 0 }, + { NULL, 0 } +}; + +static void +cleanup_managers(void) { + if (maintask != NULL) + isc_task_destroy(&maintask); + if (socketmgr != NULL) + isc_socketmgr_destroy(&socketmgr); + if (taskmgr != NULL) + isc_taskmgr_destroy(&taskmgr); + if (timermgr != NULL) + isc_timermgr_destroy(&timermgr); +} + +static isc_result_t +create_managers(unsigned int workers) { + isc_result_t result; + char *p; + + if (workers == 0) { +#ifdef ISC_PLATFORM_USETHREADS + workers = isc_os_ncpus(); +#else + workers = 1; +#endif + } + + p = getenv("ISC_TASK_WORKERS"); + if (p != NULL) { + workers = atoi(p); + } + + CHECK(isc_taskmgr_create(mctx, workers, 0, &taskmgr)); + CHECK(isc_task_create(taskmgr, 0, &maintask)); + isc_taskmgr_setexcltask(taskmgr, maintask); + + CHECK(isc_timermgr_create(mctx, &timermgr)); + CHECK(isc_socketmgr_create(mctx, &socketmgr)); + return (ISC_R_SUCCESS); + + cleanup: + cleanup_managers(); + return (result); +} + +isc_result_t +isc_test_begin(FILE *logfile, bool start_managers, + unsigned int workers) +{ + isc_result_t result; + + isc_mem_debugging |= ISC_MEM_DEBUGRECORD; + CHECK(isc_mem_create(0, 0, &mctx)); + CHECK(isc_entropy_create(mctx, &ectx)); + + CHECK(isc_hash_create(mctx, ectx, 255)); + hash_active = true; + + if (logfile != NULL) { + isc_logdestination_t destination; + isc_logconfig_t *logconfig = NULL; + + CHECK(isc_log_create(mctx, &lctx, &logconfig)); + isc_log_registercategories(lctx, categories); + isc_log_setcontext(lctx); + + destination.file.stream = logfile; + destination.file.name = NULL; + destination.file.versions = ISC_LOG_ROLLNEVER; + destination.file.maximum_size = 0; + CHECK(isc_log_createchannel(logconfig, "stderr", + ISC_LOG_TOFILEDESC, + ISC_LOG_DYNAMIC, + &destination, 0)); + CHECK(isc_log_usechannel(logconfig, "stderr", NULL, NULL)); + } + +#ifdef ISC_PLATFORM_USETHREADS + ncpus = isc_os_ncpus(); +#else + ncpus = 1; +#endif + + if (start_managers) { + CHECK(create_managers(workers)); + } + + return (ISC_R_SUCCESS); + + cleanup: + isc_test_end(); + return (result); +} + +void +isc_test_end(void) { + if (maintask != NULL) + isc_task_detach(&maintask); + if (taskmgr != NULL) + isc_taskmgr_destroy(&taskmgr); + if (hash_active) { + isc_hash_destroy(); + hash_active = false; + } + if (ectx != NULL) + isc_entropy_detach(&ectx); + + cleanup_managers(); + + if (lctx != NULL) + isc_log_destroy(&lctx); + if (mctx != NULL) + isc_mem_destroy(&mctx); +} + +/* + * Sleep for 'usec' microseconds. + */ +void +isc_test_nap(uint32_t usec) { +#ifdef HAVE_NANOSLEEP + struct timespec ts; + + ts.tv_sec = usec / 1000000; + ts.tv_nsec = (usec % 1000000) * 1000; + nanosleep(&ts, NULL); +#elif HAVE_USLEEP + usleep(usec); +#else + /* + * No fractional-second sleep function is available, so we + * round up to the nearest second and sleep instead + */ + sleep((usec / 1000000) + 1); +#endif +} diff --git a/lib/isc/tests/isctest.h b/lib/isc/tests/isctest.h new file mode 100644 index 0000000..e553b8a --- /dev/null +++ b/lib/isc/tests/isctest.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/buffer.h> +#include <isc/entropy.h> +#include <isc/hash.h> +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/result.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/timer.h> +#include <isc/util.h> + +#define CHECK(r) \ + do { \ + result = (r); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + +extern isc_mem_t *mctx; +extern isc_entropy_t *ectx; +extern isc_log_t *lctx; +extern isc_taskmgr_t *taskmgr; +extern isc_timermgr_t *timermgr; +extern isc_socketmgr_t *socketmgr; +extern int ncpus; + +isc_result_t +isc_test_begin(FILE *logfile, bool start_managers, + unsigned int workers); +/*%< + * Begin test, logging to 'logfile' or default if not specified. + * + * If 'start_managers' is set, start a task manager, timer manager, + * and socket manager. + * + * If 'workers' is zero, use the number of CPUs on the system as a default; + * otherwise, set up the task manager with the specified number of worker + * threads. The environment variable ISC_TASK_WORKERS overrides this value. + */ + +void +isc_test_end(void); + +void +isc_test_nap(uint32_t usec); diff --git a/lib/isc/tests/lex_test.c b/lib/isc/tests/lex_test.c new file mode 100644 index 0000000..1315022 --- /dev/null +++ b/lib/isc/tests/lex_test.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <atf-c.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <isc/buffer.h> +#include <isc/lex.h> +#include <isc/mem.h> +#include <isc/util.h> + +ATF_TC(lex_0xff); +ATF_TC_HEAD(lex_0xff, tc) { + atf_tc_set_md_var(tc, "descr", "check handling of 0xff"); +} +ATF_TC_BODY(lex_0xff, tc) { + isc_mem_t *mctx = NULL; + isc_result_t result; + isc_lex_t *lex = NULL; + isc_buffer_t death_buf; + isc_token_t token; + + unsigned char death[] = { EOF, 'A' }; + + UNUSED(tc); + + result = isc_mem_create(0, 0, &mctx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_lex_create(mctx, 1024, &lex); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_buffer_init(&death_buf, &death[0], sizeof(death)); + isc_buffer_add(&death_buf, sizeof(death)); + + result = isc_lex_openbuffer(lex, &death_buf); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_lex_gettoken(lex, 0, &token); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); +} + +ATF_TC(lex_setline); +ATF_TC_HEAD(lex_setline, tc) { + atf_tc_set_md_var(tc, "descr", "check setting of source line"); +} +ATF_TC_BODY(lex_setline, tc) { + isc_mem_t *mctx = NULL; + isc_result_t result; + isc_lex_t *lex = NULL; + unsigned char text[] = "text\nto\nbe\nprocessed\nby\nlexer"; + isc_buffer_t buf; + isc_token_t token; + unsigned long line; + int i; + + UNUSED(tc); + + result = isc_mem_create(0, 0, &mctx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_lex_create(mctx, 1024, &lex); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_buffer_init(&buf, &text[0], sizeof(text)); + isc_buffer_add(&buf, sizeof(text)); + + result = isc_lex_openbuffer(lex, &buf); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_lex_setsourceline(lex, 100); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + for (i = 0; i < 6; i++) { + result = isc_lex_gettoken(lex, 0, &token); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + line = isc_lex_getsourceline(lex); + ATF_REQUIRE_EQ(line, 100U + i); + } + + result = isc_lex_gettoken(lex, 0, &token); + ATF_REQUIRE_EQ(result, ISC_R_EOF); + + line = isc_lex_getsourceline(lex); + ATF_REQUIRE_EQ(line, 105U); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, lex_0xff); + ATF_TP_ADD_TC(tp, lex_setline); + return (atf_no_error()); +} + diff --git a/lib/isc/tests/mem_test.c b/lib/isc/tests/mem_test.c new file mode 100644 index 0000000..22de74f --- /dev/null +++ b/lib/isc/tests/mem_test.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#include <atf-c.h> + +#include "isctest.h" + +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/result.h> + +static void * +default_memalloc(void *arg, size_t size) { + UNUSED(arg); + if (size == 0U) + size = 1; + return (malloc(size)); +} + +static void +default_memfree(void *arg, void *ptr) { + UNUSED(arg); + free(ptr); +} + +ATF_TC(isc_mem); +ATF_TC_HEAD(isc_mem, tc) { + atf_tc_set_md_var(tc, "descr", "general memory system tests"); +} + +#define MP1_FREEMAX 10 +#define MP1_FILLCNT 10 +#define MP1_MAXALLOC 30 + +#define MP2_FREEMAX 25 +#define MP2_FILLCNT 25 + +ATF_TC_BODY(isc_mem, tc) { + isc_result_t result; + void *items1[50]; + void *items2[50]; + void *tmp; + isc_mem_t *localmctx = NULL; + isc_mempool_t *mp1 = NULL, *mp2 = NULL; + unsigned int i, j; + int rval; + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_mem_create(0, 0, &localmctx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_mempool_create(localmctx, 24, &mp1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_mempool_create(localmctx, 31, &mp2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_mempool_setfreemax(mp1, MP1_FREEMAX); + isc_mempool_setfillcount(mp1, MP1_FILLCNT); + isc_mempool_setmaxalloc(mp1, MP1_MAXALLOC); + + /* + * Allocate MP1_MAXALLOC items from the pool. This is our max. + */ + for (i = 0; i < MP1_MAXALLOC; i++) { + items1[i] = isc_mempool_get(mp1); + ATF_CHECK(items1[i] != NULL); + } + + /* + * Try to allocate one more. This should fail. + */ + tmp = isc_mempool_get(mp1); + ATF_CHECK_EQ(tmp, NULL); + + /* + * Free the first 11 items. Verify that there are 10 free items on + * the free list (which is our max). + */ + for (i = 0; i < 11; i++) { + isc_mempool_put(mp1, items1[i]); + items1[i] = NULL; + } + + rval = isc_mempool_getfreecount(mp1); + ATF_CHECK_EQ(rval, 10); + + rval = isc_mempool_getallocated(mp1); + ATF_CHECK_EQ(rval, 19); + + /* + * Now, beat up on mp2 for a while. Allocate 50 items, then free + * them, then allocate 50 more, etc. + */ + + isc_mempool_setfreemax(mp2, 25); + isc_mempool_setfillcount(mp2, 25); + + for (j = 0; j < 500000; j++) { + for (i = 0; i < 50; i++) { + items2[i] = isc_mempool_get(mp2); + ATF_CHECK(items2[i] != NULL); + } + for (i = 0; i < 50; i++) { + isc_mempool_put(mp2, items2[i]); + items2[i] = NULL; + } + } + + /* + * Free all the other items and blow away this pool. + */ + for (i = 11; i < MP1_MAXALLOC; i++) { + isc_mempool_put(mp1, items1[i]); + items1[i] = NULL; + } + + isc_mempool_destroy(&mp1); + isc_mempool_destroy(&mp2); + + isc_mem_destroy(&localmctx); + + result = isc_mem_createx2(0, 0, default_memalloc, default_memfree, + NULL, &localmctx, ISC_MEMFLAG_INTERNAL); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_mempool_create(localmctx, 2, &mp1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + tmp = isc_mempool_get(mp1); + ATF_CHECK(tmp != NULL); + + isc_mempool_put(mp1, tmp); + + isc_mempool_destroy(&mp1); + + isc_test_end(); +} + +ATF_TC(isc_mem_total); +ATF_TC_HEAD(isc_mem_total, tc) { + atf_tc_set_md_var(tc, "descr", "test TotalUse calculation"); +} + +ATF_TC_BODY(isc_mem_total, tc) { + isc_result_t result; + isc_mem_t *mctx2 = NULL; + size_t before, after; + ssize_t diff; + int i; + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* Local alloc, free */ + mctx2 = NULL; + result = isc_mem_createx2(0, 0, default_memalloc, default_memfree, + NULL, &mctx2, 0); + if (result != ISC_R_SUCCESS) + goto out; + + before = isc_mem_total(mctx2); + + for (i = 0; i < 100000; i++) { + void *ptr; + + ptr = isc_mem_allocate(mctx2, 2048); + isc_mem_free(mctx2, ptr); + } + + after = isc_mem_total(mctx2); + diff = after - before; + + printf("total_before=%lu, total_after=%lu, total_diff=%lu\n", + (unsigned long)before, (unsigned long)after, + (unsigned long)diff); + /* 2048 +8 bytes extra for size_info */ + ATF_CHECK_EQ(diff, (2048 + 8) * 100000); + + /* ISC_MEMFLAG_INTERNAL */ + + before = isc_mem_total(mctx); + + for (i = 0; i < 100000; i++) { + void *ptr; + + ptr = isc_mem_allocate(mctx, 2048); + isc_mem_free(mctx, ptr); + } + + after = isc_mem_total(mctx); + diff = after - before; + + printf("total_before=%lu, total_after=%lu, total_diff=%lu\n", + (unsigned long)before, (unsigned long)after, + (unsigned long)diff); + /* 2048 +8 bytes extra for size_info */ + ATF_CHECK_EQ(diff, (2048 + 8) * 100000); + + out: + if (mctx2 != NULL) + isc_mem_destroy(&mctx2); + + isc_test_end(); +} + +ATF_TC(isc_mem_inuse); +ATF_TC_HEAD(isc_mem_inuse, tc) { + atf_tc_set_md_var(tc, "descr", "test InUse calculation"); +} + +ATF_TC_BODY(isc_mem_inuse, tc) { + isc_result_t result; + isc_mem_t *mctx2 = NULL; + size_t before, during, after; + ssize_t diff; + void *ptr; + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + mctx2 = NULL; + result = isc_mem_createx2(0, 0, default_memalloc, default_memfree, + NULL, &mctx2, 0); + if (result != ISC_R_SUCCESS) + goto out; + + before = isc_mem_inuse(mctx2); + ptr = isc_mem_allocate(mctx2, 1024000); + during = isc_mem_inuse(mctx2); + isc_mem_free(mctx2, ptr); + after = isc_mem_inuse(mctx2); + + diff = after - before; + + printf("inuse_before=%lu, inuse_during=%lu, inuse_after=%lu\n", + (unsigned long)before, (unsigned long)during, + (unsigned long)after); + ATF_REQUIRE_EQ(diff, 0); + + out: + if (mctx2 != NULL) + isc_mem_destroy(&mctx2); + + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_mem); + ATF_TP_ADD_TC(tp, isc_mem_total); + ATF_TP_ADD_TC(tp, isc_mem_inuse); + + return (atf_no_error()); +} diff --git a/lib/isc/tests/netaddr_test.c b/lib/isc/tests/netaddr_test.c new file mode 100644 index 0000000..aa41b96 --- /dev/null +++ b/lib/isc/tests/netaddr_test.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* ! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <isc/netaddr.h> +#include <isc/sockaddr.h> +#include <isc/util.h> + +ATF_TC(netaddr_isnetzero); +ATF_TC_HEAD(netaddr_isnetzero, tc) { + atf_tc_set_md_var(tc, "descr", "test netaddr_isnetzero"); +} +ATF_TC_BODY(netaddr_isnetzero, tc) { + unsigned int i; + struct in_addr ina; + struct { + const char *address; + bool expect; + } tests[] = { + { "0.0.0.0", true }, + { "0.0.0.1", true }, + { "0.0.1.2", true }, + { "0.1.2.3", true }, + { "10.0.0.0", false }, + { "10.9.0.0", false }, + { "10.9.8.0", false }, + { "10.9.8.7", false }, + { "127.0.0.0", false }, + { "127.0.0.1", false } + }; + bool result; + isc_netaddr_t netaddr; + + for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) { + ina.s_addr = inet_addr(tests[i].address); + isc_netaddr_fromin(&netaddr, &ina); + result = isc_netaddr_isnetzero(&netaddr); + ATF_CHECK_EQ_MSG(result, tests[i].expect, + "%s", tests[i].address); + } +} + +ATF_TC(netaddr_masktoprefixlen); +ATF_TC_HEAD(netaddr_masktoprefixlen, tc) { + atf_tc_set_md_var(tc, "descr", + "isc_netaddr_masktoprefixlen() " + "calculates correct prefix lengths "); +} +ATF_TC_BODY(netaddr_masktoprefixlen, tc) { + struct in_addr na_a; + struct in_addr na_b; + struct in_addr na_c; + struct in_addr na_d; + isc_netaddr_t ina_a; + isc_netaddr_t ina_b; + isc_netaddr_t ina_c; + isc_netaddr_t ina_d; + unsigned int plen; + + UNUSED(tc); + + ATF_CHECK(inet_pton(AF_INET, "0.0.0.0", &na_a) >= 0); + ATF_CHECK(inet_pton(AF_INET, "255.255.255.254", &na_b) >= 0); + ATF_CHECK(inet_pton(AF_INET, "255.255.255.255", &na_c) >= 0); + ATF_CHECK(inet_pton(AF_INET, "255.255.255.0", &na_d) >= 0); + + isc_netaddr_fromin(&ina_a, &na_a); + isc_netaddr_fromin(&ina_b, &na_b); + isc_netaddr_fromin(&ina_c, &na_c); + isc_netaddr_fromin(&ina_d, &na_d); + + ATF_CHECK_EQ(isc_netaddr_masktoprefixlen(&ina_a, &plen), + ISC_R_SUCCESS); + ATF_CHECK_EQ(plen, 0); + + ATF_CHECK_EQ(isc_netaddr_masktoprefixlen(&ina_b, &plen), + ISC_R_SUCCESS); + ATF_CHECK_EQ(plen, 31); + + ATF_CHECK_EQ(isc_netaddr_masktoprefixlen(&ina_c, &plen), + ISC_R_SUCCESS); + ATF_CHECK_EQ(plen, 32); + + ATF_CHECK_EQ(isc_netaddr_masktoprefixlen(&ina_d, &plen), + ISC_R_SUCCESS); + ATF_CHECK_EQ(plen, 24); +} + +ATF_TC(netaddr_multicast); +ATF_TC_HEAD(netaddr_multicast, tc) { + atf_tc_set_md_var(tc, "descr", + "check multicast addresses are detected properly"); +} +ATF_TC_BODY(netaddr_multicast, tc) { + unsigned int i; + struct { + int family; + const char *addr; + bool is_multicast; + } tests[] = { + { AF_INET, "1.2.3.4", false }, + { AF_INET, "4.3.2.1", false }, + { AF_INET, "224.1.1.1", true }, + { AF_INET, "1.1.1.244", false }, + { AF_INET6, "::1", false }, + { AF_INET6, "ff02::1", true } + }; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + isc_netaddr_t na; + struct in_addr in; + struct in6_addr in6; + int r; + + if (tests[i].family == AF_INET) { + r = inet_pton(AF_INET, tests[i].addr, + (unsigned char *)&in); + ATF_REQUIRE_EQ(r, 1); + isc_netaddr_fromin(&na, &in); + } else { + r = inet_pton(AF_INET6, tests[i].addr, + (unsigned char *)&in6); + ATF_REQUIRE_EQ(r, 1); + isc_netaddr_fromin6(&na, &in6); + } + + ATF_CHECK_EQ(isc_netaddr_ismulticast(&na), + tests[i].is_multicast); + } +} +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, netaddr_isnetzero); + ATF_TP_ADD_TC(tp, netaddr_masktoprefixlen); + ATF_TP_ADD_TC(tp, netaddr_multicast); + + return (atf_no_error()); +} diff --git a/lib/isc/tests/parse_test.c b/lib/isc/tests/parse_test.c new file mode 100644 index 0000000..edc8139 --- /dev/null +++ b/lib/isc/tests/parse_test.c @@ -0,0 +1,64 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <inttypes.h> +#include <unistd.h> +#include <time.h> + +#include <isc/parseint.h> + +#include "isctest.h" + +/* + * Individual unit tests + */ + +/* Test for 32 bit overflow on 64 bit machines in isc_parse_uint32 */ +ATF_TC(parse_overflow); +ATF_TC_HEAD(parse_overflow, tc) { + atf_tc_set_md_var(tc, "descr", "Check for 32 bit overflow"); +} +ATF_TC_BODY(parse_overflow, tc) { + isc_result_t result; + uint32_t output; + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_parse_uint32(&output, "1234567890", 10); + ATF_CHECK_EQ(ISC_R_SUCCESS, result); + ATF_CHECK_EQ(1234567890, output); + + result = isc_parse_uint32(&output, "123456789012345", 10); + ATF_CHECK_EQ(ISC_R_RANGE, result); + + result = isc_parse_uint32(&output, "12345678901234567890", 10); + ATF_CHECK_EQ(ISC_R_RANGE, result); + + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, parse_overflow); + + return (atf_no_error()); +} + diff --git a/lib/isc/tests/pool_test.c b/lib/isc/tests/pool_test.c new file mode 100644 index 0000000..fb9197e --- /dev/null +++ b/lib/isc/tests/pool_test.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <unistd.h> + +#include <isc/mem.h> +#include <isc/pool.h> + +#include "isctest.h" + +static isc_result_t +poolinit(void **target, void *arg) { + isc_result_t result; + + isc_taskmgr_t *mgr = (isc_taskmgr_t *) arg; + isc_task_t *task = NULL; + result = isc_task_create(mgr, 0, &task); + if (result != ISC_R_SUCCESS) + return (result); + + *target = (void *) task; + return (ISC_R_SUCCESS); +} + +static void +poolfree(void **target) { + isc_task_t *task = *(isc_task_t **) target; + isc_task_destroy(&task); + *target = NULL; +} + +/* + * Individual unit tests + */ + +/* Create a pool */ +ATF_TC(create_pool); +ATF_TC_HEAD(create_pool, tc) { + atf_tc_set_md_var(tc, "descr", "create a pool"); +} +ATF_TC_BODY(create_pool, tc) { + isc_result_t result; + isc_pool_t *pool = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_pool_create(mctx, 8, poolfree, poolinit, taskmgr, &pool); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_pool_count(pool), 8); + + isc_pool_destroy(&pool); + ATF_REQUIRE_EQ(pool, NULL); + + isc_test_end(); +} + +/* Resize a pool */ +ATF_TC(expand_pool); +ATF_TC_HEAD(expand_pool, tc) { + atf_tc_set_md_var(tc, "descr", "expand a pool"); +} +ATF_TC_BODY(expand_pool, tc) { + isc_result_t result; + isc_pool_t *pool1 = NULL, *pool2 = NULL, *hold = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_pool_create(mctx, 10, poolfree, poolinit, taskmgr, &pool1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_pool_count(pool1), 10); + + /* resizing to a smaller size should have no effect */ + hold = pool1; + result = isc_pool_expand(&pool1, 5, &pool2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_pool_count(pool2), 10); + ATF_REQUIRE_EQ(pool2, hold); + ATF_REQUIRE_EQ(pool1, NULL); + pool1 = pool2; + pool2 = NULL; + + /* resizing to the same size should have no effect */ + hold = pool1; + result = isc_pool_expand(&pool1, 10, &pool2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_pool_count(pool2), 10); + ATF_REQUIRE_EQ(pool2, hold); + ATF_REQUIRE_EQ(pool1, NULL); + pool1 = pool2; + pool2 = NULL; + + /* resizing to larger size should make a new pool */ + hold = pool1; + result = isc_pool_expand(&pool1, 20, &pool2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_pool_count(pool2), 20); + ATF_REQUIRE(pool2 != hold); + ATF_REQUIRE_EQ(pool1, NULL); + + isc_pool_destroy(&pool2); + ATF_REQUIRE_EQ(pool2, NULL); + + isc_test_end(); +} + +/* Get objects */ +ATF_TC(get_objects); +ATF_TC_HEAD(get_objects, tc) { + atf_tc_set_md_var(tc, "descr", "get objects"); +} +ATF_TC_BODY(get_objects, tc) { + isc_result_t result; + isc_pool_t *pool = NULL; + void *item; + isc_task_t *task1 = NULL, *task2 = NULL, *task3 = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_pool_create(mctx, 2, poolfree, poolinit, taskmgr, &pool); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_pool_count(pool), 2); + + item = isc_pool_get(pool); + ATF_REQUIRE(item != NULL); + isc_task_attach((isc_task_t *) item, &task1); + + item = isc_pool_get(pool); + ATF_REQUIRE(item != NULL); + isc_task_attach((isc_task_t *) item, &task2); + + item = isc_pool_get(pool); + ATF_REQUIRE(item != NULL); + isc_task_attach((isc_task_t *) item, &task3); + + isc_task_detach(&task1); + isc_task_detach(&task2); + isc_task_detach(&task3); + + isc_pool_destroy(&pool); + ATF_REQUIRE_EQ(pool, NULL); + + isc_test_end(); +} + + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, create_pool); + ATF_TP_ADD_TC(tp, expand_pool); + ATF_TP_ADD_TC(tp, get_objects); + + return (atf_no_error()); +} + diff --git a/lib/isc/tests/print_test.c b/lib/isc/tests/print_test.c new file mode 100644 index 0000000..259e301 --- /dev/null +++ b/lib/isc/tests/print_test.c @@ -0,0 +1,184 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <atf-c.h> + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* + * Workout if we need to force the inclusion of print.c so we can test + * it on all platforms even if we don't include it in libisc. + */ +#include <isc/platform.h> +#if !defined(ISC_PLATFORM_NEEDPRINTF) && \ + !defined(ISC_PLATFORM_NEEDFPRINTF) && \ + !defined(ISC_PLATFORM_NEEDSPRINTF) && \ + !defined(ISC_PLATFORM_NEEDVSNPRINTF) +#define ISC__PRINT_SOURCE +#include "../print.c" +#else +#if !defined(ISC_PLATFORM_NEEDPRINTF) || \ + !defined(ISC_PLATFORM_NEEDFPRINTF) || \ + !defined(ISC_PLATFORM_NEEDSPRINTF) || \ + !defined(ISC_PLATFORM_NEEDVSNPRINTF) +#define ISC__PRINT_SOURCE +#endif +#include <isc/print.h> +#include <isc/types.h> +#include <isc/util.h> +#endif + +ATF_TC(snprintf); +ATF_TC_HEAD(snprintf, tc) { + atf_tc_set_md_var(tc, "descr", "snprintf implementation"); +} +ATF_TC_BODY(snprintf, tc) { + char buf[10000]; + uint64_t ll = 8589934592ULL; + uint64_t nn = 20000000000000ULL; + uint64_t zz = 10000000000000000000ULL; + float pi = 3.141; + int n; + size_t size; + + UNUSED(tc); + + /* + * 4294967296 <= 8589934592 < 1000000000^2 to verify fix for + * RT#36505. + */ + + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%" PRIu64, ll); + ATF_CHECK_EQ(n, 10); + ATF_CHECK_STREQ(buf, "8589934592"); + + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%" PRIu64, ll); + ATF_CHECK_EQ(n, 10); + ATF_CHECK_STREQ(buf, "8589934592"); + + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%" PRIu64, nn); + ATF_CHECK_EQ(n, 14); + ATF_CHECK_STREQ(buf, "20000000000000"); + + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%" PRIu64, nn); + ATF_CHECK_EQ(n, 14); + ATF_CHECK_STREQ(buf, "20000000000000"); + + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%" PRIu64, zz); + ATF_CHECK_EQ(n, 20); + ATF_CHECK_STREQ(buf, "10000000000000000000"); + + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%" PRIu64, zz); + ATF_CHECK_EQ(n, 20); + ATF_CHECK_STREQ(buf, "10000000000000000000"); + + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%" PRId64, nn); + ATF_CHECK_EQ(n, 14); + ATF_CHECK_STREQ(buf, "20000000000000"); + + size = 1000; + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%zu", size); + ATF_CHECK_EQ(n, 4); + ATF_CHECK_STREQ(buf, "1000"); + + size = 1000; + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%zx", size); + ATF_CHECK_EQ(n, 3); + ATF_CHECK_STREQ(buf, "3e8"); + + size = 1000; + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "%zo", size); + ATF_CHECK_EQ(n, 4); + ATF_CHECK_STREQ(buf, "1750"); + + zz = 0xf5f5f5f5f5f5f5f5ULL; + memset(buf, 0xff, sizeof(buf)); + n = isc_print_snprintf(buf, sizeof(buf), "0x%" PRIx64, zz); + ATF_CHECK_EQ(n, 18); + ATF_CHECK_STREQ(buf, "0xf5f5f5f5f5f5f5f5"); + + n = isc_print_snprintf(buf, sizeof(buf), "%.2f", pi); + ATF_CHECK_EQ(n, 4); + ATF_CHECK_STREQ(buf, "3.14"); + + /* Similar to the above, but additional characters follows */ + n = isc_print_snprintf(buf, sizeof(buf), "%.2f1592", pi); + ATF_CHECK_EQ(n, 8); + ATF_CHECK_STREQ(buf, "3.141592"); + + /* Similar to the above, but with leading spaces */ + n = isc_print_snprintf(buf, sizeof(buf), "% 8.2f1592", pi); + ATF_CHECK_EQ(n, 12); + ATF_CHECK_STREQ(buf, " 3.141592"); + + /* Similar to the above, but with trail spaces after the 4 */ + n = isc_print_snprintf(buf, sizeof(buf), "%-8.2f1592", pi); + ATF_CHECK_EQ(n, 12); + ATF_CHECK_STREQ(buf, "3.14 1592"); +} + +ATF_TC(fprintf); +ATF_TC_HEAD(fprintf, tc) { + atf_tc_set_md_var(tc, "descr", "fprintf implementation"); +} +ATF_TC_BODY(fprintf, tc) { + FILE *f; + int n; + size_t size; + char buf[10000]; + + UNUSED(tc); + + f = fopen("fprintf.test", "w+"); + ATF_REQUIRE(f != NULL); + + size = 1000; + n = isc_print_fprintf(f, "%zu", size); + ATF_CHECK_EQ(n, 4); + + rewind(f); + + memset(buf, 0, sizeof(buf)); + n = fread(buf, 1, sizeof(buf), f); + ATF_CHECK_EQ(n, 4); + + fclose(f); + + ATF_CHECK_STREQ(buf, "1000"); + + if ((n > 0) && (!strcmp(buf, "1000"))) + unlink("fprintf.test"); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, snprintf); + ATF_TP_ADD_TC(tp, fprintf); + return (atf_no_error()); +} diff --git a/lib/isc/tests/queue_test.c b/lib/isc/tests/queue_test.c new file mode 100644 index 0000000..843e641 --- /dev/null +++ b/lib/isc/tests/queue_test.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <unistd.h> +#include <time.h> + +#include <isc/queue.h> + +#include "isctest.h" + +typedef struct item item_t; +struct item { + int value; + ISC_QLINK(item_t) qlink; +}; + +typedef ISC_QUEUE(item_t) item_queue_t; + +static void +item_init(item_t *item, int value) { + item->value = value; + ISC_QLINK_INIT(item, qlink); +} + +/* + * Individual unit tests + */ + +/* Test UDP sendto/recv (IPv4) */ +ATF_TC(queue_valid); +ATF_TC_HEAD(queue_valid, tc) { + atf_tc_set_md_var(tc, "descr", "Check queue validity"); +} +ATF_TC_BODY(queue_valid, tc) { + isc_result_t result; + item_queue_t queue; + item_t one, two, three, four, five; + item_t *p; + + UNUSED(tc); + + ISC_QUEUE_INIT(queue, qlink); + + item_init(&one, 1); + item_init(&two, 2); + item_init(&three, 3); + item_init(&four, 4); + item_init(&five, 5); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(ISC_QUEUE_EMPTY(queue)); + + ISC_QUEUE_POP(queue, qlink, p); + ATF_CHECK(p == NULL); + + ATF_CHECK(! ISC_QLINK_LINKED(&one, qlink)); + ISC_QUEUE_PUSH(queue, &one, qlink); + ATF_CHECK(ISC_QLINK_LINKED(&one, qlink)); + + ATF_CHECK(! ISC_QUEUE_EMPTY(queue)); + + ISC_QUEUE_POP(queue, qlink, p); + ATF_REQUIRE(p != NULL); + ATF_CHECK_EQ(p->value, 1); + ATF_CHECK(ISC_QUEUE_EMPTY(queue)); + ATF_CHECK(! ISC_QLINK_LINKED(p, qlink)); + + ISC_QUEUE_PUSH(queue, p, qlink); + ATF_CHECK(! ISC_QUEUE_EMPTY(queue)); + ATF_CHECK(ISC_QLINK_LINKED(p, qlink)); + + ATF_CHECK(! ISC_QLINK_LINKED(&two, qlink)); + ISC_QUEUE_PUSH(queue, &two, qlink); + ATF_CHECK(ISC_QLINK_LINKED(&two, qlink)); + + ATF_CHECK(! ISC_QLINK_LINKED(&three, qlink)); + ISC_QUEUE_PUSH(queue, &three, qlink); + ATF_CHECK(ISC_QLINK_LINKED(&three, qlink)); + + ATF_CHECK(! ISC_QLINK_LINKED(&four, qlink)); + ISC_QUEUE_PUSH(queue, &four, qlink); + ATF_CHECK(ISC_QLINK_LINKED(&four, qlink)); + + ATF_CHECK(! ISC_QLINK_LINKED(&five, qlink)); + ISC_QUEUE_PUSH(queue, &five, qlink); + ATF_CHECK(ISC_QLINK_LINKED(&five, qlink)); + + /* Test unlink by removing one item from the middle */ + ISC_QUEUE_UNLINK(queue, &three, qlink); + + ISC_QUEUE_POP(queue, qlink, p); + ATF_REQUIRE(p != NULL); + ATF_CHECK_EQ(p->value, 1); + + ISC_QUEUE_POP(queue, qlink, p); + ATF_REQUIRE(p != NULL); + ATF_CHECK_EQ(p->value, 2); + + ISC_QUEUE_POP(queue, qlink, p); + ATF_REQUIRE(p != NULL); + ATF_CHECK_EQ(p->value, 4); + + ISC_QUEUE_POP(queue, qlink, p); + ATF_REQUIRE(p != NULL); + ATF_CHECK_EQ(p->value, 5); + + ATF_CHECK(ISC_QUEUE_EMPTY(queue)); + + ISC_QUEUE_DESTROY(queue); + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, queue_valid); + + return (atf_no_error()); +} + diff --git a/lib/isc/tests/radix_test.c b/lib/isc/tests/radix_test.c new file mode 100644 index 0000000..0a0b534 --- /dev/null +++ b/lib/isc/tests/radix_test.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <isc/mem.h> +#include <isc/netaddr.h> +#include <isc/radix.h> +#include <isc/result.h> +#include <isc/util.h> + +#include <atf-c.h> + +#include <stdlib.h> + +#include "isctest.h" + +ATF_TC(isc_radix_search); +ATF_TC_HEAD(isc_radix_search, tc) { + atf_tc_set_md_var(tc, "descr", "test radix seaching"); +} +ATF_TC_BODY(isc_radix_search, tc) { + isc_radix_tree_t *radix = NULL; + isc_radix_node_t *node; + isc_prefix_t prefix; + isc_result_t result; + struct in_addr in_addr; + isc_netaddr_t netaddr; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_radix_create(mctx, &radix, 32); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + in_addr.s_addr = inet_addr("3.3.3.0"); + isc_netaddr_fromin(&netaddr, &in_addr); + NETADDR_TO_PREFIX_T(&netaddr, prefix, 24, false); + + node = NULL; + result = isc_radix_insert(radix, &node, NULL, &prefix); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + node->data[0] = (void *)1; + isc_refcount_destroy(&prefix.refcount); + + in_addr.s_addr = inet_addr("3.3.0.0"); + isc_netaddr_fromin(&netaddr, &in_addr); + NETADDR_TO_PREFIX_T(&netaddr, prefix, 16, false); + + node = NULL; + result = isc_radix_insert(radix, &node, NULL, &prefix); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + node->data[0] = (void *)2; + isc_refcount_destroy(&prefix.refcount); + + in_addr.s_addr = inet_addr("3.3.3.3"); + isc_netaddr_fromin(&netaddr, &in_addr); + NETADDR_TO_PREFIX_T(&netaddr, prefix, 22, false); + + node = NULL; + result = isc_radix_search(radix, &node, &prefix); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(node->data[0], (void *)2); + + isc_refcount_destroy(&prefix.refcount); + + isc_radix_destroy(radix, NULL); + + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_radix_search); + + return (atf_no_error()); +} diff --git a/lib/isc/tests/random_test.c b/lib/isc/tests/random_test.c new file mode 100644 index 0000000..5637139 --- /dev/null +++ b/lib/isc/tests/random_test.c @@ -0,0 +1,670 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * IMPORTANT NOTE: + * These tests work by generating a large number of pseudo-random numbers + * and then statistically analyzing them to determine whether they seem + * random. The test is expected to fail on occasion by random happenstance. + */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/random.h> +#include <isc/result.h> +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/util.h> + +#include <atf-c.h> + +#include <stdlib.h> +#include <stdint.h> +#include <math.h> + +#define REPS 25000 + +typedef double (pvalue_func_t)(isc_mem_t *mctx, + uint16_t *values, size_t length); + +/* igamc(), igam(), etc. were adapted (and cleaned up) from the Cephes + * math library: + * + * Cephes Math Library Release 2.8: June, 2000 + * Copyright 1985, 1987, 2000 by Stephen L. Moshier + * + * The Cephes math library was released into the public domain as part + * of netlib. +*/ + +static double MACHEP = 1.11022302462515654042E-16; +static double MAXLOG = 7.09782712893383996843E2; +static double big = 4.503599627370496e15; +static double biginv = 2.22044604925031308085e-16; + +static double igamc(double a, double x); +static double igam(double a, double x); + +static double +igamc(double a, double x) { + double ans, ax, c, yc, r, t, y, z; + double pk, pkm1, pkm2, qk, qkm1, qkm2; + + if ((x <= 0) || (a <= 0)) + return (1.0); + + if ((x < 1.0) || (x < a)) + return (1.0 - igam(a, x)); + + ax = a * log(x) - x - lgamma(a); + if (ax < -MAXLOG) { + fprintf(stderr, "igamc: UNDERFLOW, ax=%f\n", ax); + return (0.0); + } + ax = exp(ax); + + /* continued fraction */ + y = 1.0 - a; + z = x + y + 1.0; + c = 0.0; + pkm2 = 1.0; + qkm2 = x; + pkm1 = x + 1.0; + qkm1 = z * x; + ans = pkm1 / qkm1; + + do { + c += 1.0; + y += 1.0; + z += 2.0; + yc = y * c; + pk = pkm1 * z - pkm2 * yc; + qk = qkm1 * z - qkm2 * yc; + if (qk != 0) { + r = pk / qk; + t = fabs((ans - r) / r); + ans = r; + } else + t = 1.0; + + pkm2 = pkm1; + pkm1 = pk; + qkm2 = qkm1; + qkm1 = qk; + + if (fabs(pk) > big) { + pkm2 *= biginv; + pkm1 *= biginv; + qkm2 *= biginv; + qkm1 *= biginv; + } + } while (t > MACHEP); + + return (ans * ax); +} + +static double +igam(double a, double x) { + double ans, ax, c, r; + + if ((x <= 0) || (a <= 0)) + return (0.0); + + if ((x > 1.0) && (x > a)) + return (1.0 - igamc(a, x)); + + /* Compute x**a * exp(-x) / md_gamma(a) */ + ax = a * log(x) - x - lgamma(a); + if( ax < -MAXLOG ) { + fprintf(stderr, "igam: UNDERFLOW, ax=%f\n", ax); + return (0.0); + } + ax = exp(ax); + + /* power series */ + r = a; + c = 1.0; + ans = 1.0; + + do { + r += 1.0; + c *= x / r; + ans += c; + } while (c / ans > MACHEP); + + return (ans * ax / a); +} + +static int8_t scounts_table[65536]; +static uint8_t bitcounts_table[65536]; + +static int8_t +scount_calculate(uint16_t n) { + int i; + int8_t sc; + + sc = 0; + for (i = 0; i < 16; i++) { + uint16_t lsb; + + lsb = n & 1; + if (lsb != 0) + sc += 1; + else + sc -= 1; + + n >>= 1; + } + + return (sc); +} + +static uint8_t +bitcount_calculate(uint16_t n) { + int i; + uint8_t bc; + + bc = 0; + for (i = 0; i < 16; i++) { + uint16_t lsb; + + lsb = n & 1; + if (lsb != 0) + bc += 1; + + n >>= 1; + } + + return (bc); +} + +static void +tables_init(void) { + uint32_t i; + + for (i = 0; i < 65536; i++) { + scounts_table[i] = scount_calculate(i); + bitcounts_table[i] = bitcount_calculate(i); + } +} + +/* + * The following code for computing Marsaglia's rank is based on the + * implementation in cdbinrnk.c from the diehard tests by George + * Marsaglia. + * + * This function destroys (modifies) the data passed in bits. + */ +static uint32_t +matrix_binaryrank(uint32_t *bits, size_t rows, size_t cols) { + size_t i, j, k; + unsigned int rt = 0; + uint32_t rank = 0; + uint32_t tmp; + + for (k = 0; k < rows; k++) { + i = k; + + while (rt >= cols || ((bits[i] >> rt) & 1) == 0) { + i++; + + if (i < rows) + continue; + else { + rt++; + if (rt < cols) { + i = k; + continue; + } + } + + return (rank); + } + + rank++; + if (i != k) { + tmp = bits[i]; + bits[i] = bits[k]; + bits[k] = tmp; + } + + for (j = i + 1; j < rows; j++) { + if (((bits[j] >> rt) & 1) == 0) + continue; + else + bits[j] ^= bits[k]; + } + + rt++; + } + + return (rank); +} + +static void +random_test(pvalue_func_t *func) { + isc_mem_t *mctx = NULL; + isc_result_t result; + isc_rng_t *rng; + uint32_t m; + uint32_t j; + uint32_t histogram[11] = { 0 }; + uint32_t passed; + double proportion; + double p_hat; + double lower_confidence, higher_confidence; + double chi_square; + double p_value_t; + double alpha; + + tables_init(); + + result = isc_mem_create(0, 0, &mctx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + rng = NULL; + result = isc_rng_create(mctx, NULL, &rng); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + m = 1000; + passed = 0; + + for (j = 0; j < m; j++) { + uint32_t i; + uint16_t values[REPS]; + double p_value; + + for (i = 0; i < REPS; i++) + values[i] = isc_rng_random(rng); + + p_value = (*func)(mctx, values, REPS); + if (p_value >= 0.01) { + passed++; + } + + ATF_REQUIRE(p_value >= 0.0); + ATF_REQUIRE(p_value <= 1.0); + + i = (int) floor(p_value * 10); + histogram[i]++; + } + + isc_rng_detach(&rng); + + /* + * Check proportion of sequences passing a test (see section + * 4.2.1 in NIST SP 800-22). + */ + alpha = 0.01; /* the significance level */ + proportion = (double) passed / (double) m; + p_hat = 1.0 - alpha; + lower_confidence = p_hat - (3.0 * sqrt((p_hat * (1.0 - p_hat)) / m)); + higher_confidence = p_hat + (3.0 * sqrt((p_hat * (1.0 - p_hat)) / m)); + + /* Debug message, not displayed when running via atf-run */ + printf("passed=%u/1000\n", passed); + printf("higher_confidence=%f, lower_confidence=%f, proportion=%f\n", + higher_confidence, lower_confidence, proportion); + + ATF_REQUIRE(proportion >= lower_confidence); + ATF_REQUIRE(proportion <= higher_confidence); + + /* + * Check uniform distribution of p-values (see section 4.2.2 in + * NIST SP 800-22). + */ + + /* Fold histogram[10] (p_value = 1.0) into histogram[9] for + * interval [0.9, 1.0] + */ + histogram[9] += histogram[10]; + histogram[10] = 0; + + /* Pre-requisite that at least 55 sequences are processed. */ + ATF_REQUIRE(m >= 55); + + chi_square = 0.0; + for (j = 0; j < 10; j++) { + double numer; + double denom; + + /* Debug message, not displayed when running via atf-run */ + printf("hist%u=%u ", j, histogram[j]); + + numer = (histogram[j] - (m / 10.0)) * + (histogram[j] - (m / 10.0)); + denom = m / 10.0; + chi_square += numer / denom; + } + + printf("\n"); + + p_value_t = igamc(9 / 2.0, chi_square / 2.0); + + ATF_REQUIRE(p_value_t >= 0.0001); +} + +/* + * This is a frequency (monobits) test taken from the NIST SP 800-22 + * RNG test suite. + */ +static double +monobit(isc_mem_t *mctx, uint16_t *values, size_t length) { + size_t i; + int32_t scount; + uint32_t numbits; + double s_obs; + double p_value; + + UNUSED(mctx); + + numbits = length * 16; + scount = 0; + + for (i = 0; i < length; i++) + scount += scounts_table[values[i]]; + + /* Preconditions (section 2.1.7 in NIST SP 800-22) */ + ATF_REQUIRE(numbits >= 100); + + /* Debug message, not displayed when running via atf-run */ + printf("numbits=%u, scount=%d\n", numbits, scount); + + s_obs = abs(scount) / sqrt(numbits); + p_value = erfc(s_obs / sqrt(2.0)); + + return (p_value); +} + +/* + * This is the runs test taken from the NIST SP 800-22 RNG test suite. + */ +static double +runs(isc_mem_t *mctx, uint16_t *values, size_t length) { + size_t i; + uint32_t bcount; + uint32_t numbits; + double pi; + double tau; + uint32_t j; + uint32_t b; + uint8_t bit_this; + uint8_t bit_prev; + uint32_t v_obs; + double numer; + double denom; + double p_value; + + UNUSED(mctx); + + numbits = length * 16; + bcount = 0; + + for (i = 0; i < REPS; i++) + bcount += bitcounts_table[values[i]]; + + /* Debug message, not displayed when running via atf-run */ + printf("numbits=%u, bcount=%u\n", numbits, bcount); + + pi = (double) bcount / (double) numbits; + tau = 2.0 / sqrt(numbits); + + /* Preconditions (section 2.3.7 in NIST SP 800-22) */ + ATF_REQUIRE(numbits >= 100); + + /* + * Pre-condition implied from the monobit test. This can fail + * for some sequences, and the p-value is taken as 0 in these + * cases. + */ + if (fabs(pi - 0.5) >= tau) + return (0.0); + + /* Compute v_obs */ + j = 0; + b = 14; + bit_prev = (values[j] & (1U << 15)) == 0 ? 0 : 1; + + v_obs = 0; + + for (i = 1; i < numbits; i++) { + bit_this = (values[j] & (1U << b)) == 0 ? 0 : 1; + if (b == 0) { + b = 15; + j++; + } else { + b--; + } + + v_obs += bit_this ^ bit_prev; + + bit_prev = bit_this; + } + + v_obs += 1; + + numer = fabs(v_obs - (2.0 * numbits * pi * (1.0 - pi))); + denom = 2.0 * sqrt(2.0 * numbits) * pi * (1.0 - pi); + + p_value = erfc(numer / denom); + + return (p_value); +} + +/* + * This is the block frequency test taken from the NIST SP 800-22 RNG + * test suite. + */ +static double +blockfrequency(isc_mem_t *mctx, uint16_t *values, size_t length) { + uint32_t i; + uint32_t numbits; + uint32_t mbits; + uint32_t mwords; + uint32_t numblocks; + double *pi; + double chi_square; + double p_value; + + numbits = length * 16; + mbits = 32000; + mwords = mbits / 16; + numblocks = numbits / mbits; + + /* Debug message, not displayed when running via atf-run */ + printf("numblocks=%u\n", numblocks); + + /* Preconditions (section 2.2.7 in NIST SP 800-22) */ + ATF_REQUIRE(numbits >= 100); + ATF_REQUIRE(mbits >= 20); + ATF_REQUIRE((double) mbits > (0.01 * numbits)); + ATF_REQUIRE(numblocks < 100); + ATF_REQUIRE(numbits >= (mbits * numblocks)); + + pi = isc_mem_get(mctx, numblocks * sizeof(double)); + ATF_REQUIRE(pi != NULL); + + for (i = 0; i < numblocks; i++) { + uint32_t j; + pi[i] = 0.0; + for (j = 0; j < mwords; j++) { + uint32_t idx; + + idx = i * mwords + j; + pi[i] += bitcounts_table[values[idx]]; + } + pi[i] /= mbits; + } + + /* Compute chi_square */ + chi_square = 0.0; + for (i = 0; i < numblocks; i++) + chi_square += (pi[i] - 0.5) * (pi[i] - 0.5); + + chi_square *= 4 * mbits; + + isc_mem_put(mctx, pi, numblocks * sizeof(double)); + + /* Debug message, not displayed when running via atf-run */ + printf("chi_square=%f\n", chi_square); + + p_value = igamc(numblocks * 0.5, chi_square * 0.5); + + return (p_value); +} + +/* + * This is the binary matrix rank test taken from the NIST SP 800-22 RNG + * test suite. + */ +static double +binarymatrixrank(isc_mem_t *mctx, uint16_t *values, size_t length) { + uint32_t i; + size_t matrix_m; + size_t matrix_q; + uint32_t num_matrices; + size_t numbits; + uint32_t fm_0; + uint32_t fm_1; + uint32_t fm_rest; + double term1; + double term2; + double term3; + double chi_square; + double p_value; + + UNUSED(mctx); + + matrix_m = 32; + matrix_q = 32; + num_matrices = length / ((matrix_m * matrix_q) / 16); + numbits = num_matrices * matrix_m * matrix_q; + + /* Preconditions (section 2.5.7 in NIST SP 800-22) */ + ATF_REQUIRE(matrix_m == 32); + ATF_REQUIRE(matrix_q == 32); + ATF_REQUIRE(numbits >= (38 * matrix_m * matrix_q)); + + fm_0 = 0; + fm_1 = 0; + fm_rest = 0; + for (i = 0; i < num_matrices; i++) { + /* + * Each uint32_t supplies 32 bits, so a 32x32 bit matrix + * takes up uint32_t array of size 32. + */ + uint32_t bits[32]; + int j; + uint32_t rank; + + for (j = 0; j < 32; j++) { + size_t idx; + uint32_t r1; + uint32_t r2; + + idx = i * ((matrix_m * matrix_q) / 16); + idx += j * 2; + + r1 = values[idx]; + r2 = values[idx + 1]; + bits[j] = (r1 << 16) | r2; + } + + rank = matrix_binaryrank(bits, matrix_m, matrix_q); + + if (rank == matrix_m) + fm_0++; + else if (rank == (matrix_m - 1)) + fm_1++; + else + fm_rest++; + } + + /* Compute chi_square */ + term1 = ((fm_0 - (0.2888 * num_matrices)) * + (fm_0 - (0.2888 * num_matrices))) / (0.2888 * num_matrices); + term2 = ((fm_1 - (0.5776 * num_matrices)) * + (fm_1 - (0.5776 * num_matrices))) / (0.5776 * num_matrices); + term3 = ((fm_rest - (0.1336 * num_matrices)) * + (fm_rest - (0.1336 * num_matrices))) / (0.1336 * num_matrices); + + chi_square = term1 + term2 + term3; + + /* Debug message, not displayed when running via atf-run */ + printf("fm_0=%u, fm_1=%u, fm_rest=%u, chi_square=%f\n", + fm_0, fm_1, fm_rest, chi_square); + + p_value = exp(-chi_square * 0.5); + + return (p_value); +} + +ATF_TC(isc_rng_monobit); +ATF_TC_HEAD(isc_rng_monobit, tc) { + atf_tc_set_md_var(tc, "descr", "Monobit test for the RNG"); +} + +ATF_TC_BODY(isc_rng_monobit, tc) { + UNUSED(tc); + + random_test(monobit); +} + +ATF_TC(isc_rng_runs); +ATF_TC_HEAD(isc_rng_runs, tc) { + atf_tc_set_md_var(tc, "descr", "Runs test for the RNG"); +} + +ATF_TC_BODY(isc_rng_runs, tc) { + UNUSED(tc); + + random_test(runs); +} + +ATF_TC(isc_rng_blockfrequency); +ATF_TC_HEAD(isc_rng_blockfrequency, tc) { + atf_tc_set_md_var(tc, "descr", "Block frequency test for the RNG"); +} + +ATF_TC_BODY(isc_rng_blockfrequency, tc) { + UNUSED(tc); + + random_test(blockfrequency); +} + +ATF_TC(isc_rng_binarymatrixrank); +ATF_TC_HEAD(isc_rng_binarymatrixrank, tc) { + atf_tc_set_md_var(tc, "descr", "Binary matrix rank test for the RNG"); +} + +/* + * This is the binary matrix rank test taken from the NIST SP 800-22 RNG + * test suite. + */ +ATF_TC_BODY(isc_rng_binarymatrixrank, tc) { + UNUSED(tc); + + random_test(binarymatrixrank); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_rng_monobit); + ATF_TP_ADD_TC(tp, isc_rng_runs); + ATF_TP_ADD_TC(tp, isc_rng_blockfrequency); + ATF_TP_ADD_TC(tp, isc_rng_binarymatrixrank); + + return (atf_no_error()); +} diff --git a/lib/isc/tests/regex_test.c b/lib/isc/tests/regex_test.c new file mode 100644 index 0000000..b5e88b3 --- /dev/null +++ b/lib/isc/tests/regex_test.c @@ -0,0 +1,1121 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <atf-c.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#ifdef HAVE_REGEX_H +#include <regex.h> +#endif + +#include <isc/regex.h> +#include <isc/print.h> +#include <isc/util.h> + +ATF_TC(regex_validate); +ATF_TC_HEAD(regex_validate, tc) { + atf_tc_set_md_var(tc, "descr", "check isc_regex_validate()"); +} +ATF_TC_BODY(regex_validate, tc) { + /* + * test regex were generated using http://code.google.com/p/regfuzz/ + * modified to use only printable characters + */ + struct { + const char * expression; + int expect; + int exception; /* regcomp accepts but is disallowed. */ + } tests[] = { + { "", -1, 0 }, + { "*", -1, 0 }, + { ".*", 0, 0 }, + { ".**", -1, 0 }, + { ".*\?", -1, 0 }, + { ".*+", -1, 0 }, + { "+", -1, 0 }, + { ".+", 0, 0 }, + { ".++", -1, 0 }, + { ".+\?", -1, 0 }, + { ".+*", -1, 0 }, + { "\?", -1, 0 }, + { ".\?", 0, 0 }, + { ".\?\?", -1, 0 }, + { ".\?*", -1, 0 }, + { ".\?+", -1, 0 }, + { "(", -1, 0 }, + { "()", 1, 0 }, + { "(|)", -1, 0 }, + { "(a|)", -1, 0 }, + { "(|b)", -1, 0 }, + { ".{", 0, 0 }, + { ".{1", -1, 0 }, + { ".\\{1", 0, 0 }, + { ".{1}", 0, 0 }, + { ".\\{1}", 0, 0 }, + { ".{,", 0, 0 }, + { ".{,}", 0, 0 }, + { ".{1,}", 0, 0 }, + { ".\\{1,}", 0, 0 }, + { ".{1,\\}", -1, 0 }, + { ".{1,", -1, 0 }, + { ".\\{1,", 0, 0 }, + { ".{1,2}", 0, 0 }, + { ".{1,2}*", -1, 0 }, + { ".{1,2}+", -1, 0 }, + { ".{1,2}\?", -1, 0 }, + { ".{1,2", -1, 0 }, + { ".{2,1}", -1, 0 }, + { "[", -1, 0 }, + { "[]", -1, 0 }, + { "[]]", 0, 0 }, + { "[[]", 0, 0 }, + { "[^]", -1, 0 }, + { "[1-2-3]", -1, 0 }, + { "[1-22-3]", 0, 0 }, + { "[+--23]", 0, 0 }, + { "[+--]", 0, 0 }, + { "[-1]", 0, 0 }, + { "[1-]", 0, 0 }, + { "[[.^.]]", 0, 0 }, + { "[^]]", 0, 0 }, + { "[^^]", 0, 0 }, + { "[]]\?", 0, 0 }, + { "[[]\?", 0, 0 }, + { "[[..]]", -1, 0 }, + { "[[...]]", 0, 0 }, + { "[[..5.]--]", -1, 0 }, + { "[[.+.]--]", 0, 0 }, + { "[[..+.]--]", -1, 0 }, + { "[[.5.]--]", -1, 0 }, + { "[1-[=x=]]", -1, 0 }, + { "[[:alpha:]]", 0, 0 }, + { "[[:alpha:]", -1, 0 }, + { "[[:alnum:]]", 0, 0 }, + { "[[:alnum:]", -1, 0 }, + { "[[:digit:]]", 0, 0 }, + { "[[:digit:]", -1, 0 }, + { "[[:punct:]]", 0, 0 }, + { "[[:punct:]", -1, 0 }, + { "[[:graph:]]", 0, 0 }, + { "[[:graph:]", -1, 0 }, + { "[[:space:]]", 0, 0 }, + { "[[:space:]", -1, 0 }, + { "[[:blank:]]", 0, 0 }, + { "[[:blank:]", -1, 0 }, + { "[[:upper:]]", 0, 0 }, + { "[[:upper:]", -1, 0 }, + { "[[:cntrl:]]", 0, 0 }, + { "[[:cntrl:]", -1, 0 }, + { "[[:print:]]", 0, 0 }, + { "[[:print:]", -1, 0 }, + { "[[:xdigit:]]", 0, 0 }, + { "[[:xdigit:]", -1, 0 }, + { "[[:unknown:]]", -1, 0 }, + { "\\[", 0, 0 }, + { "(a)\\1", 1, 0 }, + { "(a)\\2", -1, 1 }, + { "\\0", 0, 0 }, + { "[[][:g(\?(raph:][:alnu)(\?{m:][:space:]h]<Z3})AAA)S[:space:]{176,}", 0, 0 }, + { "(()IIIIIIII(III[[[[[[[[[[[[[[[[[[^[[[[[[[[ [^ fX][:ascii:].)N[:a(\?<!lpha:])][:punct:]e*y+)a{-124,223}", 3, 0 }, + { "(pP\\\\\\(\?<!\\\\\\\\\\\\\\\\\\\\\\lRRRRRRRRRRRRRRRRBBBBBBBBBBBBBBBB))kkkkkkkkkkkkkkkkkkkkk|^", 1, 0 }, + { "[^[^[{111}(\?=(\?:(\?>/r(\?<(\?=!(\?(\?!<!Q(\?:=0_{Meqipm`(\?((\?{x|N)))))|))+]+]Z)O{,-215}])}))___________________{}", 0, 0 }, + { "[C{,-218(\?=}E^< ]PP-Ga)t``````````````````````````{138}", 0, 0 }, + { "[^h(\?<!(\?>Nn(\?#])))", 0, 0 }, + { "[(\?!(\?<=[^{,37}AAAA(AAAAAAAAAAAAA])", 0, 0 }, + { "[^((\?(\?:ms(\?<!xims:A{}(\?{*</H(\?=xL $(\?<!,[})))*)qqqqqqqqqqqqqqqqqq)]33333333333333333333333333333{[:graph:]p)-+( oqD]){-10,}-{247}_______________________X-e[:alpha:][:upperword:]_(______wwwwwwwww /c[:upperword:][:alnum:][:alnum:][:pun(\?{ct:])[:blankcntrl:]})*_*", 2, 0 }, + { "[(\?<!:lowerprin(\?{t:]{}}){113,})[:punct:]IIIIIIIIIIIIIIIIIIIIIIII", 0, 0 }, + { "PP)", 0, 0 }, + { "(([^(\?<!((\?>\?=[])p.]}8X[:blankcntrl:],{-119,94})XmF1.{)-)[:upperword:])[:digit:]{zg-q", 2, 0 }, + { "[^[({(\?#254}))Z[l][x50]=444444444444(4444444444u[:punct:]\?[:punct:(\?!])])", 1, 0 }, + { "[^[^[^([^((*4[(^((\?<=])Ec)", 0, 0 }, + { "(0)Y:8biiiiiiiiiiiiiiiiiii", 1, 0 }, + { "[^w(\?!)P::::::::::::::(\?#::(\?<=:::::::::]\"\"{}[3333333333333333(\?<=33333(\?!)9Xja][:alph(\?<=a:])xB1)(PX8Cf\?4444)qq[:digit:])", 1, 0 }, + { "([U[^[^].]^m]/306KS7JJJJJJJJ{})", 1, 0 }, + { "[^[^([^[(\?!(\?>8j`Wg2(\?{,(\?>!#N++++(\?<![++++++)+44444444bA:K(\?<!O3([:digit:]3]}}}}}}}}}}}}}}}}}}}}}}}}LP})S", 0, 0 }, + { "[({(\?{,(\?(=213}*))})]WWWWWWWWWWWWWWW[:alnum:])", 0, 0 }, + { "[:(\?<=ascii:])", 0, 0 }, + { "[U(\?#)(\?<=+HzE])[:punct:]{-207,170}\?s.!", 0, 0 }, + { "{}z=jU75~n#soD\"&\?UL`X{xxxxxxxxxxxxxxxxxxxx(xxxxxx${-246,27}[:graph:]g\"{_bX)[:alnum:][:punct:]{-79,}-", 1, 0 }, + { "[^{,-186}@@@@[^(\?{@@(\?>@+(\?>l.]}))*\\BCYX]^W{52,123}(lXislccccccccccccccccc)-*)", 1, 0 }, + { "(x42+,)7=]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]", 1, 0 }, + { "[^(*[:graph:]q/TH\?B(\?{P)]})uZn[:digit:]+2", 0, 0 }, + { "([XXXXXXXXXXXXXXXXXXXXX[(:alnum:][:space:]i%[:upperw(\?=o(\?#rd:])) ", 1, 0 }, + { "(@@@@)", 1, 0 }, + { "{-18,}[:as[(\?>^[cii:]]{}>+{-46,}{,95}[:punct:]{}99999999999999])-{-134}'sK$wCKjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj", 0, 0 }, + { "(l[:alpha:(\?!]))", 1, 0 }, + { "[[^(\?{]|JJ[:alph(a:]X{})B^][:lowerprint:]n-219{-32}{19,105}k4P}){,-144}", 0, 0 }, + { "[[^]P[:punct:][:alpha:][:xdigit:]syh]|W#JS*(m<2,P-RK)cA@", 1, 0 }, + { "([^((\?({\?<=)}){[^}^]{}])^P4[:punct:[]$)]", 1, 0 }, + { "([(\?#:(\?{space:]}):{}{-242,}n)F[:alpha:]3$)d4H3up6qS[:blankcntrl:]B:C{}[:upperword:]r", 1, 0 }, + { "([(\?:]))[:digit:]mLV.{}", 1, 0 }, + { "[^PPP-[]{[,50}{128,}]111111111111111]p", 0, 0 }, + { "[^([^([^([[^[([^[^[[2[[[[[[[[[[[[[^[[[[(\?(\?{:[[[[[[(\?([-[:ascii:]--*)", -1, 0 }, + { ")!F^DA/ZZZZZZZZZZZZZZZZZZ", 0, 0 }, + { "[[[[[[[((\?=\?(\?>([[[[[[[^[[[[(\?()[[[K(\?#))])))]7Y[:space:]{,-96}pP)[:ascii:]u{-88}:N{-251}uo", 0, 0 }, + { "t[:x(\?<=digit:])eYYYYYYYYYYYYYYYYYY{,-220}A", 0, 0 }, + { "[[({10,}[:graph:]Pdddddd(\?#X)])[:alnum:(]]L-C){,50}[:blankcntrl:]p[:gra(ph:]){66,}", 0, 0 }, + { "[^[^]*4br]w[:digit(\?::]n99999999999999999)P[:punct:]pP", 0, 0 }, + { "[:digit:]{67,247}!N{122})VrXe", 0, 0 }, + { "[:xdigit:]^[:xdigit:]Z[:alnum:]^^^^1[:upperword:(\?=])[:lowerprint:]*JJ-", 0, 0 }, + { "[[(\?imsximsx:^*e(){,3[6}](V~\?^[:asc(\?!ii:]I.dZ))]$^AAAAAAAAAAAAAAAAAAAAAAAA[:space:]k)]", 1, 0 }, + { "W{,112}[:lowerp(\?<!rint:]$#GT>R7~t'\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"9,O).", 0, 0 }, + { "[^{6((\?>\?:4}(\?<=G))f)KKKKKKKKKKKKKKKKKKKKKKKKKKKKKpppppppp(\?=ppppp]{,-101}|[:blankcntrl:]Z{-182})", 0, 0 }, + { "([:punct:]@^,,,,,,,,,,,,,,,,,,,,,,,,,,0\?:-o8NPIIIIIIIII)pPKKKKKKKKKKKKKKKKKKKK", 1, 0 }, + { "([^[[^[^]]]])", 1, 0 }, + { "[([^[(333\"(\?#\\\\[)(\?isx-x:\"Tx]')", 0, 0 }, + { "[[n>^>T%.zzzzzzzzzzzzzzzzz$&|Fk.1o7^o, ^8{202,-12}$[:alnum:]]G[:upperword:]V[:xdigit:]L|[:upperword:]KKKKKKKKKKKKYX\"\")xJ ~B@[{,-68}/][:upperword:]QI.", 0, 0 }, + { "[^[]tN^hy3\"d@v T[GE\?^~{124,10(\?{2}]})\?[:upperword:]O", 0, 0 }, + { "d.``````````````````````````[:up(\?=perword:]RRRRRRRRRRRRRRR)", 0, 0 }, + { "[Z{{{{{{{{{{{{{(\?={(\?<!{{{{{{{{{(\?>{{J6N:H[tA+mN3Zmf:p\?]\?){-181,82}S4n.b[:lowerpri(\?{nt:]|ggggggggggggggggggggggggggggggg}))4)", 0, 0 }, + { "[^((/////[^////[^/////////[(^/////]fI{240}{-120}+]R]GA)", 0, 0 }, + { "[-(\?#.)(\?())[:alpha:](\?={(\?#}r)[:space:]PPW]o)", 0, 0 }, + { "[:lowerp(\?{rint:]})201{46,}[:a[^scii:]0Q{37,}][:blankcntrl:]1331", 0, 0 }, + { "[^(\?!(\?#)\\GIwxKKKKKKKKKK'$KKKKKKKK]l)bbb^&\?", 0, 0 }, + { "[:ascii:]*[:sp(\?<=ace:])", 0, 0 }, + { "({-66,}Z{})0I{-111,}[:punct(\?():])", 1, 0 }, + { "[[^(\?!()%%%%%%%%%%%%%(\?:%%%%%%%%%%%%%%%%)t(\?{VX>B#6sUU(\?<!UUUUUU(\?=UUU[^UUUUUUUUUUUU(\?((\?:UPPPPPPPPPPP)PPPPPPPPPPPPPPP]ffffffffffffffffffffffff)^[:space:]wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww{243}9[:lowerprint:]Dv[:graph:])][:blankcntrl:]V%E[:graph:]})[:space:]{-83,}cQZ{}4{-23,135}", 0, 0 }, + { "({,-76[}]O[:xdi(\?<!git:])\?5))))))))\?d[:lowerprint:]b666666[:graph:]c", 1, 0 }, + { "{}{-145,}[:(\?(spa)ce:])f", 0, 0 }, + { "[([^].{116,243}]T*[[^:punct(\?[{[^:(\?<!]]8()])[:alnum:])})]N{}{,243}*[n]][:graph:]", 1, 0 }, + { "[^w]8888888888888888_________(__________[:ascii:]BdqTE$^0|MNto*i#############[^#################])", 1, 0 }, + { "[^[[[<[()\?]GGG{,26[}[:alnum:]SSSSS.gggggggg[:graph:]CCCCCCCCCCC{79,}{138,191}][:di(git:]u]@]JJJJJJJJJJJJJJJJJJJJJJJ[:graph:(\?:][:alnum:]])[:alnum:])]", 0, 0 }, + { "[^(((BBBBBBBBBB(\?>BBBZvvvvvvvvvv(\?m(sximsx:vvv)iiiiiiii)))j>Rs:Sm]0MMMMMMMMMMM|@F)Y]*^#EEEEEEE)*", 0, 0 }, + { "([^([(U(\?!)<<<<<<<<<<(\?#<<<<(\?<!<<<)(\?=L.{73,})+]n9U}fk%Jn}'b Na<%yyyyyyyyyyyy)){-198,}]))[:space:].pP361U]3s@u_9AU Te/{s`6=IMZdL1|.ySRo", 1, 0 }, + { "[[((\?<=\?>(\?#){}]{}`){1,82}){-143[,}]^G", 0, 0 }, + { "[:digit:]W|[:up(\?<!perword:]{,-101}llllllllllllllllll[:upperword:])mmYYYYYYYYYYYYYYYYYYYYYYY*", 0, 0 }, + { "@NHy)", 0, 0 }, + { "([^[^]][:alnum:]222[^22222222(\?{2222222222222222][:lo(\?:werprint:][:xdigit:]^[:blankcntrl:]s+N)[:alpha:]-NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNWxxxxxxxxxxxxxxxxxxxxxxxxxxD[:space:]U)TTTTTTTTTTfffffffffffzzzzzzzzzzzzzzzzzzzzzzzzz})", 1, 0 }, + { "[^[^[[^[][^[]pP([^\?[^<=(\?=]){158,})]]]][:digit:]]K22222222222p^dUKJ`\">@]", 1, 0 }, + { "[^[^[(\?imsximsx::p(\?{unct:][(\?>:ascii:]5w)]{159}\\Q\?@C]4(44444444}[^)|)[:graph:]]C:b)", 1, 0 }, + { "[^[[(tYri[W<8%1(\?='yt][:lowerprint:[]))1r]][:alnum:][:digit:]{48}{-52,-183}+][:alpha:]r][:upperword:]\?{-105,155}{-55,-87}pPN#############################{63,232}]", 0, 0 }, + { "[*(\?>L(\?<(\?>=))]&&&&&&&(&&&&&&&&&&&&&&&&&&))[|WIX]{-62,-114}S K=HW60XE<2+W", 1, 0 }, + { "(00000000000)z\\\\*t{}R{88}[:alnum:]*", 1, 0 }, + { "(([^(\?=\?gggggg[gLw)]{-250,}[:xdigit:]yZ[:g(raph:]8QNr[:space:][:blankcntrl:]A)][:digit:]D)[:xdigit:])", 2, 0 }, + { "[^([^,(\?<!]*))]", 0, 0 }, + { "[^(\?{[:alnum:]]}}}}}}}}}}}}}}}}}}}}}}}){-83}", 0, 0 }, + { "WWWWWWWW[:alnum(\?<=(\?#:]{,-1})@OSSS)[:digit:]", 0, 0 }, + { "[^(\?!*]+G)", 0, 0 }, + { "[LLLLLLLLLLLLLLLLLLLLLLLLLLLLLL>s8.>[^{}$(\?(]]XXXXXXX)XXXXXXXXXXXXXX[:alpha:]Whii\?p[:xdigit:])+", 0, 0 }, + { "(7777[:blankcntrl:])", 1, 0 }, + { "[^C[:digit:]]{}YYYY(YYYYYYYYYYYYYYYY)", 1, 0 }, + { "on|,#tve%F(w-::::::::::::::::::::::::::::*=->)", 1, 0 }, + { "([((\?=(\?!((\?=')))27(<{})S-vvvvvvvvvv(\?=vvvvvvvvvvvvvvvvv[:punct:][:alnum:]}}}}}}}}}}}}}}}}}}}}}}}PPPPPPPPPPPPPPPPPPPPPPPPPPPPPgggggggggggggggggggggggggg(\?#(\?#gggggg<X){}]{-164,61})>+))uQ)W>[:punct:][:xdigit:][:digit:][:punct:]{}[:digit:][:space:]){,-105}=xiAyf}o[:alpha:]akZSYK+sl{", 1, 0 }, + { "[^[^]/S:Hq<[:upperword:(\?<=]W[:alnum:]X])1973", 0, 0 }, + { "[[^[[^([^VVVV(\?!(VVVVVVVVVVVVVVVVVVVVV[VVVVX][^]2))98ppppppppppppppppppppppppppppppp/////////////////////b.]G{-101,}[:[ascii:]P].=~])AAAAAAAAAAAAA2{-153,}]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]][:alnum:][:lowerprint:]WN/D!rD]|4444{180}]V_@3lW#lat]", 0, 0 }, + { "[^[^([^TTTTT(\?:T(\?:T7777{,59}])[:graph:][:ascii(\?<=:]))f]AD{,-43}%%%%%%%%%%%%%%%%)S|[:digit:]FZm<[:blankcntrl:]QT&xj*{-114,}$[:xdigit:]042][:xdig[it:]{-180}027[:alpha:][:ascii:][:lowerprint:][:xdigit:]^|[:alnum:][^Mi]z!suQ{-44,-32}[:digit:]]", 0, 0 }, + { ")", 0, 0 }, + { "''''''''''[:a(\?imsxisx:lnum:])P", 0, 0 }, + { "(([{20(\?<=8}[:alnum:]pP$`(\?#N)wRH[:graph:]aaaaaaaaaaaaaa(\?=aaaaaaaaaaaaaaaaP]a)))[:punct:]-\?)A^", 2, 0 }, + { "[^(.//[:punct:]&-333333333333333333333333333(\?<!33)LLLLLLLLLLLLLLLLL[:alnum:]$1]~8]|^\"A[:xdigit:]\?[:ascii:]{128,}{,-74}[:graph:]{157}3N){-196,184}D", 0, 0 }, + { "[^($(\?{(\?<=)[#)]})[:space:]]nWML0D{}", 0, 0 }, + { ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,[^]x{213,-93}(\?{A7]V{}})", 0, 0 }, + { "[k(\?=*)+^[f(])r_H6", 0, 0 }, + { "[(\?#(\?{)]q})", 0, 0 }, + { "([GLLLLLLLLLL(\?!((\?:LLLLLLLL]))C#T$Y))^|>W90DDDDDDDDDDD[^DDDDDDDDDDDDDDDDDDDD]B[:punct:]c/", 1, 0 }, + { "[^(\?<!)(\?{b}){,199}A[:space:]+++++++(\?!++++++++{36}Tn])", 0, 0 }, + { "()[:alpha:]a", 1, 0 }, + { "[(\?(:blan)kcntrl:])lUUUUUUUUUUUUUUUUUUUUUUU", 0, 0 }, + { "[^[^(s[[[[[[[[[[[[[[(\?#[[[[[[[)\?`````][:blankcntrl:(\?>]|)p1EmmmmmmmmmmmmmmmmmmmmmmmmmmmmL{-241}666666666666666666666)]^bLDDDDDDDDDDDDD]", 0, 0 }, + { "[nn(\?<!nnnnn(\?#n8)=````````````````````{41,}]U,cb*%Y[:graph:]).[:alnum:]\\\\\\\\\\gt", 0, 0 }, + { "()\?5{,-195}lm*Ga[:space:]Y", 1, 0 }, + { "[(\?:].di)c", 0, 0 }, + { "([([^([\?{})Za,$S(\?!p(\?{++(\?##V(\?<!Evuil.2(\?<![^[h|[^']C)*\"]5]", 1, 0 }, + { "[((^24(\?#4[^Kkj{}))]]{232}47)077[:alpha:]zzzzzzzz{}", 0, 0 }, + { "[^(\?:[^F]o$h)-iV%]", 0, 0 }, + { "[[^[([((([^(\?{[^((\?=)kaSx(\?imsximsx:w3A[`%+A$I{,62}ns&Y!#ay o9YAo{Y>1((\?>\?#45)Z{,108}{}11111111111111111111111111qqqq)\?][:lowerprint:]mbo#)@", 0, 0 }, + { "[^iii8(888888(\?<!8^]))s", 0, 0 }, + { "([[(\?(\?:({^]}[)[(r)])G]{,-87}", 1, 0 }, + { "([[^{249,}(\?>(\?=)]]T()[:bl(\?!ankcntrl:]=jjjjjjjjjjjjjjjj-)))t{}[:alpha:]-\":i! Gn[A4Ym7<<<<<<<<<<<<<<<<]", 2, 0 }, + { "^{}{[^,241(\?#}(\?m(\?ixim:sximsx:]t))+oD)", 0, 0 }, + { "5[(\?#:xdigit:])", 0, 0 }, + { "[^f{(\?>,22(9}[^[^])6KKKKKKKKKKKKK)]RRRRRRRRfuK99999999C}osnNR]BgCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC[:blankcntrl:]", 0, 0 }, + { "[^(\?=U){24,}W-{,17(\?:3[^}]q.nQ#PU_|i$$$$$$$$$$$$$$+)[:dig(\?<!it:]){-98}\?[:upperword:]]", -1, 0 }, + { "[(\?<=[0(\?!72])euE.]{,-159}[:alnum:]t-:l\?)$yyyyyyyyyyyyyyyyyyyyyyyyyyfffffffffffffffffffffffffff", 0, 0 }, + { "[^[^]q[:asc(\?imsxmsx:ii:]JJJJJJJJJJJJJJJJJJJJ[:graph:]]$)`#DdY^qqqqqqqqqqqqqqqqqqqqqqqqqqqu>4^4ta[:alpha:]", 0, 0 }, + { "(((b0HN)q))p5<T())`7JJv{'cv'#L8BNz", 4, 0 }, + { "[pFp2VttBg(\?<=7777777777777|TTTTTTTTTTTTTTT[:space:]Z]^p\"[:blankcntrl:])", 0, 0 }, + { ")aM@@@@@@@@@@@@@", 0, 0 }, + { "([^[(\?<![^])", 1, 0 }, + { "()Z[:ascii:]", 1, 0 }, + { "(fuPPo)..........................[:xdigit:]{}{,4}*kkkkkkkCx#,_=&~)|.2x", 1, 0 }, + { "[+(\?<=){}++++++[:alnum:](\?=+]s)[:alnum:]~~~~~~XXXXXXXXXXXXXXX.[:digit:]", 0, 0 }, + { "[{}[^^(\?(]))CCCCCCCCCCCCCCCCCCCCEg2cF]{}3", 0, 0 }, + { "([[[^[^[^([[^[^([(\?<=G[[)=(\?!===(\?isximsx:==(\?#==[^=====(\?{==================$T[[^^u_TiC.Fo.02>X)uH]$})354b[:alnum:]]]EVVVVVVVVVVVVVVVVVVVVVVVVVVVVVz[:digi(\?(t:][:upperword:])", 1, 0 }, + { "([:blankcntrl:]t-){121,}[:ascii:]444444{}[:graph:]E040", 1, 0 }, + { "[^{134,}]DzQ\?{-30,191})z,\?1Vfq!z}cgv)ERK)1T/=f\?>'", 0, 0 }, + { "@v)<yN]'l-/KKKKKKKBBBBBBBBBBBBBMa2eLA[:digit(\?<!:])\"\"e|l$&m`_yn[:blankcntrl:]uuuuuuuuuuuuuuuuuuu[:punct:]", 0, 0 }, + { "[[999999999999999(\?<=(\?:(\?ixmx:(\?>))])Y]|){,10}\?{}", 0, 0 }, + { "([[[(\?!^]P-AA[AAAAAA[A[^A)r]+B]])", 1, 0 }, + { "3}|[:ascii:][:punct:]()", 1, 0 }, + { "()dw", 1, 0 }, + { "[N]{})))))))))))))))))))))))", 0, 0 }, + { "[[[^([[(\?()(\?#)++([^\?{+++[^+++++++++++(\?!+(\?=+++++++r9/n]N7{-219}{-91}pP[:punct:]T]mROm+~[:digit:][:digit:])Y:", 0, 0 }, + { "[^'Pu[(\?<!D&]_a[:alnum:]E<,F%4&[:xdigit:])][:lowerprint:]", 0, 0 }, + { "tttt(tttttttttt*uKKUUUUU)", 1, 0 }, + { "([:ascii:]GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG)+kX______________{}GGGGG\?TUH3,{67,77}|[:graph:]C{,-136}{}[:upperword:[]{,-6}&]T84]n={C", 1, 0 }, + { "[:upperword:]DC[:u(\?<=pperword:]*d`H0\?m>~\?N|z#Ar--SO{,-141}076)G\?{,-110}M+-[:alpha:]", 0, 0 }, + { "{,-214}{,10(9})", 1, 0 }, + { "([^xxxxxxxxxxxxxxxxxMMMMMMMMMMMMMMXW])].[:punct:]Q`{-63,63}Uua[:alnum:]\?OQssb#L@@@@@@@@(@@@)[:graph:]", 2, 0 }, + { "[[^(\?!```[^``````````````(\?<=``(\?>````````M/////(\?!////////////////////[^GD!|#li]~)*.$]))Tq!]C[:lowerprint:]Qk[{}]]JJJJJJJJJJJJJJJJJJJJJJJ{e])c", 0, 0 }, + { "$[5(7ES])[:xdigit:]%{MRMtYD&aS&g6jp&ghJ@:!I~4%{P\?0vvvvvvvvvvvvvvvvvvvv\\\\\\\\\\\\\\\\\\\\\\\\x54[:lowerprint:][:upperword:]", 0, 0 }, + { "[((([(\?((\?>[:alnum:][):as(\?<!cii:(\?:]Re))K|)|^){-28,89}l<H.<H:N)QKuuuuuuuuw8E136P)^)[:ascii:]][:xdigit:]-", 0, 0 }, + { "(pjvA'x]=D\"qUby\\+'R)r\?C22[:ascii:]", 1, 0 }, + { "[]*b~y C=#P\"6(gD%#-[^FBt{}]${-244}", 0, 0 }, + { "[:up(\?!pe(\?=rword:])lA-'yb\"Xk|K_V\"/@}:&zUA-)W#{-178,-142}(){-202,}", 1, 0 }, + { "()1.WldRA-!!!!!!!!!!!!!!!!!", 1, 0 }, + { "lZZZZZZZZZZZZZZZ(Z[:al(\?:num:])ttttttttttttttttttttttttttttttg.)6$yyy", 1, 0 }, + { "[([^([^[^(([([^[^(([[$(\?{P(\?=(\?<(\?!=(\?#P[^Y])<GA[:ascii:][(\?#(\?<!:alpha:](B{100,})]}))\?)XU=", 1, 0 }, + { "[[dVw{6(\?{9,}2222kkkkkkkkkkkkkkkkkkkkkkkkkk|{}*E]]{}SB{35}-w%{eh})<{-178,}", 0, 0 }, + { "(D(~))", 2, 0 }, + { "[(:alpha:]{,90}Z|)[:ascii:]Du\?[:grap[^h:]^w+|{}][:ascii:]", 0, 0 }, + { "[:p(\?<=unct:]kkkkkkkkkkkkkkkkkkkk)", 0, 0 }, + { "{}[:((\?<!dig((\?#it(\?#:]())p))ZZZZZZZZZZ[:blankcntrl:]){}{-124,})[:ascii:]", 1, 0 }, + { "[[:graph:]{168}lRRRRRRRRRRRRR(\?#RRRRRRRRRRRRRRRRR)rrrr(\?(rrrrrr)rrrrrrrS[(\?<!@f)6>{,-49})q${98,}J\?]){", 0, 0 }, + { "([:pu(\?(nc)t:]F{-32,-102}+)\?cpP[:lowerprint:].^)", 1, 0 }, + { "([{}{210,-238}]1:h)", 1, 0 }, + { "([]QQQQ[QQQQQQQQQQQQQQQQQQ][:digit:]Z{-20,}Slllllll[:space:]C^(@{-174,-156}fx{cf2c}{-242,}rBBBBBBBBBBBBBBBBBBc[:alpha:]N\?))$[:graph:][:ascii:]P+nnnnnnnnnnnnnnnnnnnnnnn1N$r>>>>>>>>>>>>>>>>>>>>>>>>(>>{,88}{,-234}__________)[:upperword:]R.[:alnum:][:lowerprint:]^}\"", 3, 0 }, + { "([^(\?=]-))$", 1, 0 }, + { "([:ascii:]\?,D[:upperword:][:xdigit:]tttttttttttt[^tt(\?<!ttttttttt21f|.(pP[:punct:])])rrrrrrrr)", 1, 0 }, + { "([{1(\?=16}iiiiiiiiii((\?<=iiiiiiiiiiiiiiiiii|ZZZZZZZZZZZ(\?(\?#{ZZZZZZZ))c}))<<<<<(\?#<<<<<<<<<<<d7CVq8]w{-148,-168}\\Gp){-230,}D3", 1, 0 }, + { "[^8888(88888888888EX].[:alnum:]){}", 0, 0 }, + { "([^][^)2]-[:lower(\?=print:]{,79}[:graph:]n)", 1, 0 }, + { "[bSi\?x_mp(C)0{64}[:space:]hhh(\?(hhh)hhL){5,130}'w\"$l&[:xdigit:][:alpha:]IIIIIIIIIIIIIIIIIIIIIII+-SOOOOOOOOOOOO (\?( ) ]f)ed", 0, 0 }, + { "[[^[(^(C.Jl[^X&Rb64a+Sd])'m[:alpha:])]]]{134,}", 0, 0 }, + { "()L", 1, 0 }, + { "[[(({224,(\?#88})@======(\?!=========(\?{=)PPP)i^@p(\?([:punct:]})^^[^^^^^^^^^^^^^^^^^^^^^@)m]|{CS{,-3}168)-[:xdigit:][:upperword:]hnD=Bns)z)AAAAAAAAAAAAAAAAAAAAAAA[^A{}ccccccccccc)SZ]Q-p.sD]]+P", 0, 0 }, + { "[[^[^]{135,}66666666666666666666[6(666i2M9.!uhmT\?JMm.*(\?!+)[:alpha:]eeeeeeeeeeeeeeeeeeeeeeeeeee]]])ZZ[:blankcntrl:][:ascii:]", 0, 0 }, + { "(13[3Ux>{,10}[(\?<=:xdigit:]))PL9{-89,-181}F'''''''''", 1, 0 }, + { "[^.|(\?{af]})^$XE!$", 0, 0 }, + { "(WWWWWWWWWWWWWWWWWWWWWWWWWWWW#J)", 1, 0 }, + { "({}}M7we-216)L[:digit:][:upperword:]", 1, 0 }, + { "([:aln[^u(\?=m:]))].z", 1, 0 }, + { "([:alpha:]{(92})%6{41,136})Vij@[:alnum:][:lowerprint:]", 2, 0 }, + { "[[[++(\?{+++{}})n{{137,}{51,-177}Z[]M*[:ascii:]{(-29,-47}2)$e^{,-195}{-156,}^]{}{-225,69}A]{-222,}{,20}m[:blankcntrl:]", 1, 0 }, + { ")l)[:alnum:][:graph:]g8TTTTTTTTTTTTTTTTLLLLLLLLLLLLLLLLL", 0, 0 }, + { "[([(\?<=.(\?{)/})mmmmmmmm(\?(mmmmm]{-154,-176}*S)I]", 0, 0 }, + { "[(([{(\?(\?<!im(\?imsix:sim(sx:,141}])D)l{,42}ttttt[(\?::punct:])){-162,-141}{-26,})dU@@@@@@@@@@@@@@@ S)\\A\?w|VVVVVVVVV)X.kN{,21}{-208,-52}>[:lowerprint:][:ascii:]e-]]]]]]]]]]]]]]]]]]]]]", 0, 0 }, + { "[^({}(){(66(\?=,}[^]'''''QQQQQQQQQ).P#>^){86,168}Z[(\?<!:lowerprint:]{-166,-70}<k", 0, 0 }, + { "APP[:alpha:][:alnum:]nd[:upperword:(\?(]^xxxxxxxxxxxxxxxxxxx)xxxxxxxxx{-70}[:punct:]l)U-", 0, 0 }, + { "[^(.\"od~(6({[^(\?<!228}\?)\?)######(\?:#########z )c(\?<!aQ`(\?{UKSwu[})][^-17]{11,}}][:ascii:]))^RiH+WyspP[qi&)=p6])[:space:]{-221,}]6p", 0, 0 }, + { "{-78}()[:xdigit:]{155}{,-92}", 1, 0 }, + { "[(\?>Q{,147}_____________(\?!______uuuuuuuuuuuuuTr]){74,179}{}){,103}{-209,16}*RRRRRRRRRRRRRRRRw{,87}9{144}[:ascii:]'<Ab", 0, 0 }, + { "([666c] {-171}yc,8-k_)EEEEEEEEEEEEEEEEEEEEE<", 1, 0 }, + { "[^(\?>(\?<!)2(\?imim:)6HwN)^|fc!(\?(d]75))065)G", 0, 0 }, + { "[[^xDB[:alnum:][:xdigit:]][:digit:]jW]([:alpha:])", 1, 0 }, + { "[ds~T+[x55[:digit:]X[JJJJJJJ.[(\?::upperword:]){,-14}][:xdigit:]bbbbbbbbbbb", 0, 0 }, + { "[qqqqq(\?<=qqqq(\?(qqq)^G[):ascii:]])W", 0, 0 }, + { "[:space:]JJJJJJ[:alph(\?<!a:]|[:ascii:(\?(])[:x)digit:]- XSstG[:g(\?>raph:])^)Ny6RF_ndoU9@*rxW{4,41}4{}", 0, 0 }, + { "[:punct:]{162,}j[:aln(um:].....................[^...]\?>z[:l[owerprint:]){55,222}]", 0, 0 }, + { "(>vWa)OXcccccccccccccccccccccccc[:alpha:]C{,-10}81|m1D^T)[:lowerprint:]''''[:alpha:]l", 1, 0 }, + { "(XZcgM/UI-/mZq-222){-85,-196}[:alpha:]{114}rrrrrrrrrrrrrrrrrrrrrrrr{,157}ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZLkD-&&&&&&&&&&&&&&&-][:alnum:]{}{,111}[:digit:]", 1, 0 }, + { "[^(\?:]MMMMMMMMMMMMMMMMMMMMMMMMMMM)cK[KKKKKKKKKKKKKKKKKKKKKKKK]P{146}", 0, 0 }, + { "([^[^wqesa)n\?L(\?<=FH+G[^rCGmfD]w)m1D\"%}]])", 1, 0 }, + { "[((\?:[^.HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH|S)xd)*[:space:](])[:xdigit:]ngr'G#/B]-----------------------------", 0, 0 }, + { ")[:lowerprint(\?<=(:]l))G p", 0, 0 }, + { "[^[^(\?<(\?<(=(\?imsximx:![(((\?<!\?(^))\?]^)[:xdigit:][:graph:]{-104,})Gf+GD*qc)c]f))])", 0, 0 }, + { "[^([\?())P[:alnum:]w]{-186,-139}-[:space:]RN3w[Fmvpl[:space:][:digit:]&&&&&&&&&&&&}(\?#}}}}}}}}}}}}}}}}}}}])z", 0, 0 }, + { "([[^^*C[()f][(\?=:punct([\?#:]o)]V)]%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%[^x{1f948})]]", 1, 0 }, + { "[(:xdigit:])zE", 0, 0 }, + { "[:pu(\?(nc)t:])(a*){-51}", 1, 0 }, + { "[^(.NKKKKKKKKKKKKKKKKKKKKKKKK-[:upperword:][:space:]`MPi>", -1, 0 }, + { "Nvvv[vv.][:alnu[^m:]+|Crrrrrrrrrrrrrrrrrrrrr[:xdigit:]j1n)v#]", 0, 0 }, + { "[^#}[(\?>:alnum:]).QQQQ[^QQQQQQ!!![!!!!!!!-s.n]se]{-238,}Tf]p4721", 0, 0 }, + { "([((\?#\?<=)+)Hr:-H]z[:graph:].{}oooooo(ooooooooo][:punct:]k<gXG@@@@@@@@@@@@@@@@@@@{,-176}){}L`)$", 2, 0 }, + { "({,249}{-73,}Z&&&&&&&&Ds35MB<v)qqqqqqqqqqqqqqqqqqqqqqqqq", 1, 0 }, + { "[^.N][:blankcntrl:]))))))))))))))))))))))))))))))", 0, 0 }, + { "(()*){198,}", 2, 0 }, + { "{-237,}220{}[:ascii:]```````(`````````````\?{-115,185}){,-18}[:punct:]'|Kk", 1, 0 }, + { "[(\?()])", 0, 0 }, + { "([(\?#[:alnum:]CQ)}}}}}}}}(\?>}}}}}}}(}}}}}\?310[|))xA5r][[^:ascii:]^{,-156}{])CCCCCCCCCCC-145]FzwOD_u\?", 1, 0 }, + { "[^[^[]{-163}{(-203}[(\?!:upperword:]PPGjZ[:xdi(\?=git(\?#:]{-73}s)qqqq(qqqqqqqqqqqqqqqqqq{173,210}[:xdigit:(\?<(\?>=]WW[^WWWWWWW\?*O)))Q){}08)[(\?(\?<=#:blankcntrl:]{90,}]U)])L)ooooooooooooooooooooooooooox--^c[:ascii:]])s)", 2, 0 }, + { "[(\?!:punc(\?imximx:t[^:]4F<}!)]'M-)tKKKa4904", 0, 0 }, + { "[^^{}\\(\?<!\\\\\\\\\\\\\\\\\\(\?#\\\\\\\\[:punct:](\?>)T000000000(\?(000)00000))+])", 0, 0 }, + { "L[:p(\?#unct:])", 0, 0 }, + { "[:upperw(\?<!ord:])", 0, 0 }, + { "@$\"\"\"\"\"\"\"[\"\"\"\"\"\"\"\"\"\"[^(\"\"\"\"\"(\"\"][]))*U{223,138}*o```````````````(\?=[```````````````]{238}mmmPPPPPPPPPPPPPPP&&&&&&&&&&&&&&&&&&)sF$[:digit:[]]", 0, 0 }, + { "[^#Txx[xxxlPB(\?><[^U/)]]{}X3333333333(3333333f*])", 1, 0 }, + { "<<<<<<<<<<<<<<<[^<<<<<<<<<.][(\?#:ascii:])[:xdigit:]|^", 0, 0 }, + { "([:punct:]{}){-167,}{-59,}Pd\"", 1, 0 }, + { "[((\?#{,214})t$)VVV[:xdigit:]{104(\?<=}D][:graph:])|H){1,}{-176,}", 0, 0 }, + { "[[([[^N,,,,,(\?=,,(\?#(\?:,,,,,,,,,,,[^,,,,,,,,,,]<,~4::_.A]){-52,}-[:alnum:]Pnnnnnnnnnnnnnnnnnn)d", 0, 0 }, + { "{-18(3,})uT{4,}", 1, 0 }, + { "[^[^[(p+c(\?<!b$))(\?:EU(\?(.][^{}]3[:xdigi[^t):][:punct(\?>:])[])][:s[^pace:]][:alnum:][:alpha:]]kw06E", 0, 0 }, + { "[^^^^^^JJJJJJJJ(JJ(\?=JJ(.6[:space:]H]{231,}A^eqqq)[:ascii:(\?>(])[(\?>:spa(\?:ce:]xxxxxxxxx)@_t-))138GNNNNNNNNNNNNNNNNNNNNNNNNNN[:digit:]no!`#E\?&[:lowerprint:].)[:graph:]{86,}[:digit:][:alnum:]", 0, 0 }, + { "[:g(\?<=raph:]a{114,146}(){}0Y[:bl(ankcntrl:])D)\?", 1, 0 }, + { "[^[^]*H{-192,96}S|]G)6B-kLB", 0, 0 }, + { "[[^[^][/NS8`um(\?{82&{((\?{\?<!-[110,-88}]m)})kkkkkkkk$$$$$$$$$$$$[^$$$$$@n%BuK@X!P)y0v!^]YY[YYY[YYYYYYYYYYYYYYYYYY///////{}{{{{{{{{{{{{{oiiii})]8{-2[53}w{82,}]{,245}]{-134}]fffffffffffffffffff]\"I>DW>9tN%{113}{unE", 0, 0 }, + { "[:(\?(alpha:]`))Y2sCqWQ104", 0, 0 }, + { "(([^()Wcccccccc(\?{cccccccccccccccccc(\?<!c(ccccc[:space:]$)(\?>)FZ{}{}`|||||||||||||*````````````````````````````'=dLQmx/Y.A7j'o}jn{}:})][:punct:]$|,-)!&Y:Ys#ykL7JJJJJJJJJJJJJJJJJJJJJJJJJ8yex>#mv[:punct:](x@)$[:uppe(\?<!rword:])_)", 3, 0 }, + { "[[(^HHHHHHHHHHHH(\?imsximx:HH(HHHHHH(\?{HH[HH])qjR>9))i})]a!lBW3p{A=or)ShE%[:punct:]{}]5r", 0, 0 }, + { "[:pu[nc[^t:]]]}}}}}}}[}}}}}}}(\?#}])@@@@@@@@@@@@@@@@@@DDDDDDDDDDDDDDDDDDD\?]xA2\?", 0, 0 }, + { "(.[:alpha:]xB7[:alnu(\?{m:]})RRRRRRRRRRRRRRRRRRRRRRRRRRRL)[:space:]G\?", 1, 0 }, + { "[:blan(\?<!(\?=kcntrl:]){71,})!ooooooooooooN", 0, 0 }, + { "()e$$$$$$$$$$$$$$$$$$$$iiiiiiii", 1, 0 }, + { "(b[:ascii:]67777777777777777777777777)({-106}kkk^F-------------------------------{13}A)f00000000sBAddddd{-66}kd!D'", 2, 0 }, + { "(Q ^])[^lf][:space:][:lowerprint:]\?", 1, 0 }, + { "[[^]\\S{152}W![:digit:][[^:space:(\?(]=pEhwY][:alnum:][:digit):][:graph:]])QQIC9h-oowf[:xdigit:]{-52}{,190}1111111111111111111fX{-189,226}W", 0, 0 }, + { "[^(\?!(\?<=)]).h[:as(\?>cii:])[:alnum:]$$$$$[:space:]3$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$1", 0, 0 }, + { "[[$zQ================(\?<!=(\?>=========(\?====D[^))|i{}\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?)][:s(pace:]]))]", 0, 0 }, + { "[^{,-[15(\?#6}]Vwjjjjjjjj[jjjjjjjjjjjjjjjjjjS9999)]q]rWWWWWWWWWWWWWWWW[:punct:]@@@@@@@@@@@@@@@@@@@@@@@@gO[:blankcntrl:]>L[:ascii:]:::::::::::::::::::x11uuuuuuuuuuuuuuuuuuuuuuuuuuuuu{-124,114}[:graph:]C#{tcg[:xdigit:]gZZZZ[:lowerprint:]nA(_{{{{{{{{{{{{{{{{{{{{SS)\\D[:alpha:]", 1, 0 }, + { "[^(\?())]!T\?[:asc[^ii:]E:4},,]I[^b(\?:n4(njj~+{\?'k{7}{189,-194}{ig.[[[[[[(\?#[[[_bs6,JD`1(\?<!WBo]F+{d*VO22z2K1][:xdigit:]))Suuuuuuuuuuu[^u{,117}\?YYYYYYYYYYYYYYYYYYYYYYYYB^]|q]:eY1GGGGGGGGGGGGGGGGGGGGGGGGGGGGe\?)bU[:punct:]", 0, 0 }, + { "[\?UA(\?:]\?)[:xdigit:]A^mmmmmmmmmmmmmm>>>>>>>>>>>>>>>>>>>>>>>>>>>>[^>>>(\?(>)){,-165}]", 0, 0 }, + { "([^[][^n(\?{[[p]#})|][^]L|66666666666[:graph:]][:graph:]2[:xdigit:][:space:]9b})[:digit(\?imsximsx::]+PZ):{}|E)[:xdigit[^:]|>]^[:alpha:]::::::::[:ascii:]````[:ascii:]:", 1, 0 }, + { "[:lowerprint(\?<!:])", 0, 0 }, + { "[[^[]{-47}[:lowerprint:][:punct:]L[(\?::g(raph:]lY[:alnum:])qWYU)}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}[c%$dp5[:alnum:]DDDDDDDD^^%&{,-94}E]{-8,175}[:alpha:]-.^[:digi(t:]CCCC(CCCCCCCCC]).ax72)", 1, 0 }, + { "[[^($$$$$$$$$$$$$$$$$$[^$I((\?{\?(u)\"YuK ZpOHq[!(\?>t|LQT(|)L[(:ascii:])", 0, 0 }, + { "[^[^([:graph:](QpPdyDQ`[:alpha:](.X[:digit:]wwwwwwwwwwwwww(\?imxims:wwwwwwwe(\?<!z)ONNN(\?#)[^])[:space:](KKKKKKKKK{113,}327[:xdigit:]k)]CeeeeeeeeeeeeeeeeeMMMMMMMMMMMMMMMMM)[:lowerprint:]]HHHHHHHHHHHHHHHHHHH]]]]]]]]]]]]]", 1, 0 }, + { "[Q(r(\?=)v]dm[:alnum:][:b(\?{lankcntrl:][:xdigit(\?=:])})P[:graph:]bd/Rx){50}{-150,-172}", 0, 0 }, + { "[(\?(im(\?:sxims:))9]))L", 0, 0 }, + { "[[^[(\?{^Z][^0[:alpha:]]\\XB*{-151}t})][:alnum:]]", 0, 0 }, + { "[([(D\?/////////////////////.'yvYysU&5AU-]kV)*){,123}z]", 0, 0 }, + { "[:alnu(\?{m:][:a(\?=lpha:][:alpha:])n}))7[:ascii:][:xdigit:][:punct:]-", 0, 0 }, + { "[^[:graph:]IIIIIIIIIIIIIIIIIIIIIII][:sp(\?<!ace:])", 0, 0 }, + { "[[[(\?=[[[cDD(\?<!D(\?:DDDDDDDDDDDD(\?<=DDD(DDDDDD(\?:DDDDDDD(\?<=D(\?()])rvp{243,}D$<[:space:]([:lowerpr)int:])])Ea{}U[:upperword:][:xdigit(\?#:]or}Z+34gD{/P NJ", 1, 0 }, + { "[^(,H>)*d2K0DNX5)T(].)[:digit:].", 0, 0 }, + { "([:punct:(\?#])})JJJJJJJJ[:xdigit:]PPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU.......................0hSk{,89}[:xdigit:].[:xdigit:]Z", 1, 0 }, + { "(LGTTTTTTTTTTTTTTTTTTTTTTTTTT[:alpha:]){-106,113}[:punct:]d|[:digit:]kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk\?wP", 1, 0 }, + { "([^[^<N_-k\?{(\?#18}]i]::::::::::::::::::::::::::)1+LLLLn{}/){-198}", 1, 0 }, + { "([[^(AAAAAAAAAA(\?(AAAAA)AAAAf).LzHHHHHHHHHHHHHHHHHHHHH(\?#HHHHH|)[ZEEEEE(\?#EEEEEEEEE(\?<!EEEEEEEEsG)q[:punct:]{}][:upperword:]D)[:space:][:digit:]+e[:ascii:]].i|JJJJJJJJ+n][:xdigit:]Se)P[:lowerprint:]_______________________________.[:punct:]pP{-172,86}iiiiiiiiiiiiiiiiiiiiiiiii){,-178}", 1, 0 }, + { "([\?=[[^,BDRRPZ{129}*D-[:punct:]]])([:upperword:]ud)\?][:punct:]A", -1, 0 }, + { "(([(\?#((\?{\?=^])c-)C[:lowerprint:]xvkR}k\")ccccccccccccccccccccNNNNNNN[:alp[ha:]{,93}vhlX:|A]2})nSw)]N.", 2, 0 }, + { "()g/qzyiV(x3d|A0wllllll){162}[:space:]", 2, 0 }, + { "qqqqqqqqqqqqqqqqqqqqvvvvvvvvvvvv8[:x(\?imsxmsx:digit:][:alpha:]''''''''''''''''''''''''''')", 0, 0 }, + { "({,226}nf^W=vs$xK^=A=M#b,)V", 1, 0 }, + { "(_T 2BC9N'cccccccccc-87EF#&^eQfDDDn._,m&c`tjAwR #~A)[:(\?imsimx:alpha:])/yHYL6|{-40,47}", 1, 0 }, + { "[[^]{-8(4,138})z[:xdigit:]{180,}]", 1, 0 }, + { "[([^T____________________(\?:__C(\?<=]-)])+[:ascii:])r[:graph:].----------", 0, 0 }, + { "[f{}LLLL(LLp((((\?<!((((((((((((((({,56}]BR`{,52}){-22,}\?[:space:]h>Sow", 0, 0 }, + { "{-179}^[:alpha:(\?!].a'5wacA3\\\\\\\\AAAAAAAA)~^]wC", 0, 0 }, + { ">[:digit:]{,-212}+(`)LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL[:ascii:][:digit:][:space:]", 1, 0 }, + { "[[^[[^RBW{,255(}(\?(\?>=(W)_]uu][:blankcntrl:])O)]]", 0, 0 }, + { "(C_______________________________)2", 1, 0 }, + { "([/ntf_a3].)", 1, 0 }, + { "[:space:]+[(:upperword:],c7[:asci(\?<=i:]ggggggggggg)[:ascii:]/1$$$$$$$$$$$$$$$$$$$$$$$$$$)", 0, 0 }, + { "Xq{109}~EEEEEEEE[:upper[^word:]lgB:X(h[:alpha:]B[:space:]].)IkaH@3}}H'yK~\?[:upperw(\?#ord:(\?:]){=================[:blankcntrl:])", 1, 0 }, + { "(([[^]]$3Xr^$%%%%%%%%%%%%%%%%%%%%%================U[:ascii:])X).FFFFFFFFFFgO[:punct:]oooooooooooooooooooBC[:blankcntrl:]mmmmmmmmmmmmmmmmmmmm[:lowerprint:]rBM~<HAc#Sb&&&&&&&&&&&&&&&&&&&&&&&&&&&&&Cy", 2, 0 }, + { "([([([^(\?:)D]-{M#H >rERRRRRRR[^RRRRR(\?>RRRRR])[(\?=^)X]{207,}U])))Z[:blankcntrl:]]yyyyyyyyyyyyyyyy\?", 1, 0 }, + { "[Q(\?{*[^(\?(\?!!])[:graph:]]})[:alnum:]iE)dGGGGGGG[^GGGGGGGGGG[:xdigit:]w]", 0, 0 }, + { "[^Z(\?!6(\?(\?><=)[:graph:])]BBBBBBBBBBBBBBBB^)", 0, 0 }, + { "[[^([^[^][[[[[[[(\?({[[(\?(\?imsxmsx(\?imsi[ms:::[[[[[[[[[}))]$)){12,})|:::::::::::::::::::[:lowerprint:]{}{-96,-147}){13,}`[:digit:]]\"^Ca%%%%%%%%%%%%%%%%%%%%%%%%%%UUUUUUUUUUUUUUUUUU]]9", 0, 0 }, + { "[^(\?(\?(\?#!<=))JLBS\"zi)'''''''''''['''''''''''''piiiiiiiiiiiii(\?<=iiii]])ZZZZZZZZZZZZZZZZZZ[:space:]", 0, 0 }, + { "({})[:punct:]", 1, 0 }, + { "E9[:blankc(\?{ntrl:]})N", 0, 0 }, + { "[:alph(\?#a:]){198,}sq\?X0B7", 0, 0 }, + { "[^\\\\\\\\(\\\\\\[\\\\\\\\\\\\[(\?<(\?isximsx:={11(\?(9,}\?0])]]))\?FN3M\?{-128,}Z444444)444fbLiVN8)", 0, 0 }, + { "[[^[^([[[[[[[[[(\?>[[[[[[[[[[[[[[[[[[[[[{53(\?<=,-175(\?>}ggggggggggggggggg%))[:alnum:])[:punct:]kkkkkkkkkkkkkkkkkkkkkkkkk)+Soooooooooooooooooooooooooooooooo](WR+--)x36+llllllllllll{,35}]Fqb^=F]KKKKKKaaaaa{,131}", 1, 0 }, + { "(g\"Ssqw<&{Cl{82,}Mdf|9cIlmCW{}[:digit:]4C{}[:alnum:]PP)", 1, 0 }, + { "OOOOOOOU[*evVIIIIIIIIIIIIIIIII(\?#(\?#IIII)]PP[:xdigit:]2222222222222222[:xdigit:]Kx)p[:digit:]", 0, 0 }, + { "([[{248,16(\?=5(\?#}][:alpha:])|[:p(\?!unct:(\?(]", 1, 0 }, + { "[pP((\?=S)(\?#)]$[:aln(\?(um:)]2\?)$GGGGGGGGGGGGGGGGG({-U:c){-61,}[:ascii:]{-202}G", 1, 0 }, + { "()$D[:alnum:]", 1, 0 }, + { "[(\?#^]){}[:ascii:]", 0, 0 }, + { "[uuuuuuuuuuuuuuuuuuuuuuuuuuuuuu]FFFFFFFFFFFFFFFFFFFFFF&2e\?)%oP'mc@z2b}n{<b4_Laz^0LLLLLLLLLLLLLLLLLLLLLLL,,,d", 0, 0 }, + { "{}(^________________''|$)RRRRRRRRRRRRRRRRRRR", 1, 0 }, + { "(H)####################bbbbbbbbbbbbbbbbVSSSSSSSSSSS|tdU\"goeAbPP{-248,81}", 1, 0 }, + { "[^[(\?ims(\?>xisx:)UHpP*n{}]{}fx14<7OEpE>n2150)8888888888888888]^GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGS", 0, 0 }, + { "(d)+", 1, 0 }, + { "[^.(\?(>)(\?=e)])al[:space:]x", 0, 0 }, + { "[^256c(\?!]){-19,}", 0, 0 }, + { "Q)", 0, 0 }, + { "[^s\?\?(\?{\?\?\?(\?#\?(\?<!\?\?\?\?\?\?\?\?\?\?\?(\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?{}]F\?j(jjjjjjjjjjjjjjjjjjn)kTI1f[{1|(\?<=^[^+[:digit:]{}^s^))})))T]{-17}{CCCCCCCCCCa{-21,}{,-146}^uZQB]YuLu-|tUGRMz^^", 1, 0 }, + { "([^.{}.EE[EEEEEEEE(\?<=EEEEEEEEEEEEEEEU]]-@s))$", 1, 0 }, + { "[^([((\?#[#])|a)])[cccccccccccccccc][:digit:]LLLLLLL[:alnum:]}[P%vzl{}^]&", 0, 0 }, + { "({}[:space:]E)101+A{-35,11}", 1, 0 }, + { "(va:7)u[:alpha:]", 1, 0 }, + { "([^[[rrrrrrrrrr(\?:rrrrrrrrrr(\?<!rrrrrrrrry|D'*AH@a{}\?[:space:][:alpha:]^]$ {-225}[(\?(:as)(\?(>cii:])){-107,-139}6/{^[:upperw(\?imsxmsx:ord:]{,-47} ]wuH#nAn)GGGGGGGGGGGGGGGGGr[)]T{91}lJ))[:lowerprint:][:xdigit:][:lowerprint:])]*", 1, 0 }, + { "()[:space:]~!$[:alnum:]JJJJ[:ascii:]", 1, 0 }, + { "[^(\?<=)-]()k", 1, 0 }, + { "(()W){,8}ea", 2, 0 }, + { "({,-56}5G&&&&rrrrrrrrrrrrrrrrrrrrrrrrrrk.8) hWJ,TM)0Yd-", 1, 0 }, + { "(Z-fddddddddddddddddddddddd)-{9}", 1, 0 }, + { "[^<[(\?!:asc(\?:i(\?<!i:])F])[:alp(ha:]b))-}Wwx8B", 0, 0 }, + { "[^[^[^([(\?{}(\?=)(\?())-CCCCCCCCCCC(\?=CCCCCCCC(CCCCC(\?:CCCCCCCC(\?{l[(\?!:space:]})[:upperwor(\?:d:]{-27}[:al[^pha:][:xdigit:]^f", 0, 0 }, + { "[[^]G@>2!+[:punct:(\?<!]{,189}6ZF[:blankcntrl:][:digit:]{,214}){-115,-14}l[:upperword:]{101,}Z[:ascii:]Ld&02|c]<0~<bc", 0, 0 }, + { "(Q)[:digit:]x", 1, 0 }, + { "hT[[:alnum:]\?]O[OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOxFF%^(\?(_LN 8uXQT\"*/L)+l)>qQ[^]e[:ascii:]PP()[:digit:]NQ8%6d=&2I{-62,-142}w]].e{}*", 1, 0 }, + { "{,-219}xxxtEEEEEEEEEEEEEEEE[:pun(\?(ct:])qqq)nnnnnnnnnnnnnnnnnnnnnnnnnnn", 0, 0 }, + { "[:di(\?>git:])W4", 0, 0 }, + { "([^y])Fkvto$", 1, 0 }, + { "[^($$$$$$(\?!$$$$$(\?{$$$$$$(\?<=$$$$$$$$$$$+===)[:alnum:]MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM)Z]{}^[:blankcntrl:]--xxxxxxxxxxxxxxx[^xxxxxxx)\?tVG\?{232,81}{121,}xn{,-226}})tttttttttttttttttttttttmu(\?<!&&&&&&&&&&&&&&&&&&&&&&0b]z)$87{,-192}{}{-242,}", 0, 0 }, + { "l[:dig(\?(it:]|s*)aA[:digit(\?<=:].^.))x[:digit:]", 0, 0 }, + { "[:grap[^(\?#h:]').]Z", 0, 0 }, + { "[:gra[^ph:]t[:digit:]222222222222(22222222222222222H qM]pWZr[:ascii:]-hRb_.)Q{-228,-204}{}", 1, 0 }, + { "AAAAAAAAAAAAAAA(AA)YeX", 1, 0 }, + { "(!dqqqF*^){(,-79}s!!!!!!!!!!!!)", 2, 0 }, + { "[^(\?msxm(\?#sx:]|)ZHYup)j{95}0L:vXB#')d'DX\?m.T034\\\\\\\\\\\\\\\\\\\\\\y5rV{}S", 0, 0 }, + { "(W*O+yl([\?!P(\?:)I]${}{-195,-14}[:upperword:]{}[:xdi[^git:][:space:]X[:grap[^h:]~]zzzzzzzzzzzzzzzzzzzzzzzL)+)Y b.-=jf{-216,}${/!}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}|]", 2, 0 }, + { "[^\\\\\\\\\\(\?<=\\\\\\\\\\\\\\\\m]{-48,234}[:alpha:]s)", 0, 0 }, + { "[(\?{U}(\?<!)])LLLLLLLLLLLLLLsssssssssssssssssssssssssss[:ascii:][:blankcntrl:]---------b", 0, 0 }, + { "[^[^[(\?#)(\?imsxims[x:)<<<<[<<<<<<<<<<(\?<!<<<<<<<([^\?(<<<<<<<<<<z(\?(zu(\?<=~83}aZpIE)[:alnum:](\?imsximsx:(\?!jrE6(\?<!\?V(SzDU)000[000000000((\?=\?)=0])L|lOYuWXk", 0, 0 }, + { "$o[:dig(it:]nnnnnnnnnnnnnnn{-94}|G)[:alpha(\?!:] {,-108}D=\?>[:digit:]S[:space:]t", 0, 0 }, + { "()n", 1, 0 }, + { "[:upp(erword:]$)<}.vZM<lEY5Y*", 0, 0 }, + { "[^([^\?>)rCD&{5(\?msxisx:7,}qqqqqqqqqqqqqqqqqq{31,}@w#W:(@(\?:zp$YYYYA[:alpha:]{1}A)*dZJ\"5OG|\?(\?#a])]|){-150}[:xdigit:]", 0, 0 }, + { "[($)gwo{`\"]{-160,}\\\\\\\\\\\\\\\\\\\\\\\\\\66666666666666888888888888", -1, 1 }, + { "((}DA+Rc000000000000000000)%vvvvvvvvvvvvvvvvvvvvv%C&emZ*[:alnum:]#m/D[:graph:][:blank[^cntrl:]E{,168})kkkkkkkkkk000000000000000]", 2, 0 }, + { "[^[u*(\?#x01234)oxGGGGG(\?([GGGG)GGGGGGGGG]^U)!!CCCCBM`4QB^XEN]{,-60}[:upperword:]G]", 0, 0 }, + { "(%)~t{S,K^MI3PMo)=b", 1, 0 }, + { "[[[^]{}eU([:xdigit:]&&&&&&&&&&&&&&&&&)\"W|43[:alpha:][:graph:]J8b[:blankcntrl:]gggggQ{,183}{,-254}\?[:ascii(:]{,134}", 1, 0 }, + { "[[([^[^([^(\?=)1RRRRRRRRRRRRRRRRRRRRRR(\?:(\?(\?(\?!=#RRRRR(\?=RRRR(\?<[^!Ru)])]o[:[graph:[^]{,7})[:digit(\?::]{-215,}e[:space:]]", 0, 0 }, + { "({{{{{{{{{{{{{{{{{{KKKKKKKKKKKKKKKKKKKKKKKKKKKKBBBBBBBBBBBB)[:space:]0[:alnum:]HcctQA", 1, 0 }, + { "[^(pP7(HsN[^g{186,-87}\?\?]EQ%u:-Y)+>>>>>>>>>>>>>>>>>>>>>pP][:alpha:]", 0, 0 }, + { "[(.{141}h|)((\?:\?=@Q} ghcC{+*(R)D+][:lo(\?#werprint:]zzzzzzzzzzzzzzzzzzzzzzzzzzzzzz))", 0, 0 }, + { "[^({}S)PPFl(])-216", 0, 0 }, + { "[([[^(((([(\?#^[^[^\?4[(:[dig[^it(\?(:]{122,})y\?", 0, 0 }, + { "[[2${188}u{1(4(\?(1,1(\?{98}e{&tbaoI]q)[:punct:])d}))Nqffffffffffffffffffffffffffff[:ascii:]+]", 0, 0 }, + { "()K-", 1, 0 }, + { "[[{2(2((\?(\?!()2})])[:alpha:]fVVVVVVVVV{-47}):::::::::::)\?vwyyyyyyyyyyyyyyyyyyyyyyyyy-]{}", 0, 0 }, + { "ivcs)g", 0, 0 }, + { "(hhhh[^hhhh(\?{h\?]})%%%%%%%%%%%%%%%)\"+38mbY:s9{/d# zaNnbQb)b:*zpKI{-26,-189}", 1, 0 }, + { "S*(#)[:graph:]lllllllll&G)t", 1, 0 }, + { "([^[(([\?=\?<!)]]___{-63,})]nt", 1, 0 }, + { "[:b(lankcntrl:][:alpha:]*[:pu[^[nct:][:alpha:]A]$aaaaaaaaaaaa*)A[:digit:]U][:alnum:]", 0, 0 }, + { "[^f[^p000{68(\?isxmx:,}(\?!vvvvvv)$)]PP#*{(})[:punct:]&&&&&&&&&&&&&[:punct:]\?][:blankcntrl:]", 1, 0 }, + { "[^(((\?(\?(()))GGGGGGGGG{(\?!($)))((\?!)V^{228,145}))]{-229}Qjjjjj[:punct:]R)", 0, 0 }, + { "[(Q[^((\?{(\?:]~z)})gE(.<){}|)Kuuuuu$*222222222222222222222D]", -1, 0 }, + { "([^`(\?<=`````[^`````````M]\?)=L74A[:upperword:]]P", 1, 0 }, + { "(({}[:space:]qv-T){,-192}{-45}{65}9\?X).d", 2, 0 }, + { "_[(:upperword:]mU(P}qX>\?%)$Lwq[:alpha:]{-115,}================================{127,}", 1, 0 }, + { "e)", 0, 0 }, + { "[{,2[5}Klen+D0'YX(\?<=|_H]I,Y\"*/<3sM[:digit:]])#.", 0, 0 }, + { "[:(xdigit:]){[:digit(\?mxmsx::][:as(\?<=cii:]d!{135})#)pP[:space:]Syyyyyyyyyyyyyyyyyyyy\"Gg8", 0, 0 }, + { "[(\?()])", 0, 0 }, + { "[^([^[^[[^[:alpha:]SIus[^f<f]}}}}}}}}}}][:xdigit(\?=:]Z{-13}*]_[]LLLL)]E[:alnum:]b$)]]]]]]]]]]]]]]]]]]]]]]]]][:lowerprint:][:ascii:]{,40}{86,}333333333999999999999999999999999999*fffffffffffffffffffffffff99999999U9|[:digit:][:upperword:]oowwwwwwww[wwwwwwwwww{195}[:xdigit:]]H{-73,153}R+zAz{}r/////////////{232,}kAoffffffffff[:blankcntrl:]xxxxxxxxxxxxxxx]KKKKKl0,[:alpha:]|{,-165}Qc{96}CCCCCCCCCCCCCCCCCCCC/", 0, 0 }, + { "{}:V(7O-)[:ascii:][:graph:]PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#", 1, 0 }, + { "[^(\?<[^=CC(CC$)]* c)BBBBBBBBBBBBBBBBBBBBBBB]z{-18,}", 0, 0 }, + { "[[qqqqqqqqqqq(\?(qq235|ttttttttttttttttttttttttttttt[[ttt<<<<(\?{<<<<<<<<<<<<)<<<<<<<<p)/S9(\?{OOOOOOO(\?<!OOOk)})]nIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIb]Z})", 0, 0 }, + { "[^[^(\?>][^((\?<!C(\?!+(\?=)]^8)6nx).)){,-13}[:blankcntrl:]\"(L{}){,29}nnnnn{-83}]l[:upperword:])", 1, 0 }, + { "[(ZZ\"#(\?#Nb(\?<!:U)oRRRR])Zei${Ec/)s", 0, 0 }, + { "[^[^[(\?(t(\?:3```````)`````)|#CB)/////////////////////////////*!liB#|CCCCCCCCCCCCCC(\?=CCCCCCa7N]weTTTTTTTTTTTTTTTT1{}o\?{}BBBBBBBBBBBBBBBBBBBBBBBB.])u{-218,126}.,[:space:]]", 0, 0 }, + { "[[([:alnum:])yyy(\?!yyyyyyyyyy(\?!yyyyyyyyyyyyyyyyyyy[:graph:]I])Uw*X.^[:ascii:]{,-63}[:digit:]{-88})&&&&&&&&&&&&&&]*", 0, 0 }, + { "[[[^K(\?=KKKKKKKKKKKK(\?:KKKKKKKKK[KKKKKK]]U[:digit:])]dd)({,16})xy+Pu)JJJJJJJJJJJJJJJ[:space:][:ascii:][:upperword:]ql_jywmt4B+]{-30,}^555555555Xza[:punct:]", 1, 0 }, + { "[[^^XXX(\?:XXX((XXXXXXXXXXXXXXXXXXXX)v)$N9$r\"\"\"\"\"\"\"\"\"\"\"\"\"].{,239}$[:punct:]\"9999][:alpha:]{}c){,55}s[:upperword:][:xdigit:]310", 0, 0 }, + { "[@([^I8oNl)]-{-203,-224}{-78,}KKKKKKKKc{-66}[:xdi(\?=git:]==========){}f{-124,}[:upperword:][:lowerprint:]]{}--------l+", 0, 0 }, + { "[^]ozp+0(\?#\"[(\?()X]))[:blankcntrl:][^e{99,222}JJJJJJJJJJJJJJJ3F]\?[:blankcntrl:]l$ot", 0, 0 }, + { "[[^[[((\?isximx:)2222222222(\?=22222[:graph:])+U)((\?{\?<=(\?()iYv8qc@#y)G])+}))FvnP\"7OZ-b273[:ascii:]Ak6*`S[:digit:][:graph:]]{2}^G{79,}DDDDDbbbbbbbbbbbbbbbbbbbbbbbb(bbbbbbb)|tP48y{wNJ_S hJbY]]dc", 1, 0 }, + { "[:alph(\?{a:]p1[:lowerprint:]}){163,}", 0, 0 }, + { "W()", 1, 0 }, + { "()``````````````````````````[:ascii:][:alnum:]{,26}[:graph:]", 1, 0 }, + { "[:al(\?<!num:]|byyy,*)U5%u${190}-{-221,-33}k7777777777777777777777777777777+eXXXXXXXXXXXXXXXXX[X(\?(XX)XX)S'vEAa]*e", -1, 0 }, + { "[^(([R_AC[lE'{2(\?{28(]8LTt[]b[:punct:]]O)|2[:graph:][:space:]}) x3C[:alpha:])uI+dddddddddddddddddddddddd{-165,}FFFFFFFFFFFFFFFFFFFFFFF)cccc*[:upperword:]]G{,-38}{24,}555555555555555555555555555VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVZ[:blankcntrl:][:ascii:]", 0, 0 }, + { "[^QQQQQQQ(\?#QQ(QQQQQQ[:punct:][:space:]){(\?(\?:!}[:graph:]t}}[^}}(}}}}}444444[^444444444444444444444]\?]G)E)L{,-103}{84,}r$ii]-[:alp(\?<=ha:]S5G~9>n*)P<3tttttttttttttttttttttttttt)n{}[:graph:]eeeeeeeeeeeeeeeeeeeeeeeeeeeeeee{,83}[:digit:])0BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB[:alpha:]{-155,}{151,}", 0, 0 }, + { "Ue{,254}+f[:lowerp(\?<=rint:]U.fff)", 0, 0 }, + { "QQQQQQQQQQQQQQQQQQQQQQQQAY<J)'MPi_u%#2doopqU7/{103}[:graph:]e!7{GOr", 0, 0 }, + { "[^({,[^233}[^d)BBBBBBBBBBBBBBB=======(\?>===========[^=S|[^[:alpha:]G/]qqqqqqqqq{}[:xdigit:])..k", 0, 0 }, + { "[([^[[:space:]ffffff(\?=ff]M]))[:xdigit:]UbCI,CzalLU*y5I[:digit:]r{-30,180}{-209,-45}Paf]", 0, 0 }, + { "[^[h(\?{hhhhhhhhhhhhhhhhhhhhh})]{,143}[:lowerprint:][:ascii:((\?(\?=])[:asc)ii:])zp]", 0, 0 }, + { "[[(\?{]})]", 0, 0 }, + { "[[1\"3m^,(\?<!2((\?!\?#t```````````````````````````)\?)|c^)A^~]{61}W\\\\\\vvvvrrrrrrrrrrr[:digit(\?#:])]F[:upperword:]dX\\\\", 0, 0 }, + { "([${144,}(\?<!)-RAk_F(\?imsxisx:=9]z/))", 1, 0 }, + { "[[^[[[^([[^[^[^([[^([[Uiiiii#####(\?(\?{(\?<!#########(\?=#####).^)(.|>2m[M/2222222222222222222222222222(\?:22222222222(\?#22(\?:(\?=22222{,243}]x68+I/K)11111111111]\\pP[:graph:]$[:space:]^{}A)[:xdigit:]-={>", 0, 0 }, + { "[(\?>[(^()Vty2vvvvvvvvvvvvvvvvz^])ZZZZZZZZZZZZZZZZZZZ----------------5\\dVLSp8UE2m+z3X/Sd", 0, 0 }, + { "[}}}}}}}}}}}}}}}}}}}(\?#}}(\?<=)|*C ]*29JW7O9mEB]pE_OoxN)[:alpha:]", 0, 0 }, + { "([^((\?<=\?)D{,200}.[(\?#:ascii:])[:space:].)[:alpha:]D|[:graph:]{,-41}*LLUUUUUUUUUUUUU{-189,-131}]qHR<k2@P{27}<^e,ub%\?/4){-243}+[:digit:]%*x9lA^", 1, 0 }, + { "([:alpha:]bT&+_)$Z{,212}x26`", 1, 0 }, + { "[^([^(A{[^}g(\?()A9p#54b]-------------------------------).wzD#=f\\)A)8a]]DNNNNNNNNNNNNNNNNNNNNNNNNNN", 0, 0 }, + { "(W000000000000000000000000000000)", 1, 0 }, + { "www(wwwwwwwwwwwww)", 1, 0 }, + { "()555555555555{18}i+[:alnum:]E {}U", 1, 0 }, + { "SqbHoooooooooooo[^oooooo([^ooooooo])\\N[:xdigit:]]oooo`", 0, 0 }, + { "[999999999999999999uE{193,0}lx{7917}[:punct:]4&d]{221,}[:digit:]{49,156}[:lowe(\?<=rprint:])[:space:]{-33}w+", 0, 0 }, + { "[^(\?{})<{220,-193}[(\?=:xdigit:]UUUUUUUUUUUUUUUUUUU'{-18}])", 0, 0 }, + { "b[(\?<=:upperw(\?{ord:][:digit:]})EEEEEEEEEEEEEEEEEEEEE//////////////////){177}C", 0, 0 }, + { "(^).[:alnum:][^[(\?=[(\?{[})DA5{)[[I~y&O\?9>])]][:blankcntrl:]M[:alpha:]x9[:upperword:]|[:xdigit:]b", 1, 0 }, + { "()[:digit:][^[U}-]]{,206}V*WJ@R]\?", 1, 0 }, + { "[^](\?#{}(\?[<=)yv)]r", 0, 0 }, + { "({,-192}//////////////////////7!eW_0eoL){}", 1, 0 }, + { "^[:punct:(]+)IIIIIIIII[:punct:]P$pP", 0, 0 }, + { "[(\?=|U)^-]{-52,-72}[:digit:]*6666666666\?{{{", 0, 0 }, + { "([^f(\?:+{1((\?=34,}]))^)s0bux7\?5`Bwr[:upperword:])Dy+", 1, 0 }, + { "AL{}:::::::::::::::::::::::::::::::{,(104}~@,Ysey@h).", 1, 0 }, + { "[^((.)))(\?()))))))))))))))))))))(\?msxims:))))))))))[)][:upperword:][:alpha:])", 0, 0 }, + { "[^(()f])G^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^T{}N*nK[G]{,61}^^^^^^^^]", 0, 0 }, + { "[(N::(\?<=[:digit:][:graph:][:space:]xB5[(:xdigit:]|Yv{}HHHHHHHHHHHHHHHHHHHHHHHHd).[:g(\?<=raph:])[:digit:]<<)[:digit:])[:space:]Q[:punct:]x7C]", 0, 0 }, + { "[^((\?(\?(())a)(\?!){})W)pP3333333333(33333333333333333333hhh]{})", 0, 0 }, + { "[^ [ a*FFFFF[^FFFFFFFFFFF(\?<[^!FFFF(\?=FF])])L1]{,-52}{B-bxsPKg{,8}[:digit:][:punct:][:upperword:]DD${,-131}", 0, 0 }, + { "($$$$$$$$$$$$$$$$$$$$$$$$$$$$$^pP),,,,,,,,,,,,,(,,,,,,,,,,,,)QQQQQQQQQQQQQQQQQQQQQQQQ", 2, 0 }, + { "[:lowerprint:]|l{(,-54}C{}*-)IIIIIIIIIIIIIIIII", 1, 0 }, + { "()+", 1, 0 }, + { "[(([(\?{[:punct:]]|))[[[[[[[[[[})]WWWWWWWW&$$$$$$$[:graph:]", 0, 0 }, + { "[^(\?{}){(107[(^,}][:space:[]))^w,&aPPPPPP[^PPPPP{117,-213}s\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?]]]222222[:d(\?(igit:]NNNNNN)NNNNNNNNNNNNN8)I", 0, 0 }, + { "[^(\?<!$)|TTTTTTTTTTTTTTTTTTTTTT(TTTT]a8)2<", 0, 0 }, + { "([^[]%[^[^]-][:alpha:]37*:[:space:]]lQvu)[:xdigit:][:blankcntrl:]", 1, 0 }, + { "[[Bl_>9C^:\?X_KK]2sw@hHZT!],uuuuuuut|lFW()'''''''''''''''''''''[:graph:]<~v{-251}0[:digit:]C[{222,}]{,41}{}*g^UuS/{-114}", 1, 0 }, + { "(D{,-79}[:gra(ph:(\?(]C[:ascii:]))I[tC.%tkllll[^llllllllllllllll]&&&&&&)&&&&&&&&&&&&&&&&&&&&&&)]10435", 1, 0 }, + { "[:al(\?{[^num:]]})}x'[:(\?#xdigit:])xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxKKKKKKKKKKKKKKKKKKKKKKKKKKKKTTTr*%{~f", 0, 0 }, + { "[ZQKEEEEEEEEEEEEEEEEE(\?<!]3|.~~~~~~~~~~~~~~303)33333333333333333", 0, 0 }, + { "(-62([:ascii:]5555){-230,}<<<<<<SM[:punct:]{72}|E{160,})Pfqba!{,-188}DS{ +2tRu\"0JG$", 2, 0 }, + { "([^(\?:(Ea00000000000000[:punct:][:graph:]{}]))[:xdigit:]{-65}t){164,}", 1, 0 }, + { "[\?$$$$$$$$$$$$$$$$$$$$$$$$$F......(\?(.).q#R:j6%TTLCdtuM|8*54<GHoqEh9FBW0:W]L0)o][:upperword:]", 0, 0 }, + { "[(\?>[:alnum:]W[:space:]]D)|L", 0, 0 }, + { "(M(MM)[:alnum:]|[:lowerprint:]4)", 2, 0 }, + { "[[^(\?:{}{2[2(\?>0,})]]]Etu)-)", 0, 0 }, + { "([^[^^z[:graph:]]#{-144,96}[:punct:]!4LY//////////////////SSSSSSSSSSSSSSSSSSSSSSSSS[[^:xdigit:]\?`-!L#p0{52}]%{-121,}[:graph:]]WqJ>$6UBg{,7}[:blankcntrl:])[:upperword:]y2wW!A[:blankcntrl:]0CN\?", 1, 0 }, + { "[[^(\?:|+bII(IIIIIII(\?(\?>!)275SIIIIIIIIII(IIIIIII(\?=IIIIII[:graph:]|)`]S\?.}A)[:alnum:]Jgggggggggg{-150,}{-89,})[:alpha:]Q)|07be5:j)]", 0, 0 }, + { "([(\?i(ms(\?=x-x(\?>:))C)]){})>eIqm~lFb[:upperword:][:blankcntrl:]w=[:digit:][:graph:]", 1, 0 }, + { "([HHHHHHHHHHHHHHHHHHHHHHHHHH[^HHH(\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?!!!!!!!!!!!!!!!!!!!!{23}]~J=[:ascii:]tttttttttttttttt])-216", 1, 0 }, + { "B{[^-32,246}{13(\?!0}q>GVQw*[:digit:][:punct:].77777777777777777777`T(-t01odD]\?${}{-247}+gV{131})+[:lowerprint:]m/z~d", 0, 0 }, + { "[t[$FV+(\?=E=[^])]-$U{-22[5,}{253,}08g]$[{}][:xdigit:][:punct:]{-18}{-173,}]{,-191}V_|90", 0, 0 }, + { "()$", 1, 0 }, + { "[^[^((((((((((((((W[(\?::blankcntrl:]&-JH]J){93}LLLLLLL|r{,221}tY/172]-AS", 0, 0 }, + { "[^()(\?{qqqq(\?msimsx:qqqqqqqqqq3999999999999GGGGG|S*W%{,128}][:xdigit:]AJt]}\"Zf!lRpr{>){,36}})", 0, 0 }, + { "[([]^]^)", 0, 0 }, + { "([.(\?#){}[:alpha:]\?S{2}P%Gw]nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnYiq5)>i*r<", 1, 0 }, + { "[ggggggggggg$PPP:S (:]N{239,}|A[:lowerprint:]vvvvvvvvvv[:lower(print:]{-184}({-133,}+)[:punct:]P/Q.OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO", 1, 0 }, + { "(RRRRRRR[^RRRRR[RRRRRRRRR])]", 1, 0 }, + { "[(\?:^])D%", 0, 0 }, + { "()[]#C[+[j]{,29}-]", 1, 0 }, + { "(([(\?(((\?{\?!(\?=\?=#[Es*){02$r'}(\?:3pz)uPPPPPPPPPPPPPPPP(\?(\?>:PPPP][:graph:][:ascii:]`.)[:punct:][:a(\?mxi:lnum:])r)$)[:xdigit:]$[:(\?=digit:])aa[^]a)\?])sQQQQQQQQQQQQQQQQQQQQQQQQQQ^|$)-}))", 2, 0 }, + { "z@@@@@@@y${}[:(\?:upperword:]l\?{,144}-)", 0, 0 }, + { "[:aln(\?:(\?>um:(\?imximsx:]){})FGGGGGGGGGG|-p){,105}", 0, 0 }, + { "[[{17}llllllllllllllll(\?:lllllllll{,(\?#-94}OUUUUUUU(\?#UUUUUUUUUUUUUAA]p[:digit:]{-1(57,}5yyyyyyyyyyyyyyyyyyyyy[:alnum:]v{-185}^^^^^^^^^^^^^)d[[[p)]))", 1, 0 }, + { "()|[:digit:].E2o", 1, 0 }, + { "()3[:lowerprint:]", 1, 0 }, + { "[(\?{(\?#(\?>SN}[^)z+r^t[:digit:]seP[:alnum:]$b1ZY[U(\?<!U4IIIIIIIIIIIII(\?<=IIIIIIIIII]m)]))]4)", 0, 0 }, + { "{,74} qkk[^p]kbi6>{}000000000000000000000000000000$|)", 0, 0 }, + { "[:(\?=digit:])v{164}", 0, 0 }, + { "[:graph:]h[:upper(\?(wo(\?{rd:)])00000[^000000000000}).4OEVf{,-46}]A", 0, 0 }, + { "[](((((((((((((((N{{{{{{{{{{{{{{{{,-1}e]a{-166,-44}", 0, 0 }, + { "([[^[^[(^[]]YYYYYYYYYYY]D.cQ{}[:alpha:]ttttttt000000[^0000(\?<!0000000000000000N::::::::].][:alpha:]#5\?{}{-253,-193}]\\[:ascii:]tS{,35}B)ffffffffffffffffffffffff))/", 1, 0 }, + { "(G)[:alpha:(\?#])W{-197,-220}w8", 1, 0 }, + { "{-2[^00,(\?#-([84}ig+)]]l[:graph:][:graph:][:space:])aaaaaaaaaaaaaaaaaaa{-208,}ea{,224}", 0, 0 }, + { "[^[W(\?<=[B[:xdigit:]{255,}FAAAAAAAAAAAAAAAAAAAPP])[:xdigit:]+][:lowerprint:]${-195}", 0, 0 }, + { "[v{104,}BB].HHHHHHHHHHHH[:ascii:]bbbbbbbbbbbbbbbbbbbbbbbbbbbb(btttttttttttttttttttttttttt){180}", 1, 0 }, + { "[^(i[^iiiiiiiiiiiiiiiiii(ii)n])#######################]", 0, 0 }, + { "(([:space:])[:g(\?>raph:])[:punct:][:upperword:]LV\"t+t!)[:ascii:][:lowerprint:]q", 2, 0 }, + { "[[[^([7(\?[<!)\\PP~D7L (\?imsimsx:(\?= $GS26L3-J(\?()!)]]{-178}%$[:p(\?!unct:]))yyyyyyyyyyyyyy@w,[11!R86:)G*[(\?(:blankcntrl:]267$~L\?{-108}k[:alnum:]So\?Y/eq]-|[:xdigit:]555555555555555555555555555)55555........W*O))][:alnum:]]I{,-126}[:lowerprint:]8\?[:xdigit:]u%wHc6\?:Pc...........................,,,,,,,,,,,,,,,,,,,,,,,,,,,]", 0, 0 }, + { "((3pPp))QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ", 2, 0 }, + { "[[^]{-244[}(\?([^|W0E4]UUUUUUUUUUUUUU[:upper)word:][:space:]{-57,})+L>R]]$PeFuufcBA`qr!!!!!!!!!!!!!!!!!!!!!!!!!", 0, 0 }, + { "[[(\?#F^(\?<!)|)fff(\?!fffffffffffffffff(\?{ffffff(\?:ffffff[:alnum:])]]c.\?-}))", 0, 0 }, + { "[^[^((\?:)ww[wwww(\?>wwwww)3z/57z){34}]/(/////////////[^//////////////////)]E%)L{-133}]*$]", 1, 0 }, + { "(!)GS[:ascii:][:punct:]{235}T'&-_h\"", 1, 0 }, + { "(){}", 1, 0 }, + { "[[^((\?!(\?<=)*QF[:alpha:])([^[^\?<!x60t(\?<!UUUUUUUUUUUUUUUUUUUU)K&d{118}z7nM.G)```````````````````````````E:(\?(){31,}){}]k]){,109}[:space:]]ZZ[:xdigit:]]{-68,}`{}{}e\?[:alnum:]", 0, 0 }, + { "[^{223}.^,-qqqqqqqqq((\?!\?>qqqqqqqqqqqqqqqqqqqqqqqP6W0_'O)Bur*'6&*t)]{65})+", 0, 0 }, + { "([(\?=)]wr$7f5ru){100,}[:xdigit:]y{}[:digit:]{}2n@P|9#mru~97{-189,73}$a", 1, 0 }, + { "({-113,213}){-172,221}B[:ascii:]{,-48}", 1, 0 }, + { "[^[[Xf`````((\?{(\?<=\?imsmsx:`````````(\?!`````````[```(\?mximsx:``(\?(&|o{xIaO][:)space:]3))\?])+)*<|@@@@@@@@@@@@@@@@@@@@@@){-251,}{}]*[:graph:]1!azE\?|-120u*][:lowerprint:]})", 0, 0 }, + { "[[[^##(\?################(\?>(\?(##t)][:punct:])b))<<<<<<<<<<<<<<<<<<<<<<<<<<[:alnum:]y >u=l:rp8i3Ci#]46%NIO-W[:space:]IIIIIIIIIIIIIIIIII]W[:space:]f]l{-253}", 0, 0 }, + { "[:graph:]L{-136,175}{[^}h(\?=t)Q]ooooooooo(ooooooooooooooooo_)[:space:]q\?", 1, 0 }, + { "()$.", 1, 0 }, + { "[(\?<!^$.\?{197}B]$)", 0, 0 }, + { "[:di(git:])[:low(erprint:])qqqqqqqqqqqqqqqq[:digit:]", 0, 0 }, + { "((zzzzzzzzzzzzAUUUU)l$]VD z~)n", 2, 0 }, + { "([^[(\?<=^[]{}][.WWWW)044444444444(\?=44(\?{444(\?{(444444444444e{(\?=}}))..t]+[:(\?<!xdigit:]P]-N}))))|)", 1, 0 }, + { "\\ce[:(\?#asc(\?{ii:])})[:upperword:]`^", 0, 0 }, + { "[:graph:(\?<=])[:alpha:]", 0, 0 }, + { "([:upp(\?=erword:])pC)lp\?", 1, 0 }, + { "(oooooooooooooo\?fN)-[:alpha:]{-213}[:alnum:]qHEu", 1, 0 }, + { "[:punct:]TTTTTTTTTTTTTTTTTTT[:d(\?#igit:])[:alpha:]", 0, 0 }, + { "([^[^[^J4(+++++++++++++++++++++SgDE(\?>\"y8].]:::::::::::::::)pP5-]p)O{,199}xxxxxxxxxxxxxxxxxxxxxx[:ascii:]%", 1, 0 }, + { "([:alpha:]Fs)Z", 1, 0 }, + { "[()]{209}[:alpha:]hhhhhhhhh(hhhhhhhhhhhhhhhhhhhhh)pP<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", 1, 0 }, + { "-{-8,}.[:(\?imsxx:ascii(\?<!:]{-231}aa*{}K^UQL\?)d\?[:lowerprint:]W)q>D9'", 0, 0 }, + { "[#(\?msximsx:#########################-IIIIIIIIIIIIII(IIII(\?#IIIII((\?#[^III{})N.[(\?=:lowerprint:]))CwT,,,,,,,,,,,,,,,,,,,,Sq]$CCCCCCCCCCCCCCCCCCCCCCCuuuuuuuu])))", 0, 0 }, + { "[:xdigit:][(\?#]){13}{,75}lllllllll", 0, 0 }, + { "[c]QQQQQQQQ1+{-252[(\?#}33333])[:upperword:]", 0, 0 }, + { "P@i #>>PF!@8G<[(\?:^P]-)D", 0, 0 }, + { "uZZZZZZZZZZZZZZ[^ZZZZZl*-211{199}(\?!p])EEEEEEEEEEEEEEEEEEEEEEEEEEED[:lowerp(\?msximsx:rint:])", 0, 0 }, + { "[(\?!^])021[:graph:]'", 0, 0 }, + { "\\(\?>[(\?<=:ascii:]{}[:alpha:]d8}G))", 0, 0 }, + { "[^[((\?!1)[^,a|]\?{,242}[:alnum:])X\"a", 0, 0 }, + { "pP[((\?simx::a(\?!lnum:]vvvvvvvvvvvvvvvvvvvvvvvvv)|O0)[:digit:]ooooooooooooooooooooo)\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"", 0, 0 }, + { "_ L:8J-~ Y$[:uppe(rword:]{,-184}]{}6.A)", 0, 0 }, + { "{,105}.(9]]{-12})N@0nOOE", 1, 0 }, + { "HHHHHHHHHH[:xdigit:]uuuuuuuuuuuu{}E^X\\\\\\12601", -1, 1 }, + { "( o)=\"OU7h{V>", 1, 0 }, + { "[[:xdigit:])))))$[:xdigit:]+{152}{,-50}(c),,,,,,!!!!!!!!!!!!!!(\?>!!!!!!!!!!!!!.[:digit:]i>\"O'i9])-175d_", 0, 0 }, + { "[([^[^[^([[Eeee[^eeeeeee(\?(\?<!(eeeeeeeeeeeeeeeeeef|]][:alph()\?>(\?!(\?>a:]a{,166})/////////////////////[:gr[^aph:])Gpu", 0, 0 }, + { "(7)NNNNNNNNNNN132", 1, 0 }, + { "[([\?#^[]{QKm$v])][:alp[^ha:]]", 0, 0 }, + { "(:{86})7{K|[:alpha:]{O", 1, 0 }, + { "([Y(\?{[[^:alnum:][:alnum:][:digit:][:a(\?(lpha(\?(:].})", 1, 0 }, + { "[[({29,-30}([[^:digit:])Y]]J=~{,220}[:blankcntrl:])0ooooooooooooooooooooooooooooooo[:punct:]&]", 0, 0 }, + { "[^1Dx32[:alnum:]]{[(\?::punct:]MMMMMMMMMM)12759", 0, 0 }, + { "([[[]]*|(_])[:u(\?{pperword:]})", 2, 0 }, + { "[:upper(\?(wo)rd:]){-16,250}", 0, 0 }, + { "([^{194}i(\?({161)}PP\\S{}{,-14}]))z{208,225}BpPEt", 1, 0 }, + { "[(\?m-ms:)}&!@29k0sUqzt9}<-x|A$!+G>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>CCCCCCCCCC-][[:space:]][:space:]El", 0, 0 }, + { "()[:digit:(\?isx(\?>ix:]K^WQQQQQQQQQQQQQQs)[:lowerprint:])", 1, 0 }, + { "[a|(\?imix:S(\?(SSSS)SS(\?>S)]W)8t[:ascii:]f$)[:alnum:]111111111111111111111[^[:space:]x{12729}+'''''''''''''''']", 0, 0 }, + { "[^(\?!(\?(\?#=)a)[:punct:]=2)(){}$$$$$$$$$(\?ims(\?#-isx:$$$$$$$$$$$$$$$$(\?#$$s)x{294b}##############################slllll)]){,209}333333333333333333G:v2/K", 0, 0 }, + { "[^]ub(\?<=)vQ6(\?#Z\"3.)[:space:]u[[:digit:]]7777777777777777U'{}sssssssssss", 0, 0 }, + { "(([(])`[:ascii:]b)", 2, 0 }, + { "[[[^[^([^[^(\?=(\?imxisx:[[^w])", 0, 0 }, + { "pppp(pppppppppp-{-175}Nb>k&)sssss{-190,-54}", 1, 0 }, + { "()OJ@`'%[:(as(\?!cii(\?#:]))+pffffffffffffffffffffffffffff{,162}[:ascii:]5)s-[:graph:]", 1, 0 }, + { "[(M{}Ux5{jaW/{}[^u[:alpha:]s^{84,}PPb@Wt$(\?>nha<Yf41a)]{}[:lowerprint:])*[:lowerprint:]][:upperword:]^1gS.^=pp{}FFFFFFFFFFFFFFFFFFFFFFFFFFF33333333333{}", 0, 0 }, + { ")\?L9~h4BQnNp F\\Q{}", 0, 0 }, + { "($)[:upperwor(\?:d:])N[:alnum:]bcccccccc5555555555555555555555555.N[:blankcntrl:]", 1, 0 }, + { "2222222222222222222ppppppppppppppppp[:lowerprint:]))[^B\\e{{{{{f]6#+{,-104}{{{{{{{{{{{{{", 0, 0 }, + { "<[(\?>:al[^pha:]])\"O\"vN", 0, 0 }, + { "[(\?>d8E@b.{(\?<=,-250}(\?=mx48[:punct:]^&)]nAeYY)W)-13272", 0, 0 }, + { "22222222222222222222222222///////////////////[:digi(\?#t:]eM)[:lowerprint:][:alpha:][:alpha:]EEEEEEEEEEE", 0, 0 }, + { "[(\?={38,223})^\\\\\\\\\\\\\\\\L(\?:{,-50}3|)}r]aW\\x70U{-110,}8LUf)w]4+oav", 0, 0 }, + { "G[:upperword:]v[:lowerprint:]-tu)j8CK", 0, 0 }, + { "[([([^().(\?(\?><=c)'(\?<(='(\?<!''''''''(\?(\?<!!'''''''''''(\?=''''''/(|dHj(P>L\?q!G))|)(\?=n(\?(^tk)T-z$q!D|2<rc[^{,53})]jZy))))6)[:bla)nkcntrl:])010])7pE`l[:space:]([:lowerprint:]eXXXXXXXXXXXXXXXXXXXTTTrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr]+[:alph(\?!a:]7)444444444444444444444444l{34,}]J{}yyyyyyyyyyyyyyyyyyyyyyyyyyy)\?'z9~9s.mA", 1, 0 }, + { "().", 1, 0 }, + { "{-205(,}[:al(ph(\?>[^a:]W,[4DLR[^^8THMtVv~KKw(\?>)pPF)].{-245,}]))fffffffffd[:alpha:]zzzzzzzzzzzzzzzzzzzzzzzzzzzzz", 1, 0 }, + { "[^[[^]{-[1(\?imximx:83,}{,182}][:graph:]]^])-bTO X0P", 0, 0 }, + { "[11111111111(\?#11111111]U[:asc([\?!ii:]{,37}+{-89}){-170,218}{-21,})f[:xdigit:]]P.[:xdig(\?:it:]145)YYYYYY$S@:@@@@@@@@@{-150,-109}", 0, 0 }, + { "{-40}<o][^D[(:graph:]]d).Q", 0, 0 }, + { "()APPLn[:xdigit:]", 1, 0 }, + { "[([^\?+++++++++++ [ (\?> (\?( (\?{ (\?!]E{-29})pP)})ZpP", 0, 0 }, + { "(t|{}c[^z^\?(@YLD]bSSSSSSSSSSSSSSS)+{{{{{{{{{{{{{{{[:xdigit:]n>1)WkF}7", 1, 0 }, + { "W22[0Q[^d-d{}PPPPPPPPPPPPPPP<^FZ(\?<=\"[U]Yo}9H'cYy]S[:alnum:]^8wTDH)^u", 0, 0 }, + { "([^[(\?:(\?>((\?#$)(\?{^(\?>))///////////(\?>/ggggggggggggggggg{1(\?!90,-13}\\D)Dyyyyyyyyyyyy(\?!y(\?<!yyyyyyy)})]]$)[:xdigit:]|{}-)#a))nPpP[:lowerprint:]AA)V+q^[:blankcntrl:]", 1, 0 }, + { "([^(\?!]))D{,97}", 1, 0 }, + { "(c){,141}", 1, 0 }, + { "nn[:s(\?<=pace:])[:upperword:]ooooooooooooooooooo*^[:space:]`{-188,129}mmmmmmmmmmmmm^.", 0, 0 }, + { "[[G{(\?imsximsx:2(49}{,-46}r(\?(\?=#Gw]u))[:bl(\?>ankcntrl:]))(^m+)zSiZ F4[!]VV$E{-9,-100}''''('''''''''\?DEOOOOOOOOOOOO###############[:space:])HHHH)[:digit:]'////////////", 2, 0 }, + { "[^*}(\?>)(\?:7Q=#+]KKKKKKKKKKKKKKKKKKKKKKKKKKKG)]]]]]]]]]]]]]]]]]]]]]]]]]][:alpha:]-{}", 0, 0 }, + { "[n(\?<(\?#!nnnnnn55555{205,}!)[:alnum:]^]!!!!!!!!!!!!!!!!!!!!!!![:punct:])[:x(\?(digit:]vr)|'n6W5 D&jk[:punct:]5)", 0, 0 }, + { "[^P(P{(\?i(msxisx:235,}))***])[:alpha:]^", 0, 0 }, + { "[([t(\?<!(\?<!4])[:u(\?=pperword:]))-])}}}}}}}}}}}}}}}}}c{-39,}[:digit:]$-", 0, 0 }, + { "([^)]{241}[:xdigit:][:upp(\?=erwo(\?(rd:]-xF5b{})q[:ascii:])T4U{185}9999999999)()X&Ny[:alpha:]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@{69,}[:alnum:]x{d7f8}p-[:digit:]", 2, 0 }, + { "(f)(${,111}{25,}!\\d{,94}[:blankcntrl:]@[:space:][:ascii:])-237{,232}DQVVVVVVVVVVVVVV)-", 2, 0 }, + { "PP[:g(\?!raph:]){}", 0, 0 }, + { "([[^-][^4[:digit:]NNNNNNNNNNN]TVU:])[:ascii:]", 1, 0 }, + { "(([^(\?[[^<=)][:graph:]+iiiiiiiiiiiiiiiiiiiiiiiiii0INFX[:xdigi(\?(t:][:blankcntrl:]][:graph:]qM6A[:alpha:][:graph:])1*]eFvvvvvvvvvv)v-)U))t{89}", 2, 0 }, + { "[^ZZZZZZZZZZZZZZZiiiiiiiiiiiii(iiiiiiiiiiiiiii{}))))))))))))))))))]))))))))))))))))))))))))[:digit:]-", 0, 0 }, + { "ddddddddd+zzzzzzzzzzzz[:graph(:])ssssssM{-223}[:graph:]", 0, 0 }, + { "[:alph(\?>a:])x11{-144,45}.", 0, 0 }, + { "[]{#y.^(\?{{}&&&&(\?:[^&&&&&&&&)[:punct:]n{190}OylBQ{(\?!-73})2u',x(\?#Ds(\?#{})j(\?{-})})u0(((((((\?{(((([:alnum:])MC})b=71TncyE>[:xdigit:]*\\f]{}]\"p#!8twZT\")[:punct:][:space:]", 0, 0 }, + { "[^(Z6]8)|'@p8{}[:upperword:]MMMMMMMMMMMMMMMMMMMMMMMMMMMM{}7c", 0, 0 }, + { "$0)@#vp,VcJ.Bdh", 0, 0 }, + { "[[^(-])nnnn+s`[:alpha:][:blankcnt[^rl:][:upperword:]{-15,}][:g(raph:]c]){,-177}6[:upperword:]##################{,-14}", 0, 0 }, + { "[[(5C{86(,}PPrrrrrrrrrrrrrrrrrrrrr{150,182})N{}LSC|)-[:alnum:]{}KKKKKKKKKKKKKKKK<4=~7K3PPPPPPPPPPPPPPPPPPPPPPP[:lowerprint:]]]", -1, 0 }, + { "([^(x{145b[5}^hfc.0)+]z@_&lA{-34,}])X\?", 1, 0 }, + { "([(\?<=)(\?!])l)L", 1, 0 }, + { "({-104,}DrPPDF4444444444444[:space:])[:space:]", 1, 0 }, + { "())))", 1, 0 }, + { "[[^((\?>\?(\?[{})q5v}r7t(P)xtffffffffffff))]{,-66}kdExX&-SCeCzzzzzzzzzEc)E,\"^I]x{e629}|{}]", 0, 0 }, + { "[h[:punct:]p\\[\\\\(\?:\\\\[^\\\\)Eo#:C$u[^T/ysA[*%nM:f]{,221}[:lowerprin[^t:]{]bx{f285}E]E[:alnum:]+]1oe3B][:alp(ha:]]fh7}M$l)D{17}", 0, 0 }, + { "IIIIIIII[^IIIIIIX]-_S[:digit(\?#:])33333333333333333333333333[:punct:]iiiiiiiiiiiiiiiiii", 0, 0 }, + { "[^[[:punct:](\?((\?:^ #Q_po(\?=[:alpha:]{}z()(\?!======'wq$Q2)LLLLLLLLLLLLLLLe(C9gggggggggggggggggg[(\?<=:alnum:]()\?<!{-85,}W[[[[[[[[[[[[[[[[(\?{[[[[[[^)(]\?])|uuu[uuuuuuuuuuuuuuuuuu{,-20}p${}]MHI&7s:\?$[:digit:]-:)_V`*{-52,}{250}$:ME9izF/uP[:blankcntrl:]})''''''''''''''''''''''''''''')CCCCCCCCCCCCCCCCCCCCCCCCdd[:ascii:][:lowerprint:].Mcccccccccc2B{-230,}$[:digit:]", 1, 0 }, + { "()|mOAuK~P144[:space:]^9dddddddddddddddddddddddddddddd[:blankcntrl:]", 1, 0 }, + { "[^[^[^.L[^-vEUl(\?>(\?=a!Ib1P]])])~~~~~~~]xE9", 0, 0 }, + { "X()", 1, 0 }, + { "[^()(\?#G(\?<!)(\?=^r])*,XXXXXXXXXXXXXXXXX@)444444444", 0, 0 }, + { "([[((\?<=({,-70})-[:xd(\?=igit:]{,138})", -1, 0 }, + { "[(^]{62,67})", 0, 0 }, + { "([((])[:space:]))", 1, 0 }, + { "(a{(109,})[:alpha:]{,-121}{})]RRRRRRRRRRRRRRRRRRRRRRRR{}{125,}ttttttttt{46,}`[:space:]", 2, 0 }, + { "[^[^([q[8]~.IPmiBSspP)]QpX[pT==8@lulANS]]{,-98}]", 0, 0 }, + { "[^77777777777777777777777(\?>777777])", 0, 0 }, + { "(),e<^X~{[:alpha:]{}G{70}", 1, 0 }, + { "({-211,}'){}", 1, 0 }, + { "[^(\?imsxsx:{}[*])cccccccccccccccccccccccccccccccc<z0W8]$", 0, 0 }, + { "(){2,89}$z", 1, 0 }, + { "((050[^\"\"\"\"\"\"\"\"z]8|j{}{,-112}$).pP)qq1~hW}L", 2, 0 }, + { "[[^[(+xx(\?<!xxxxxxxx(\?!xxxxxxxxxx(\?#(\?>[x))(\?:]r.]]]))[:graph(\?<=:])))", 0, 0 }, + { "[^([(\?#)(\?(\?(<=)l|\?(\?!])kkkkkkkkkkkkkkkkkkkkkkkkkk", 0, 0 }, + { "[:xdigit:]K(KKKKKKK)^3c.OOO{-240,-10}2{-97,-139}*{-34,}[:xdigit:]", 1, 0 }, + { "[([^66666666F(\?>FFFFFFFFFFwpP)LLLLLDeDA&Am$l[:xdigit:]!T5#]n[:alpha:]U*)))))))))))))PP]", 0, 0 }, + { "[[[:punct:]u^[:xdigit:]L(\?:[:xdigit:][[:graph:]PP{21}A[:alpha:]8%I(M%b<eE~#C@r=uG~~~~~~~~~~~~~~~~~~~~~~~~~~~~+w]pP)T]]$$$$$$$$$$$$$$${-121,}|l", 0, 0 }, + { "([(107{,-4(\?=}~[^D)])f]{,46}+ri<)", 1, 0 }, + { "[(\?<=]{,208}+~)", 0, 0 }, + { "[^444(\?<=4444444[:alnum:]&[,i]0)[:alpha:][:upperword:]", 0, 0 }, + { "[^([^(\?()*+)SS(\?>SSSSSSSSSSSSSSSSSSSSSS]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]{,-1}])[:blankcntrl:]===============================[:punct:][:blankcntrl:]Z[:space:][:ascii:]$|$[:blankcntrl:] JR.{,133}[:alpha:]$\?)<]", -1, 0 }, + { "(OL[:u[pperword(:][:s[^pace:].[:spac(e:],,,,]*])$)\?)", 1, 0 }, + { "(VI[:digit:][:alpha:]6)EG", 1, 0 }, + { "({}){-2,-40}rrrrrrrrrrrrrrrrrrrrrrr[:punct:]", 1, 0 }, + { "()q", 1, 0 }, + { "[^([^[([^C|])]{,-56}[:xdigit:]{-144,}V])fYv{-[40,-58}$@@@@@@@@@@@@@]|Y(-]-.]h-[:dig(it:])>>>dddddddddddddddddddddddddd{101,}", 1, 0 }, + { "([P,{1(\?(\?(<=28,-218[^)}LoZX)])!!!!!!!!!!!!!!*[:blank(\?!cntrl:]ed)\\\\\\\\\\\\\\\\\\\\[\\L\?][:graph:]:*Y{-108,120}xCC)]", 1, 0 }, + { "(A[:space:]PP{185}a^!!!!!!lllllll)*db\?$Pfr", 1, 0 }, + { "{-21,-118}kG[(\?{:xdigit:]})[:punct:]{69}Qyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy5{}TTTTTTTTTTTTTTTTTTTTT", 0, 0 }, + { "[[^[P(\?<=P$X>0^d.[:punct:](\?#ccccccccccccccccccccccccc{}3N000(\?>00000000000000000000000000000]f[:punct:]5)).R================{,222}^wwwwwwww$)]-{} ]{,-22}CjP{242,}", 0, 0 }, + { "[(\?#^]{})", 0, 0 }, + { "[^([[([([[([^[^(\?:(\?(\?(!)]\"))h>\"RRRRRRRRRRRRRRRR[^RRRRR{68,-65}7Q(\?{]", 0, 0 }, + { "(P{}){175,}PP{}rttttttttttt", 1, 0 }, + { "[:bla(\?{nkcntrl(\?#:]})))))))))))))))))))))))!!!!sR{})", 0, 0 }, + { " [:digit:]dAAAAAAAAAAAAA^[:ascii(:]55)^", 0, 0 }, + { "($*)dZY", -1, 0 }, + { "[:graph:][:lowerprint:]S[:gr(\?=aph:]{-128,}666666666666666666666{}[:upperword:]|nnnnnnnnnnnnnnnnnnnnnnnnnnB)c[:xdigit:]{-225,}{-4,}{-192,}QQQQQQQQQQQQQQQ@@@@@@@@@@@@@@@@@@@@@@.", 0, 0 }, + { "([:digit:]s{44,}{}{-31,}c{,-130}pP){-241,}UeN", 1, 0 }, + { "([^)((\?>\?#{}hK\"V2\?d][KKK(\?imsxim:KKKKKKKKKKKKKKKKKKKK[^KKKKKKKKKWWWW[WWWWWWWWWWWWWWWWW)B])_l_3", 1, 0 }, + { "[(^[(\?!*){[^,91}].j]*]L)*c|[:alpha:]&", 0, 0 }, + { "[^[[[^[777GGG(\?:W_U(\?imsxms:[:punct:]A]-)[:digit:][:blankcntrl(\?(:]][:alnum:)])]WRRRRRRRRRRRRRRRRRRRRRRRRRRR]{31,}[:xdigit:]][:xdigit:]))))))))))))))))))))))$[:xdigit:]", 0, 0 }, + { "[:ascii:]m*[:punct:]#[(\?<!:punct:][:alpha:]-,7vyXeeeeeeeeeeeeeeeeeeeeeeeee^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%%%%%%%%%%%%%%%%%%%%%%%%%%%%[:digit:]''''''''''''''''')", 0, 0 }, + { "([^*[(:punct:]9999999999999999999{147,}]j{,193}{171}Z-)){208}0[:graph:]yDt", 1, 0 }, + { "(dw[[:alpha:]U]ttt[tttttttttttttttttttt]Q^171e)[:xdigit:]/", 1, 0 }, + { "[[^((\?#)Tqqqqqqqqqqqqqqqqqqqqqqqqq105++++++++++++++++++++++++++b7V+7dit]])|D", 0, 0 }, + { "{}P7.Ajh[:xdigit:]^[:blankc((\?(\?<=nt[rl:]FFF)-]){}o|a[:grap(\?!h:]))PsssssssssssssssssssssssssssssssN^{-60,}Kb", 0, 0 }, + { "[:alpha(\?(:]$!_+777777777777777777777777O)666)lll[^llllll[^l{{{{{{{{{{{{{{{{{{{{{{|]{-217,}MoEl`7)^)LlU[:alph[a:]({-241,27})]]{-212}{,249}n)X", 1, 0 }, + { "[U|ajP[:alnum:]n[(:digit:]]W)[:graph:]b[:xdigit:].P", 0, 0 }, + { "(([:low(\?-imsx:erprint:]|{}[:ascii:][:gr(\?:aph:])>>>>>>>>>>>>>{,-129}))\?{-226,}^P)R", 2, 0 }, + { "[^[[nnnnnnnnnn(\?=nnnn(\?!nnnnnnnnnnnn(\?#nnnnnn{,-38}N){202,}]$[:alnum:])]t][:alnum:[]^=w){237}][:alpha:]-[:alpha:]+e", 0, 0 }, + { "()[(\?(:digit):]+qc)O88888888{,151}aJ", 1, 0 }, + { "([^([(\?!sv(\?=)d]{-200,})N))]Z{-73,15}", 1, 0 }, + { "([\?\?\?\?|||||||||||(\?{||(\?=||||||||-}[))Ehhhhhhhhhhhhh{,202}&TcfL((\?:>)((\?!\?>$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$8[:alpha:]\\d])]C[:graph:]h*,\"\?u{|mU,a)[:blankcntrl:][:lowerp(\?>rint:])PPnP+9.[:xdigit:]*PPjjjjjjjjjj~y<#*scf_\"^e[:xdig(\?(i)t(:])~$y)^){-131,77}^L%", 1, 0 }, + { "[^[(((\?>)$}h9$B5+yhU/Nqh$YYYYYYYYYYYYYYYYYYYYYShK)3WHw1vMMMMMMMMMMMMM(\?=MMMMMMMMMMMM[:alnum:]/)dddddddddddd(dddddd\"e5zLW)+![:space:]+BHGHfAS]\?IIIIIIIIIIIIIIII*&&&&&&&&&&&&&&&&&&)NNvwDteepjdm<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<${61,219}D][:digit:]0", -1, 0 }, + { "[:punct:][{177,(\?=234}]ix9*)", 0, 0 }, + { "([^K{,3(\?<=4}]I)\?U)", 1, 0 }, + { "[([^[[[^([([^[^(\?=])X", 0, 0 }, + { "[:blankcntrl:(])qd_R\?{\?r[=\"[^[^6]vX8)a+{C%H84CK6Uy#E]sE{208}", 0, 0 }, + { "PPPPPPPPPPPPPPPPPPPPPPPPPPnnnnnnnnnn()[:upperword:]us", 1, 0 }, + { "x{,46}[:graph:]LU{}CU)", 0, 0 }, + { "()-t|[^W{}][:lo[^werprint:]{}]\?b5", 1, 0 }, + { "()x5A", 1, 0 }, + { "[([^]-217)]s{-47,135}0000000000000000000000000000000{,-108}", 0, 0 }, + { "[^((\?{[^L\?u]})f", 0, 0 }, + { "()[[^^(\?{y(\?=VF_(\?<=]D}))]-= {46,})^5bIEQ{,-96}Z", 1, 0 }, + { "([^{}f[:punct:]\"X%%%%%%%%%%%%%%%%%%%%]5{-194}A[:punct:]mnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn+AAAAAAAAAA-)", 1, 0 }, + { "(CCCCCCCCCCCCCCCCCCCC{-230}352{-182,-68}O4{})", 1, 0 }, + { "([^[^\?[:space:]$TTTTTTTTTTTTLLLLL[^LLLLLLLLLL[^({}{4,-179}]]J] C]){}C{}{-224,})QQQQQQQQQQQQQQQQQ^", 1, 0 }, + { "([[:alnum:]].){-155,-82}dzI{55,}^", 1, 0 }, + { "([[:alnum:](\?#{88,-178})[:graph:]NC\"pI[:punct:]rmWd5y^p+gUP]YYYYYYYYYYYYYYYYYYYY~{,-62}{,200}{-109}{}+333333333333333333333333333333{}p)^.hhhhhhhhhhhhhhh", 1, 0 }, + { "[000000(\?mmsx:00000000000000000000000)M]]]]2*`[^]QQQQQQQQ(\?<=QQQQQQQQQQQQQQQQQQQQQQQ])\"<h\?", 0, 0 }, + { "[^((<g(\?>5j[bbbbbbb(\?{bb)o{}3(\?imxisx:E]g})YYYYY[:blankcntr(\?#l:].(()w264[:ascii:]^)[:ascii:]G)&(n {^PGn[:xdigit:])nv_e|]{-103,30}", 3, 0 }, + { "[^(([(\?!{}@[^HCO[[^^D[|]{,-49}][:xdigit:]]c`4[:ascii(\?<!:])$66666666666)*)]PP$Z[:alpha:]{,-235}UK],(aT/+6rbMqs60EloA)[:g(\?isx:raph:]!)]z$o{-24,}x1E[:blankcntrl:]ZDFvk", 1, 0 }, + { "[:blank(\?=cntrl:]US@.!\"[:digit:]*E)$16182", 0, 0 }, + { "[-{}x{3772[}][:(\?<=xdigit:][:u(\?#pperword:].W)aD)<pfN<b=C|-{-38}EZdOP|!>ggggggggggggggg\\\\\\\\\\\\\\\\\\\\\\\\\\Ef[:space:]\?][:ascii:]{21,}", 0, 0 }, + { "([:xdigit:]W[:u(pperword(\?::]jS [:upperword:]*)[:alpha:]nnnnnnnnnnn))-148}SSu", 1, 0 }, + { "([^(\?!\?)[(:upperword:])Bx^x$~lCr6*)6", 1, 0 }, + { "[{,-78}Y[:xdigit:][^s(\?>]P[:space:])]YYYYYYYYY[:punct:][:alnum:][:blankcntrl:]", 0, 0 }, + { "([MMMMMM(\?(MMM)M(\?<=MMMMMMMMMMMMMMM[^M)]en][:punct:]-[:alpha:]))Nr[:space:]", 1, 0 }, + { "~=1([^(\?=(\?:l){}])j{-44}{-18}[^u[:graph:]]{-187,}[:xdigit:]w[:alpha:])", 1, 0 }, + { "[ccccc(\?>c(\?{cccc[ccccwetoCei+)w&-+{,-142}[:alpha:]PP66io4(|zkA=],,,,,,,,,,,,,,,,,,,,,Lx5Cx{d2bb}]{188}U~~~~~~~~~~~~~~~~~~~~~~~})", 0, 0 }, + { "Q|0\"[:d(\?:igit:]^{,-174})", 0, 0 }, + { "[^[(\?>rh])]", 0, 0 }, + { "[ees{{{{{{{{{{{{{{{{{bbbbbbb4`ml******(\?=****+])", 0, 0 }, + { "((hdG[((\?<=:dig(it:])[^[:alpha:]$(\?sxi:)x{11390}[(\?{:upperword:]~)i 8[:blankcn[trl:(])]+{,-183}Zqp", 2, 0 }, + { "Dd{D8`+DW={-[53,1(\?<=71}])", 0, 0 }, + { "[:(\?(alpha:][:punct:])", 0, 0 }, + { ".LLLLLLLLLLLLLLLLLLLLLLLLLLLL{}pP[:punct:]x0CZ{30,}!!!(!!!!!!!!!!!!!!!!!!!!!!!!!==@77.%[:graph:]D)", 1, 0 }, + { "[^[^[[r(\?#]){-237,}RRRRRRRRRRRRRRRRRRRRRRRR[^Rll(\?!(\?{lllll]", 0, 0 }, + { "()*ooooooooooooooooooooyyyyyyyyyyyyyyy", 1, 0 }, + { "{,4(}D)JJJJJJJJJJJJJJJJJJJJJJJJJ", 1, 0 }, + { "((b.D{}[:al[pha:]{64}]{})==========================[:alnum:]h>77b)!Ab", 2, 0 }, + { "([^[^[^oooooooooooooooooooooo][:space:][:punct:]PeniKe*~$g\?${>[:lowerprint:]w))))))))))))))){}yyyyyyyyyyyyyyyyyy]pP.|QhZ]{,190})sssssssssssssr+=[:blankcntrl:]WWWWWWWWWWWWWWWWWWWWW", 1, 0 }, + { "([*(\?{})hhhhhhhhhhhhhhhh]G{,-170}QdErrrrrrrc-jjjjjjjjjjjjjjjjjjjjn+{-130,-10})PpDS@Bee", 1, 0 }, + { "([:b(\?=lankcntrl:]))T[:alnum:]{-224}ywt", 1, 0 }, + { "([633(\?<=333(\?<=3333333333(333333)^\?]aGA)[:digi(\?>(\?{t:])$[[:space:][:xdigit:])|8T\?',_{171}{}{113}b\?5kAv0/7{})`huh>xM]C8pYRz]s$Eu08)", 1, 0 }, + { "-(pP)[:alnum:]$^", 1, 0 }, + { "[^x(\?{{17681}]P*)U(_t/8E_\"iN})3333333", 1, 0 }, + { "(([^([[r(\?=[[^^*kx$][:alpha:]:::[:::::[^[^::::::::((\?{\?{::]).^p[:space:]}){52}{}]W{}fn", 2, 0 }, + { "[:(\?>punct:]Ef[:xdigit:]x{c07b}{-50}Z{129,}YL1T`\\A)x[:punc(\?=t:]e[:xdigit:]2c6E46Y)+n ", 0, 0 }, + { "[^(\?!{,-79}[:punct:]'|}>,)][:blankcntrl:]{-118,-231}{-119,-50}:XXXXXXXXXXXXXXXXX-~{}$txlB)3KFL", 0, 0 }, + { "[^(([^fccccccccccccccccccc(\?<!ccccgQeKMfKzz]X$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$[:l(\?<=(\?<=owerprint:]))s{-97}{}))EUi${,-132}'{79}---------{,-93}77777777777777777[:lowerprint:].:H)[:punct:]nnnnnncP\?s1:dGed{186}N@pppppppppppppppppppppP{-212,-110}[:space:][:lowerprint:]$S}7{-112,164}-*.{-184,}OOOOOOOOOO]f\?", 0, 0 }, + { "(([\?#(\?>)])qcU$Q7|82\?{})", 2, 0 }, + { "[^yyyyyyyyyyyyyyyyyyyy(\?#yyyyyyyyyyya][:ascii:]\?)", 0, 0 }, + { "(([((\?{)EEEE(\?<!EEEEE(\?:EEEEEE~)}){244,}QQQQQQQQQQQQQQQQQQQ(\?>QQQQQQ(\?!QQQQQ][:digit:]\?))99999999999999)[:digit:][:upperword:]b))PP{}{}", 2, 0 }, + { "(K(c=B))", 2, 0 }, + { "(G`*s\?b[:g(raph:]))", 1, 0 }, + { "[^[([[[*QQQQQQQQQQQQQQQQ(\?=(\?=QQQQQQ(\?<!QQQQQQQQZddddddddd((\?{\?>ddddddddddc{22,}iiiiiiiii(iiiiiiiiiiiiiii(\?#iiiiiii[^i))\?\?\?\?\?\?]WWW)[:lowerprint:])]{-60,202}+[:upperword:]f[:xdigit:][:alnum:]{,-214})1~~~~~~~MMMMMMMMMMMMMMMMMM.", 0, 0 }, + { "({-102,})A.", 1, 0 }, + { "[((((\?<!(\?[^>(\?#\?()))p\"JD.{}(\?>)))((\?{l(\?<=).'053][:xdigit:]N+)})]WWWW%[:asc(\?{ii:]}))B[:alnum:]X){}s[:digit:]", 0, 0 }, + { "x7&{139}WWWWWWWWWWWWWW[:blankcntr[^(\?<!l:]-71]\"{-167}cqkI)[:dig[^it:]{}{}[:digit:]*[:punct:]-[l11111111111111111(\?(111111111{175,-216}~[:alnum:]`+X1F)vCpWSp(\?>~[^n@f`````````````)````````P])Y,N{}{}]{}pXF@)", 0, 0 }, + { "G[([(\?(^)$])P]^[:alnum:]){,-48}[:blankcntrl:]{}", 0, 0 }, + { "[[^[^f(\?=f(\?<=fffffff[^fffffffff[^fffffffff(\?<=fff]){-194,150}fx{e5a4}V", 0, 0 }, + { "9[:xdigit(\?{:]})", 0, 0 }, + { "[^([[(\?>()$xxxxxxxxxxxxxx[xxxxxxxxxxxxxxxx((\?=aA)s13]])pp[(\?>pppppppppppppppp|{}){20,}]b)]{-179,183}{-204,}[:ascii:])]-11111111{}{,132}qooooooooooooooooooo{}${}|9t", 0, 0 }, + { "([^[{}]\"[^6]*-{,-106}{}u]BR~8WG,U-)[:blankcntrl:]", 1, 0 }, + { "[''''''''(''''''''''z])c", 0, 0 }, + { "[^[(\?>])[:alnum:]r[:alnum:]+{,215}D]", 0, 0 }, + { "([({,127}7Qr(\?:z)pPNev%}(\?msximsx:4(\?<!){}&.D5555(\?<=55555555555555555555i$[:xdigit:]){,-157}[:graph:]U[:punct:]nn(\?=nnnnnnnnnnnn(\?>nn(\?:nnnnnnnn_U{}]E)):^oooooooooooooooooooooooooooo)", 1, 0 }, + { "[^(\?#)(\?<!k2z]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]BW[:alnum:][:graph:]{157}Y]s$C)[:graph:]{,-189}", 0, 0 }, + { "$+CCCCCCCCC[^CCCCCC(\?<=Ca=]r{-81}[:alpha:][:alpha:])E=", -1, 0 }, + { "[(((\?=\?{([^(\?<=)])>!(([:alnum:]{252}{}})ffffffffffffl){}A2r\?~ImE\"[:punct:]){}[:digit:]", 2, 0 }, + { "([:blank[cntrl:]].t^P)", 1, 0 }, + { "[^[(\?:X])|rrrrrrrrrrrrrrrrrrrrrrrrrr*P]Q", 0, 0 }, + { "[[[^(\?{((\?<!))s})(\?<!A){14}(\?:L*+TTTTTTT]U{[^-12([\?!,}\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?)Y`Y)L]|]]|]", 0, 0 }, + { "hkXzf',]yP$+[:u(pperword:])", -1, 0 }, + { "(#[:blankcnt(\?iximsx:rl:])$QQQQQQ{}[:digit:])\?A", 1, 0 }, + { "(B{-34,})*{,106}", 1, 0 }, + { "[(\?{:graph:]})", 0, 0 }, + { "((){}{,63}[:punct:]^t[:space:])^17737", 2, 0 }, + { "([^[SSSSSSSSS[SSSSSSSSSSSSSSS[([[[{38,}]Jn][:alpha:]])])$'", 1, 0 }, + { "[^({}{95})B{1(\?>15}]x{f779}ZZ,Wo)O[:alpha:][:lowerprint:]{81,228}Q[:upperword:]", 0, 0 }, + { "[[^[^()n[[[[[[[[[[[[^[[[[[[[[[[(\?: G)(\?{K![^m) j(\?:C|((\?:n*Xlaa908:n$m,))[:xdigit:]x(\?{{1a5cd}pppppppppppppp(\?(pppp)p(pQ)))ddddddddddddddddddddddddddddddd]q[:alnum:(\?{]Ga})\?})@[:lowerprint:]{,169}[:blankcntrl:][:graph:]]n{-76,}|U\"{,-54}t]I{}{-64,-232}]\?].\?{-111,227}) @hFp\?j=H$Wbu<{,209}De{,145}{206}-})[:blankcntrl:]", 0, 0 }, + { "[^[^(LLLLLLLLLLLLLL[^L[L[:alpha:]3{,189}(\?#(\?>n){}^EXXXXXXXXXXXXXXXXXXXXXXXXX]c*)^r=$WWWWWWWWWWWWW", 0, 0 }, + { ")w###################", 0, 0 }, + { "{,121}[:d(\?(i)git:])E\?[:punct:]LLLLLLLLL[:ascii:]+", 0, 0 }, + { "([]]]]]]]]]]]]][:space:]Jrt3o.]b)pwwwwwwwwwwwQfm~", 1, 0 }, + { "[+-{,-120}*(\?!()t*(\?(\?{>G)F)yd]V{}f<\?}){245}xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx[:upperword:]", 0, 0 }, + { "(DDDDDDDDDDDDDDDDDDDDDDDDDDDDDc[:space:][:pu[^nct:]{-11,12}[:ascii:][:alpha:]{,155}P])", 1, 0 }, + { "()ggggggg{-136,-21}", 1, 0 }, + { "([^((\?<=U\?)(\?=^^^^^^^^^^^[^^^^^^^^^^^^^///(\?#//[////////////////////(\?()#######b+]$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$^[:digit:])\\U]Q8@}4d)\\U", 1, 0 }, + { "A[:graph(\?::])-mo=U[:upperword:]ttttttttttttttttttttttttttt", 0, 0 }, + { "[^(((\?=\?im-m(sx:)c~~[^~~~~~~~~~~~~~(\?>~~~~~~~~~~~~~SSSSSSSSSSSSSSSSSSSS]{51,}[:digit:]{,-179}N))kk[kkkkkkkkkkkkkkg$)[(\?::punct:]zWl)]|)*", 0, 0 }, + { "[((\?=()+A)][:graph:]x0B)[:graph:]", 0, 0 }, + { "(nR%B[:blankcntrl:]C=|en-[:digit:]n[:graph:]HHHH[HH]D\?%[:digit:]MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM.z(oF9zW8A7cfff(f))-[:blankcntrl:][:blankcntrl:]A[:digit:])D{,-243}", 3, 0 }, + { "([[()]]{,-251(})\?L)uw@", 2, 0 }, + { "\"|{(,-144})A.ooooooooo(ooooooFFFFFFFFFFFFF\?)n{,-18}", 2, 0 }, + { "([^([(([[^([000000[0(0(\?!0(\?=0000000])45|E]", 1, 0 }, + { "[B[[[[[[[[[[[|{}*oKqv%(\?<=wsQ{1pMeK1^6%nLNqi<@ge][:punct:]= M@* D|NwL\\-117\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"~Qnd]h.O\"01x:[:alpha:]^){}D}\"", 0, 0 }, + { "([[RRRRRRRRRRRRRRRRRRRRRRRRRRRRxpSrx{7d79}*oJ2`Ft{n1,3g:1H@bT$D &[n/Cg)=ld@Ir{Fk>*4*`(\?>````````````````````(\?:`````.....................]]{,246})7 \"F4[^F|/g)]+e`rw@{,-69}H)", 1, 0 }, + { "([(\?<=)X[:digit:]PP.[(\?#:((\?#\?#graph:])[:digit:][Q+)(N][:alpha:]]f)[:graph:])+Elllllllllllllllll[:digit:]=)pP{uU-20bzY|ZKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKt<c", 1, 0 }, + { "[^(([^$(\?:(\?#w)[(\?::punct:]]d{-149,}[:ascii:])[:blankcntrl:]@@@@@[@@@@@@@@@@@@@@[:graph:][:xdigit:]O[:alpha:]2$-[:graph:])[:lowerprint:]-\?#S[:blankcntrl:][:alnum:]){-77,}]d[:digit:]N5v+Sqqqqqqq^% -I4]*.)^[:alnum:]JDfjMRU7ttttttttttttjjjjjjjjjjjjjjjjjjjjjjCCCCCCCCCCCCCCCCCCCD{,21}{0,67}[:graph:]{,208}B", -1, 0 }, + { "(%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%[:ascii:])i{}[:lowerprint:]epxxxxxxxxxxxxxx[:lowerprint:]r-", 1, 0 }, + { "([(^w(\?!)()])-s", 1, 0 }, + { "[aIIIIIIIIIIIII(\?imsxims(\?=x:IIIIIIIm^NXXXXX(\?!(\?isximsx:XXXXXXXXXXXXXS0]F)z))+rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr{,-237})ZZZZZZZZZZZZZZZZZZZZZZ", 0, 0 }, + { "(Z)[:alpha:]", 1, 0 }, + { "U#Z(=)", 1, 0 }, + { "([:lowerprint:][:punct:])1cVb*[:xdigit:]&&&&&&&&&&&&&&&&&&&&&&&&O", 1, 0 }, + { "()~K`3/[^*h[]G6[:upperw(\?()ord:]w)[:punct:]]{}", 1, 0 }, + { "[[[]V[:digit(\?>:])|l*KKKKKKKKKKKKKKKKK,,,,,,,[,,,s.{148,}P33333][:lo(\?<!werprin(\?!t:]ZZZZZZZZZZZZZZZZZZZZZZZ]{,-229}{-160,}){,-211}XPPP].{}z[:alnum:][:alpha:(\?=]t{166,}uuuuu6]i*p(m))[:space:]E|S", 1, 0 }, + { "[^(h(\?(\?({#2})(\?(\?#>Q){,57}%[:digit:]\?\?\?\?\?\?\?\?\?\?.[)]]d{)-49,}f)^O{,68})\?C", 0, 0 }, + { "(}u])18621", 1, 0 }, + { "[:as(\?=cii:][^(\?=)(S-{.F-[:punct:]3-105^[:lowerprint:]111111111111111111111111---)][:alnum:][:ascii:]JJJJJwHSk", -1, 0 }, + { "[^3>>>>>>-sZ^^^^(\?>]Y[:di(\?(\?imxim:#git:]{-158,-102}[:punct:]{}{87,})))[:upperword:]", 0, 0 }, + { "[(\?<!^r]$W){}*[:alpha:].[:digit:]", 0, 0 }, + { "[:ascii(\?::[^])X]-", 0, 0 }, + { "[([^]Z)[:upperword:]N{}*[:graph:]*^", 0, 0 }, + { "([[(\?#^[(:graph:]]){205,}[:gr(aph:]T%]^MMMMMMMMMMMMMMMMMMMM){) <v\\[:digit:])", 1, 0 }, + { "[^Y.h~b(\?<=~P{(\?=169,65}\?[^\?\?\?\?\?\?\?\?\?[\?\?\?\?\?\?\?\?\?K\"s`[yT7oP[:alpha:]{})]zrrrrrrrrrrrrrr)]KKKKKKKKKKKKKKK[:digit:]S][:lowerprint:][:digit:]", 0, 0 }, + { "(s)", 1, 0 }, + { "[u(\?!uuuuuuuuuuuuuuuuuuuu[:digit:]{,48}[:graph:]WL[:alnum:]]v=_)VN>{AjBBBBBBBBBBBBBBBBBBBBBBB[:upperword:]`'W)", 0, 0 }, + { "[^([[()DN1[^][|]\?]{-104,}])[:space:]][:lowerprint:]r[:alpha:].DU", 0, 0 }, + { "[^((33333333333333333333333(\?<=3333333D))kkkkkkkkkkkkkkkkkkkkkkk[k[:alpha:]])]X+", 0, 0 }, + { "[({,-17})[@e{220,(\?#41}])]]{-213,-225}", 0, 0 }, + { "[[^(\?#[(\?:^[[(\?(^]))]])]vvvvvvvvvvvvvvvvvvvvv{,96}|m]{-79,248}[:alpha:])", 0, 0 }, + { "([[(\?imsisx:^}$,-[:al(\?>num:]Xqqqqqqqqqqqq{-185,154}]b#+T){-241,})A{-27}[(\?<!:lowerprint:]X)[:punct:]ME-]+BBBBBBBBBBBBBBBBa|{-40}M8mhgD 0HU]{16})", -1, 0 }, + { "[^(\?>([\?()(\?#))]--R1rk^UnP.[(\?!:digit:]])^)[:upperword:]{}0000000000000000000000000000000~U{-139,-19}z<L-228", 0, 0 }, + { "()-:=3uE$[:alnum:]bP%{-210,}", 1, 0 }, + { "(U)7777]]]]]]]]]]]]]]]]]]]]]]]]]]]]]c::AA[:alpha:]{,3}f1{NzH@3lTf{}{", 1, 0 }, + { "[C{(\?>})RR(\?=R<]p'N~&.-})6]", 0, 0 }, + { "[^\?[^(\?(lFt]).[^7Q-])kkkkkkkkkkkk]XTFy\"1Deiv!,'xVK", 0, 0 }, + { "[^$[^[:xdigit:](\?{*{245,99}h8v(\?!)]]u)Z[:punct:]})[:alnum:]+|[:blankcntrl:]u{}[:lowerprint:]+bBJ4+k-v{-116}", 0, 0 }, + { "S)f{,180}[:graph:]&{12,244}", 0, 0 }, + { "(([[(.()[^^{80(\?>(\?<=,235})ddddddddd[^ddddddddd(\?<=d.__B{36}````````````````(\?:```(\?>```````,,,,,,,(\?:,,)P$U,[:xdigit:])zzzzzzzzzzzzz]UUUU[uB]n<&[(:ascii:].][:alnum:])\?S]{})d{138,}s9========[:lowerprint:]]OOOOOOOOOOOOOOO|yyyyyyyyyyyyyyy$LZ[:lowerprint:]EEEEEEE[:ascii:][:punct:]VpP^{-48}D){,46}x))2P))a[:lowerprint:]r", 2, 0 }, + { "[^(((\?<!):())PPPPPPPPPPPPPPP(\?=[PPPPPPP(\?{PPPPPPPP$)})77777777777777777]{,-57}::::::::::::(::::::::::::::::)]g{89}__________________[:xdigit:]l[:punct:])N", 1, 0 }, + { ":02-k\?p3I7aEhJ\\265-[:space:]pP[:space:]x0F[:alnum:]aM4[:lowerprint:]sA@@@@@@@@@@@@@@@@@@@@@@@@@@@@", -1, 1 }, + { "a[:upper(\?{word:]})X{-173,}-2F[:lowerprint:]", 0, 0 }, + { "u,w<g*Q002S{,130}{239}[:lower(print:]cr{-165,}#$k<L/&)[:blankcntrl:]aaaaaaaaaaaaaaaaaaaaaa[:ascii:]", 0, 0 }, + { "(xFA^{-161,93})U[:xdigit:]", 1, 0 }, + { "[^(\?=]{})mE`", 0, 0 }, + { "[[((\?(\?#:alnum:]])x6CS[:digit:]{-197,}.)N", 0, 0 }, + { "[^(\?![])C*[:upp(erword:])-176]", 0, 0 }, + { "[[^[[^[55555555555555555555555555(\?>555(\?<!555)S][]]A[:l(\?>owerp(rint:]])]*", 0, 0 }, + { "Au)khgzAfXIZoZ=g[:digit:]){,186}Upvf=x<]Tbd5Rq\?.", 0, 0 }, + { "b{-176,}B^[:bla(\?(<!nkcntrl:]{-6,133}#B :)<<<<<<<<<<<<<<<<<<<)[:alnum:]$}}}}}}}}}}}}}}}}}}}}}}}[:xdigit:]tw", 0, 0 }, + { "(4IIIII(IIIIIIIIIIIIIIIII{})W{-152,-238}){,-56}^{-142,}", 2, 0 }, + { "[^([[(\?(\?(!)>>>>>>>>>>>>>(>>>>>>>>D)Ix{(1(\?imxmsx:762)c}))A)[[[[[[[[[[[[[[5Rp]DDDDDDDDDDDDDDDDDDDD]Us+\\w[:digit:]{-47}[:xdigit:][:blankcntrl:])ddddddddddddddd[^ddddddddddddd[:digit:]|]]*{-165,-230}{-212}{53,}]\?", 0, 0 }, + { "[^[^]]|[:(\?:alnum:])}}}}}}}}}}}}}}}}}}}}", 0, 0 }, + { "VVVVVVVVVVVVVVVVVVVVVVVVVVVV[:d(i(\?#git:])){{{{{{[:digit:]ZfQ55555555{}Z", 0, 0 }, + { "[L][:blankcnt(\?((\?=rl:(\?=]){-35,[^}){)eJb>>>>>>>>>>>>>>>>>>>>>>$ [:xdigit:]l0Tv2Tw2@C[:space:]Zc/{*)>]N3j~.dMBBBB", 0, 0 }, + { "[[^(\?>(([]))])[:graph:]]{65,}as#Q:lQ", 0, 0 }, + { "[^[fPPUUUUUUUUUUU(\?#UUU[^UUUUUU(\?<=UUUUUUUUUGGGGGGGGGGGGGGGGGGG((\?{\?=GGGGGG.MK))+]+)&UxFW)rwv\?@D.", 0, 0 }, + { "{-(60,})m", 1, 0 }, + { "b[(])^w", 0, 0 }, + { "[][^qVs(\?:(p])X)\?'", 0, 0 }, + { "()8", 1, 0 }, + { "(t[:punc[^t:(\?{][:blankcntrl:])})[^8\?]z*]", 1, 0 }, + { "[:lowerprint:])[:graph:]lppppppppppppppppppppppppppppf", 0, 0 }, + { "[:alph(a:])[:ascii:]g +z-Bc-U{,%Gk", 0, 0 }, + { "u[:graph:(\?=]*)W:::", 0, 0 }, + { "([:alnum(:])l)", 1, 0 }, + { "[[[}}}}}}}}(\?<!}}}}}}}+(\?{),,,,,,,,,,,,,,(\?!,,,,,,,,]99999999999&R[:ascii:]ZZZZ-{-10,}{96}Ed*][:graph:])]}){}{}G{-9,}", 0, 0 }, + { "([^[{}]]Z[[^:graph:]{-47}55555555555555555555555555555[:ascii:]s]6,$:3qAew1Y)+)[:punct:]", 1, 0 }, + { "[[[[[([[[[[[[[[[[[[[[[[[[[[[[[8!1i]')", 0, 0 }, + { "([((\?(\?#>)(\?{,)At]%M9FSq5)EB", 1, 0 }, + { "(}````````````````(``{210,})[:(\?#space:]P[:digit:])PP.{-227,}$pK~mm ImR|{,51}[:alnum:]<)[:alpha:]", 2, 0 }, + { "[^(\?<=])[:digit:]", 0, 0 }, + { "[^'''''''{(\?:178,}e{,16}$QQQQQQQQQQQQQQQQQQQQQQQ$])", 0, 0 }, + { "[^(\?>@K*)(\?#d18]{78,}B)[:digit:]{-193,}=wg{,59}", 0, 0 }, + { "[^.{156,}!(\?<=!!!!!!!!!!!!!!(\?{!(\?(!!!!!!!!!!!!!)})TTTTTTTTTTTTTTTTTTTTTTTTTTTTT[^}}}}}}}}}}}})}}}}}}}}}}}}}]]){}^L#%-{}FC", 0, 0 }, + { "(eeeee{-169,-100}-fa[:upperword:]N)$Nellllllllllllll", 1, 0 }, + { "[[(\?!())\?[(\?!:alnum:]e{,28}M])[:punct:]CCCCCCCCCCCCCCCCCCCC]{-150,}{-167}", 0, 0 }, + { "[[@[@(\?#@[@]P]Z{')]{-186,117}]+)7f-", 0, 0 }, + { "\\Q+kD}]AEM)u ", 0, 0 }, + { "([(\?{(\?=:::::::::::::&){,210}]^})P{-31,}8[:space:]C[:alnum:][:a(scii:]z|[:upperword:])[:alnum:][:graph:])zr~Zk", 1, 0 }, + { ".[:space:]e[:g(\?{(\?{raph:]})})@@@@@@@@@@@@@wb|~k", 0, 0 }, + { "()ooooooooo\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"[:graph:]", 1, 0 }, + { "[^64h(\?(@Eyw][:xdi[git:]pP%%%%%u(uuu[:up[perword:]`8Utdh{)}]]))lW[:punct:]W.hhhhhhhhhhhhhhhhhhhhhhhh'm<<}O8`ZXtG.$", 1, 0 }, + { "BPP[:digit:]bbbbbbbbbbb(bb)S+[:alnum:]", 1, 0 }, + { "um.[:ascii((\?#\?!:])*)+KKKKKKKKKKKKKKKKKKKKKKKKKS.=<Bf", 0, 0 }, + { "", -1, 0 }, + { "(()$[:lowerprint:][:s[pace:]2]bbbbbbbbbyoooooooooooooooooo*{39,}$')qV`AcH>,eDl", -1, 0 }, + { "(()[^])e{-241,}", -1, 0 }, + { "()[:alpha:]rliiiiiiii[:alnum:]Mb*QW9N.>\?{115,}&u*j", -1, 0 }, + { "()[]p", -1, 0 }, + { "(I[^]pfL)$[:punct:]", -1, 0 }, + { "([])>>>>>>>>>>[:alnum:]", -1, 0 }, + { "([])O\\\\\\\\\\\\\\fffffffffffffffffffffff=s6jCZy/b+ir2'*{151,}", -1, 0 }, + { "([])nnnnnnnnnnnnnnnnnnnnnnnnnn[:xdigit:]^N$f", -1, 0 }, + { "([]M)[:lowerprint:]a(pg$Z[:punct:])77777777777.", -1, 0 }, + { "([]XXXXXXXXXXXXXXXXXXXXXX-===========)", -1, 0 }, + { "([]lkX{-224}[:blankcntrl:]$gPKIZlSC#F@XX I'^}{234}yZm)uuuuuuuuuuuuuuuuuuuuuurS", -1, 0 }, + { "([^0kYkg9])IIIIIIIIIIIIIIIIIIIIII/{(192,-118}l+FoSD6\?A)c[:xdigit:]`````````````````e-{-4,-170}x{4620}Z[:upperword:]", -1, 0 }, + { "([^[^[^()(\?>){}B]XYF+#[:alpha:]{-85((,-55[^}t]n).{,-33}]](bQJ!|O+{175,})RFh)Z+^.{137,}:VpP[:alpha:]-MceqVVkkkk(kkkkkkkkkkkkkkkkkk)\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?{-115,-67})``````````````````````````````", -1, 0 }, + { "([^]EzU[:alnum:]+^^^^^^^^^^^^^^^^^^^)[:xdigit:]HHHHHHHH$66666666666666666666666666666666UUUUUUUUUUUUUUUUUUUUL{}iiii{-76}X", -1, 0 }, + { "([^]~~~~~~~~~~{240,})]NOp", -1, 0 }, + { "(sb)[:digit:]VVVVVVVVx{9569}52,|]", -1, 0 }, + { "(x{19762}){}", -1, 0 }, + { "-[:xdigit:][]", -1, 0 }, + { "121|", -1, 0 }, + { "141[:xdigit:][:lowerprint:]{24}{59,191}[:digit:]/", -1, 0 }, + { "G[^],,,,,,,,,,,,,+\"DiX", -1, 0 }, + { "Gm(ho9:\"8{-188,-200}Z[:blankcntrl:]{,171}\?\?\?\?\?\?\?\?\?\?\?[:blankcntrl:]LLLLLLLLLLLLLLLLLLLLLLL{}^[:graph:][:blankc(\?#ntrl:])w", -1, 0 }, + { "N\"\"\"\"\"\"\"-------------------------|[:alnum:]AAAAAAAAAAAAAAAAAAAAf\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?", -1, 0 }, + { "U{-30,}^\?\?\?", -1, 0 }, + { "W^*04rAY(Ee*>[^o3[]]_)", -1, 0 }, + { "X[^]}*C[:alnum:]", -1, 0 }, + { "[${,-3}]+^\?[|x8A|][:space:]'''''['''''JJJJJJJJJJJJJJJJJJJJJJJJJJJJJyl}.Y7G]", -1, 0 }, + { "[()&[&&&]\?\?[\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?pg%k8ug`Wqk4|NR{h[CK5Ez=]jHpQw&`{:]{,91}D", -1, 0 }, + { "[(\?#(\?:)[)([\?>)(\?>(\?:[:alnum:])]G]{85}[^)w]N]gYrUs|", -1, 0 }, + { "[(\?<=)[:digit:]\?]{152,}VR|", -1, 0 }, + { "[****(\?>**********(\?<!*******Q)Vr){[^25,}*:FFFFFFFFFFFFFFFFFFFFFFFF(\?{FFFF(({}D]|", -1, 0 }, + { "[:ascii:]+{124,}:*]\?$-{92}D[:lowerprint:]`````````````````````", -1, 0 }, + { "[:ascii:]\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?A<", -1, 0 }, + { "[:blankcntrl:]p\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?$\?TTTTTTTTTTTTTTTTT[:ascii:][:upperword:]", -1, 0 }, + { "[:punct:]{254}DDDDDDDDDDDDDDD@[:alpha:]Z\?\?-----R", -1, 0 }, + { "[:upperword:]J\?\?nqCAdfyW5", -1, 0 }, + { "[:upperword:]{-39}|", -1, 0 }, + { "[:xdigit:]^\?", -1, 0 }, + { "[Z*e ]NdmP\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?\?", -1, 0 }, + { "[[:punct:]q]ex{15625}-", -1, 0 }, + { "[[[^([^L((\?{b(\?=C\?]-134{,-207}[:ascii:]Hz}XIz}|", -1, 0 }, + { "[[^V(\?:(\?<!(\?>))TTTTTTTTTTTTTTTTTTTTTTT)[:punct:][:digit:]]GGGGGGGGGGGGGGGGGGGGG,]|.{-224}{96}{239,}1", -1, 0 }, + { "[[^^PP]{,-222}{182}{141}]zFD}-.", -1, 0 }, + { "[] Hn&[:xdigit:][:upperword:]f", -1, 0 }, + { "[]$.B", -1, 0 }, + { "[]&&&&&&&&&&&&&&&&&&&&&&&", -1, 0 }, + { "[]()[:xdigit:]er063{132,140}$", -1, 0 }, + { "[]+1434", -1, 0 }, + { "[]-", -1, 0 }, + { "[]-#yyK", -1, 0 }, + { "[]-(S$5)AxbdTKO[:alnum:]", -1, 0 }, + { "[]2883", -1, 0 }, + { "[]2dhd-[:alpha:]sssssssssssssssss55555555555555555555555555555555Z[:punct:]", -1, 0 }, + { "[]4", -1, 0 }, + { "[]44444444444444444G", -1, 0 }, + { "[]\?", -1, 0 }, + { "[]A", -1, 0 }, + { "[]Gap8bc", -1, 0 }, + { "[]OOOO", -1, 0 }, + { "[]PP", -1, 0 }, + { "[]QQ", -1, 0 }, + { "[]WaFaGO,o", -1, 0 }, + { "[]Z", -1, 0 }, + { "[][:alpha:]|[:digit:]Ls$I-Ff~+xA3e", -1, 0 }, + { "[][:ascii:]-218", -1, 0 }, + { "[][:ascii:]N}}}}}}}}}}}}}}}-{137,}8682", -1, 0 }, + { "[][:lowerprint:]Ur", -1, 0 }, + { "[][:space:]15097", -1, 0 }, + { "[][:xdigit:]", -1, 0 }, + { "[]dpSSSSSSSS", -1, 0 }, + { "[]e13768", -1, 0 }, + { "[]gT", -1, 0 }, + { "[]h", -1, 0 }, + { "[]n", -1, 0 }, + { "[]vvvvvvvvvvvvvvvvvvvvvvvvvv*[:xdigit:]", -1, 0 }, + { "[]{,-212}1111111111111111111C3821", -1, 0 }, + { "[]{-128,}hc", -1, 0 }, + { "[]{-181,}&[:xdigit:].\?}}}}}}}}}}}}}}}}}}}}}}", -1, 0 }, + { "[]{}F&}i`7|ZAH", -1, 0 }, + { "[^(\?())u{196,}pP][r^ndddddddddddddddddddddd]{31,246}\?J", -1, 0 }, + { "[^.ii.1-S]lwwwwwwwwwwwwwwwwww[^wwwwwwwwwwwwww[:alnum:]DOpP+<N][^]44{179}{-194,56}", -1, 0 }, + { "[^2[:alnum:]]\?t\?\?", -1, 0 }, + { "[^[((\?{[^^<<<<(\?(\?<!{)})(\?<!]{,184}{-213}|", -1, 0 }, + { "[^[^[]\?{89,}PPsvf{[:space:]]]vd{161,}", -1, 0 }, + { "[^[^].]+{0}s", -1, 0 }, + { "[^]${}", -1, 0 }, + { "[^]([:punct:]),%[:xdigit:]w^0\?{-233}", -1, 0 }, + { "[^]-", -1, 0 }, + { "[^].^", -1, 0 }, + { "[^]6743", -1, 0 }, + { "[^]JD", -1, 0 }, + { "[^]N=[:upperword:]zzzzzzzzzzzzzzzzz.", -1, 0 }, + { "[^]OLz_6", -1, 0 }, + { "[^]PP[:digit:]0eBEx=", -1, 0 }, + { "[^]SHzuKp", -1, 0 }, + { "[^][:upperword:]{111}-TpmXw", -1, 0 }, + { "[^]^''''''''z{-73,}", -1, 0 }, + { "[^]^{,141}e", -1, 0 }, + { "[^]aaaaaaaaaaaaaaaaaaa{-98,43}", -1, 0 }, + { "[^]f", -1, 0 }, + { "[^]l", -1, 0 }, + { "[^]n\"Wt", -1, 0 }, + { "[^]pPZ\?q+m0LJ+", -1, 0 }, + { "[^]p[:upperword:]L:", -1, 0 }, + { "[^]q\?{,-18}-", -1, 0 }, + { "[^]s[:space:(\?<=]$", -1, 0 }, + { "[^]{,58}t", -1, 0 }, + { "[^]{255,}JJJJJJJJJJJJJJJJJJJJJJJJJJ", -1, 0 }, + { "[^]{45}", -1, 0 }, + { "[^]{W", -1, 0 }, + { "[^]{}{-22}", -1, 0 }, + { "[^]{}{}{}[:xdigit:]+", -1, 0 }, + { "[^]|9{,-108}{}.LVIJJJJJJJJJJJJJJJPP", -1, 0 }, + { "[^{,-254}]|", -1, 0 }, + { "[o(\?{(\?<=}[))f++++++++++++++++777777777777777777777777yzPPs]\?\?dRRRRRRRRRRRRRRRRRRRRRRRRRRRR&]>%fffffffffff", -1, 0 }, + { "aW|", -1, 0 }, + { "cT{}[]C^r2``tm", -1, 0 }, + { "kkkkkkkkkkkkkkkkkkkkkkk[:blankcntrl:]|{}3{26,}{151,}[:punct:]JJJlH$gP%(2WUE%%%%%%%%%%%%%%%%%%%%a){ibf{}\?", -1, 0 }, + { "lZ\?\?\?\?\?\?\?\?\?\?\?-P2eZt[:punct:]", -1, 0 }, + { "vF3qn[^]N.", -1, 0 }, + { "wwwwwwwwwwwwww{-176,}275[^]>.UUUUUUUUUUUUUUUUUUUUeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee2$Yd", -1, 0 }, + { "{-197,223}bf]]]]]]]]]]\?&}/s\?\?~c", -1, 0 }, + { "{-37,}EpP|", -1, 0 }, + { "{}@]a[][:xdigit:]z{a", -1, 0 }, + { "}02|", -1, 0 }, + { "}}}}}}}}}(}}){}[llll]^N|", -1, 0 }, + }; + unsigned int i; + int r; + + UNUSED(tc); + +#ifdef HAVE_REGEX_H + /* + * Check if we get the expected response. + */ + for (i = 0; i < sizeof(tests)/sizeof(*tests); i++) { + regex_t preg; + + memset(&preg, 0, sizeof(preg)); + r = regcomp(&preg, tests[i].expression, REG_EXTENDED); + if (((r != 0 && tests[i].expect != -1) || + (r == 0 && tests[i].expect == -1)) && !tests[i].exception) + fprintf(stderr, "regcomp(%s) -> %s expected %s\n", + tests[i].expression, r != 0 ? "bad" : "good", + tests[i].expect == -1 ? "bad" : "good"); + else if (r == 0 && + preg.re_nsub != (unsigned int)tests[i].expect && + !tests[i].exception) { + fprintf(stderr, "%s preg.re_nsub %lu expected %d\n", + tests[i].expression, + (unsigned long)preg.re_nsub, tests[i].expect); + tests[i].expect = preg.re_nsub; + } + if (r == 0) + regfree(&preg); + } +#endif + + /* + * Check if we get the expected response. + */ + for (i = 0; i < sizeof(tests)/sizeof(*tests); i++) { + r = isc_regex_validate(tests[i].expression); + if (r != tests[i].expect) + fprintf(stderr, "%s -> %d expected %d\n", + tests[i].expression, r, tests[i].expect); + ATF_CHECK_EQ(r, tests[i].expect); + } +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, regex_validate); + return (atf_no_error()); +} + diff --git a/lib/isc/tests/result_test.c b/lib/isc/tests/result_test.c new file mode 100644 index 0000000..cfaabb3 --- /dev/null +++ b/lib/isc/tests/result_test.c @@ -0,0 +1,56 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <atf-c.h> + +#include <string.h> + +#include <isc/result.h> + +ATF_TC(isc_result_toid); +ATF_TC_HEAD(isc_result_toid, tc) { + atf_tc_set_md_var(tc, "descr", "convert result to identifier string"); +} +ATF_TC_BODY(isc_result_toid, tc) { + const char *id; + + id = isc_result_toid(ISC_R_SUCCESS); + ATF_REQUIRE_STREQ("ISC_R_SUCCESS", id); + + id = isc_result_toid(ISC_R_FAILURE); + ATF_REQUIRE_STREQ("ISC_R_FAILURE", id); +} + +ATF_TC(isc_result_totext); +ATF_TC_HEAD(isc_result_totext, tc) { + atf_tc_set_md_var(tc, "descr", "convert result to description string"); +} +ATF_TC_BODY(isc_result_totext, tc) { + const char *str; + + str = isc_result_totext(ISC_R_SUCCESS); + ATF_REQUIRE_STREQ("success", str); + + str = isc_result_totext(ISC_R_FAILURE); + ATF_REQUIRE_STREQ("failure", str); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_result_toid); + ATF_TP_ADD_TC(tp, isc_result_totext); + + return (atf_no_error()); +} diff --git a/lib/isc/tests/safe_test.c b/lib/isc/tests/safe_test.c new file mode 100644 index 0000000..f721cd1 --- /dev/null +++ b/lib/isc/tests/safe_test.c @@ -0,0 +1,112 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/* ! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <stdio.h> +#include <string.h> + +#include <isc/safe.h> +#include <isc/util.h> + +ATF_TC(isc_safe_memequal); +ATF_TC_HEAD(isc_safe_memequal, tc) { + atf_tc_set_md_var(tc, "descr", "safe memequal()"); +} +ATF_TC_BODY(isc_safe_memequal, tc) { + UNUSED(tc); + + ATF_CHECK(isc_safe_memequal("test", "test", 4)); + ATF_CHECK(!isc_safe_memequal("test", "tesc", 4)); + ATF_CHECK(isc_safe_memequal("\x00\x00\x00\x00", + "\x00\x00\x00\x00", 4)); + ATF_CHECK(!isc_safe_memequal("\x00\x00\x00\x00", + "\x00\x00\x00\x01", 4)); + ATF_CHECK(!isc_safe_memequal("\x00\x00\x00\x02", + "\x00\x00\x00\x00", 4)); +} + +ATF_TC(isc_safe_memcompare); +ATF_TC_HEAD(isc_safe_memcompare, tc) { + atf_tc_set_md_var(tc, "descr", "safe memcompare()"); +} +ATF_TC_BODY(isc_safe_memcompare, tc) { + UNUSED(tc); + + ATF_CHECK(isc_safe_memcompare("test", "test", 4) == 0); + ATF_CHECK(isc_safe_memcompare("test", "tesc", 4) > 0); + ATF_CHECK(isc_safe_memcompare("test", "tesy", 4) < 0); + ATF_CHECK(isc_safe_memcompare("\x00\x00\x00\x00", + "\x00\x00\x00\x00", 4) == 0); + ATF_CHECK(isc_safe_memcompare("\x00\x00\x00\x00", + "\x00\x00\x00\x01", 4) < 0); + ATF_CHECK(isc_safe_memcompare("\x00\x00\x00\x02", + "\x00\x00\x00\x00", 4) > 0); +} + +ATF_TC(isc_safe_memwipe); +ATF_TC_HEAD(isc_safe_memwipe, tc) { + atf_tc_set_md_var(tc, "descr", "isc_safe_memwipe()"); +} +ATF_TC_BODY(isc_safe_memwipe, tc) { + UNUSED(tc); + + /* These should pass. */ + isc_safe_memwipe(NULL, 0); + isc_safe_memwipe((void *) -1, 0); + isc_safe_memwipe(NULL, 42); + + /* + * isc_safe_memwipe(ptr, size) should function same as + * memset(ptr, 0, size); + */ + { + char buf1[4] = { 1, 2, 3, 4 }; + char buf2[4] = { 1, 2, 3, 4 }; + + isc_safe_memwipe(buf1, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + + ATF_CHECK(memcmp(buf1, buf2, sizeof(buf1)) == 0); + } + + /* + * Boundary test. + */ + { + char buf1[4] = { 1, 2, 3, 4 }; + char buf2[4] = { 1, 2, 3, 4 }; + + /* + * We wipe 3 elements on purpose, keeping the 4th in + * place. + */ + isc_safe_memwipe(buf1, 3); + memset(buf2, 0, 3); + + ATF_CHECK(memcmp(buf1, buf2, sizeof(buf1)) == 0); + } +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_safe_memequal); + ATF_TP_ADD_TC(tp, isc_safe_memcompare); + ATF_TP_ADD_TC(tp, isc_safe_memwipe); + return (atf_no_error()); +} diff --git a/lib/isc/tests/sockaddr_test.c b/lib/isc/tests/sockaddr_test.c new file mode 100644 index 0000000..fd6494f --- /dev/null +++ b/lib/isc/tests/sockaddr_test.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <stdbool.h> +#include <unistd.h> + +#include <isc/netaddr.h> +#include <isc/sockaddr.h> +#include <isc/print.h> + +#include "isctest.h" + +/* + * Individual unit tests + */ + +ATF_TC(sockaddr_hash); +ATF_TC_HEAD(sockaddr_hash, tc) { + atf_tc_set_md_var(tc, "descr", "sockaddr hash"); +} +ATF_TC_BODY(sockaddr_hash, tc) { + isc_result_t result; + isc_sockaddr_t addr; + struct in_addr in; + struct in6_addr in6; + unsigned int h1, h2, h3, h4; + int ret; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + in.s_addr = inet_addr("127.0.0.1"); + isc_sockaddr_fromin(&addr, &in, 1); + h1 = isc_sockaddr_hash(&addr, true); + h2 = isc_sockaddr_hash(&addr, false); + ATF_CHECK(h1 != h2); + + ret = inet_pton(AF_INET6, "::ffff:127.0.0.1", &in6); + ATF_CHECK(ret == 1); + isc_sockaddr_fromin6(&addr, &in6, 1); + h3 = isc_sockaddr_hash(&addr, true); + h4 = isc_sockaddr_hash(&addr, false); + ATF_CHECK(h1 == h3); + ATF_CHECK(h2 == h4); + + isc_test_end(); +} + +ATF_TC(sockaddr_isnetzero); +ATF_TC_HEAD(sockaddr_isnetzero, tc) { + atf_tc_set_md_var(tc, "descr", "sockaddr is net zero"); +} +ATF_TC_BODY(sockaddr_isnetzero, tc) { + isc_sockaddr_t addr; + struct in_addr in; + struct in6_addr in6; + bool r; + int ret; + size_t i; + struct { + const char *string; + bool expect; + } data4[] = { + { "0.0.0.0", true }, + { "0.0.0.1", true }, + { "0.0.1.0", true }, + { "0.1.0.0", true }, + { "1.0.0.0", false }, + { "0.0.0.127", true }, + { "0.0.0.255", true }, + { "127.0.0.1", false }, + { "255.255.255.255", false }, + }; + /* + * Mapped addresses are currently not netzero. + */ + struct { + const char *string; + bool expect; + } data6[] = { + { "::ffff:0.0.0.0", false }, + { "::ffff:0.0.0.1", false }, + { "::ffff:0.0.0.127", false }, + { "::ffff:0.0.0.255", false }, + { "::ffff:127.0.0.1", false }, + { "::ffff:255.255.255.255", false }, + }; + + UNUSED(tc); + + for (i = 0; i < sizeof(data4)/sizeof(data4[0]); i++) { + in.s_addr = inet_addr(data4[i].string); + isc_sockaddr_fromin(&addr, &in, 1); + r = isc_sockaddr_isnetzero(&addr); + ATF_CHECK_EQ_MSG(r, data4[i].expect, "%s", data4[i].string); + } + + for (i = 0; i < sizeof(data6)/sizeof(data6[0]); i++) { + ret = inet_pton(AF_INET6, data6[i].string, &in6); + ATF_CHECK_EQ(ret, 1); + isc_sockaddr_fromin6(&addr, &in6, 1); + r = isc_sockaddr_isnetzero(&addr); + ATF_CHECK_EQ_MSG(r, data6[i].expect, "%s", data6[i].string); + } +} + +ATF_TC(sockaddr_eqaddrprefix); +ATF_TC_HEAD(sockaddr_eqaddrprefix, tc) { + atf_tc_set_md_var(tc, "descr", + "isc_sockaddr_eqaddrprefix() returns true when " + "prefixes of a and b are equal, and false when " + "they are not equal"); +} +ATF_TC_BODY(sockaddr_eqaddrprefix, tc) { + struct in_addr ina_a; + struct in_addr ina_b; + struct in_addr ina_c; + isc_sockaddr_t isa_a; + isc_sockaddr_t isa_b; + isc_sockaddr_t isa_c; + + UNUSED(tc); + + ATF_CHECK(inet_pton(AF_INET, "194.100.32.87", &ina_a) >= 0); + ATF_CHECK(inet_pton(AF_INET, "194.100.32.80", &ina_b) >= 0); + ATF_CHECK(inet_pton(AF_INET, "194.101.32.87", &ina_c) >= 0); + + isc_sockaddr_fromin(&isa_a, &ina_a, 0); + isc_sockaddr_fromin(&isa_b, &ina_b, 42); + isc_sockaddr_fromin(&isa_c, &ina_c, 0); + + ATF_CHECK(isc_sockaddr_eqaddrprefix(&isa_a, &isa_b, 0)); + ATF_CHECK(isc_sockaddr_eqaddrprefix(&isa_a, &isa_b, 29)); + ATF_CHECK(isc_sockaddr_eqaddrprefix(&isa_a, &isa_c, 8)); + + ATF_CHECK(! isc_sockaddr_eqaddrprefix(&isa_a, &isa_b, 30)); + ATF_CHECK(! isc_sockaddr_eqaddrprefix(&isa_a, &isa_b, 32)); + ATF_CHECK(! isc_sockaddr_eqaddrprefix(&isa_a, &isa_c, 16)); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, sockaddr_hash); + ATF_TP_ADD_TC(tp, sockaddr_isnetzero); + ATF_TP_ADD_TC(tp, sockaddr_eqaddrprefix); + + return (atf_no_error()); +} diff --git a/lib/isc/tests/socket_test.c b/lib/isc/tests/socket_test.c new file mode 100644 index 0000000..3f3f249 --- /dev/null +++ b/lib/isc/tests/socket_test.c @@ -0,0 +1,923 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <stdbool.h> +#include <unistd.h> +#include <time.h> + +#include <isc/platform.h> +#include <isc/socket.h> +#include <isc/print.h> + +#include "../task_p.h" +#include "../unix/socket_p.h" +#include "isctest.h" + +static bool recv_dscp; +static unsigned int recv_dscp_value; +static bool recv_trunc; + +/* + * Helper functions + */ + +typedef struct { + bool done; + isc_result_t result; + isc_socket_t *socket; +} completion_t; + +static void +completion_init(completion_t *completion) { + completion->done = false; + completion->socket = NULL; +} + +static void +accept_done(isc_task_t *task, isc_event_t *event) { + isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event; + completion_t *completion = event->ev_arg; + + UNUSED(task); + + completion->result = nevent->result; + completion->done = true; + if (completion->result == ISC_R_SUCCESS) + completion->socket = nevent->newsocket; + + isc_event_free(&event); +} + +static void +event_done(isc_task_t *task, isc_event_t *event) { + isc_socketevent_t *dev; + completion_t *completion = event->ev_arg; + + UNUSED(task); + + dev = (isc_socketevent_t *) event; + completion->result = dev->result; + completion->done = true; + if ((dev->attributes & ISC_SOCKEVENTATTR_DSCP) != 0) { + recv_dscp = true; + recv_dscp_value = dev->dscp;; + } else { + recv_dscp = false; + } + recv_trunc = (dev->attributes & ISC_SOCKEVENTATTR_TRUNC); + isc_event_free(&event); +} + +static isc_result_t +waitfor(completion_t *completion) { + int i = 0; + while (!completion->done && i++ < 5000) { +#ifndef ISC_PLATFORM_USETHREADS + while (isc__taskmgr_ready(taskmgr)) + isc__taskmgr_dispatch(taskmgr); +#endif + isc_test_nap(1000); + } + if (completion->done) + return (ISC_R_SUCCESS); + return (ISC_R_FAILURE); +} + +#if 0 +static isc_result_t +waitfor(completion_t *completion) { + int i = 0; + while (!completion->done && i++ < 5000) { + waitbody(); + } + if (completion->done) + return (ISC_R_SUCCESS); + return (ISC_R_FAILURE); +} +#endif + +static void +waitbody(void) { +#ifndef ISC_PLATFORM_USETHREADS + struct timeval tv; + isc_socketwait_t *swait = NULL; + + while (isc__taskmgr_ready(taskmgr)) + isc__taskmgr_dispatch(taskmgr); + if (socketmgr != NULL) { + tv.tv_sec = 0; + tv.tv_usec = 1000 ; + if (isc__socketmgr_waitevents(socketmgr, &tv, &swait) > 0) + isc__socketmgr_dispatch(socketmgr, swait); + } else +#endif + isc_test_nap(1000); +} + +static isc_result_t +waitfor2(completion_t *c1, completion_t *c2) { + int i = 0; + + while (!(c1->done && c2->done) && i++ < 5000) { + waitbody(); + } + if (c1->done && c2->done) + return (ISC_R_SUCCESS); + return (ISC_R_FAILURE); +} + +/* + * Individual unit tests + */ + +/* Test UDP sendto/recv (IPv4) */ +ATF_TC(udp_sendto); +ATF_TC_HEAD(udp_sendto, tc) { + atf_tc_set_md_var(tc, "descr", "UDP sendto/recv"); +} +ATF_TC_BODY(udp_sendto, tc) { + isc_result_t result; + isc_sockaddr_t addr1, addr2; + struct in_addr in; + isc_socket_t *s1 = NULL, *s2 = NULL; + isc_task_t *task = NULL; + char sendbuf[BUFSIZ], recvbuf[BUFSIZ]; + completion_t completion; + isc_region_t r; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + in.s_addr = inet_addr("127.0.0.1"); + isc_sockaddr_fromin(&addr1, &in, 0); + isc_sockaddr_fromin(&addr2, &in, 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_udp, &s1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_bind(s1, &addr1, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_getsockname(s1, &addr1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr1) != 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_udp, &s2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_bind(s2, &addr2, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_getsockname(s2, &addr2); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr2) != 0); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + snprintf(sendbuf, sizeof(sendbuf), "Hello"); + r.base = (void *) sendbuf; + r.length = strlen(sendbuf) + 1; + + completion_init(&completion); + result = isc_socket_sendto(s1, &r, task, event_done, &completion, + &addr2, NULL); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + result = isc_socket_recv(s2, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "Hello"); + + isc_task_detach(&task); + + isc_socket_detach(&s1); + isc_socket_detach(&s2); + + isc_test_end(); +} + +/* Test UDP sendto/recv with duplicated socket */ +ATF_TC(udp_dup); +ATF_TC_HEAD(udp_dup, tc) { + atf_tc_set_md_var(tc, "descr", "duplicated socket sendto/recv"); +} +ATF_TC_BODY(udp_dup, tc) { + isc_result_t result; + isc_sockaddr_t addr1, addr2; + struct in_addr in; + isc_socket_t *s1 = NULL, *s2 = NULL, *s3 = NULL; + isc_task_t *task = NULL; + char sendbuf[BUFSIZ], recvbuf[BUFSIZ]; + completion_t completion; + isc_region_t r; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + in.s_addr = inet_addr("127.0.0.1"); + isc_sockaddr_fromin(&addr1, &in, 0); + isc_sockaddr_fromin(&addr2, &in, 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_udp, &s1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_bind(s1, &addr1, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_getsockname(s1, &addr1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr1) != 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_udp, &s2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_bind(s2, &addr2, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_getsockname(s2, &addr2); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr2) != 0); + + result = isc_socket_dup(s2, &s3); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + snprintf(sendbuf, sizeof(sendbuf), "Hello"); + r.base = (void *) sendbuf; + r.length = strlen(sendbuf) + 1; + + completion_init(&completion); + result = isc_socket_sendto(s1, &r, task, event_done, &completion, + &addr2, NULL); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + snprintf(sendbuf, sizeof(sendbuf), "World"); + r.base = (void *) sendbuf; + r.length = strlen(sendbuf) + 1; + + completion_init(&completion); + result = isc_socket_sendto(s1, &r, task, event_done, &completion, + &addr2, NULL); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + result = isc_socket_recv(s2, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "Hello"); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + result = isc_socket_recv(s3, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "World"); + + isc_task_detach(&task); + + isc_socket_detach(&s1); + isc_socket_detach(&s2); + isc_socket_detach(&s3); + + isc_test_end(); +} + +/* Test TCP sendto/recv (IPv4) */ +ATF_TC(udp_dscp_v4); +ATF_TC_HEAD(udp_dscp_v4, tc) { + atf_tc_set_md_var(tc, "descr", "UDP DSCP IPV4"); +} +ATF_TC_BODY(udp_dscp_v4, tc) { + isc_result_t result; + isc_sockaddr_t addr1, addr2; + struct in_addr in; + isc_socket_t *s1 = NULL, *s2 = NULL; + isc_task_t *task = NULL; + char sendbuf[BUFSIZ], recvbuf[BUFSIZ]; + completion_t completion; + isc_region_t r; + isc_socketevent_t *socketevent; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + in.s_addr = inet_addr("127.0.0.1"); + isc_sockaddr_fromin(&addr1, &in, 0); + isc_sockaddr_fromin(&addr2, &in, 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_udp, &s1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_bind(s1, &addr1, ISC_SOCKET_REUSEADDRESS); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_getsockname(s1, &addr1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr1) != 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_udp, &s2); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_bind(s2, &addr2, ISC_SOCKET_REUSEADDRESS); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_getsockname(s2, &addr2); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr2) != 0); + + result = isc_task_create(taskmgr, 0, &task); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + + snprintf(sendbuf, sizeof(sendbuf), "Hello"); + r.base = (void *) sendbuf; + r.length = strlen(sendbuf) + 1; + + completion_init(&completion); + + socketevent = isc_socket_socketevent(mctx, s1, ISC_SOCKEVENT_SENDDONE, + event_done, &completion); + ATF_REQUIRE(socketevent != NULL); + + if ((isc_net_probedscp() & ISC_NET_DSCPPKTV4) != 0) { + socketevent->dscp = 056; /* EF */ + socketevent->attributes |= ISC_SOCKEVENTATTR_DSCP; + } else if ((isc_net_probedscp() & ISC_NET_DSCPSETV4) != 0) { + isc_socket_dscp(s1, 056); /* EF */ + socketevent->dscp = 0; + socketevent->attributes &= ~ISC_SOCKEVENTATTR_DSCP; + } + + recv_dscp = false; + recv_dscp_value = 0; + + result = isc_socket_sendto2(s1, &r, task, &addr2, NULL, socketevent, 0); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + result = isc_socket_recv(s2, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "Hello"); + + if ((isc_net_probedscp() & ISC_NET_DSCPRECVV4) != 0) { + ATF_CHECK(recv_dscp); + ATF_CHECK_EQ(recv_dscp_value, 056); + } else + ATF_CHECK(!recv_dscp); + isc_task_detach(&task); + + isc_socket_detach(&s1); + isc_socket_detach(&s2); + + isc_test_end(); +} + +/* Test TCP sendto/recv (IPv4) */ +ATF_TC(udp_dscp_v6); +ATF_TC_HEAD(udp_dscp_v6, tc) { + atf_tc_set_md_var(tc, "descr", "udp dscp ipv6"); +} +ATF_TC_BODY(udp_dscp_v6, tc) { +#if defined(ISC_PLATFORM_HAVEIPV6) && defined(WANT_IPV6) + isc_result_t result; + isc_sockaddr_t addr1, addr2; + struct in6_addr in6; + isc_socket_t *s1 = NULL, *s2 = NULL; + isc_task_t *task = NULL; + char sendbuf[BUFSIZ], recvbuf[BUFSIZ]; + completion_t completion; + isc_region_t r; + isc_socketevent_t *socketevent; + int n; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + n = inet_pton(AF_INET6, "::1", &in6.s6_addr); + ATF_REQUIRE(n == 1); + isc_sockaddr_fromin6(&addr1, &in6, 0); + isc_sockaddr_fromin6(&addr2, &in6, 0); + + result = isc_socket_create(socketmgr, PF_INET6, isc_sockettype_udp, + &s1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_bind(s1, &addr1, 0); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_getsockname(s1, &addr1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr1) != 0); + + result = isc_socket_create(socketmgr, PF_INET6, isc_sockettype_udp, + &s2); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_bind(s2, &addr2, 0); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_getsockname(s2, &addr2); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr2) != 0); + + result = isc_task_create(taskmgr, 0, &task); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + + snprintf(sendbuf, sizeof(sendbuf), "Hello"); + r.base = (void *) sendbuf; + r.length = strlen(sendbuf) + 1; + + completion_init(&completion); + + socketevent = isc_socket_socketevent(mctx, s1, ISC_SOCKEVENT_SENDDONE, + event_done, &completion); + ATF_REQUIRE(socketevent != NULL); + + if ((isc_net_probedscp() & ISC_NET_DSCPPKTV6) != 0) { + socketevent->dscp = 056; /* EF */ + socketevent->attributes = ISC_SOCKEVENTATTR_DSCP; + } else if ((isc_net_probedscp() & ISC_NET_DSCPSETV6) != 0) + isc_socket_dscp(s1, 056); /* EF */ + + recv_dscp = false; + recv_dscp_value = 0; + + result = isc_socket_sendto2(s1, &r, task, &addr2, NULL, socketevent, 0); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + result = isc_socket_recv(s2, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "Hello"); + if ((isc_net_probedscp() & ISC_NET_DSCPRECVV6) != 0) { + ATF_CHECK(recv_dscp); + ATF_CHECK_EQ(recv_dscp_value, 056); + } else + ATF_CHECK(!recv_dscp); + + isc_task_detach(&task); + + isc_socket_detach(&s1); + isc_socket_detach(&s2); + + isc_test_end(); +#else + UNUSED(tc); + atf_tc_skip("IPv6 not available"); +#endif +} + +/* Test TCP sendto/recv (IPv4) */ +ATF_TC(tcp_dscp_v4); +ATF_TC_HEAD(tcp_dscp_v4, tc) { + atf_tc_set_md_var(tc, "descr", "tcp dscp ipv4"); +} +ATF_TC_BODY(tcp_dscp_v4, tc) { + isc_result_t result; + isc_sockaddr_t addr1; + struct in_addr in; + isc_socket_t *s1 = NULL, *s2 = NULL, *s3 = NULL; + isc_task_t *task = NULL; + char sendbuf[BUFSIZ], recvbuf[BUFSIZ]; + completion_t completion, completion2; + isc_region_t r; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + in.s_addr = inet_addr("127.0.0.1"); + isc_sockaddr_fromin(&addr1, &in, 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_tcp, &s1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_socket_bind(s1, &addr1, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_getsockname(s1, &addr1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr1) != 0); + + result = isc_socket_listen(s1, 3); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_tcp, &s2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + completion_init(&completion2); + result = isc_socket_accept(s1, task, accept_done, &completion2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + completion_init(&completion); + result = isc_socket_connect(s2, &addr1, task, event_done, &completion); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + waitfor2(&completion, &completion2); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK(completion2.done); + ATF_CHECK_EQ(completion2.result, ISC_R_SUCCESS); + s3 = completion2.socket; + + isc_socket_dscp(s2, 056); /* EF */ + + snprintf(sendbuf, sizeof(sendbuf), "Hello"); + r.base = (void *) sendbuf; + r.length = strlen(sendbuf) + 1; + + recv_dscp = false; + recv_dscp_value = 0; + + completion_init(&completion); + result = isc_socket_sendto(s2, &r, task, event_done, &completion, + NULL, NULL); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + result = isc_socket_recv(s3, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "Hello"); + + if ((isc_net_probedscp() & ISC_NET_DSCPRECVV4) != 0) { + if (recv_dscp) + ATF_CHECK_EQ(recv_dscp_value, 056); + } else + ATF_CHECK(!recv_dscp); + + isc_task_detach(&task); + + isc_socket_detach(&s1); + isc_socket_detach(&s2); + isc_socket_detach(&s3); + + isc_test_end(); +} + +/* Test TCP sendto/recv (IPv6) */ +ATF_TC(tcp_dscp_v6); +ATF_TC_HEAD(tcp_dscp_v6, tc) { + atf_tc_set_md_var(tc, "descr", "tcp dscp ipv6"); +} +ATF_TC_BODY(tcp_dscp_v6, tc) { +#ifdef ISC_PLATFORM_HAVEIPV6 + isc_result_t result; + isc_sockaddr_t addr1; + struct in6_addr in6; + isc_socket_t *s1 = NULL, *s2 = NULL, *s3 = NULL; + isc_task_t *task = NULL; + char sendbuf[BUFSIZ], recvbuf[BUFSIZ]; + completion_t completion, completion2; + isc_region_t r; + int n; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + n = inet_pton(AF_INET6, "::1", &in6.s6_addr); + ATF_REQUIRE(n == 1); + isc_sockaddr_fromin6(&addr1, &in6, 0); + + result = isc_socket_create(socketmgr, PF_INET6, isc_sockettype_tcp, + &s1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_socket_bind(s1, &addr1, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_socket_getsockname(s1, &addr1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr1) != 0); + + result = isc_socket_listen(s1, 3); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_socket_create(socketmgr, PF_INET6, isc_sockettype_tcp, + &s2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + completion_init(&completion2); + result = isc_socket_accept(s1, task, accept_done, &completion2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + completion_init(&completion); + result = isc_socket_connect(s2, &addr1, task, event_done, &completion); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + waitfor2(&completion, &completion2); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK(completion2.done); + ATF_CHECK_EQ(completion2.result, ISC_R_SUCCESS); + s3 = completion2.socket; + + isc_socket_dscp(s2, 056); /* EF */ + + snprintf(sendbuf, sizeof(sendbuf), "Hello"); + r.base = (void *) sendbuf; + r.length = strlen(sendbuf) + 1; + + recv_dscp = false; + recv_dscp_value = 0; + + completion_init(&completion); + result = isc_socket_sendto(s2, &r, task, event_done, &completion, + NULL, NULL); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + result = isc_socket_recv(s3, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "Hello"); + + if ((isc_net_probedscp() & ISC_NET_DSCPRECVV6) != 0) { + /* + * IPV6_RECVTCLASS is undefined for TCP however + * if we do get it it should be the value we set. + */ + if (recv_dscp) + ATF_CHECK_EQ(recv_dscp_value, 056); + } else + ATF_CHECK(!recv_dscp); + + isc_task_detach(&task); + + isc_socket_detach(&s1); + isc_socket_detach(&s2); + isc_socket_detach(&s3); + + isc_test_end(); +#else + UNUSED(tc); + atf_tc_skip("IPv6 not available"); +#endif +} + +ATF_TC(net_probedscp); +ATF_TC_HEAD(net_probedscp, tc) { + atf_tc_set_md_var(tc, "descr", "probe dscp capabilities"); +} +ATF_TC_BODY(net_probedscp, tc) { + unsigned int n; + + UNUSED(tc); + + n = isc_net_probedscp(); + ATF_CHECK((n & ~ISC_NET_DSCPALL) == 0); + + /* ISC_NET_DSCPSETV4 MUST be set if any is set. */ + if (n & (ISC_NET_DSCPSETV4|ISC_NET_DSCPPKTV4|ISC_NET_DSCPRECVV4)) + ATF_CHECK_MSG((n & ISC_NET_DSCPSETV4) != 0, + "IPv4:%s%s%s\n", + (n & ISC_NET_DSCPSETV4) ? " set" : " none", + (n & ISC_NET_DSCPPKTV4) ? " packet" : "", + (n & ISC_NET_DSCPRECVV4) ? " receive" : ""); + + /* ISC_NET_DSCPSETV6 MUST be set if any is set. */ + if (n & (ISC_NET_DSCPSETV6|ISC_NET_DSCPPKTV6|ISC_NET_DSCPRECVV6)) + ATF_CHECK_MSG((n & ISC_NET_DSCPSETV6) != 0, + "IPv6:%s%s%s\n", + (n & ISC_NET_DSCPSETV6) ? " set" : " none", + (n & ISC_NET_DSCPPKTV6) ? " packet" : "", + (n & ISC_NET_DSCPRECVV6) ? " receive" : ""); + +#if 0 + fprintf(stdout, "IPv4:%s%s%s\n", + (n & ISC_NET_DSCPSETV4) ? " set" : "none", + (n & ISC_NET_DSCPPKTV4) ? " packet" : "", + (n & ISC_NET_DSCPRECVV4) ? " receive" : ""); + + fprintf(stdout, "IPv6:%s%s%s\n", + (n & ISC_NET_DSCPSETV6) ? " set" : "none", + (n & ISC_NET_DSCPPKTV6) ? " packet" : "", + (n & ISC_NET_DSCPRECVV6) ? " receive" : ""); +#endif +} + +/* Test UDP truncation detection */ +ATF_TC(udp_trunc); +ATF_TC_HEAD(udp_trunc, tc) { + atf_tc_set_md_var(tc, "descr", "UDP Truncation detection"); +} +ATF_TC_BODY(udp_trunc, tc) { + isc_result_t result; + isc_sockaddr_t addr1, addr2; + struct in_addr in; + isc_socket_t *s1 = NULL, *s2 = NULL; + isc_task_t *task = NULL; + char sendbuf[BUFSIZ*2], recvbuf[BUFSIZ]; + completion_t completion; + isc_region_t r; + isc_socketevent_t *socketevent; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + in.s_addr = inet_addr("127.0.0.1"); + isc_sockaddr_fromin(&addr1, &in, 0); + isc_sockaddr_fromin(&addr2, &in, 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_udp, &s1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_bind(s1, &addr1, ISC_SOCKET_REUSEADDRESS); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_getsockname(s1, &addr1); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr1) != 0); + + result = isc_socket_create(socketmgr, PF_INET, isc_sockettype_udp, &s2); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_bind(s2, &addr2, ISC_SOCKET_REUSEADDRESS); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + result = isc_socket_getsockname(s2, &addr2); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + ATF_REQUIRE(isc_sockaddr_getport(&addr2) != 0); + + result = isc_task_create(taskmgr, 0, &task); + ATF_CHECK_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + + /* + * Send a message that will not be truncated. + */ + memset(sendbuf, 0xff, sizeof(sendbuf)); + snprintf(sendbuf, sizeof(sendbuf), "Hello"); + r.base = (void *) sendbuf; + r.length = strlen(sendbuf) + 1; + + completion_init(&completion); + + socketevent = isc_socket_socketevent(mctx, s1, ISC_SOCKEVENT_SENDDONE, + event_done, &completion); + ATF_REQUIRE(socketevent != NULL); + + result = isc_socket_sendto2(s1, &r, task, &addr2, NULL, socketevent, 0); + ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + recv_trunc = false; + result = isc_socket_recv(s2, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "Hello"); + ATF_CHECK_EQ(recv_trunc, false); + + /* + * Send a message that will be truncated. + */ + memset(sendbuf, 0xff, sizeof(sendbuf)); + snprintf(sendbuf, sizeof(sendbuf), "Hello"); + r.base = (void *) sendbuf; + r.length = sizeof(sendbuf); + + completion_init(&completion); + + socketevent = isc_socket_socketevent(mctx, s1, ISC_SOCKEVENT_SENDDONE, + event_done, &completion); + ATF_REQUIRE(socketevent != NULL); + + result = isc_socket_sendto2(s1, &r, task, &addr2, NULL, socketevent, 0); + ATF_REQUIRE_EQ_MSG(result, ISC_R_SUCCESS, "%s", + isc_result_totext(result)); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + + r.base = (void *) recvbuf; + r.length = BUFSIZ; + completion_init(&completion); + recv_trunc = false; + result = isc_socket_recv(s2, &r, 1, task, event_done, &completion); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + waitfor(&completion); + ATF_CHECK(completion.done); + ATF_CHECK_EQ(completion.result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(recvbuf, "Hello"); + ATF_CHECK_EQ(recv_trunc, true); + + isc_task_detach(&task); + + isc_socket_detach(&s1); + isc_socket_detach(&s2); + + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, udp_sendto); + ATF_TP_ADD_TC(tp, udp_dup); + ATF_TP_ADD_TC(tp, tcp_dscp_v4); + ATF_TP_ADD_TC(tp, tcp_dscp_v6); + ATF_TP_ADD_TC(tp, udp_dscp_v4); + ATF_TP_ADD_TC(tp, udp_dscp_v6); + ATF_TP_ADD_TC(tp, net_probedscp); + ATF_TP_ADD_TC(tp, udp_trunc); + + return (atf_no_error()); +} diff --git a/lib/isc/tests/symtab_test.c b/lib/isc/tests/symtab_test.c new file mode 100644 index 0000000..a50553b --- /dev/null +++ b/lib/isc/tests/symtab_test.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <unistd.h> + +#include <isc/symtab.h> +#include <isc/print.h> + +#include "isctest.h" + +static void +undefine(char *key, unsigned int type, isc_symvalue_t value, void *arg) { + UNUSED(arg); + + ATF_REQUIRE_EQ(type, 1); + isc_mem_free(mctx, key); + isc_mem_free(mctx, value.as_pointer); +} + +/* + * Individual unit tests + */ + +ATF_TC(symtab_grow); +ATF_TC_HEAD(symtab_grow, tc) { + atf_tc_set_md_var(tc, "descr", "symbol table growth"); +} +ATF_TC_BODY(symtab_grow, tc) { + isc_result_t result; + isc_symtab_t *st = NULL; + isc_symvalue_t value; + isc_symexists_t policy = isc_symexists_reject; + int i; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_symtab_create(mctx, 3, undefine, NULL, false, &st); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE(st != NULL); + + /* Nothing should be in the table yet */ + + /* + * Put 1024 entries in the table (this should necessate + * regrowing the hash table several times + */ + for (i = 0; i < 1024; i++) { + char str[16], *key; + + snprintf(str, sizeof(str), "%04x", i); + key = isc_mem_strdup(mctx, str); + ATF_REQUIRE(key != NULL); + value.as_pointer = isc_mem_strdup(mctx, str); + ATF_REQUIRE(value.as_pointer != NULL); + result = isc_symtab_define(st, key, 1, value, policy); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + if (result != ISC_R_SUCCESS) + undefine(key, 1, value, NULL); + } + + /* + * Try to put them in again; this should fail + */ + for (i = 0; i < 1024; i++) { + char str[16], *key; + + snprintf(str, sizeof(str), "%04x", i); + key = isc_mem_strdup(mctx, str); + ATF_REQUIRE(key != NULL); + value.as_pointer = isc_mem_strdup(mctx, str); + ATF_REQUIRE(value.as_pointer != NULL); + result = isc_symtab_define(st, key, 1, value, policy); + ATF_CHECK_EQ(result, ISC_R_EXISTS); + undefine(key, 1, value, NULL); + } + + /* + * Retrieve them; this should succeed + */ + for (i = 0; i < 1024; i++) { + char str[16]; + + snprintf(str, sizeof(str), "%04x", i); + result = isc_symtab_lookup(st, str, 0, &value); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + ATF_CHECK_STREQ(str, (char *)value.as_pointer); + } + + /* + * Undefine them + */ + for (i = 0; i < 1024; i++) { + char str[16]; + + snprintf(str, sizeof(str), "%04x", i); + result = isc_symtab_undefine(st, str, 1); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + } + + /* + * Retrieve them again; this should fail + */ + for (i = 0; i < 1024; i++) { + char str[16]; + + snprintf(str, sizeof(str), "%04x", i); + result = isc_symtab_lookup(st, str, 0, &value); + ATF_CHECK_EQ(result, ISC_R_NOTFOUND); + } + + isc_symtab_destroy(&st); + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, symtab_grow); + + return (atf_no_error()); +} + diff --git a/lib/isc/tests/task_test.c b/lib/isc/tests/task_test.c new file mode 100644 index 0000000..c991272 --- /dev/null +++ b/lib/isc/tests/task_test.c @@ -0,0 +1,1474 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> + +#include <isc/condition.h> +#include <isc/mem.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/task.h> +#include <isc/time.h> +#include <isc/timer.h> +#include <isc/util.h> + +#include "../task_p.h" +#include "isctest.h" + +/* + * Helper functions + */ + +static isc_mutex_t lock; +int counter = 0; +static int active[10]; +static bool done = false; + +#ifdef ISC_PLATFORM_USETHREADS +static isc_condition_t cv; +#endif + +static void +set(isc_task_t *task, isc_event_t *event) { + int *value = (int *) event->ev_arg; + + UNUSED(task); + + isc_event_free(&event); + LOCK(&lock); + *value = counter++; + UNLOCK(&lock); +} + +static void +set_and_drop(isc_task_t *task, isc_event_t *event) { + int *value = (int *) event->ev_arg; + + UNUSED(task); + + isc_event_free(&event); + LOCK(&lock); + *value = (int) isc_taskmgr_mode(taskmgr); + counter++; + UNLOCK(&lock); + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_normal); +} + +/* + * Individual unit tests + */ + +/* Create a task */ +ATF_TC(create_task); +ATF_TC_HEAD(create_task, tc) { + atf_tc_set_md_var(tc, "descr", "create and destroy a task"); +} +ATF_TC_BODY(create_task, tc) { + isc_result_t result; + isc_task_t *task = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_task_destroy(&task); + ATF_REQUIRE_EQ(task, NULL); + + isc_test_end(); +} + +/* Process events */ +ATF_TC(all_events); +ATF_TC_HEAD(all_events, tc) { + atf_tc_set_md_var(tc, "descr", "process task events"); +} +ATF_TC_BODY(all_events, tc) { + isc_result_t result; + isc_task_t *task = NULL; + isc_event_t *event = NULL; + int a = 0, b = 0; + int i = 0; + + UNUSED(tc); + + counter = 1; + + result = isc_mutex_init(&lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* First event */ + event = isc_event_allocate(mctx, task, ISC_TASKEVENT_TEST, + set, &a, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(a, 0); + isc_task_send(task, &event); + + event = isc_event_allocate(mctx, task, ISC_TASKEVENT_TEST, + set, &b, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(b, 0); + isc_task_send(task, &event); + + while ((a == 0 || b == 0) && i++ < 5000) { +#ifndef ISC_PLATFORM_USETHREADS + while (isc__taskmgr_ready(taskmgr)) + isc__taskmgr_dispatch(taskmgr); +#endif + isc_test_nap(1000); + } + + ATF_CHECK(a != 0); + ATF_CHECK(b != 0); + + isc_task_destroy(&task); + ATF_REQUIRE_EQ(task, NULL); + + isc_test_end(); +} + +/* Privileged events */ +ATF_TC(privileged_events); +ATF_TC_HEAD(privileged_events, tc) { + atf_tc_set_md_var(tc, "descr", "process privileged events"); +} +ATF_TC_BODY(privileged_events, tc) { + isc_result_t result; + isc_task_t *task1 = NULL, *task2 = NULL; + isc_event_t *event = NULL; + int a = 0, b = 0, c = 0, d = 0, e = 0; + int i = 0; + + UNUSED(tc); + + counter = 1; + result = isc_mutex_init(&lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + +#ifdef ISC_PLATFORM_USETHREADS + /* + * Pause the task manager so we can fill up the work queue + * without things happening while we do it. + */ + isc__taskmgr_pause(taskmgr); +#endif + + result = isc_task_create(taskmgr, 0, &task1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_task_setname(task1, "privileged", NULL); + ATF_CHECK(!isc_task_privilege(task1)); + isc_task_setprivilege(task1, true); + ATF_CHECK(isc_task_privilege(task1)); + + result = isc_task_create(taskmgr, 0, &task2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_task_setname(task2, "normal", NULL); + ATF_CHECK(!isc_task_privilege(task2)); + + /* First event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set, &a, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(a, 0); + isc_task_send(task1, &event); + + /* Second event: not privileged */ + event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST, + set, &b, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(b, 0); + isc_task_send(task2, &event); + + /* Third event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set, &c, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(c, 0); + isc_task_send(task1, &event); + + /* Fourth event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set, &d, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(d, 0); + isc_task_send(task1, &event); + + /* Fifth event: not privileged */ + event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST, + set, &e, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(e, 0); + isc_task_send(task2, &event); + + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal); + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged); + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_privileged); + +#ifdef ISC_PLATFORM_USETHREADS + isc__taskmgr_resume(taskmgr); +#endif + + /* We're waiting for *all* variables to be set */ + while ((a == 0 || b == 0 || c == 0 || d == 0 || e == 0) && i++ < 5000) { +#ifndef ISC_PLATFORM_USETHREADS + while (isc__taskmgr_ready(taskmgr)) + isc__taskmgr_dispatch(taskmgr); +#endif + isc_test_nap(1000); + } + + /* + * We can't guarantee what order the events fire, but + * we do know the privileged tasks that set a, c, and d + * would have fired first. + */ + ATF_CHECK(a <= 3); + ATF_CHECK(c <= 3); + ATF_CHECK(d <= 3); + + /* ...and the non-privileged tasks that set b and e, last */ + ATF_CHECK(b >= 4); + ATF_CHECK(e >= 4); + + ATF_CHECK_EQ(counter, 6); + + isc_task_setprivilege(task1, false); + ATF_CHECK(!isc_task_privilege(task1)); + + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal); + + isc_task_destroy(&task1); + ATF_REQUIRE_EQ(task1, NULL); + isc_task_destroy(&task2); + ATF_REQUIRE_EQ(task2, NULL); + + isc_test_end(); +} + +/* + * Edge case: this tests that the task manager behaves as expected when + * we explicitly set it into normal mode *while* running privileged. + */ +ATF_TC(privilege_drop); +ATF_TC_HEAD(privilege_drop, tc) { + atf_tc_set_md_var(tc, "descr", "process privileged events"); +} +ATF_TC_BODY(privilege_drop, tc) { + isc_result_t result; + isc_task_t *task1 = NULL, *task2 = NULL; + isc_event_t *event = NULL; + int a = -1, b = -1, c = -1, d = -1, e = -1; /* non valid states */ + int i = 0; + + UNUSED(tc); + + counter = 1; + result = isc_mutex_init(&lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + +#ifdef ISC_PLATFORM_USETHREADS + /* + * Pause the task manager so we can fill up the work queue + * without things happening while we do it. + */ + isc__taskmgr_pause(taskmgr); +#endif + + result = isc_task_create(taskmgr, 0, &task1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_task_setname(task1, "privileged", NULL); + ATF_CHECK(!isc_task_privilege(task1)); + isc_task_setprivilege(task1, true); + ATF_CHECK(isc_task_privilege(task1)); + + result = isc_task_create(taskmgr, 0, &task2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_task_setname(task2, "normal", NULL); + ATF_CHECK(!isc_task_privilege(task2)); + + /* First event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set_and_drop, &a, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(a, -1); + isc_task_send(task1, &event); + + /* Second event: not privileged */ + event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST, + set_and_drop, &b, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(b, -1); + isc_task_send(task2, &event); + + /* Third event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set_and_drop, &c, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(c, -1); + isc_task_send(task1, &event); + + /* Fourth event: privileged */ + event = isc_event_allocate(mctx, task1, ISC_TASKEVENT_TEST, + set_and_drop, &d, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(d, -1); + isc_task_send(task1, &event); + + /* Fifth event: not privileged */ + event = isc_event_allocate(mctx, task2, ISC_TASKEVENT_TEST, + set_and_drop, &e, sizeof (isc_event_t)); + ATF_REQUIRE(event != NULL); + + ATF_CHECK_EQ(e, -1); + isc_task_send(task2, &event); + + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal); + isc_taskmgr_setmode(taskmgr, isc_taskmgrmode_privileged); + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_privileged); + +#ifdef ISC_PLATFORM_USETHREADS + isc__taskmgr_resume(taskmgr); +#endif + + /* We're waiting for all variables to be set. */ + while ((a == -1 || b == -1 || c == -1 || d == -1 || e == -1) && + i++ < 5000) { +#ifndef ISC_PLATFORM_USETHREADS + while (isc__taskmgr_ready(taskmgr)) + isc__taskmgr_dispatch(taskmgr); +#endif + isc_test_nap(1000); + } + + /* + * We can't guarantee what order the events fire, but + * we do know *exactly one* of the privileged tasks will + * have run in privileged mode... + */ + ATF_CHECK(a == isc_taskmgrmode_privileged || + c == isc_taskmgrmode_privileged || + d == isc_taskmgrmode_privileged); + ATF_CHECK(a + c + d == isc_taskmgrmode_privileged); + + /* ...and neither of the non-privileged tasks did... */ + ATF_CHECK(b == isc_taskmgrmode_normal || e == isc_taskmgrmode_normal); + + /* ...but all five of them did run. */ + ATF_CHECK_EQ(counter, 6); + + ATF_CHECK_EQ(isc_taskmgr_mode(taskmgr), isc_taskmgrmode_normal); + + isc_task_destroy(&task1); + ATF_REQUIRE_EQ(task1, NULL); + isc_task_destroy(&task2); + ATF_REQUIRE_EQ(task2, NULL); + + isc_test_end(); +} + +/* + * Basic task functions: + */ +static void +basic_cb(isc_task_t *task, isc_event_t *event) { + int i; + int j; + + UNUSED(task); + + j = 0; + for (i = 0; i < 1000000; i++) { + j += 100; + } + + printf("task %s\n", (char *)event->ev_arg); + isc_event_free(&event); +} + +static void +basic_shutdown(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + printf("shutdown %s\n", (char *)event->ev_arg); + isc_event_free(&event); +} + +static void +basic_tick(isc_task_t *task, isc_event_t *event) { + + UNUSED(task); + + printf("%s\n", (char *)event->ev_arg); + isc_event_free(&event); +} + +static char one[] = "1"; +static char two[] = "2"; +static char three[] = "3"; +static char four[] = "4"; +static char tick[] = "tick"; +static char tock[] = "tock"; + + +ATF_TC(basic); +ATF_TC_HEAD(basic, tc) { + atf_tc_set_md_var(tc, "descr", "basic task system check"); +} +ATF_TC_BODY(basic, tc) { + isc_result_t result; + isc_task_t *task1 = NULL; + isc_task_t *task2 = NULL; + isc_task_t *task3 = NULL; + isc_task_t *task4 = NULL; + isc_event_t *event = NULL; + isc_timer_t *ti1 = NULL; + isc_timer_t *ti2 = NULL; + isc_time_t absolute; + isc_interval_t interval; + char *testarray[] = { + one, one, one, one, one, one, one, one, one, + two, three, four, two, three, four, NULL + }; + int i; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_task_create(taskmgr, 0, &task2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_task_create(taskmgr, 0, &task3); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_task_create(taskmgr, 0, &task4); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_onshutdown(task1, basic_shutdown, one); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_task_onshutdown(task2, basic_shutdown, two); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_task_onshutdown(task3, basic_shutdown, three); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + result = isc_task_onshutdown(task4, basic_shutdown, four); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_time_settoepoch(&absolute); + isc_interval_set(&interval, 1, 0); + result = isc_timer_create(timermgr, isc_timertype_ticker, + &absolute, &interval, + task1, basic_tick, tick, &ti1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + ti2 = NULL; + isc_time_settoepoch(&absolute); + isc_interval_set(&interval, 1, 0); + result = isc_timer_create(timermgr, isc_timertype_ticker, + &absolute, &interval, + task2, basic_tick, tock, &ti2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + +#ifndef WIN32 + sleep(2); +#else + Sleep(2000); +#endif + + for (i = 0; testarray[i] != NULL; i++) { + /* + * Note: (void *)1 is used as a sender here, since some + * compilers don't like casting a function pointer to a + * (void *). + * + * In a real use, it is more likely the sender would be a + * structure (socket, timer, task, etc) but this is just a + * test program. + */ + event = isc_event_allocate(mctx, (void *)1, 1, basic_cb, + testarray[i], sizeof(*event)); + ATF_REQUIRE(event != NULL); + isc_task_send(task1, &event); + } + + (void)isc_task_purge(task3, NULL, 0, 0); + + isc_task_detach(&task1); + isc_task_detach(&task2); + isc_task_detach(&task3); + isc_task_detach(&task4); + +#ifndef WIN32 + sleep(10); +#else + Sleep(10000); +#endif + isc_timer_detach(&ti1); + isc_timer_detach(&ti2); + + isc_test_end(); +} + +/* + * Exclusive mode test: + * When one task enters exclusive mode, all other active + * tasks complete first. + */ +static +int spin(int n) { + int i; + int r = 0; + for (i = 0; i < n; i++) { + r += i; + if (r > 1000000) + r = 0; + } + return (r); +} + +static void +exclusive_cb(isc_task_t *task, isc_event_t *event) { + int taskno = *(int *)(event->ev_arg); + + printf("task enter %d\n", taskno); + + /* task chosen from the middle of the range */ + if (taskno == 6) { + isc_result_t result; + int i; + + result = isc_task_beginexclusive(task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + for (i = 0; i < 10; i++) { + ATF_CHECK(active[i] == 0); + } + + isc_task_endexclusive(task); + done = true; + } else { + active[taskno]++; + (void) spin(10000000); + active[taskno]--; + } + + printf("task exit %d\n", taskno); + + if (done) { + isc_mem_put(event->ev_destroy_arg, event->ev_arg, sizeof (int)); + isc_event_free(&event); + } else { + isc_task_send(task, &event); + } +} + +ATF_TC(task_exclusive); +ATF_TC_HEAD(task_exclusive, tc) { + atf_tc_set_md_var(tc, "descr", "test exclusive mode"); +} +ATF_TC_BODY(task_exclusive, tc) { + isc_task_t *tasks[10]; + isc_result_t result; + int i; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 4); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + for (i = 0; i < 10; i++) { + isc_event_t *event = NULL; + int *v; + + tasks[i] = NULL; + + result = isc_task_create(taskmgr, 0, &tasks[i]); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + v = isc_mem_get(mctx, sizeof *v); + ATF_REQUIRE(v != NULL); + + *v = i; + + event = isc_event_allocate(mctx, NULL, 1, exclusive_cb, + v, sizeof(*event)); + ATF_REQUIRE(event != NULL); + + isc_task_send(tasks[i], &event); + } + + for (i = 0; i < 10; i++) { + isc_task_detach(&tasks[i]); + } + isc_test_end(); +} + +/* + * The remainder of these tests require threads + */ +#ifdef ISC_PLATFORM_USETHREADS +/* + * Max tasks test: + * The task system can create and execute many tasks. Tests with 10000. + */ +static void +maxtask_shutdown(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + if (event->ev_arg != NULL) { + isc_task_destroy((isc_task_t**) &event->ev_arg); + } else { + LOCK(&lock); + done = true; + SIGNAL(&cv); + UNLOCK(&lock); + + isc_event_free(&event); + isc_taskmgr_destroy(&taskmgr); + isc_mem_destroy(&mctx); + + isc_condition_destroy(&cv); + DESTROYLOCK(&lock); + } +} + +static void +maxtask_cb(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + + if (event->ev_arg != NULL) { + isc_task_t *newtask = NULL; + + event->ev_arg = (void *)(((uintptr_t) event->ev_arg) - 1); + + /* + * Create a new task and forward the message. + */ + result = isc_task_create(taskmgr, 0, &newtask); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_onshutdown(newtask, maxtask_shutdown, + (void *)task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_task_send(newtask, &event); + } else if (task != NULL) { + isc_task_destroy(&task); + } +} + +ATF_TC(manytasks); +ATF_TC_HEAD(manytasks, tc) { + atf_tc_set_md_var(tc, "descr", "many tasks"); +} +ATF_TC_BODY(manytasks, tc) { + isc_result_t result; + isc_event_t *event = NULL; + uintptr_t ntasks = 10000; + + UNUSED(tc); + + printf("Testing with %lu tasks\n", (unsigned long)ntasks); + + result = isc_mutex_init(&lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_condition_init(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_mem_create(0, 0, &mctx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_taskmgr_create(mctx, 4, 0, &taskmgr); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + event = isc_event_allocate(mctx, (void *)1, 1, maxtask_cb, + (void *)ntasks, sizeof(*event)); + ATF_REQUIRE(event != NULL); + + LOCK(&lock); + maxtask_cb(NULL, event); + while (!done) { + WAIT(&cv, &lock); + } +} + + +/* + * Shutdown test: + * When isc_task_shutdown() is called, shutdown events are posted + * in LIFO order. + */ + +static int senders[4]; +static int nevents = 0; +static int nsdevents = 0; + +static void +sd_sde1(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + ATF_CHECK_EQ(nevents, 256); + ATF_REQUIRE_EQ(nsdevents, 1); + ++nsdevents; + printf("shutdown 1\n"); + + isc_event_free(&event); +} + +static void +sd_sde2(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + ATF_CHECK_EQ(nevents, 256); + ATF_REQUIRE_EQ(nsdevents, 0); + ++nsdevents; + printf("shutdown 2\n"); + + isc_event_free(&event); +} + +static void +sd_event1(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + LOCK(&lock); + while (!done) { + WAIT(&cv, &lock); + } + + printf("event 1\n"); + + isc_event_free(&event); +} + +static void +sd_event2(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + ++nevents; + + printf("event 2\n"); + + isc_event_free(&event); +} + +ATF_TC(shutdown); +ATF_TC_HEAD(shutdown, tc) { + atf_tc_set_md_var(tc, "descr", "task shutdown"); +} +ATF_TC_BODY(shutdown, tc) { + isc_result_t result; + isc_eventtype_t event_type; + isc_event_t *event = NULL; + isc_task_t *task = NULL; + int i; + + nevents = nsdevents = 0; + done = false; + + event_type = 3; + + result = isc_test_begin(NULL, true, 4); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_mutex_init(&lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_condition_init(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + LOCK(&lock); + + task = NULL; + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* + * This event causes the task to wait on cv. + */ + event = isc_event_allocate(mctx, &senders[1], event_type, sd_event1, + NULL, sizeof(*event)); + ATF_REQUIRE(event != NULL); + isc_task_send(task, &event); + + /* + * Now we fill up the task's event queue with some events. + */ + for (i = 0; i < 256; ++i) { + event = isc_event_allocate(mctx, &senders[1], event_type, + sd_event2, NULL, sizeof(*event)); + ATF_REQUIRE(event != NULL); + isc_task_send(task, &event); + } + + /* + * Now we register two shutdown events. + */ + result = isc_task_onshutdown(task, sd_sde1, NULL); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_onshutdown(task, sd_sde2, NULL); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_task_shutdown(task); + + /* + * Now we free the task by signaling cv. + */ + done = true; + SIGNAL(&cv); + UNLOCK(&lock); + + isc_task_detach(&task); + + isc_test_end(); + + ATF_REQUIRE_EQ(nsdevents, 2); +} + +/* + * Post-shutdown test: + * After isc_task_shutdown() has been called, any call to + * isc_task_onshutdown() will return ISC_R_SHUTTINGDOWN. + */ +static void +psd_event1(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + LOCK(&lock); + + while (!done) { + WAIT(&cv, &lock); + } + + UNLOCK(&lock); + + isc_event_free(&event); +} + +static void +psd_sde(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + isc_event_free(&event); +} + +ATF_TC(post_shutdown); +ATF_TC_HEAD(post_shutdown, tc) { + atf_tc_set_md_var(tc, "descr", "post-shutdown"); +} +ATF_TC_BODY(post_shutdown, tc) { + isc_result_t result; + isc_eventtype_t event_type; + isc_event_t *event; + isc_task_t *task; + + done = false; + event_type = 4; + + result = isc_mutex_init(&lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_condition_init(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_test_begin(NULL, true, 2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + LOCK(&lock); + + task = NULL; + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* + * This event causes the task to wait on cv. + */ + event = isc_event_allocate(mctx, &senders[1], event_type, psd_event1, + NULL, sizeof(*event)); + ATF_REQUIRE(event != NULL); + isc_task_send(task, &event); + + isc_task_shutdown(task); + + result = isc_task_onshutdown(task, psd_sde, NULL); + ATF_CHECK_EQ(result, ISC_R_SHUTTINGDOWN); + + /* + * Release the task. + */ + done = true; + + SIGNAL(&cv); + UNLOCK(&lock); + + isc_task_detach(&task); + isc_test_end(); + + (void) isc_condition_destroy(&cv); + DESTROYLOCK(&lock); +} + +/* + * Helper for the purge tests below: + */ + +#define SENDERCNT 3 +#define TYPECNT 4 +#define TAGCNT 5 +#define NEVENTS (SENDERCNT * TYPECNT * TAGCNT) + +static bool testrange; +static void *purge_sender; +static isc_eventtype_t purge_type_first; +static isc_eventtype_t purge_type_last; +static void *purge_tag; +static int eventcnt; + +bool started = false; + +static void +pg_event1(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + LOCK(&lock); + while (!started) { + WAIT(&cv, &lock); + } + UNLOCK(&lock); + + isc_event_free(&event); +} + +static void +pg_event2(isc_task_t *task, isc_event_t *event) { + bool sender_match = false; + bool type_match = false; + bool tag_match = false; + + UNUSED(task); + + if ((purge_sender == NULL) || (purge_sender == event->ev_sender)) { + sender_match = true; + } + + if (testrange) { + if ((purge_type_first <= event->ev_type) && + (event->ev_type <= purge_type_last)) + { + type_match = true; + } + } else { + if (purge_type_first == event->ev_type) { + type_match = true; + } + } + + if ((purge_tag == NULL) || (purge_tag == event->ev_tag)) { + tag_match = true; + } + + if (sender_match && type_match && tag_match) { + if (event->ev_attributes & ISC_EVENTATTR_NOPURGE) { + printf("event %p,%d,%p matched but was not purgeable\n", + event->ev_sender, (int)event->ev_type, + event->ev_tag); + ++eventcnt; + } else { + printf("*** event %p,%d,%p not purged\n", + event->ev_sender, (int)event->ev_type, + event->ev_tag); + } + } else { + ++eventcnt; + } + + isc_event_free(&event); +} + +static void +pg_sde(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + LOCK(&lock); + done = true; + SIGNAL(&cv); + UNLOCK(&lock); + + isc_event_free(&event); +} + +static void +test_purge(int sender, int type, int tag, int exp_purged) { + isc_result_t result; + isc_task_t *task = NULL; + isc_event_t *eventtab[NEVENTS]; + isc_event_t *event = NULL; + isc_interval_t interval; + isc_time_t now; + int sender_cnt, type_cnt, tag_cnt, event_cnt, i; + int purged = 0; + + started = false; + done = false; + eventcnt = 0; + + result = isc_test_begin(NULL, true, 2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_mutex_init(&lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_condition_init(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_onshutdown(task, pg_sde, NULL); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* + * Block the task on cv. + */ + event = isc_event_allocate(mctx, (void *)1, 9999, + pg_event1, NULL, sizeof(*event)); + + ATF_REQUIRE(event != NULL); + isc_task_send(task, &event); + + /* + * Fill the task's queue with some messages with varying + * sender, type, tag, and purgeable attribute values. + */ + event_cnt = 0; + for (sender_cnt = 0; sender_cnt < SENDERCNT; ++sender_cnt) { + for (type_cnt = 0; type_cnt < TYPECNT; ++type_cnt) { + for (tag_cnt = 0; tag_cnt < TAGCNT; ++tag_cnt) { + eventtab[event_cnt] = + isc_event_allocate(mctx, + &senders[sender + sender_cnt], + (isc_eventtype_t)(type + type_cnt), + pg_event2, NULL, sizeof(*event)); + + ATF_REQUIRE(eventtab[event_cnt] != NULL); + + eventtab[event_cnt]->ev_tag = + (void *)((uintptr_t)tag + tag_cnt); + + /* + * Mark events as non-purgeable if + * sender, type and tag are all + * odd-numbered. (There should be 4 + * of these out of 60 events total.) + */ + if (((sender_cnt % 2) != 0) && + ((type_cnt % 2) != 0) && + ((tag_cnt % 2) != 0)) + { + eventtab[event_cnt]->ev_attributes |= + ISC_EVENTATTR_NOPURGE; + } + ++event_cnt; + } + } + } + + for (i = 0; i < event_cnt; ++i) { + isc_task_send(task, &eventtab[i]); + } + + if (testrange) { + /* + * We're testing isc_task_purgerange. + */ + purged = isc_task_purgerange(task, purge_sender, + (isc_eventtype_t)purge_type_first, + (isc_eventtype_t)purge_type_last, + purge_tag); + ATF_CHECK_EQ(purged, exp_purged); + } else { + /* + * We're testing isc_task_purge. + */ + printf("purge events %p,%u,%p\n", + purge_sender, purge_type_first, purge_tag); + purged = isc_task_purge(task, purge_sender, + (isc_eventtype_t)purge_type_first, + purge_tag); + printf("purged %d expected %d\n", purged, exp_purged); + ATF_CHECK_EQ(purged, exp_purged); + } + + /* + * Unblock the task, allowing event processing. + */ + LOCK(&lock); + started = true; + SIGNAL(&cv); + + isc_task_shutdown(task); + + isc_interval_set(&interval, 5, 0); + + /* + * Wait for shutdown processing to complete. + */ + while (!done) { + result = isc_time_nowplusinterval(&now, &interval); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + WAITUNTIL(&cv, &lock, &now); + } + + UNLOCK(&lock); + + isc_task_detach(&task); + + isc_test_end(); + DESTROYLOCK(&lock); + (void) isc_condition_destroy(&cv); + + ATF_CHECK_EQ(eventcnt, event_cnt - exp_purged); +} + +/* + * Purge test: + * A call to isc_task_purge(task, sender, type, tag) purges all events of + * type 'type' and with tag 'tag' not marked as unpurgeable from sender + * from the task's " queue and returns the number of events purged. + */ +ATF_TC(purge); +ATF_TC_HEAD(purge, tc) { + atf_tc_set_md_var(tc, "descr", "purge"); +} +ATF_TC_BODY(purge, tc) { + /* Try purging on a specific sender. */ + printf("testing purge on 2,4,8 expecting 1\n"); + purge_sender = &senders[2]; + purge_type_first = 4; + purge_type_last = 4; + purge_tag = (void *)8; + testrange = false; + test_purge(1, 4, 7, 1); + + /* Try purging on all senders. */ + printf("testing purge on 0,4,8 expecting 3\n"); + purge_sender = NULL; + purge_type_first = 4; + purge_type_last = 4; + purge_tag = (void *)8; + testrange = false; + test_purge(1, 4, 7, 3); + + /* Try purging on all senders, specified type, all tags. */ + printf("testing purge on 0,4,0 expecting 15\n"); + purge_sender = NULL; + purge_type_first = 4; + purge_type_last = 4; + purge_tag = NULL; + testrange = false; + test_purge(1, 4, 7, 15); + + /* Try purging on a specified tag, no such type. */ + printf("testing purge on 0,99,8 expecting 0\n"); + purge_sender = NULL; + purge_type_first = 99; + purge_type_last = 99; + purge_tag = (void *)8; + testrange = false; + test_purge(1, 4, 7, 0); + + /* + * Try purging on specified sender, type, all tags. + */ + printf("testing purge on 3,5,0 expecting 5\n"); + purge_sender = &senders[3]; + purge_type_first = 5; + purge_type_last = 5; + purge_tag = NULL; + testrange = false; + test_purge(1, 4, 7, 5); +} + +/* + * Purge range test: + * A call to isc_event_purgerange(task, sender, first, last, tag) purges + * all events not marked unpurgeable from sender 'sender' and of type within + * the range 'first' to 'last' inclusive from the task's event queue and + * returns the number of tasks purged. + */ + +ATF_TC(purgerange); +ATF_TC_HEAD(purgerange, tc) { + atf_tc_set_md_var(tc, "descr", "purge-range"); +} +ATF_TC_BODY(purgerange, tc) { + /* Now let's try some ranges. */ + printf("testing purgerange on 2,4-5,8 expecting 1\n"); + purge_sender = &senders[2]; + purge_type_first = 4; + purge_type_last = 5; + purge_tag = (void *)8; + testrange = true; + test_purge(1, 4, 7, 1); + + /* Try purging on all senders. */ + printf("testing purge on 0,4-5,8 expecting 5\n"); + purge_sender = NULL; + purge_type_first = 4; + purge_type_last = 5; + purge_tag = (void *)8; + testrange = true; + test_purge(1, 4, 7, 5); + + /* Try purging on all senders, specified type, all tags. */ + printf("testing purge on 0,5-6,0 expecting 28\n"); + purge_sender = NULL; + purge_type_first = 5; + purge_type_last = 6; + purge_tag = NULL; + testrange = true; + test_purge(1, 4, 7, 28); + + /* Try purging on a specified tag, no such type. */ + printf("testing purge on 0,99-101,8 expecting 0\n"); + purge_sender = NULL; + purge_type_first = 99; + purge_type_last = 101; + purge_tag = (void *)8; + testrange = true; + test_purge(1, 4, 7, 0); + + /* Try purging on specified sender, type, all tags. */ + printf("testing purge on 3,5-6,0 expecting 10\n"); + purge_sender = &senders[3]; + purge_type_first = 5; + purge_type_last = 6; + purge_tag = NULL; + testrange = true; + test_purge(1, 4, 7, 10); +} + +/* + * Helpers for purge event tests + */ +static void +pge_event1(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + LOCK(&lock); + while (!started) { + WAIT(&cv, &lock); + } + UNLOCK(&lock); + + isc_event_free(&event); +} + +static void +pge_event2(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + ++eventcnt; + isc_event_free(&event); +} + + +static void +pge_sde(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + LOCK(&lock); + done = true; + SIGNAL(&cv); + UNLOCK(&lock); + + isc_event_free(&event); +} + +static void +try_purgeevent(bool purgeable) { + isc_result_t result; + isc_task_t *task = NULL; + bool purged; + isc_event_t *event1 = NULL; + isc_event_t *event2 = NULL; + isc_event_t *event2_clone = NULL;; + isc_time_t now; + isc_interval_t interval; + + started = false; + done = false; + eventcnt = 0; + + result = isc_mutex_init(&lock); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_condition_init(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_test_begin(NULL, true, 2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_onshutdown(task, pge_sde, NULL); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* + * Block the task on cv. + */ + event1 = isc_event_allocate(mctx, (void *)1, (isc_eventtype_t)1, + pge_event1, NULL, sizeof(*event1)); + ATF_REQUIRE(event1 != NULL); + isc_task_send(task, &event1); + + event2 = isc_event_allocate(mctx, (void *)1, (isc_eventtype_t)1, + pge_event2, NULL, sizeof(*event2)); + ATF_REQUIRE(event2 != NULL); + + event2_clone = event2; + + if (purgeable) { + event2->ev_attributes &= ~ISC_EVENTATTR_NOPURGE; + } else { + event2->ev_attributes |= ISC_EVENTATTR_NOPURGE; + } + + isc_task_send(task, &event2); + + purged = isc_task_purgeevent(task, event2_clone); + ATF_CHECK_EQ(purgeable, purged); + + /* + * Unblock the task, allowing event processing. + */ + LOCK(&lock); + started = true; + SIGNAL(&cv); + + isc_task_shutdown(task); + + isc_interval_set(&interval, 5, 0); + + /* + * Wait for shutdown processing to complete. + */ + while (!done) { + result = isc_time_nowplusinterval(&now, &interval); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + WAITUNTIL(&cv, &lock, &now); + } + + UNLOCK(&lock); + + isc_task_detach(&task); + + isc_test_end(); + DESTROYLOCK(&lock); + (void) isc_condition_destroy(&cv); + + ATF_REQUIRE_EQ(eventcnt, (purgeable ? 0 : 1)); +} + +/* + * Purge event test: + * When the event is marked as purgeable, a call to + * isc_task_purgeevent(task, event) purges the event 'event' from the + * task's queue and returns true. + */ + +ATF_TC(purgeevent); +ATF_TC_HEAD(purgeevent, tc) { + atf_tc_set_md_var(tc, "descr", "purge-event"); +} +ATF_TC_BODY(purgeevent, tc) { + try_purgeevent(true); +} + +/* + * Purge event not purgeable test: + * When the event is not marked as purgable, a call to + * isc_task_purgeevent(task, event) does not purge the event + * 'event' from the task's queue and returns false. + */ + +ATF_TC(purgeevent_notpurge); +ATF_TC_HEAD(purgeevent_notpurge, tc) { + atf_tc_set_md_var(tc, "descr", "purge-event"); +} +ATF_TC_BODY(purgeevent_notpurge, tc) { + try_purgeevent(false); +} +#endif + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, create_task); + ATF_TP_ADD_TC(tp, all_events); + ATF_TP_ADD_TC(tp, privileged_events); + ATF_TP_ADD_TC(tp, privilege_drop); + ATF_TP_ADD_TC(tp, basic); + ATF_TP_ADD_TC(tp, task_exclusive); + +#ifdef ISC_PLATFORM_USETHREADS + ATF_TP_ADD_TC(tp, manytasks); + ATF_TP_ADD_TC(tp, shutdown); + ATF_TP_ADD_TC(tp, post_shutdown); + ATF_TP_ADD_TC(tp, purge); + ATF_TP_ADD_TC(tp, purgerange); + ATF_TP_ADD_TC(tp, purgeevent); + ATF_TP_ADD_TC(tp, purgeevent_notpurge); +#endif + + return (atf_no_error()); +} diff --git a/lib/isc/tests/taskpool_test.c b/lib/isc/tests/taskpool_test.c new file mode 100644 index 0000000..ff2de9e --- /dev/null +++ b/lib/isc/tests/taskpool_test.c @@ -0,0 +1,204 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <unistd.h> + +#include <isc/task.h> +#include <isc/taskpool.h> + +#include "isctest.h" + +/* + * Individual unit tests + */ + +/* Create a taskpool */ +ATF_TC(create_pool); +ATF_TC_HEAD(create_pool, tc) { + atf_tc_set_md_var(tc, "descr", "create a taskpool"); +} +ATF_TC_BODY(create_pool, tc) { + isc_result_t result; + isc_taskpool_t *pool = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_taskpool_create(taskmgr, mctx, 8, 2, &pool); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_taskpool_size(pool), 8); + + isc_taskpool_destroy(&pool); + ATF_REQUIRE_EQ(pool, NULL); + + isc_test_end(); +} + +/* Resize a taskpool */ +ATF_TC(expand_pool); +ATF_TC_HEAD(expand_pool, tc) { + atf_tc_set_md_var(tc, "descr", "expand a taskpool"); +} +ATF_TC_BODY(expand_pool, tc) { + isc_result_t result; + isc_taskpool_t *pool1 = NULL, *pool2 = NULL, *hold = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_taskpool_create(taskmgr, mctx, 10, 2, &pool1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_taskpool_size(pool1), 10); + + /* resizing to a smaller size should have no effect */ + hold = pool1; + result = isc_taskpool_expand(&pool1, 5, &pool2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_taskpool_size(pool2), 10); + ATF_REQUIRE_EQ(pool2, hold); + ATF_REQUIRE_EQ(pool1, NULL); + pool1 = pool2; + pool2 = NULL; + + /* resizing to the same size should have no effect */ + hold = pool1; + result = isc_taskpool_expand(&pool1, 10, &pool2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_taskpool_size(pool2), 10); + ATF_REQUIRE_EQ(pool2, hold); + ATF_REQUIRE_EQ(pool1, NULL); + pool1 = pool2; + pool2 = NULL; + + /* resizing to larger size should make a new pool */ + hold = pool1; + result = isc_taskpool_expand(&pool1, 20, &pool2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_taskpool_size(pool2), 20); + ATF_REQUIRE(pool2 != hold); + ATF_REQUIRE_EQ(pool1, NULL); + + isc_taskpool_destroy(&pool2); + ATF_REQUIRE_EQ(pool2, NULL); + + isc_test_end(); +} + +/* Get tasks */ +ATF_TC(get_tasks); +ATF_TC_HEAD(get_tasks, tc) { + atf_tc_set_md_var(tc, "descr", "create a taskpool"); +} +ATF_TC_BODY(get_tasks, tc) { + isc_result_t result; + isc_taskpool_t *pool = NULL; + isc_task_t *task1 = NULL, *task2 = NULL, *task3 = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_taskpool_create(taskmgr, mctx, 2, 2, &pool); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_taskpool_size(pool), 2); + + /* two tasks in pool; make sure we can access them more than twice */ + isc_taskpool_gettask(pool, &task1); + ATF_REQUIRE(ISCAPI_TASK_VALID(task1)); + + isc_taskpool_gettask(pool, &task2); + ATF_REQUIRE(ISCAPI_TASK_VALID(task2)); + + isc_taskpool_gettask(pool, &task3); + ATF_REQUIRE(ISCAPI_TASK_VALID(task3)); + + isc_task_destroy(&task1); + isc_task_destroy(&task2); + isc_task_destroy(&task3); + + isc_taskpool_destroy(&pool); + ATF_REQUIRE_EQ(pool, NULL); + + isc_test_end(); +} + +/* Get tasks */ +ATF_TC(set_privilege); +ATF_TC_HEAD(set_privilege, tc) { + atf_tc_set_md_var(tc, "descr", "create a taskpool"); +} +ATF_TC_BODY(set_privilege, tc) { + isc_result_t result; + isc_taskpool_t *pool = NULL; + isc_task_t *task1 = NULL, *task2 = NULL, *task3 = NULL; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_taskpool_create(taskmgr, mctx, 2, 2, &pool); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_taskpool_size(pool), 2); + + isc_taskpool_setprivilege(pool, true); + + isc_taskpool_gettask(pool, &task1); + isc_taskpool_gettask(pool, &task2); + isc_taskpool_gettask(pool, &task3); + + ATF_CHECK(ISCAPI_TASK_VALID(task1)); + ATF_CHECK(ISCAPI_TASK_VALID(task2)); + ATF_CHECK(ISCAPI_TASK_VALID(task3)); + + ATF_CHECK(isc_task_privilege(task1)); + ATF_CHECK(isc_task_privilege(task2)); + ATF_CHECK(isc_task_privilege(task3)); + + isc_taskpool_setprivilege(pool, false); + + ATF_CHECK(!isc_task_privilege(task1)); + ATF_CHECK(!isc_task_privilege(task2)); + ATF_CHECK(!isc_task_privilege(task3)); + + isc_task_destroy(&task1); + isc_task_destroy(&task2); + isc_task_destroy(&task3); + + isc_taskpool_destroy(&pool); + ATF_REQUIRE_EQ(pool, NULL); + + isc_test_end(); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, create_pool); + ATF_TP_ADD_TC(tp, expand_pool); + ATF_TP_ADD_TC(tp, get_tasks); + ATF_TP_ADD_TC(tp, set_privilege); + + return (atf_no_error()); +} + diff --git a/lib/isc/tests/testdata/file/keep b/lib/isc/tests/testdata/file/keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/isc/tests/testdata/file/keep diff --git a/lib/isc/tests/time_test.c b/lib/isc/tests/time_test.c new file mode 100644 index 0000000..c548869 --- /dev/null +++ b/lib/isc/tests/time_test.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> +#include <stdlib.h> + +#include <atf-c.h> + +#include <isc/time.h> +#include <isc/result.h> + +ATF_TC(isc_time_parsehttptimestamp); +ATF_TC_HEAD(isc_time_parsehttptimestamp, tc) { + atf_tc_set_md_var(tc, "descr", "parse http time stamp"); +} +ATF_TC_BODY(isc_time_parsehttptimestamp, tc) { + isc_result_t result; + isc_time_t t, x; + char buf[ISC_FORMATHTTPTIMESTAMP_SIZE]; + + setenv("TZ", "PST8PDT", 1); + result = isc_time_now(&t); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_time_formathttptimestamp(&t, buf, sizeof(buf)); + result = isc_time_parsehttptimestamp(buf, &x); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + ATF_REQUIRE_EQ(isc_time_seconds(&t), isc_time_seconds(&x)); +} + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_time_parsehttptimestamp); + return (atf_no_error()); +} + diff --git a/lib/isc/tests/timer_test.c b/lib/isc/tests/timer_test.c new file mode 100644 index 0000000..7a32f1d --- /dev/null +++ b/lib/isc/tests/timer_test.c @@ -0,0 +1,593 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <atf-c.h> + +#include <unistd.h> + +#include <isc/condition.h> +#include <isc/mem.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/task.h> +#include <isc/time.h> +#include <isc/timer.h> +#include <isc/util.h> +#include <isc/util.h> + +#include "isctest.h" + +/* + * This entire test requires threads. + */ +#ifdef ISC_PLATFORM_USETHREADS + +/* + * Helper functions + */ +#define FUDGE_SECONDS 0 /* in absence of clock_getres() */ +#define FUDGE_NANOSECONDS 500000000 /* in absence of clock_getres() */ + +static isc_timer_t *timer = NULL; +static isc_condition_t cv; +static isc_mutex_t mx; +static isc_time_t endtime; +static isc_time_t lasttime; +static int seconds; +static int nanoseconds; +static int eventcnt; +static int nevents; + +static void +shutdown(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + + UNUSED(task); + + /* + * Signal shutdown processing complete. + */ + result = isc_mutex_lock(&mx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_condition_signal(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_mutex_unlock(&mx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_event_free(&event); +} + +static void +setup_test(isc_timertype_t timertype, isc_time_t *expires, + isc_interval_t *interval, + void (*action)(isc_task_t *, isc_event_t *)) +{ + isc_result_t result; + isc_task_t *task = NULL; + isc_time_settoepoch(&endtime); + eventcnt = 0; + + result = isc_mutex_init(&mx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_condition_init(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + LOCK(&mx); + + result = isc_task_create(taskmgr, 0, &task); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_onshutdown(task, shutdown, NULL); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_time_now(&lasttime); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_timer_create(timermgr, timertype, expires, interval, + task, action, (void *)timertype, + &timer); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* + * Wait for shutdown processing to complete. + */ + while (eventcnt != nevents) { + result = isc_condition_wait(&cv, &mx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + } + + UNLOCK(&mx); + + isc_task_detach(&task); + DESTROYLOCK(&mx); + (void) isc_condition_destroy(&cv); +} + +static void +ticktock(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + isc_time_t now; + isc_time_t base; + isc_time_t ulim; + isc_time_t llim; + isc_interval_t interval; + isc_eventtype_t expected_event_type; + + ++eventcnt; + + printf("tick %d\n", eventcnt); + + expected_event_type = ISC_TIMEREVENT_LIFE; + if ((isc_timertype_t) event->ev_arg == isc_timertype_ticker) { + expected_event_type = ISC_TIMEREVENT_TICK; + } + + if (event->ev_type != expected_event_type) { + printf("expected event type %u, got %u\n", + expected_event_type, event->ev_type); + } + + result = isc_time_now(&now); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, seconds, nanoseconds); + result = isc_time_add(&lasttime, &interval, &base); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, FUDGE_SECONDS, FUDGE_NANOSECONDS); + result = isc_time_add(&base, &interval, &ulim); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_time_subtract(&base, &interval, &llim); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(isc_time_compare(&llim, &now) <= 0); + ATF_CHECK(isc_time_compare(&ulim, &now) >= 0); + lasttime = now; + + if (eventcnt == nevents) { + result = isc_time_now(&endtime); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + isc_timer_detach(&timer); + isc_task_shutdown(task); + } + + isc_event_free(&event); +} + +/* + * Individual unit tests + */ + +ATF_TC(ticker); +ATF_TC_HEAD(ticker, tc) { + atf_tc_set_md_var(tc, "descr", "timer type ticker"); +} +ATF_TC_BODY(ticker, tc) { + isc_result_t result; + isc_time_t expires; + isc_interval_t interval; + + UNUSED(tc); + + nevents = 12; + seconds = 0; + nanoseconds = 500000000; + + result = isc_test_begin(NULL, true, 2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, seconds, nanoseconds); + isc_time_settoepoch(&expires); + + setup_test(isc_timertype_ticker, &expires, &interval, ticktock); + + isc_test_end(); +} + +ATF_TC(once_life); +ATF_TC_HEAD(once_life, tc) { + atf_tc_set_md_var(tc, "descr", "timer type once reaches lifetime"); +} +ATF_TC_BODY(once_life, tc) { + isc_result_t result; + isc_time_t expires; + isc_interval_t interval; + + UNUSED(tc); + + nevents = 1; + seconds = 1; + nanoseconds = 100000000; + + result = isc_test_begin(NULL, true, 2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, seconds, nanoseconds); + result = isc_time_nowplusinterval(&expires, &interval); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, 0, 0); + + setup_test(isc_timertype_once, &expires, &interval, ticktock); + + isc_test_end(); +} + + +static void +test_idle(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + isc_time_t now; + isc_time_t base; + isc_time_t ulim; + isc_time_t llim; + isc_interval_t interval; + + ++eventcnt; + + printf("tick %d\n", eventcnt); + + result = isc_time_now(&now); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, seconds, nanoseconds); + result = isc_time_add(&lasttime, &interval, &base); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, FUDGE_SECONDS, FUDGE_NANOSECONDS); + result = isc_time_add(&base, &interval, &ulim); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_time_subtract(&base, &interval, &llim); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(isc_time_compare(&llim, &now) <= 0); + ATF_CHECK(isc_time_compare(&ulim, &now) >= 0); + lasttime = now; + + ATF_CHECK_EQ(event->ev_type, ISC_TIMEREVENT_IDLE); + + isc_timer_detach(&timer); + isc_task_shutdown(task); + isc_event_free(&event); +} + +ATF_TC(once_idle); +ATF_TC_HEAD(once_idle, tc) { + atf_tc_set_md_var(tc, "descr", "timer type once idles out"); +} +ATF_TC_BODY(once_idle, tc) { + isc_result_t result; + isc_time_t expires; + isc_interval_t interval; + + UNUSED(tc); + + nevents = 1; + seconds = 1; + nanoseconds = 200000000; + + result = isc_test_begin(NULL, true, 2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, seconds + 1, nanoseconds); + result = isc_time_nowplusinterval(&expires, &interval); + ATF_CHECK_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, seconds, nanoseconds); + + setup_test(isc_timertype_once, &expires, &interval, test_idle); + + isc_test_end(); +} + +static void +test_reset(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + isc_time_t now; + isc_time_t base; + isc_time_t ulim; + isc_time_t llim; + isc_time_t expires; + isc_interval_t interval; + + ++eventcnt; + + printf("tick %d\n", eventcnt); + + /* + * Check expired time. + */ + + result = isc_time_now(&now); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, seconds, nanoseconds); + result = isc_time_add(&lasttime, &interval, &base); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, FUDGE_SECONDS, FUDGE_NANOSECONDS); + result = isc_time_add(&base, &interval, &ulim); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_time_subtract(&base, &interval, &llim); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + ATF_CHECK(isc_time_compare(&llim, &now) <= 0); + ATF_CHECK(isc_time_compare(&ulim, &now) >= 0); + lasttime = now; + + if (eventcnt < 3) { + ATF_CHECK_EQ(event->ev_type, ISC_TIMEREVENT_TICK); + + if (eventcnt == 2) { + isc_interval_set(&interval, seconds, nanoseconds); + result = isc_time_nowplusinterval(&expires, &interval); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, 0, 0); + result = isc_timer_reset(timer, isc_timertype_once, + &expires, &interval, + false); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + } + } else { + ATF_CHECK_EQ(event->ev_type, ISC_TIMEREVENT_LIFE); + + isc_timer_detach(&timer); + isc_task_shutdown(task); + } + + isc_event_free(&event); +} + +ATF_TC(reset); +ATF_TC_HEAD(reset, tc) { + atf_tc_set_md_var(tc, "descr", "timer reset"); +} +ATF_TC_BODY(reset, tc) { + isc_result_t result; + isc_time_t expires; + isc_interval_t interval; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + nevents = 3; + seconds = 0; + nanoseconds = 750000000; + + isc_interval_set(&interval, seconds, nanoseconds); + isc_time_settoepoch(&expires); + + setup_test(isc_timertype_ticker, &expires, &interval, test_reset); + + isc_test_end(); +} + +static int startflag; +static int shutdownflag; +static isc_timer_t *tickertimer = NULL; +static isc_timer_t *oncetimer = NULL; +static isc_task_t *task1 = NULL; +static isc_task_t *task2 = NULL; + +/* + * task1 blocks on mx while events accumulate + * in its queue, until signaled by task2. + */ + +static void +start_event(isc_task_t *task, isc_event_t *event) { + UNUSED(task); + + printf("start_event\n"); + + LOCK(&mx); + while (! startflag) { + (void) isc_condition_wait(&cv, &mx); + } + UNLOCK(&mx); + + isc_event_free(&event); +} + +static void +tick_event(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + isc_time_t expires; + isc_interval_t interval; + + UNUSED(task); + + ++eventcnt; + printf("tick_event %d\n", eventcnt); + + /* + * On the first tick, purge all remaining tick events + * and then shut down the task. + */ + if (eventcnt == 1) { + isc_time_settoepoch(&expires); + isc_interval_set(&interval, seconds, 0); + result = isc_timer_reset(tickertimer, isc_timertype_ticker, + &expires, &interval, true); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_task_shutdown(task); + } + + isc_event_free(&event); +} + +static void +once_event(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + + printf("once_event\n"); + + /* + * Allow task1 to start processing events. + */ + LOCK(&mx); + startflag = 1; + + result = isc_condition_broadcast(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + UNLOCK(&mx); + + isc_event_free(&event); + isc_task_shutdown(task); +} + +static void +shutdown_purge(isc_task_t *task, isc_event_t *event) { + isc_result_t result; + + UNUSED(task); + UNUSED(event); + + printf("shutdown_event\n"); + + /* + * Signal shutdown processing complete. + */ + LOCK(&mx); + shutdownflag = 1; + + result = isc_condition_signal(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + UNLOCK(&mx); + + isc_event_free(&event); +} + +ATF_TC(purge); +ATF_TC_HEAD(purge, tc) { + atf_tc_set_md_var(tc, "descr", "timer events purged"); +} +ATF_TC_BODY(purge, tc) { + isc_result_t result; + isc_event_t *event = NULL; + isc_time_t expires; + isc_interval_t interval; + + UNUSED(tc); + + result = isc_test_begin(NULL, true, 2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + startflag = 0; + shutdownflag = 0; + eventcnt = 0; + seconds = 1; + nanoseconds = 0; + + result = isc_mutex_init(&mx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_condition_init(&cv); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task1); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_onshutdown(task1, shutdown_purge, NULL); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + result = isc_task_create(taskmgr, 0, &task2); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + LOCK(&mx); + + event = isc_event_allocate(mctx, (void *)1 , (isc_eventtype_t)1, + start_event, NULL, sizeof(*event)); + ATF_REQUIRE(event != NULL); + isc_task_send(task1, &event); + + isc_time_settoepoch(&expires); + isc_interval_set(&interval, seconds, 0); + + tickertimer = NULL; + result = isc_timer_create(timermgr, isc_timertype_ticker, + &expires, &interval, task1, + tick_event, NULL, &tickertimer); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + oncetimer = NULL; + + isc_interval_set(&interval, (seconds * 2) + 1, 0); + result = isc_time_nowplusinterval(&expires, &interval); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + isc_interval_set(&interval, 0, 0); + result = isc_timer_create(timermgr, isc_timertype_once, + &expires, &interval, task2, + once_event, NULL, &oncetimer); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + + /* + * Wait for shutdown processing to complete. + */ + while (! shutdownflag) { + result = isc_condition_wait(&cv, &mx); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + } + + UNLOCK(&mx); + + ATF_CHECK_EQ(eventcnt, 1); + + isc_timer_detach(&tickertimer); + isc_timer_detach(&oncetimer); + isc_task_destroy(&task1); + isc_task_destroy(&task2); + DESTROYLOCK(&mx); + + isc_test_end(); +} +#else +ATF_TC(untested); +ATF_TC_HEAD(untested, tc) { + atf_tc_set_md_var(tc, "descr", "skipping nsec3 test"); +} +ATF_TC_BODY(untested, tc) { + UNUSED(tc); + atf_tc_skip("DNSSEC not available"); +} +#endif + +/* + * Main + */ +ATF_TP_ADD_TCS(tp) { +#ifdef ISC_PLATFORM_USETHREADS + ATF_TP_ADD_TC(tp, ticker); + ATF_TP_ADD_TC(tp, once_life); + ATF_TP_ADD_TC(tp, once_idle); + ATF_TP_ADD_TC(tp, reset); + ATF_TP_ADD_TC(tp, purge); +#else + ATF_TP_ADD_TC(tp, untested); +#endif + + return (atf_no_error()); +} diff --git a/lib/isc/timer.c b/lib/isc/timer.c new file mode 100644 index 0000000..2baa9e6 --- /dev/null +++ b/lib/isc/timer.c @@ -0,0 +1,1191 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/app.h> +#include <isc/condition.h> +#include <isc/heap.h> +#include <isc/log.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/once.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/task.h> +#include <isc/thread.h> +#include <isc/time.h> +#include <isc/timer.h> +#include <isc/util.h> + +#ifdef OPENSSL_LEAKS +#include <openssl/err.h> +#endif + +/* See task.c about the following definition: */ +#ifdef ISC_PLATFORM_USETHREADS +#define USE_TIMER_THREAD +#else +#define USE_SHARED_MANAGER +#endif /* ISC_PLATFORM_USETHREADS */ + +#ifndef USE_TIMER_THREAD +#include "timer_p.h" +#endif /* USE_TIMER_THREAD */ + +#ifdef ISC_TIMER_TRACE +#define XTRACE(s) fprintf(stderr, "%s\n", (s)) +#define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t)) +#define XTRACETIME(s, d) fprintf(stderr, "%s %u.%09u\n", (s), \ + (d).seconds, (d).nanoseconds) +#define XTRACETIME2(s, d, n) fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \ + (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds) +#define XTRACETIMER(s, t, d) fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \ + (d).seconds, (d).nanoseconds) +#else +#define XTRACE(s) +#define XTRACEID(s, t) +#define XTRACETIME(s, d) +#define XTRACETIME2(s, d, n) +#define XTRACETIMER(s, t, d) +#endif /* ISC_TIMER_TRACE */ + +#define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R') +#define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC) + +typedef struct isc__timer isc__timer_t; +typedef struct isc__timermgr isc__timermgr_t; + +struct isc__timer { + /*! Not locked. */ + isc_timer_t common; + isc__timermgr_t * manager; + isc_mutex_t lock; + /*! Locked by timer lock. */ + unsigned int references; + isc_time_t idle; + /*! Locked by manager lock. */ + isc_timertype_t type; + isc_time_t expires; + isc_interval_t interval; + isc_task_t * task; + isc_taskaction_t action; + void * arg; + unsigned int index; + isc_time_t due; + LINK(isc__timer_t) link; +}; + +#define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M') +#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC) + +struct isc__timermgr { + /* Not locked. */ + isc_timermgr_t common; + isc_mem_t * mctx; + isc_mutex_t lock; + /* Locked by manager lock. */ + bool done; + LIST(isc__timer_t) timers; + unsigned int nscheduled; + isc_time_t due; +#ifdef USE_TIMER_THREAD + isc_condition_t wakeup; + isc_thread_t thread; +#endif /* USE_TIMER_THREAD */ +#ifdef USE_SHARED_MANAGER + unsigned int refs; +#endif /* USE_SHARED_MANAGER */ + isc_heap_t * heap; +}; + +/*% + * The following are intended for internal use (indicated by "isc__" + * prefix) but are not declared as static, allowing direct access from + * unit tests etc. + */ + +isc_result_t +isc__timer_create(isc_timermgr_t *manager, isc_timertype_t type, + const isc_time_t *expires, const isc_interval_t *interval, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_timer_t **timerp); +isc_result_t +isc__timer_reset(isc_timer_t *timer, isc_timertype_t type, + const isc_time_t *expires, const isc_interval_t *interval, + bool purge); +isc_timertype_t +isc_timer_gettype(isc_timer_t *timer); +isc_result_t +isc__timer_touch(isc_timer_t *timer); +void +isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp); +void +isc__timer_detach(isc_timer_t **timerp); +isc_result_t +isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp); +void +isc_timermgr_poke(isc_timermgr_t *manager0); +void +isc__timermgr_destroy(isc_timermgr_t **managerp); + +static struct isc__timermethods { + isc_timermethods_t methods; + + /*% + * The following are defined just for avoiding unused static functions. + */ + void *gettype; +} timermethods = { + { + isc__timer_attach, + isc__timer_detach, + isc__timer_reset, + isc__timer_touch + }, + (void *)isc_timer_gettype +}; + +static struct isc__timermgrmethods { + isc_timermgrmethods_t methods; + void *poke; /* see above */ +} timermgrmethods = { + { + isc__timermgr_destroy, + isc__timer_create + }, + (void *)isc_timermgr_poke +}; + +#ifdef USE_SHARED_MANAGER +/*! + * If the manager is supposed to be shared, there can be only one. + */ +static isc__timermgr_t *timermgr = NULL; +#endif /* USE_SHARED_MANAGER */ + +static inline isc_result_t +schedule(isc__timer_t *timer, isc_time_t *now, bool signal_ok) { + isc_result_t result; + isc__timermgr_t *manager; + isc_time_t due; + int cmp; +#ifdef USE_TIMER_THREAD + bool timedwait; +#endif + + /*! + * Note: the caller must ensure locking. + */ + + REQUIRE(timer->type != isc_timertype_inactive); + +#ifndef USE_TIMER_THREAD + UNUSED(signal_ok); +#endif /* USE_TIMER_THREAD */ + + manager = timer->manager; + +#ifdef USE_TIMER_THREAD + /*! + * If the manager was timed wait, we may need to signal the + * manager to force a wakeup. + */ + timedwait = (manager->nscheduled > 0 && + isc_time_seconds(&manager->due) != 0); +#endif + + /* + * Compute the new due time. + */ + if (timer->type != isc_timertype_once) { + result = isc_time_add(now, &timer->interval, &due); + if (result != ISC_R_SUCCESS) + return (result); + if (timer->type == isc_timertype_limited && + isc_time_compare(&timer->expires, &due) < 0) + due = timer->expires; + } else { + if (isc_time_isepoch(&timer->idle)) + due = timer->expires; + else if (isc_time_isepoch(&timer->expires)) + due = timer->idle; + else if (isc_time_compare(&timer->idle, &timer->expires) < 0) + due = timer->idle; + else + due = timer->expires; + } + + /* + * Schedule the timer. + */ + + if (timer->index > 0) { + /* + * Already scheduled. + */ + cmp = isc_time_compare(&due, &timer->due); + timer->due = due; + switch (cmp) { + case -1: + isc_heap_increased(manager->heap, timer->index); + break; + case 1: + isc_heap_decreased(manager->heap, timer->index); + break; + case 0: + /* Nothing to do. */ + break; + } + } else { + timer->due = due; + result = isc_heap_insert(manager->heap, timer); + if (result != ISC_R_SUCCESS) { + INSIST(result == ISC_R_NOMEMORY); + return (ISC_R_NOMEMORY); + } + manager->nscheduled++; + } + + XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, + ISC_MSG_SCHEDULE, "schedule"), timer, due); + + /* + * If this timer is at the head of the queue, we need to ensure + * that we won't miss it if it has a more recent due time than + * the current "next" timer. We do this either by waking up the + * run thread, or explicitly setting the value in the manager. + */ +#ifdef USE_TIMER_THREAD + + /* + * This is a temporary (probably) hack to fix a bug on tru64 5.1 + * and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually + * return when the time expires, so here, we check to see if + * we're 15 seconds or more behind, and if we are, we signal + * the dispatcher. This isn't such a bad idea as a general purpose + * watchdog, so perhaps we should just leave it in here. + */ + if (signal_ok && timedwait) { + isc_interval_t fifteen; + isc_time_t then; + + isc_interval_set(&fifteen, 15, 0); + result = isc_time_add(&manager->due, &fifteen, &then); + + if (result == ISC_R_SUCCESS && + isc_time_compare(&then, now) < 0) { + SIGNAL(&manager->wakeup); + signal_ok = false; + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_TIMER, ISC_LOG_WARNING, + "*** POKED TIMER ***"); + } + } + + if (timer->index == 1 && signal_ok) { + XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, + ISC_MSG_SIGNALSCHED, + "signal (schedule)")); + SIGNAL(&manager->wakeup); + } +#else /* USE_TIMER_THREAD */ + if (timer->index == 1 && + isc_time_compare(&timer->due, &manager->due) < 0) + manager->due = timer->due; +#endif /* USE_TIMER_THREAD */ + + return (ISC_R_SUCCESS); +} + +static inline void +deschedule(isc__timer_t *timer) { +#ifdef USE_TIMER_THREAD + bool need_wakeup = false; +#endif + isc__timermgr_t *manager; + + /* + * The caller must ensure locking. + */ + + manager = timer->manager; + if (timer->index > 0) { +#ifdef USE_TIMER_THREAD + if (timer->index == 1) + need_wakeup = true; +#endif + isc_heap_delete(manager->heap, timer->index); + timer->index = 0; + INSIST(manager->nscheduled > 0); + manager->nscheduled--; +#ifdef USE_TIMER_THREAD + if (need_wakeup) { + XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, + ISC_MSG_SIGNALDESCHED, + "signal (deschedule)")); + SIGNAL(&manager->wakeup); + } +#endif /* USE_TIMER_THREAD */ + } +} + +static void +destroy(isc__timer_t *timer) { + isc__timermgr_t *manager = timer->manager; + + /* + * The caller must ensure it is safe to destroy the timer. + */ + + LOCK(&manager->lock); + + (void)isc_task_purgerange(timer->task, + timer, + ISC_TIMEREVENT_FIRSTEVENT, + ISC_TIMEREVENT_LASTEVENT, + NULL); + deschedule(timer); + UNLINK(manager->timers, timer, link); + + UNLOCK(&manager->lock); + + isc_task_detach(&timer->task); + DESTROYLOCK(&timer->lock); + timer->common.impmagic = 0; + timer->common.magic = 0; + isc_mem_put(manager->mctx, timer, sizeof(*timer)); +} + +isc_result_t +isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type, + const isc_time_t *expires, const isc_interval_t *interval, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_timer_t **timerp) +{ + isc__timermgr_t *manager = (isc__timermgr_t *)manager0; + isc__timer_t *timer; + isc_result_t result; + isc_time_t now; + + /* + * Create a new 'type' timer managed by 'manager'. The timers + * parameters are specified by 'expires' and 'interval'. Events + * will be posted to 'task' and when dispatched 'action' will be + * called with 'arg' as the arg value. The new timer is returned + * in 'timerp'. + */ + + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(task != NULL); + REQUIRE(action != NULL); + if (expires == NULL) + expires = isc_time_epoch; + if (interval == NULL) + interval = isc_interval_zero; + REQUIRE(type == isc_timertype_inactive || + !(isc_time_isepoch(expires) && isc_interval_iszero(interval))); + REQUIRE(timerp != NULL && *timerp == NULL); + REQUIRE(type != isc_timertype_limited || + !(isc_time_isepoch(expires) || isc_interval_iszero(interval))); + + /* + * Get current time. + */ + if (type != isc_timertype_inactive) { + TIME_NOW(&now); + } else { + /* + * We don't have to do this, but it keeps the compiler from + * complaining about "now" possibly being used without being + * set, even though it will never actually happen. + */ + isc_time_settoepoch(&now); + } + + + timer = isc_mem_get(manager->mctx, sizeof(*timer)); + if (timer == NULL) + return (ISC_R_NOMEMORY); + + timer->manager = manager; + timer->references = 1; + + if (type == isc_timertype_once && !isc_interval_iszero(interval)) { + result = isc_time_add(&now, interval, &timer->idle); + if (result != ISC_R_SUCCESS) { + isc_mem_put(manager->mctx, timer, sizeof(*timer)); + return (result); + } + } else + isc_time_settoepoch(&timer->idle); + + timer->type = type; + timer->expires = *expires; + timer->interval = *interval; + timer->task = NULL; + isc_task_attach(task, &timer->task); + timer->action = action; + /* + * Removing the const attribute from "arg" is the best of two + * evils here. If the timer->arg member is made const, then + * it affects a great many recipients of the timer event + * which did not pass in an "arg" that was truly const. + * Changing isc_timer_create() to not have "arg" prototyped as const, + * though, can cause compilers warnings for calls that *do* + * have a truly const arg. The caller will have to carefully + * keep track of whether arg started as a true const. + */ + DE_CONST(arg, timer->arg); + timer->index = 0; + result = isc_mutex_init(&timer->lock); + if (result != ISC_R_SUCCESS) { + isc_task_detach(&timer->task); + isc_mem_put(manager->mctx, timer, sizeof(*timer)); + return (result); + } + ISC_LINK_INIT(timer, link); + timer->common.impmagic = TIMER_MAGIC; + timer->common.magic = ISCAPI_TIMER_MAGIC; + timer->common.methods = (isc_timermethods_t *)&timermethods; + + LOCK(&manager->lock); + + /* + * Note we don't have to lock the timer like we normally would because + * there are no external references to it yet. + */ + + if (type != isc_timertype_inactive) + result = schedule(timer, &now, true); + else + result = ISC_R_SUCCESS; + if (result == ISC_R_SUCCESS) { + *timerp = (isc_timer_t *)timer; + APPEND(manager->timers, timer, link); + } + + UNLOCK(&manager->lock); + + if (result != ISC_R_SUCCESS) { + timer->common.impmagic = 0; + timer->common.magic = 0; + DESTROYLOCK(&timer->lock); + isc_task_detach(&timer->task); + isc_mem_put(manager->mctx, timer, sizeof(*timer)); + return (result); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type, + const isc_time_t *expires, const isc_interval_t *interval, + bool purge) +{ + isc__timer_t *timer = (isc__timer_t *)timer0; + isc_time_t now; + isc__timermgr_t *manager; + isc_result_t result; + + /* + * Change the timer's type, expires, and interval values to the given + * values. If 'purge' is true, any pending events from this timer + * are purged from its task's event queue. + */ + + REQUIRE(VALID_TIMER(timer)); + manager = timer->manager; + REQUIRE(VALID_MANAGER(manager)); + + if (expires == NULL) + expires = isc_time_epoch; + if (interval == NULL) + interval = isc_interval_zero; + REQUIRE(type == isc_timertype_inactive || + !(isc_time_isepoch(expires) && isc_interval_iszero(interval))); + REQUIRE(type != isc_timertype_limited || + !(isc_time_isepoch(expires) || isc_interval_iszero(interval))); + + /* + * Get current time. + */ + if (type != isc_timertype_inactive) { + TIME_NOW(&now); + } else { + /* + * We don't have to do this, but it keeps the compiler from + * complaining about "now" possibly being used without being + * set, even though it will never actually happen. + */ + isc_time_settoepoch(&now); + } + + LOCK(&manager->lock); + LOCK(&timer->lock); + + if (purge) + (void)isc_task_purgerange(timer->task, + timer, + ISC_TIMEREVENT_FIRSTEVENT, + ISC_TIMEREVENT_LASTEVENT, + NULL); + timer->type = type; + timer->expires = *expires; + timer->interval = *interval; + if (type == isc_timertype_once && !isc_interval_iszero(interval)) { + result = isc_time_add(&now, interval, &timer->idle); + } else { + isc_time_settoepoch(&timer->idle); + result = ISC_R_SUCCESS; + } + + if (result == ISC_R_SUCCESS) { + if (type == isc_timertype_inactive) { + deschedule(timer); + result = ISC_R_SUCCESS; + } else + result = schedule(timer, &now, true); + } + + UNLOCK(&timer->lock); + UNLOCK(&manager->lock); + + return (result); +} + +isc_timertype_t +isc_timer_gettype(isc_timer_t *timer0) { + isc__timer_t *timer = (isc__timer_t *)timer0; + isc_timertype_t t; + + REQUIRE(VALID_TIMER(timer)); + + LOCK(&timer->lock); + t = timer->type; + UNLOCK(&timer->lock); + + return (t); +} + +isc_result_t +isc__timer_touch(isc_timer_t *timer0) { + isc__timer_t *timer = (isc__timer_t *)timer0; + isc_result_t result; + isc_time_t now; + + /* + * Set the last-touched time of 'timer' to the current time. + */ + + REQUIRE(VALID_TIMER(timer)); + + LOCK(&timer->lock); + + /* + * We'd like to + * + * REQUIRE(timer->type == isc_timertype_once); + * + * but we cannot without locking the manager lock too, which we + * don't want to do. + */ + + TIME_NOW(&now); + result = isc_time_add(&now, &timer->interval, &timer->idle); + + UNLOCK(&timer->lock); + + return (result); +} + +void +isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) { + isc__timer_t *timer = (isc__timer_t *)timer0; + + /* + * Attach *timerp to timer. + */ + + REQUIRE(VALID_TIMER(timer)); + REQUIRE(timerp != NULL && *timerp == NULL); + + LOCK(&timer->lock); + timer->references++; + UNLOCK(&timer->lock); + + *timerp = (isc_timer_t *)timer; +} + +void +isc__timer_detach(isc_timer_t **timerp) { + isc__timer_t *timer; + bool free_timer = false; + + /* + * Detach *timerp from its timer. + */ + + REQUIRE(timerp != NULL); + timer = (isc__timer_t *)*timerp; + REQUIRE(VALID_TIMER(timer)); + + LOCK(&timer->lock); + REQUIRE(timer->references > 0); + timer->references--; + if (timer->references == 0) + free_timer = true; + UNLOCK(&timer->lock); + + if (free_timer) + destroy(timer); + + *timerp = NULL; +} + +static void +dispatch(isc__timermgr_t *manager, isc_time_t *now) { + bool done = false, post_event, need_schedule; + isc_timerevent_t *event; + isc_eventtype_t type = 0; + isc__timer_t *timer; + isc_result_t result; + bool idle; + + /*! + * The caller must be holding the manager lock. + */ + + while (manager->nscheduled > 0 && !done) { + timer = isc_heap_element(manager->heap, 1); + INSIST(timer != NULL && timer->type != isc_timertype_inactive); + if (isc_time_compare(now, &timer->due) >= 0) { + if (timer->type == isc_timertype_ticker) { + type = ISC_TIMEREVENT_TICK; + post_event = true; + need_schedule = true; + } else if (timer->type == isc_timertype_limited) { + int cmp; + cmp = isc_time_compare(now, &timer->expires); + if (cmp >= 0) { + type = ISC_TIMEREVENT_LIFE; + post_event = true; + need_schedule = false; + } else { + type = ISC_TIMEREVENT_TICK; + post_event = true; + need_schedule = true; + } + } else if (!isc_time_isepoch(&timer->expires) && + isc_time_compare(now, + &timer->expires) >= 0) { + type = ISC_TIMEREVENT_LIFE; + post_event = true; + need_schedule = false; + } else { + idle = false; + + LOCK(&timer->lock); + if (!isc_time_isepoch(&timer->idle) && + isc_time_compare(now, + &timer->idle) >= 0) { + idle = true; + } + UNLOCK(&timer->lock); + if (idle) { + type = ISC_TIMEREVENT_IDLE; + post_event = true; + need_schedule = false; + } else { + /* + * Idle timer has been touched; + * reschedule. + */ + XTRACEID(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_TIMER, + ISC_MSG_IDLERESCHED, + "idle reschedule"), + timer); + post_event = false; + need_schedule = true; + } + } + + if (post_event) { + XTRACEID(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_TIMER, + ISC_MSG_POSTING, + "posting"), timer); + /* + * XXX We could preallocate this event. + */ + event = (isc_timerevent_t *)isc_event_allocate(manager->mctx, + timer, + type, + timer->action, + timer->arg, + sizeof(*event)); + + if (event != NULL) { + event->due = timer->due; + isc_task_send(timer->task, + ISC_EVENT_PTR(&event)); + } else + UNEXPECTED_ERROR(__FILE__, __LINE__, "%s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_TIMER, + ISC_MSG_EVENTNOTALLOC, + "couldn't " + "allocate event")); + } + + timer->index = 0; + isc_heap_delete(manager->heap, 1); + manager->nscheduled--; + + if (need_schedule) { + result = schedule(timer, now, false); + if (result != ISC_R_SUCCESS) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "%s: %u", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_TIMER, + ISC_MSG_SCHEDFAIL, + "couldn't schedule " + "timer"), + result); + } + } else { + manager->due = timer->due; + done = true; + } + } +} + +#ifdef USE_TIMER_THREAD +static isc_threadresult_t +#ifdef _WIN32 /* XXXDCL */ +WINAPI +#endif +run(void *uap) { + isc__timermgr_t *manager = uap; + isc_time_t now; + isc_result_t result; + + LOCK(&manager->lock); + while (!manager->done) { + TIME_NOW(&now); + + XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_RUNNING, + "running"), now); + + dispatch(manager, &now); + + if (manager->nscheduled > 0) { + XTRACETIME2(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_WAITUNTIL, + "waituntil"), + manager->due, now); + result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due); + INSIST(result == ISC_R_SUCCESS || + result == ISC_R_TIMEDOUT); + } else { + XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_WAIT, "wait"), now); + WAIT(&manager->wakeup, &manager->lock); + } + XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, + ISC_MSG_WAKEUP, "wakeup")); + } + UNLOCK(&manager->lock); + +#ifdef OPENSSL_LEAKS + ERR_remove_state(0); +#endif + + return ((isc_threadresult_t)0); +} +#endif /* USE_TIMER_THREAD */ + +static bool +sooner(void *v1, void *v2) { + isc__timer_t *t1, *t2; + + t1 = v1; + t2 = v2; + REQUIRE(VALID_TIMER(t1)); + REQUIRE(VALID_TIMER(t2)); + + if (isc_time_compare(&t1->due, &t2->due) < 0) + return (true); + return (false); +} + +static void +set_index(void *what, unsigned int index) { + isc__timer_t *timer; + + timer = what; + REQUIRE(VALID_TIMER(timer)); + + timer->index = index; +} + +isc_result_t +isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) { + isc__timermgr_t *manager; + isc_result_t result; + + /* + * Create a timer manager. + */ + + REQUIRE(managerp != NULL && *managerp == NULL); + +#ifdef USE_SHARED_MANAGER + if (timermgr != NULL) { + timermgr->refs++; + *managerp = (isc_timermgr_t *)timermgr; + return (ISC_R_SUCCESS); + } +#endif /* USE_SHARED_MANAGER */ + + manager = isc_mem_get(mctx, sizeof(*manager)); + if (manager == NULL) + return (ISC_R_NOMEMORY); + + manager->common.impmagic = TIMER_MANAGER_MAGIC; + manager->common.magic = ISCAPI_TIMERMGR_MAGIC; + manager->common.methods = (isc_timermgrmethods_t *)&timermgrmethods; + manager->mctx = NULL; + manager->done = false; + INIT_LIST(manager->timers); + manager->nscheduled = 0; + isc_time_settoepoch(&manager->due); + manager->heap = NULL; + result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap); + if (result != ISC_R_SUCCESS) { + INSIST(result == ISC_R_NOMEMORY); + isc_mem_put(mctx, manager, sizeof(*manager)); + return (ISC_R_NOMEMORY); + } + result = isc_mutex_init(&manager->lock); + if (result != ISC_R_SUCCESS) { + isc_heap_destroy(&manager->heap); + isc_mem_put(mctx, manager, sizeof(*manager)); + return (result); + } + isc_mem_attach(mctx, &manager->mctx); +#ifdef USE_TIMER_THREAD + if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) { + isc_mem_detach(&manager->mctx); + DESTROYLOCK(&manager->lock); + isc_heap_destroy(&manager->heap); + isc_mem_put(mctx, manager, sizeof(*manager)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + return (ISC_R_UNEXPECTED); + } + if (isc_thread_create(run, manager, &manager->thread) != + ISC_R_SUCCESS) { + isc_mem_detach(&manager->mctx); + (void)isc_condition_destroy(&manager->wakeup); + DESTROYLOCK(&manager->lock); + isc_heap_destroy(&manager->heap); + isc_mem_put(mctx, manager, sizeof(*manager)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_thread_create() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + return (ISC_R_UNEXPECTED); + } + isc_thread_setname(manager->thread, "isc-timer"); +#endif +#ifdef USE_SHARED_MANAGER + manager->refs = 1; + timermgr = manager; +#endif /* USE_SHARED_MANAGER */ + + *managerp = (isc_timermgr_t *)manager; + + return (ISC_R_SUCCESS); +} + +void +isc_timermgr_poke(isc_timermgr_t *manager0) { +#ifdef USE_TIMER_THREAD + isc__timermgr_t *manager = (isc__timermgr_t *)manager0; + + REQUIRE(VALID_MANAGER(manager)); + + SIGNAL(&manager->wakeup); +#else + UNUSED(manager0); +#endif +} + +void +isc__timermgr_destroy(isc_timermgr_t **managerp) { + isc__timermgr_t *manager; + isc_mem_t *mctx; + + /* + * Destroy a timer manager. + */ + + REQUIRE(managerp != NULL); + manager = (isc__timermgr_t *)*managerp; + REQUIRE(VALID_MANAGER(manager)); + + LOCK(&manager->lock); + +#ifdef USE_SHARED_MANAGER + manager->refs--; + if (manager->refs > 0) { + UNLOCK(&manager->lock); + *managerp = NULL; + return; + } + timermgr = NULL; +#endif /* USE_SHARED_MANAGER */ + +#ifndef USE_TIMER_THREAD + isc__timermgr_dispatch((isc_timermgr_t *)manager); +#endif + + REQUIRE(EMPTY(manager->timers)); + manager->done = true; + +#ifdef USE_TIMER_THREAD + XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER, + ISC_MSG_SIGNALDESTROY, "signal (destroy)")); + SIGNAL(&manager->wakeup); +#endif /* USE_TIMER_THREAD */ + + UNLOCK(&manager->lock); + +#ifdef USE_TIMER_THREAD + /* + * Wait for thread to exit. + */ + if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_thread_join() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); +#endif /* USE_TIMER_THREAD */ + + /* + * Clean up. + */ +#ifdef USE_TIMER_THREAD + (void)isc_condition_destroy(&manager->wakeup); +#endif /* USE_TIMER_THREAD */ + DESTROYLOCK(&manager->lock); + isc_heap_destroy(&manager->heap); + manager->common.impmagic = 0; + manager->common.magic = 0; + mctx = manager->mctx; + isc_mem_put(mctx, manager, sizeof(*manager)); + isc_mem_detach(&mctx); + + *managerp = NULL; + +#ifdef USE_SHARED_MANAGER + timermgr = NULL; +#endif +} + +#ifndef USE_TIMER_THREAD +isc_result_t +isc__timermgr_nextevent(isc_timermgr_t *manager0, isc_time_t *when) { + isc__timermgr_t *manager = (isc__timermgr_t *)manager0; + +#ifdef USE_SHARED_MANAGER + if (manager == NULL) + manager = timermgr; +#endif + if (manager == NULL || manager->nscheduled == 0) + return (ISC_R_NOTFOUND); + *when = manager->due; + return (ISC_R_SUCCESS); +} + +void +isc__timermgr_dispatch(isc_timermgr_t *manager0) { + isc__timermgr_t *manager = (isc__timermgr_t *)manager0; + isc_time_t now; + +#ifdef USE_SHARED_MANAGER + if (manager == NULL) + manager = timermgr; +#endif + if (manager == NULL) + return; + TIME_NOW(&now); + dispatch(manager, &now); +} +#endif /* USE_TIMER_THREAD */ + +isc_result_t +isc__timer_register(void) { + return (isc_timer_register(isc__timermgr_create)); +} + +static isc_mutex_t createlock; +static isc_once_t once = ISC_ONCE_INIT; +static isc_timermgrcreatefunc_t timermgr_createfunc = NULL; + +static void +initialize(void) { + RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS); +} + +isc_result_t +isc_timer_register(isc_timermgrcreatefunc_t createfunc) { + isc_result_t result = ISC_R_SUCCESS; + + RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS); + + LOCK(&createlock); + if (timermgr_createfunc == NULL) + timermgr_createfunc = createfunc; + else + result = ISC_R_EXISTS; + UNLOCK(&createlock); + + return (result); +} + +isc_result_t +isc_timermgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, + isc_timermgr_t **managerp) +{ + isc_result_t result; + + LOCK(&createlock); + + REQUIRE(timermgr_createfunc != NULL); + result = (*timermgr_createfunc)(mctx, managerp); + + UNLOCK(&createlock); + + if (result == ISC_R_SUCCESS) + isc_appctx_settimermgr(actx, *managerp); + + return (result); +} + +isc_result_t +isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) { + isc_result_t result; + + if (isc_bind9) + return (isc__timermgr_create(mctx, managerp)); + + LOCK(&createlock); + + REQUIRE(timermgr_createfunc != NULL); + result = (*timermgr_createfunc)(mctx, managerp); + + UNLOCK(&createlock); + + return (result); +} + +void +isc_timermgr_destroy(isc_timermgr_t **managerp) { + REQUIRE(*managerp != NULL && ISCAPI_TIMERMGR_VALID(*managerp)); + + if (isc_bind9) + isc__timermgr_destroy(managerp); + else + (*managerp)->methods->destroy(managerp); + + ENSURE(*managerp == NULL); +} + +isc_result_t +isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type, + const isc_time_t *expires, const isc_interval_t *interval, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_timer_t **timerp) +{ + REQUIRE(ISCAPI_TIMERMGR_VALID(manager)); + + if (isc_bind9) + return (isc__timer_create(manager, type, expires, interval, + task, action, arg, timerp)); + + return (manager->methods->timercreate(manager, type, expires, + interval, task, action, arg, + timerp)); +} + +void +isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) { + REQUIRE(ISCAPI_TIMER_VALID(timer)); + REQUIRE(timerp != NULL && *timerp == NULL); + + if (isc_bind9) + isc__timer_attach(timer, timerp); + else + timer->methods->attach(timer, timerp); + + ENSURE(*timerp == timer); +} + +void +isc_timer_detach(isc_timer_t **timerp) { + REQUIRE(timerp != NULL && ISCAPI_TIMER_VALID(*timerp)); + + if (isc_bind9) + isc__timer_detach(timerp); + else + (*timerp)->methods->detach(timerp); + + ENSURE(*timerp == NULL); +} + +isc_result_t +isc_timer_reset(isc_timer_t *timer, isc_timertype_t type, + const isc_time_t *expires, const isc_interval_t *interval, + bool purge) +{ + REQUIRE(ISCAPI_TIMER_VALID(timer)); + + if (isc_bind9) + return (isc__timer_reset(timer, type, expires, + interval, purge)); + + return (timer->methods->reset(timer, type, expires, interval, purge)); +} + +isc_result_t +isc_timer_touch(isc_timer_t *timer) { + REQUIRE(ISCAPI_TIMER_VALID(timer)); + + if (isc_bind9) + return (isc__timer_touch(timer)); + + return (timer->methods->touch(timer)); +} diff --git a/lib/isc/timer_p.h b/lib/isc/timer_p.h new file mode 100644 index 0000000..815f611 --- /dev/null +++ b/lib/isc/timer_p.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id: timer_p.h,v 1.12 2009/09/02 23:48:02 tbox Exp $ */ + +#ifndef ISC_TIMER_P_H +#define ISC_TIMER_P_H + +/*! \file */ + +isc_result_t +isc__timermgr_nextevent(isc_timermgr_t *timermgr, isc_time_t *when); + +void +isc__timermgr_dispatch(isc_timermgr_t *timermgr); + +#endif /* ISC_TIMER_P_H */ diff --git a/lib/isc/tm.c b/lib/isc/tm.c new file mode 100644 index 0000000..c390396 --- /dev/null +++ b/lib/isc/tm.c @@ -0,0 +1,439 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*- + * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <ctype.h> + +#include <isc/tm.h> +#include <isc/util.h> + +/* + * Portable conversion routines for struct tm, replacing + * timegm() and strptime(), which are not available on all + * platforms and don't always behave the same way when they + * are. + */ + +/* + * We do not implement alternate representations. However, we always + * check whether a given modifier is allowed for a certain conversion. + */ +#define ALT_E 0x01 +#define ALT_O 0x02 +#define LEGAL_ALT(x) { if (alt_format & ~(x)) return (0); } + +#ifndef TM_YEAR_BASE +#define TM_YEAR_BASE 1900 +#endif + +static const char *day[7] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday" +}; +static const char *abday[7] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; +static const char *mon[12] = { + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December" +}; +static const char *abmon[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +static const char *am_pm[2] = { + "AM", "PM" +}; + +static int +conv_num(const char **buf, int *dest, int llim, int ulim) { + int result = 0; + + /* The limit also determines the number of valid digits. */ + int rulim = ulim; + + if (**buf < '0' || **buf > '9') + return (0); + + do { + result *= 10; + result += *(*buf)++ - '0'; + rulim /= 10; + } while ((result * 10 <= ulim) && + rulim && **buf >= '0' && **buf <= '9'); + + if (result < llim || result > ulim) + return (0); + + *dest = result; + return (1); +} + +time_t +isc_tm_timegm(struct tm *tm) { + time_t ret; + int i, yday = 0, leapday; + int mdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 }; + + leapday = ((((tm->tm_year + 1900 ) % 4) == 0 && + ((tm->tm_year + 1900 ) % 100) != 0) || + ((tm->tm_year + 1900 ) % 400) == 0) ? 1 : 0; + mdays[1] += leapday; + + yday = tm->tm_mday - 1; + for (i = 1; i <= tm->tm_mon; i++) + yday += mdays[i - 1]; + ret = tm->tm_sec + + (60 * tm->tm_min) + + (3600 * tm->tm_hour) + + (86400 * (yday + + ((tm->tm_year - 70) * 365) + + ((tm->tm_year - 69) / 4) - + ((tm->tm_year - 1) / 100) + + ((tm->tm_year + 299) / 400))); + return (ret); +} + +char * +isc_tm_strptime(const char *buf, const char *fmt, struct tm *tm) { + char c, *ret; + const char *bp; + size_t len = 0; + int alt_format, i, split_year = 0; + + REQUIRE(buf != NULL); + REQUIRE(fmt != NULL); + REQUIRE(tm != NULL); + + memset(tm, 0, sizeof(struct tm)); + + bp = buf; + + while ((c = *fmt) != '\0') { + /* Clear `alternate' modifier prior to new conversion. */ + alt_format = 0; + + /* Eat up white-space. */ + if (isspace((unsigned char) c)) { + while (isspace((unsigned char) *bp)) + bp++; + + fmt++; + continue; + } + + if ((c = *fmt++) != '%') + goto literal; + + +again: switch (c = *fmt++) { + case '%': /* "%%" is converted to "%". */ +literal: + if (c != *bp++) + return (0); + break; + + /* + * "Alternative" modifiers. Just set the appropriate flag + * and start over again. + */ + case 'E': /* "%E?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_E; + goto again; + + case 'O': /* "%O?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_O; + goto again; + + /* + * "Complex" conversion rules, implemented through recursion. + */ + case 'c': /* Date and time, using the locale's format. */ + LEGAL_ALT(ALT_E); + if (!(bp = isc_tm_strptime(bp, "%x %X", tm))) + return (0); + break; + + case 'D': /* The date as "%m/%d/%y". */ + LEGAL_ALT(0); + if (!(bp = isc_tm_strptime(bp, "%m/%d/%y", tm))) + return (0); + break; + + case 'R': /* The time as "%H:%M". */ + LEGAL_ALT(0); + if (!(bp = isc_tm_strptime(bp, "%H:%M", tm))) + return (0); + break; + + case 'r': /* The time in 12-hour clock representation. */ + LEGAL_ALT(0); + if (!(bp = isc_tm_strptime(bp, "%I:%M:%S %p", tm))) + return (0); + break; + + case 'T': /* The time as "%H:%M:%S". */ + LEGAL_ALT(0); + if (!(bp = isc_tm_strptime(bp, "%H:%M:%S", tm))) + return (0); + break; + + case 'X': /* The time, using the locale's format. */ + LEGAL_ALT(ALT_E); + if (!(bp = isc_tm_strptime(bp, "%H:%M:%S", tm))) + return (0); + break; + + case 'x': /* The date, using the locale's format. */ + LEGAL_ALT(ALT_E); + if (!(bp = isc_tm_strptime(bp, "%m/%d/%y", tm))) + return (0); + break; + + /* + * "Elementary" conversion rules. + */ + case 'A': /* The day of week, using the locale's form. */ + case 'a': + LEGAL_ALT(0); + for (i = 0; i < 7; i++) { + /* Full name. */ + len = strlen(day[i]); + if (strncasecmp(day[i], bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(abday[i]); + if (strncasecmp(abday[i], bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 7) + return (0); + + tm->tm_wday = i; + bp += len; + break; + + case 'B': /* The month, using the locale's form. */ + case 'b': + case 'h': + LEGAL_ALT(0); + for (i = 0; i < 12; i++) { + /* Full name. */ + len = strlen(mon[i]); + if (strncasecmp(mon[i], bp, len) == 0) + break; + + /* Abbreviated name. */ + len = strlen(abmon[i]); + if (strncasecmp(abmon[i], bp, len) == 0) + break; + } + + /* Nothing matched. */ + if (i == 12) + return (0); + + tm->tm_mon = i; + bp += len; + break; + + case 'C': /* The century number. */ + LEGAL_ALT(ALT_E); + if (!(conv_num(&bp, &i, 0, 99))) + return (0); + + if (split_year) { + tm->tm_year = (tm->tm_year % 100) + (i * 100); + } else { + tm->tm_year = i * 100; + split_year = 1; + } + break; + + case 'd': /* The day of month. */ + case 'e': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_mday, 1, 31))) + return (0); + break; + + case 'k': /* The hour (24-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'H': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_hour, 0, 23))) + return (0); + break; + + case 'l': /* The hour (12-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'I': + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_hour, 1, 12))) + return (0); + if (tm->tm_hour == 12) + tm->tm_hour = 0; + break; + + case 'j': /* The day of year. */ + LEGAL_ALT(0); + if (!(conv_num(&bp, &i, 1, 366))) + return (0); + tm->tm_yday = i - 1; + break; + + case 'M': /* The minute. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_min, 0, 59))) + return (0); + break; + + case 'm': /* The month. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &i, 1, 12))) + return (0); + tm->tm_mon = i - 1; + break; + + case 'p': /* The locale's equivalent of AM/PM. */ + LEGAL_ALT(0); + /* AM? */ + if (strcasecmp(am_pm[0], bp) == 0) { + if (tm->tm_hour > 11) + return (0); + + bp += strlen(am_pm[0]); + break; + } + /* PM? */ + else if (strcasecmp(am_pm[1], bp) == 0) { + if (tm->tm_hour > 11) + return (0); + + tm->tm_hour += 12; + bp += strlen(am_pm[1]); + break; + } + + /* Nothing matched. */ + return (0); + + case 'S': /* The seconds. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_sec, 0, 61))) + return (0); + break; + + case 'U': /* The week of year, beginning on sunday. */ + case 'W': /* The week of year, beginning on monday. */ + LEGAL_ALT(ALT_O); + /* + * XXX This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so just check the + * range for now. + */ + if (!(conv_num(&bp, &i, 0, 53))) + return (0); + break; + + case 'w': /* The day of week, beginning on sunday. */ + LEGAL_ALT(ALT_O); + if (!(conv_num(&bp, &tm->tm_wday, 0, 6))) + return (0); + break; + + case 'Y': /* The year. */ + LEGAL_ALT(ALT_E); + if (!(conv_num(&bp, &i, 0, 9999))) + return (0); + + tm->tm_year = i - TM_YEAR_BASE; + break; + + case 'y': /* The year within 100 years of the epoch. */ + LEGAL_ALT(ALT_E | ALT_O); + if (!(conv_num(&bp, &i, 0, 99))) + return (0); + + if (split_year) { + tm->tm_year = ((tm->tm_year / 100) * 100) + i; + break; + } + split_year = 1; + if (i <= 68) + tm->tm_year = i + 2000 - TM_YEAR_BASE; + else + tm->tm_year = i + 1900 - TM_YEAR_BASE; + break; + + /* + * Miscellaneous conversions. + */ + case 'n': /* Any kind of white-space. */ + case 't': + LEGAL_ALT(0); + while (isspace((unsigned char) *bp)) + bp++; + break; + + + default: /* Unknown/unsupported conversion. */ + return (0); + } + + + } + + /* LINTED functional specification */ + DE_CONST(bp, ret); + return (ret); +} diff --git a/lib/isc/unix/Makefile.in b/lib/isc/unix/Makefile.in new file mode 100644 index 0000000..bdd7bfd --- /dev/null +++ b/lib/isc/unix/Makefile.in @@ -0,0 +1,44 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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}/include \ + -I${srcdir}/../@ISC_THREAD_DIR@/include \ + -I../include \ + -I${srcdir}/../include \ + -I${srcdir}/.. @ISC_OPENSSL_INC@ + +CDEFINES = @CRYPTO@ +CWARNINGS = + +# Alphabetically +OBJS = @ISC_IPV6_O@ @ISC_PK11_API_O@ \ + app.@O@ dir.@O@ entropy.@O@ errno.@O@ errno2result.@O@ \ + file.@O@ fsaccess.@O@ interfaceiter.@O@ \ + keyboard.@O@ meminfo.@O@ \ + net.@O@ os.@O@ resource.@O@ socket.@O@ stdio.@O@ stdtime.@O@ \ + strerror.@O@ syslog.@O@ time.@O@ + +# Alphabetically +SRCS = @ISC_IPV6_C@ @ISC_PK11_API_C@ \ + app.c dir.c entropy.c errno.c errno2result.c \ + file.c fsaccess.c interfaceiter.c keyboard.c meminfo.c \ + net.c os.c resource.c socket.c stdio.c stdtime.c \ + strerror.c syslog.c time.c + +SUBDIRS = include +TARGETS = ${OBJS} + +@BIND9_MAKE_RULES@ + +interfaceiter.@O@: interfaceiter.c ifiter_ioctl.c ifiter_sysctl.c ifiter_getifaddrs.c + diff --git a/lib/isc/unix/app.c b/lib/isc/unix/app.c new file mode 100644 index 0000000..7e5a0ee --- /dev/null +++ b/lib/isc/unix/app.c @@ -0,0 +1,1038 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */ +#include <sys/types.h> + +#include <stddef.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#ifdef HAVE_EPOLL +#include <sys/epoll.h> +#endif + +#include <isc/app.h> +#include <isc/condition.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/mutex.h> +#include <isc/event.h> +#include <isc/platform.h> +#include <isc/strerror.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/time.h> +#include <isc/util.h> + +#ifdef ISC_PLATFORM_USETHREADS +#include <pthread.h> +#endif + +/*% + * For BIND9 internal applications built with threads, we use a single app + * context and let multiple worker, I/O, timer threads do actual jobs. + * For other cases (including BIND9 built without threads) an app context acts + * as an event loop dispatching various events. + */ +#ifndef ISC_PLATFORM_USETHREADS +#include "../timer_p.h" +#include "../task_p.h" +#include "socket_p.h" +#endif /* ISC_PLATFORM_USETHREADS */ + +#ifdef ISC_PLATFORM_USETHREADS +static pthread_t blockedthread; +#endif /* ISC_PLATFORM_USETHREADS */ + +/*% + * The following are intended for internal use (indicated by "isc__" + * prefix) but are not declared as static, allowing direct access from + * unit tests etc. + */ +isc_result_t isc__app_start(void); +isc_result_t isc__app_ctxstart(isc_appctx_t *ctx); +isc_result_t isc__app_onrun(isc_mem_t *mctx, isc_task_t *task, + isc_taskaction_t action, void *arg); +isc_result_t isc__app_ctxrun(isc_appctx_t *ctx); +isc_result_t isc__app_run(void); +isc_result_t isc__app_ctxshutdown(isc_appctx_t *ctx); +isc_result_t isc__app_shutdown(void); +isc_result_t isc__app_reload(void); +isc_result_t isc__app_ctxsuspend(isc_appctx_t *ctx); +void isc__app_ctxfinish(isc_appctx_t *ctx); +void isc__app_finish(void); +void isc__app_block(void); +void isc__app_unblock(void); +isc_result_t isc__appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp); +void isc__appctx_destroy(isc_appctx_t **ctxp); +void isc__appctx_settaskmgr(isc_appctx_t *ctx, isc_taskmgr_t *taskmgr); +void isc__appctx_setsocketmgr(isc_appctx_t *ctx, isc_socketmgr_t *socketmgr); +void isc__appctx_settimermgr(isc_appctx_t *ctx, isc_timermgr_t *timermgr); +isc_result_t isc__app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, + isc_task_t *task, isc_taskaction_t action, + void *arg); + +/* + * The application context of this module. This implementation actually + * doesn't use it. (This may change in the future). + */ +#define APPCTX_MAGIC ISC_MAGIC('A', 'p', 'c', 'x') +#define VALID_APPCTX(c) ISC_MAGIC_VALID(c, APPCTX_MAGIC) + +typedef struct isc__appctx { + isc_appctx_t common; + isc_mem_t *mctx; + isc_mutex_t lock; + isc_eventlist_t on_run; + bool shutdown_requested; + bool running; + + /*! + * We assume that 'want_shutdown' can be read and written atomically. + */ + bool want_shutdown; + /* + * We assume that 'want_reload' can be read and written atomically. + */ + bool want_reload; + + bool blocked; + + isc_taskmgr_t *taskmgr; + isc_socketmgr_t *socketmgr; + isc_timermgr_t *timermgr; +#ifdef ISC_PLATFORM_USETHREADS + isc_mutex_t readylock; + isc_condition_t ready; +#endif /* ISC_PLATFORM_USETHREADS */ +} isc__appctx_t; + +static isc__appctx_t isc_g_appctx; + +static struct { + isc_appmethods_t methods; + + /*% + * The following are defined just for avoiding unused static functions. + */ + void *run, *shutdown, *start, *reload, *finish, *block, *unblock; +} appmethods = { + { + isc__appctx_destroy, + isc__app_ctxstart, + isc__app_ctxrun, + isc__app_ctxsuspend, + isc__app_ctxshutdown, + isc__app_ctxfinish, + isc__appctx_settaskmgr, + isc__appctx_setsocketmgr, + isc__appctx_settimermgr, + isc__app_ctxonrun + }, + (void *)isc__app_run, + (void *)isc__app_shutdown, + (void *)isc__app_start, + (void *)isc__app_reload, + (void *)isc__app_finish, + (void *)isc__app_block, + (void *)isc__app_unblock +}; + +#ifdef HAVE_LINUXTHREADS +/*! + * Linux has sigwait(), but it appears to prevent signal handlers from + * running, even if they're not in the set being waited for. This makes + * it impossible to get the default actions for SIGILL, SIGSEGV, etc. + * Instead of messing with it, we just use sigsuspend() instead. + */ +#undef HAVE_SIGWAIT +/*! + * We need to remember which thread is the main thread... + */ +static pthread_t main_thread; +#endif + +#ifndef HAVE_SIGWAIT +static void +exit_action(int arg) { + UNUSED(arg); + isc_g_appctx.want_shutdown = true; +} + +static void +reload_action(int arg) { + UNUSED(arg); + isc_g_appctx.want_reload = true; +} +#endif + +static isc_result_t +handle_signal(int sig, void (*handler)(int)) { + struct sigaction sa; + char strbuf[ISC_STRERRORSIZE]; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = handler; + + if (sigfillset(&sa.sa_mask) != 0 || + sigaction(sig, &sa, NULL) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_APP, + ISC_MSG_SIGNALSETUP, + "handle_signal() %d setup: %s"), + sig, strbuf); + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__app_ctxstart(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + isc_result_t result; + int presult; + sigset_t sset; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(VALID_APPCTX(ctx)); + + /* + * Start an ISC library application. + */ + +#ifdef NEED_PTHREAD_INIT + /* + * BSDI 3.1 seg faults in pthread_sigmask() if we don't do this. + */ + presult = pthread_init(); + if (presult != 0) { + isc__strerror(presult, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_start() pthread_init: %s", strbuf); + return (ISC_R_UNEXPECTED); + } +#endif + +#ifdef ISC_PLATFORM_USETHREADS +#ifdef HAVE_LINUXTHREADS + main_thread = pthread_self(); +#endif /* HAVE_LINUXTHREADS */ + + result = isc_mutex_init(&ctx->readylock); + if (result != ISC_R_SUCCESS) + return (result); + + result = isc_condition_init(&ctx->ready); + if (result != ISC_R_SUCCESS) + goto cleanup_rlock; + + result = isc_mutex_init(&ctx->lock); + if (result != ISC_R_SUCCESS) + goto cleanup_rcond; +#else /* ISC_PLATFORM_USETHREADS */ + result = isc_mutex_init(&ctx->lock); + if (result != ISC_R_SUCCESS) + goto cleanup; +#endif /* ISC_PLATFORM_USETHREADS */ + + ISC_LIST_INIT(ctx->on_run); + + ctx->shutdown_requested = false; + ctx->running = false; + ctx->want_shutdown = false; + ctx->want_reload = false; + ctx->blocked = false; + +#ifndef HAVE_SIGWAIT + /* + * Install do-nothing handlers for SIGINT and SIGTERM. + * + * We install them now because BSDI 3.1 won't block + * the default actions, regardless of what we do with + * pthread_sigmask(). + */ + result = handle_signal(SIGINT, exit_action); + if (result != ISC_R_SUCCESS) + goto cleanup; + result = handle_signal(SIGTERM, exit_action); + if (result != ISC_R_SUCCESS) + goto cleanup; +#endif + + /* + * Always ignore SIGPIPE. + */ + result = handle_signal(SIGPIPE, SIG_IGN); + if (result != ISC_R_SUCCESS) + goto cleanup; + + /* + * On Solaris 2, delivery of a signal whose action is SIG_IGN + * will not cause sigwait() to return. We may have inherited + * unexpected actions for SIGHUP, SIGINT, and SIGTERM from our parent + * process (e.g, Solaris cron). Set an action of SIG_DFL to make + * sure sigwait() works as expected. Only do this for SIGTERM and + * SIGINT if we don't have sigwait(), since a different handler is + * installed above. + */ + result = handle_signal(SIGHUP, SIG_DFL); + if (result != ISC_R_SUCCESS) + goto cleanup; + +#ifdef HAVE_SIGWAIT + result = handle_signal(SIGTERM, SIG_DFL); + if (result != ISC_R_SUCCESS) + goto cleanup; + result = handle_signal(SIGINT, SIG_DFL); + if (result != ISC_R_SUCCESS) + goto cleanup; +#endif + +#ifdef ISC_PLATFORM_USETHREADS + /* + * Block SIGHUP, SIGINT, SIGTERM. + * + * If isc_app_start() is called from the main thread before any other + * threads have been created, then the pthread_sigmask() call below + * will result in all threads having SIGHUP, SIGINT and SIGTERM + * blocked by default, ensuring that only the thread that calls + * sigwait() for them will get those signals. + */ + if (sigemptyset(&sset) != 0 || + sigaddset(&sset, SIGHUP) != 0 || + sigaddset(&sset, SIGINT) != 0 || + sigaddset(&sset, SIGTERM) != 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_start() sigsetops: %s", strbuf); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + presult = pthread_sigmask(SIG_BLOCK, &sset, NULL); + if (presult != 0) { + isc__strerror(presult, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_start() pthread_sigmask: %s", + strbuf); + result = ISC_R_UNEXPECTED; + goto cleanup; + } +#else /* ISC_PLATFORM_USETHREADS */ + /* + * Unblock SIGHUP, SIGINT, SIGTERM. + * + * If we're not using threads, we need to make sure that SIGHUP, + * SIGINT and SIGTERM are not inherited as blocked from the parent + * process. + */ + if (sigemptyset(&sset) != 0 || + sigaddset(&sset, SIGHUP) != 0 || + sigaddset(&sset, SIGINT) != 0 || + sigaddset(&sset, SIGTERM) != 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_start() sigsetops: %s", strbuf); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + presult = sigprocmask(SIG_UNBLOCK, &sset, NULL); + if (presult != 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_start() sigprocmask: %s", strbuf); + result = ISC_R_UNEXPECTED; + goto cleanup; + } +#endif /* ISC_PLATFORM_USETHREADS */ + + return (ISC_R_SUCCESS); + + cleanup: +#ifdef ISC_PLATFORM_USETHREADS + cleanup_rcond: + (void)isc_condition_destroy(&ctx->ready); + + cleanup_rlock: + (void)isc_mutex_destroy(&ctx->readylock); +#endif /* ISC_PLATFORM_USETHREADS */ + return (result); +} + +isc_result_t +isc__app_start(void) { + isc_g_appctx.common.impmagic = APPCTX_MAGIC; + isc_g_appctx.common.magic = ISCAPI_APPCTX_MAGIC; + isc_g_appctx.common.methods = &appmethods.methods; + isc_g_appctx.mctx = NULL; + /* The remaining members will be initialized in ctxstart() */ + + return (isc__app_ctxstart((isc_appctx_t *)&isc_g_appctx)); +} + +isc_result_t +isc__app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action, + void *arg) +{ + return (isc__app_ctxonrun((isc_appctx_t *)&isc_g_appctx, mctx, + task, action, arg)); +} + +isc_result_t +isc__app_ctxonrun(isc_appctx_t *ctx0, isc_mem_t *mctx, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + isc_event_t *event; + isc_task_t *cloned_task = NULL; + isc_result_t result; + + LOCK(&ctx->lock); + + if (ctx->running) { + result = ISC_R_ALREADYRUNNING; + goto unlock; + } + + /* + * Note that we store the task to which we're going to send the event + * in the event's "sender" field. + */ + isc_task_attach(task, &cloned_task); + event = isc_event_allocate(mctx, cloned_task, ISC_APPEVENT_SHUTDOWN, + action, arg, sizeof(*event)); + if (event == NULL) { + isc_task_detach(&cloned_task); + result = ISC_R_NOMEMORY; + goto unlock; + } + + ISC_LIST_APPEND(ctx->on_run, event, ev_link); + + result = ISC_R_SUCCESS; + + unlock: + UNLOCK(&ctx->lock); + + return (result); +} + +#ifndef ISC_PLATFORM_USETHREADS +/*! + * Event loop for nonthreaded programs. + */ +static isc_result_t +evloop(isc__appctx_t *ctx) { + isc_result_t result; + + while (!ctx->want_shutdown) { + int n; + isc_time_t when, now; + struct timeval tv, *tvp; + isc_socketwait_t *swait; + bool readytasks; + bool call_timer_dispatch = false; + + /* + * Check the reload (or suspend) case first for exiting the + * loop as fast as possible in case: + * - the direct call to isc__taskmgr_dispatch() in + * isc__app_ctxrun() completes all the tasks so far, + * - there is thus currently no active task, and + * - there is a timer event + */ + if (ctx->want_reload) { + ctx->want_reload = false; + return (ISC_R_RELOAD); + } + + readytasks = isc__taskmgr_ready(ctx->taskmgr); + if (readytasks) { + tv.tv_sec = 0; + tv.tv_usec = 0; + tvp = &tv; + call_timer_dispatch = true; + } else { + result = isc__timermgr_nextevent(ctx->timermgr, &when); + if (result != ISC_R_SUCCESS) + tvp = NULL; + else { + uint64_t us; + + TIME_NOW(&now); + us = isc_time_microdiff(&when, &now); + if (us == 0) + call_timer_dispatch = true; + tv.tv_sec = us / 1000000; + tv.tv_usec = us % 1000000; + tvp = &tv; + } + } + + swait = NULL; + n = isc__socketmgr_waitevents(ctx->socketmgr, tvp, &swait); + + if (n == 0 || call_timer_dispatch) { + /* + * We call isc__timermgr_dispatch() only when + * necessary, in order to reduce overhead. If the + * select() call indicates a timeout, we need the + * dispatch. Even if not, if we set the 0-timeout + * for the select() call, we need to check the timer + * events. In the 'readytasks' case, there may be no + * timeout event actually, but there is no other way + * to reduce the overhead. + * Note that we do not have to worry about the case + * where a new timer is inserted during the select() + * call, since this loop only runs in the non-thread + * mode. + */ + isc__timermgr_dispatch(ctx->timermgr); + } + if (n > 0) + (void)isc__socketmgr_dispatch(ctx->socketmgr, swait); + (void)isc__taskmgr_dispatch(ctx->taskmgr); + } + return (ISC_R_SUCCESS); +} + +/* + * This is a gross hack to support waiting for condition + * variables in nonthreaded programs in a limited way; + * see lib/isc/nothreads/include/isc/condition.h. + * We implement isc_condition_wait() by entering the + * event loop recursively until the want_shutdown flag + * is set by isc_condition_signal(). + */ + +/*! + * \brief True if we are currently executing in the recursive + * event loop. + */ +static bool in_recursive_evloop = false; + +/*! + * \brief True if we are exiting the event loop as the result of + * a call to isc_condition_signal() rather than a shutdown + * or reload. + */ +static bool signalled = false; + +isc_result_t +isc__nothread_wait_hack(isc_condition_t *cp, isc_mutex_t *mp) { + isc_result_t result; + + UNUSED(cp); + UNUSED(mp); + + INSIST(!in_recursive_evloop); + in_recursive_evloop = true; + + INSIST(*mp == 1); /* Mutex must be locked on entry. */ + --*mp; + + result = evloop(&isc_g_appctx); + if (result == ISC_R_RELOAD) + isc_g_appctx.want_reload = true; + if (signalled) { + isc_g_appctx.want_shutdown = false; + signalled = false; + } + + ++*mp; + in_recursive_evloop = false; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__nothread_signal_hack(isc_condition_t *cp) { + + UNUSED(cp); + + INSIST(in_recursive_evloop); + + isc_g_appctx.want_shutdown = true; + signalled = true; + return (ISC_R_SUCCESS); +} +#endif /* ISC_PLATFORM_USETHREADS */ + +isc_result_t +isc__app_ctxrun(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + int result; + isc_event_t *event, *next_event; + isc_task_t *task; +#ifdef ISC_PLATFORM_USETHREADS + sigset_t sset; + char strbuf[ISC_STRERRORSIZE]; +#ifdef HAVE_SIGWAIT + int sig; +#endif /* HAVE_SIGWAIT */ +#endif /* ISC_PLATFORM_USETHREADS */ + + REQUIRE(VALID_APPCTX(ctx)); + +#ifdef HAVE_LINUXTHREADS + REQUIRE(main_thread == pthread_self()); +#endif + + LOCK(&ctx->lock); + + if (!ctx->running) { + ctx->running = true; + + /* + * Post any on-run events (in FIFO order). + */ + for (event = ISC_LIST_HEAD(ctx->on_run); + event != NULL; + event = next_event) { + next_event = ISC_LIST_NEXT(event, ev_link); + ISC_LIST_UNLINK(ctx->on_run, event, ev_link); + task = event->ev_sender; + event->ev_sender = NULL; + isc_task_sendanddetach(&task, &event); + } + + } + + UNLOCK(&ctx->lock); + +#ifndef ISC_PLATFORM_USETHREADS + if (isc_bind9 && ctx == &isc_g_appctx) { + result = handle_signal(SIGHUP, reload_action); + if (result != ISC_R_SUCCESS) + return (ISC_R_SUCCESS); + } + + (void) isc__taskmgr_dispatch(ctx->taskmgr); + result = evloop(ctx); + return (result); +#else /* ISC_PLATFORM_USETHREADS */ + /* + * BIND9 internal tools using multiple contexts do not + * rely on signal. + */ + if (isc_bind9 && ctx != &isc_g_appctx) + return (ISC_R_SUCCESS); + + /* + * There is no danger if isc_app_shutdown() is called before we + * wait for signals. Signals are blocked, so any such signal will + * simply be made pending and we will get it when we call + * sigwait(). + */ + while (!ctx->want_shutdown) { +#ifdef HAVE_SIGWAIT + if (isc_bind9) { + /* + * BIND9 internal; single context: + * Wait for SIGHUP, SIGINT, or SIGTERM. + */ + if (sigemptyset(&sset) != 0 || + sigaddset(&sset, SIGHUP) != 0 || + sigaddset(&sset, SIGINT) != 0 || + sigaddset(&sset, SIGTERM) != 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_run() sigsetops: %s", + strbuf); + return (ISC_R_UNEXPECTED); + } + +#ifndef HAVE_UNIXWARE_SIGWAIT + result = sigwait(&sset, &sig); + if (result == 0) { + if (sig == SIGINT || sig == SIGTERM) + ctx->want_shutdown = true; + else if (sig == SIGHUP) + ctx->want_reload = true; + } + +#else /* Using UnixWare sigwait semantics. */ + sig = sigwait(&sset); + if (sig >= 0) { + if (sig == SIGINT || sig == SIGTERM) + ctx->want_shutdown = true; + else if (sig == SIGHUP) + ctx->want_reload = true; + } +#endif /* HAVE_UNIXWARE_SIGWAIT */ + } else { + /* + * External, or BIND9 using multiple contexts: + * wait until woken up. + */ + LOCK(&ctx->readylock); + if (ctx->want_shutdown) { + /* shutdown() won the race. */ + UNLOCK(&ctx->readylock); + break; + } + if (!ctx->want_reload) + WAIT(&ctx->ready, &ctx->readylock); + UNLOCK(&ctx->readylock); + } +#else /* Don't have sigwait(). */ + if (isc_bind9) { + /* + * BIND9 internal; single context: + * Install a signal handler for SIGHUP, then wait for + * all signals. + */ + result = handle_signal(SIGHUP, reload_action); + if (result != ISC_R_SUCCESS) + return (ISC_R_SUCCESS); + + if (sigemptyset(&sset) != 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_run() sigsetops: %s", + strbuf); + return (ISC_R_UNEXPECTED); + } +#ifdef HAVE_GPERFTOOLS_PROFILER + if (sigaddset(&sset, SIGALRM) != 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_run() sigsetops: %s", + strbuf); + return (ISC_R_UNEXPECTED); + } +#endif + (void)sigsuspend(&sset); + } else { + /* + * External, or BIND9 using multiple contexts: + * wait until woken up. + */ + LOCK(&ctx->readylock); + if (ctx->want_shutdown) { + /* shutdown() won the race. */ + UNLOCK(&ctx->readylock); + break; + } + if (!ctx->want_reload) + WAIT(&ctx->ready, &ctx->readylock); + UNLOCK(&ctx->readylock); + } +#endif /* HAVE_SIGWAIT */ + + if (ctx->want_reload) { + ctx->want_reload = false; + return (ISC_R_RELOAD); + } + + if (ctx->want_shutdown && ctx->blocked) + exit(1); + } + + return (ISC_R_SUCCESS); +#endif /* ISC_PLATFORM_USETHREADS */ +} + +isc_result_t +isc__app_run(void) { + return (isc__app_ctxrun((isc_appctx_t *)&isc_g_appctx)); +} + +isc_result_t +isc__app_ctxshutdown(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + bool want_kill = true; +#ifdef ISC_PLATFORM_USETHREADS + char strbuf[ISC_STRERRORSIZE]; +#endif /* ISC_PLATFORM_USETHREADS */ + + REQUIRE(VALID_APPCTX(ctx)); + + LOCK(&ctx->lock); + + REQUIRE(ctx->running); + + if (ctx->shutdown_requested) + want_kill = false; + else + ctx->shutdown_requested = true; + + UNLOCK(&ctx->lock); + + if (want_kill) { + if (isc_bind9 && ctx != &isc_g_appctx) + /* BIND9 internal, but using multiple contexts */ + ctx->want_shutdown = true; + else { +#ifndef ISC_PLATFORM_USETHREADS + ctx->want_shutdown = true; +#else /* ISC_PLATFORM_USETHREADS */ +#ifdef HAVE_LINUXTHREADS + if (isc_bind9) { + /* BIND9 internal, single context */ + int result; + + result = pthread_kill(main_thread, SIGTERM); + if (result != 0) { + isc__strerror(result, + strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_shutdown() " + "pthread_kill: %s", + strbuf); + return (ISC_R_UNEXPECTED); + } + } +#else + if (isc_bind9) { + /* BIND9 internal, single context */ + if (kill(getpid(), SIGTERM) < 0) { + isc__strerror(errno, + strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_shutdown() " + "kill: %s", strbuf); + return (ISC_R_UNEXPECTED); + } + } +#endif /* HAVE_LINUXTHREADS */ + else { + /* External, multiple contexts */ + LOCK(&ctx->readylock); + ctx->want_shutdown = true; + UNLOCK(&ctx->readylock); + SIGNAL(&ctx->ready); + } +#endif /* ISC_PLATFORM_USETHREADS */ + } + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__app_shutdown(void) { + return (isc__app_ctxshutdown((isc_appctx_t *)&isc_g_appctx)); +} + +isc_result_t +isc__app_ctxsuspend(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + bool want_kill = true; +#ifdef ISC_PLATFORM_USETHREADS + char strbuf[ISC_STRERRORSIZE]; +#endif + + REQUIRE(VALID_APPCTX(ctx)); + + LOCK(&ctx->lock); + + REQUIRE(ctx->running); + + /* + * Don't send the reload signal if we're shutting down. + */ + if (ctx->shutdown_requested) + want_kill = false; + + UNLOCK(&ctx->lock); + + if (want_kill) { + if (isc_bind9 && ctx != &isc_g_appctx) + /* BIND9 internal, but using multiple contexts */ + ctx->want_reload = true; + else { +#ifndef ISC_PLATFORM_USETHREADS + ctx->want_reload = true; +#else /* ISC_PLATFORM_USETHREADS */ +#ifdef HAVE_LINUXTHREADS + if (isc_bind9) { + /* BIND9 internal, single context */ + int result; + + result = pthread_kill(main_thread, SIGHUP); + if (result != 0) { + isc__strerror(result, + strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_reload() " + "pthread_kill: %s", + strbuf); + return (ISC_R_UNEXPECTED); + } + } +#else + if (isc_bind9) { + /* BIND9 internal, single context */ + if (kill(getpid(), SIGHUP) < 0) { + isc__strerror(errno, + strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_app_reload() " + "kill: %s", strbuf); + return (ISC_R_UNEXPECTED); + } + } +#endif /* HAVE_LINUXTHREADS */ + else { + /* External, multiple contexts */ + LOCK(&ctx->readylock); + ctx->want_reload = true; + UNLOCK(&ctx->readylock); + SIGNAL(&ctx->ready); + } +#endif /* ISC_PLATFORM_USETHREADS */ + } + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__app_reload(void) { + return (isc__app_ctxsuspend((isc_appctx_t *)&isc_g_appctx)); +} + +void +isc__app_ctxfinish(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + + REQUIRE(VALID_APPCTX(ctx)); + + DESTROYLOCK(&ctx->lock); +} + +void +isc__app_finish(void) { + isc__app_ctxfinish((isc_appctx_t *)&isc_g_appctx); +} + +void +isc__app_block(void) { +#ifdef ISC_PLATFORM_USETHREADS + sigset_t sset; +#endif /* ISC_PLATFORM_USETHREADS */ + REQUIRE(isc_g_appctx.running); + REQUIRE(!isc_g_appctx.blocked); + + isc_g_appctx.blocked = true; +#ifdef ISC_PLATFORM_USETHREADS + blockedthread = pthread_self(); + RUNTIME_CHECK(sigemptyset(&sset) == 0 && + sigaddset(&sset, SIGINT) == 0 && + sigaddset(&sset, SIGTERM) == 0); + RUNTIME_CHECK(pthread_sigmask(SIG_UNBLOCK, &sset, NULL) == 0); +#endif /* ISC_PLATFORM_USETHREADS */ +} + +void +isc__app_unblock(void) { +#ifdef ISC_PLATFORM_USETHREADS + sigset_t sset; +#endif /* ISC_PLATFORM_USETHREADS */ + + REQUIRE(isc_g_appctx.running); + REQUIRE(isc_g_appctx.blocked); + + isc_g_appctx.blocked = false; + +#ifdef ISC_PLATFORM_USETHREADS + REQUIRE(blockedthread == pthread_self()); + + RUNTIME_CHECK(sigemptyset(&sset) == 0 && + sigaddset(&sset, SIGINT) == 0 && + sigaddset(&sset, SIGTERM) == 0); + RUNTIME_CHECK(pthread_sigmask(SIG_BLOCK, &sset, NULL) == 0); +#endif /* ISC_PLATFORM_USETHREADS */ +} + +isc_result_t +isc__appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp) { + isc__appctx_t *ctx; + + REQUIRE(mctx != NULL); + REQUIRE(ctxp != NULL && *ctxp == NULL); + + ctx = isc_mem_get(mctx, sizeof(*ctx)); + if (ctx == NULL) + return (ISC_R_NOMEMORY); + + ctx->common.impmagic = APPCTX_MAGIC; + ctx->common.magic = ISCAPI_APPCTX_MAGIC; + ctx->common.methods = &appmethods.methods; + + ctx->mctx = NULL; + isc_mem_attach(mctx, &ctx->mctx); + + ctx->taskmgr = NULL; + ctx->socketmgr = NULL; + ctx->timermgr = NULL; + + *ctxp = (isc_appctx_t *)ctx; + + return (ISC_R_SUCCESS); +} + +void +isc__appctx_destroy(isc_appctx_t **ctxp) { + isc__appctx_t *ctx; + + REQUIRE(ctxp != NULL); + ctx = (isc__appctx_t *)*ctxp; + REQUIRE(VALID_APPCTX(ctx)); + + isc_mem_putanddetach(&ctx->mctx, ctx, sizeof(*ctx)); + + *ctxp = NULL; +} + +void +isc__appctx_settaskmgr(isc_appctx_t *ctx0, isc_taskmgr_t *taskmgr) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + + REQUIRE(VALID_APPCTX(ctx)); + + ctx->taskmgr = taskmgr; +} + +void +isc__appctx_setsocketmgr(isc_appctx_t *ctx0, isc_socketmgr_t *socketmgr) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + + REQUIRE(VALID_APPCTX(ctx)); + + ctx->socketmgr = socketmgr; +} + +void +isc__appctx_settimermgr(isc_appctx_t *ctx0, isc_timermgr_t *timermgr) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + + REQUIRE(VALID_APPCTX(ctx)); + + ctx->timermgr = timermgr; +} + +isc_result_t +isc__app_register(void) { + return (isc_app_register(isc__appctx_create)); +} + +#include "../app_api.c" diff --git a/lib/isc/unix/dir.c b/lib/isc/unix/dir.c new file mode 100644 index 0000000..dae0e66 --- /dev/null +++ b/lib/isc/unix/dir.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <unistd.h> + +#include <isc/dir.h> +#include <isc/magic.h> +#include <isc/netdb.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/util.h> + +#include "errno2result.h" + +#define ISC_DIR_MAGIC ISC_MAGIC('D', 'I', 'R', '*') +#define VALID_DIR(dir) ISC_MAGIC_VALID(dir, ISC_DIR_MAGIC) + +void +isc_dir_init(isc_dir_t *dir) { + REQUIRE(dir != NULL); + + dir->entry.name[0] = '\0'; + dir->entry.length = 0; + + dir->handle = NULL; + + dir->magic = ISC_DIR_MAGIC; +} + +/*! + * \brief 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 = ISC_R_SUCCESS; + + REQUIRE(VALID_DIR(dir)); + REQUIRE(dirname != NULL); + + /* + * 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++ = '/'; + *p++ = '*'; + *p = '\0'; + + /* + * Open stream. + */ + dir->handle = opendir(dirname); + + if (dir->handle == NULL) { + return (isc__errno2result(errno)); + } + + return (result); +} + +/*! + * \brief 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) { + struct dirent *entry; + + REQUIRE(VALID_DIR(dir) && dir->handle != NULL); + + /* + * Fetch next file in directory. + */ + entry = readdir(dir->handle); + + if (entry == NULL) + return (ISC_R_NOMORE); + + /* + * Make sure that the space for the name is long enough. + */ + if (sizeof(dir->entry.name) <= strlen(entry->d_name)) + return (ISC_R_UNEXPECTED); + + strlcpy(dir->entry.name, entry->d_name, sizeof(dir->entry.name)); + + /* + * Some dirents have d_namlen, but it is not portable. + */ + dir->entry.length = strlen(entry->d_name); + + return (ISC_R_SUCCESS); +} + +/*! + * \brief Close directory stream. + */ +void +isc_dir_close(isc_dir_t *dir) { + REQUIRE(VALID_DIR(dir) && dir->handle != NULL); + + (void)closedir(dir->handle); + dir->handle = NULL; +} + +/*! + * \brief Reposition directory stream at start. + */ +isc_result_t +isc_dir_reset(isc_dir_t *dir) { + REQUIRE(VALID_DIR(dir) && dir->handle != NULL); + + rewinddir(dir->handle); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_dir_chdir(const char *dirname) { + /*! + * \brief 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) { +#ifdef HAVE_CHROOT + void *tmp; +#endif + + REQUIRE(dirname != NULL); + +#ifdef HAVE_CHROOT + /* + * Try to use getservbyname and getprotobyname before chroot. + * If WKS records are used in a zone under chroot, Name Service Switch + * may fail to load library in chroot. + * Do not report errors if it fails, we do not need any result now. + */ + tmp = getprotobyname("udp"); + if (tmp != NULL) + (void) getservbyname("domain", "udp"); + + if (chroot(dirname) < 0 || chdir("/") < 0) + return (isc__errno2result(errno)); + + return (ISC_R_SUCCESS); +#else + return (ISC_R_NOTIMPLEMENTED); +#endif +} + +isc_result_t +isc_dir_createunique(char *templet) { + isc_result_t result; + char *x; + char *p; + int i; + int pid; + + REQUIRE(templet != NULL); + + /*! + * \brief 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, 0700); + if (i == 0 || errno != EEXIST) + break; + + /* + * The BSD algorithm. + */ + p = x; + while (*p != '\0') { + if (isdigit(*p & 0xff)) + *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/unix/entropy.c b/lib/isc/unix/entropy.c new file mode 100644 index 0000000..18eb938 --- /dev/null +++ b/lib/isc/unix/entropy.c @@ -0,0 +1,603 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* \file unix/entropy.c + * \brief + * This is the system dependent part of the ISC entropy API. + */ + +#include <config.h> + +#include <stdbool.h> + +#include <sys/param.h> /* Openserver 5.0.6A and FD_SETSIZE */ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/un.h> + +#ifdef HAVE_NANOSLEEP +#include <time.h> +#endif +#include <unistd.h> + +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/strerror.h> +#include <isc/string.h> + +#ifdef ISC_PLATFORM_NEEDSYSSELECTH +#include <sys/select.h> +#endif + +#include "errno2result.h" + +/*% + * There is only one variable in the entropy data structures that is not + * system independent, but pulling the structure that uses it into this file + * ultimately means pulling several other independent structures here also to + * resolve their interdependencies. Thus only the problem variable's type + * is defined here. + */ +#define FILESOURCE_HANDLE_TYPE int + +typedef struct { + int handle; + enum { + isc_usocketsource_disconnected, + isc_usocketsource_connecting, + isc_usocketsource_connected, + isc_usocketsource_ndesired, + isc_usocketsource_wrote, + isc_usocketsource_reading + } status; + size_t sz_to_recv; +} isc_entropyusocketsource_t; + +#include "../entropy.c" + +static unsigned int +get_from_filesource(isc_entropysource_t *source, uint32_t desired) { + isc_entropy_t *ent = source->ent; + unsigned char buf[128]; + int fd = source->sources.file.handle; + ssize_t n, ndesired; + unsigned int added; + + if (source->bad) + return (0); + + desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0); + + added = 0; + while (desired > 0) { + ndesired = ISC_MIN(desired, sizeof(buf)); + n = read(fd, buf, ndesired); + if (n < 0) { + if (errno == EAGAIN || errno == EINTR) + goto out; + goto err; + } + if (n == 0) + goto err; + + entropypool_adddata(ent, buf, n, n * 8); + added += n * 8; + desired -= n; + } + goto out; + + err: + (void)close(fd); + source->sources.file.handle = -1; + source->bad = true; + + out: + return (added); +} + +static unsigned int +get_from_usocketsource(isc_entropysource_t *source, uint32_t desired) { + isc_entropy_t *ent = source->ent; + unsigned char buf[128]; + int fd = source->sources.usocket.handle; + ssize_t n = 0, ndesired; + unsigned int added; + size_t sz_to_recv = source->sources.usocket.sz_to_recv; + + if (source->bad) + return (0); + + desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0); + + added = 0; + while (desired > 0) { + ndesired = ISC_MIN(desired, sizeof(buf)); + eagain_loop: + + switch ( source->sources.usocket.status ) { + case isc_usocketsource_ndesired: + buf[0] = ndesired; + if ((n = sendto(fd, buf, 1, 0, NULL, 0)) < 0) { + if (errno == EWOULDBLOCK || errno == EINTR || + errno == ECONNRESET) + goto out; + goto err; + } + INSIST(n == 1); + source->sources.usocket.status = + isc_usocketsource_wrote; + goto eagain_loop; + + case isc_usocketsource_connecting: + case isc_usocketsource_connected: + buf[0] = 1; + buf[1] = ndesired; + if ((n = sendto(fd, buf, 2, 0, NULL, 0)) < 0) { + if (errno == EWOULDBLOCK || errno == EINTR || + errno == ECONNRESET) + goto out; + goto err; + } + if (n == 1) { + source->sources.usocket.status = + isc_usocketsource_ndesired; + goto eagain_loop; + } + INSIST(n == 2); + source->sources.usocket.status = + isc_usocketsource_wrote; + /* FALLTHROUGH */ + + case isc_usocketsource_wrote: + if (recvfrom(fd, buf, 1, 0, NULL, NULL) != 1) { + if (errno == EAGAIN) { + /* + * The problem of EAGAIN (try again + * later) is a major issue on HP-UX. + * Solaris actually tries the recvfrom + * call again, while HP-UX just dies. + * This code is an attempt to let the + * entropy pool fill back up (at least + * that's what I think the problem is.) + * We go to eagain_loop because if we + * just "break", then the "desired" + * amount gets borked. + */ +#ifdef HAVE_NANOSLEEP + struct timespec ts; + + ts.tv_sec = 0; + ts.tv_nsec = 1000000; + nanosleep(&ts, NULL); +#else + usleep(1000); +#endif + goto eagain_loop; + } + if (errno == EWOULDBLOCK || errno == EINTR) + goto out; + goto err; + } + source->sources.usocket.status = + isc_usocketsource_reading; + sz_to_recv = buf[0]; + source->sources.usocket.sz_to_recv = sz_to_recv; + if (sz_to_recv > sizeof(buf)) + goto err; + /* FALLTHROUGH */ + + case isc_usocketsource_reading: + if (sz_to_recv != 0U) { + n = recv(fd, buf, sz_to_recv, 0); + if (n < 0) { + if (errno == EWOULDBLOCK || + errno == EINTR) + goto out; + goto err; + } + } else + n = 0; + break; + + default: + goto err; + } + + if ((size_t)n != sz_to_recv) + source->sources.usocket.sz_to_recv -= n; + else + source->sources.usocket.status = + isc_usocketsource_connected; + + if (n == 0) + goto out; + + entropypool_adddata(ent, buf, n, n * 8); + added += n * 8; + desired -= n; + } + goto out; + + err: + close(fd); + source->bad = true; + source->sources.usocket.status = isc_usocketsource_disconnected; + source->sources.usocket.handle = -1; + + out: + return (added); +} + +/* + * Poll each source, trying to get data from it to stuff into the entropy + * pool. + */ +static void +fillpool(isc_entropy_t *ent, unsigned int desired, bool blocking) { + unsigned int added; + unsigned int remaining; + unsigned int needed; + unsigned int nsource; + isc_entropysource_t *source; + + REQUIRE(VALID_ENTROPY(ent)); + + needed = desired; + + /* + * This logic is a little strange, so an explanation is in order. + * + * If needed is 0, it means we are being asked to "fill to whatever + * we think is best." This means that if we have at least a + * partially full pool (say, > 1/4th of the pool) we probably don't + * need to add anything. + * + * Also, we will check to see if the "pseudo" count is too high. + * If it is, try to mix in better data. Too high is currently + * defined as 1/4th of the pool. + * + * Next, if we are asked to add a specific bit of entropy, make + * certain that we will do so. Clamp how much we try to add to + * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy). + * + * Note that if we are in a blocking mode, we will only try to + * get as much data as we need, not as much as we might want + * to build up. + */ + if (needed == 0) { + REQUIRE(!blocking); + + if ((ent->pool.entropy >= RND_POOLBITS / 4) + && (ent->pool.pseudo <= RND_POOLBITS / 4)) + return; + + needed = THRESHOLD_BITS * 4; + } else { + needed = ISC_MAX(needed, THRESHOLD_BITS); + needed = ISC_MIN(needed, RND_POOLBITS); + } + + /* + * In any case, clamp how much we need to how much we can add. + */ + needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy); + + /* + * But wait! If we're not yet initialized, we need at least + * THRESHOLD_BITS + * of randomness. + */ + if (ent->initialized < THRESHOLD_BITS) + needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized); + + /* + * Poll each file source to see if we can read anything useful from + * it. XXXMLG When where are multiple sources, we should keep a + * record of which one we last used so we can start from it (or the + * next one) to avoid letting some sources build up entropy while + * others are always drained. + */ + + added = 0; + remaining = needed; + if (ent->nextsource == NULL) { + ent->nextsource = ISC_LIST_HEAD(ent->sources); + if (ent->nextsource == NULL) + return; + } + source = ent->nextsource; + again_file: + for (nsource = 0; nsource < ent->nsources; nsource++) { + unsigned int got; + + if (remaining == 0) + break; + + got = 0; + + switch ( source->type ) { + case ENTROPY_SOURCETYPE_FILE: + got = get_from_filesource(source, remaining); + break; + + case ENTROPY_SOURCETYPE_USOCKET: + got = get_from_usocketsource(source, remaining); + break; + } + + added += got; + + remaining -= ISC_MIN(remaining, got); + + source = ISC_LIST_NEXT(source, link); + if (source == NULL) + source = ISC_LIST_HEAD(ent->sources); + } + ent->nextsource = source; + + if (blocking && remaining != 0) { + int fds; + + fds = wait_for_sources(ent); + if (fds > 0) + goto again_file; + } + + /* + * Here, if there are bits remaining to be had and we can block, + * check to see if we have a callback source. If so, call them. + */ + source = ISC_LIST_HEAD(ent->sources); + while ((remaining != 0) && (source != NULL)) { + unsigned int got; + + got = 0; + + if (source->type == ENTROPY_SOURCETYPE_CALLBACK) + got = get_from_callback(source, remaining, blocking); + + added += got; + remaining -= ISC_MIN(remaining, got); + + if (added >= needed) + break; + + source = ISC_LIST_NEXT(source, link); + } + + /* + * Mark as initialized if we've added enough data. + */ + if (ent->initialized < THRESHOLD_BITS) + ent->initialized += added; +} + +static int +wait_for_sources(isc_entropy_t *ent) { + isc_entropysource_t *source; + int maxfd, fd; + int cc; + fd_set reads; + fd_set writes; + + maxfd = -1; + FD_ZERO(&reads); + FD_ZERO(&writes); + + source = ISC_LIST_HEAD(ent->sources); + while (source != NULL) { + if (source->type == ENTROPY_SOURCETYPE_FILE) { + fd = source->sources.file.handle; + if (fd >= 0) { + maxfd = ISC_MAX(maxfd, fd); + FD_SET(fd, &reads); + } + } + if (source->type == ENTROPY_SOURCETYPE_USOCKET) { + fd = source->sources.usocket.handle; + if (fd >= 0) { + switch (source->sources.usocket.status) { + case isc_usocketsource_disconnected: + break; + case isc_usocketsource_connecting: + case isc_usocketsource_connected: + case isc_usocketsource_ndesired: + maxfd = ISC_MAX(maxfd, fd); + FD_SET(fd, &writes); + break; + case isc_usocketsource_wrote: + case isc_usocketsource_reading: + maxfd = ISC_MAX(maxfd, fd); + FD_SET(fd, &reads); + break; + } + } + } + source = ISC_LIST_NEXT(source, link); + } + + if (maxfd < 0) + return (-1); + + cc = select(maxfd + 1, &reads, &writes, NULL, NULL); + if (cc < 0) + return (-1); + + return (cc); +} + +static void +destroyfilesource(isc_entropyfilesource_t *source) { + (void)close(source->handle); +} + +static void +destroyusocketsource(isc_entropyusocketsource_t *source) { + close(source->handle); +} + +/* + * Make a fd non-blocking + */ +static isc_result_t +make_nonblock(int fd) { + int ret; + char strbuf[ISC_STRERRORSIZE]; +#ifdef USE_FIONBIO_IOCTL + int on = 1; +#else + int flags; +#endif + +#ifdef USE_FIONBIO_IOCTL + ret = ioctl(fd, FIONBIO, (char *)&on); +#else + flags = fcntl(fd, F_GETFL, 0); + flags |= PORT_NONBLOCK; + ret = fcntl(fd, F_SETFL, flags); +#endif + + if (ret == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, +#ifdef USE_FIONBIO_IOCTL + "ioctl(%d, FIONBIO, &on): %s", fd, +#else + "fcntl(%d, F_SETFL, %d): %s", fd, flags, +#endif + strbuf); + + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) { + int fd; + struct stat _stat; + bool is_usocket = false; + bool is_connected = false; + isc_result_t ret; + isc_entropysource_t *source; + + REQUIRE(VALID_ENTROPY(ent)); + REQUIRE(fname != NULL); + + LOCK(&ent->lock); + + if (stat(fname, &_stat) < 0) { + ret = isc__errno2result(errno); + goto errout; + } + /* + * Solaris 2.5.1 does not have support for sockets (S_IFSOCK), + * but it does return type S_IFIFO (the OS believes that + * the socket is a fifo). This may be an issue if we tell + * the program to look at an actual FIFO as its source of + * entropy. + */ +#if defined(S_ISSOCK) + if (S_ISSOCK(_stat.st_mode)) + is_usocket = true; +#endif +#if defined(S_ISFIFO) && defined(sun) + if (S_ISFIFO(_stat.st_mode)) + is_usocket = true; +#endif + if (is_usocket) + fd = socket(PF_UNIX, SOCK_STREAM, 0); + else + fd = open(fname, O_RDONLY | PORT_NONBLOCK, 0); + + if (fd < 0) { + ret = isc__errno2result(errno); + goto errout; + } + + ret = make_nonblock(fd); + if (ret != ISC_R_SUCCESS) + goto closefd; + + if (is_usocket) { + struct sockaddr_un sname; + + memset(&sname, 0, sizeof(sname)); + sname.sun_family = AF_UNIX; + strlcpy(sname.sun_path, fname, sizeof(sname.sun_path)); +#ifdef ISC_PLATFORM_HAVESALEN +#if !defined(SUN_LEN) +#define SUN_LEN(su) \ + (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) +#endif + sname.sun_len = SUN_LEN(&sname); +#endif + + if (connect(fd, (struct sockaddr *) &sname, + sizeof(struct sockaddr_un)) < 0) { + if (errno != EINPROGRESS) { + ret = isc__errno2result(errno); + goto closefd; + } + } else + is_connected = true; + } + + source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); + if (source == NULL) { + ret = ISC_R_NOMEMORY; + goto closefd; + } + + /* + * From here down, no failures can occur. + */ + source->magic = SOURCE_MAGIC; + source->ent = ent; + source->total = 0; + source->bad = false; + memset(source->name, 0, sizeof(source->name)); + ISC_LINK_INIT(source, link); + if (is_usocket) { + source->sources.usocket.handle = fd; + if (is_connected) + source->sources.usocket.status = + isc_usocketsource_connected; + else + source->sources.usocket.status = + isc_usocketsource_connecting; + source->sources.usocket.sz_to_recv = 0; + source->type = ENTROPY_SOURCETYPE_USOCKET; + } else { + source->sources.file.handle = fd; + source->type = ENTROPY_SOURCETYPE_FILE; + } + + /* + * Hook it into the entropy system. + */ + ISC_LIST_APPEND(ent->sources, source, link); + ent->nsources++; + + UNLOCK(&ent->lock); + return (ISC_R_SUCCESS); + + closefd: + (void)close(fd); + + errout: + UNLOCK(&ent->lock); + + return (ret); +} diff --git a/lib/isc/unix/errno.c b/lib/isc/unix/errno.c new file mode 100644 index 0000000..a2445f3 --- /dev/null +++ b/lib/isc/unix/errno.c @@ -0,0 +1,24 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <isc/errno.h> +#include <isc/util.h> + +#include "errno2result.h" + +isc_result_t +isc_errno_toresult(int err) { + return (isc___errno2result(err, false, 0, 0)); +} diff --git a/lib/isc/unix/errno2result.c b/lib/isc/unix/errno2result.c new file mode 100644 index 0000000..2f12bcc --- /dev/null +++ b/lib/isc/unix/errno2result.c @@ -0,0 +1,125 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/result.h> +#include <isc/strerror.h> +#include <isc/util.h> + +#include "errno2result.h" + +/*% + * 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___errno2result(int posixerrno, bool dolog, + const char *file, unsigned int line) +{ + char strbuf[ISC_STRERRORSIZE]; + + switch (posixerrno) { + case ENOTDIR: + case ELOOP: + case EINVAL: /* XXX sometimes this is not for files */ + case ENAMETOOLONG: + case EBADF: + return (ISC_R_INVALIDFILE); + case ENOENT: + return (ISC_R_FILENOTFOUND); + case EACCES: + case EPERM: + return (ISC_R_NOPERM); + case EEXIST: + return (ISC_R_FILEEXISTS); + case EIO: + return (ISC_R_IOERROR); + case ENOMEM: + return (ISC_R_NOMEMORY); + case ENFILE: + case EMFILE: + return (ISC_R_TOOMANYOPENFILES); +#ifdef EOVERFLOW + case EOVERFLOW: + return (ISC_R_RANGE); +#endif + case EPIPE: +#ifdef ECONNRESET + case ECONNRESET: +#endif +#ifdef ECONNABORTED + case ECONNABORTED: +#endif + return (ISC_R_CONNECTIONRESET); +#ifdef ENOTCONN + case ENOTCONN: + return (ISC_R_NOTCONNECTED); +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: + return (ISC_R_TIMEDOUT); +#endif +#ifdef ENOBUFS + case ENOBUFS: + return (ISC_R_NORESOURCES); +#endif +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + return (ISC_R_FAMILYNOSUPPORT); +#endif +#ifdef ENETDOWN + case ENETDOWN: + return (ISC_R_NETDOWN); +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: + return (ISC_R_HOSTDOWN); +#endif +#ifdef ENETUNREACH + case ENETUNREACH: + return (ISC_R_NETUNREACH); +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: + return (ISC_R_HOSTUNREACH); +#endif +#ifdef EADDRINUSE + case EADDRINUSE: + return (ISC_R_ADDRINUSE); +#endif + case EADDRNOTAVAIL: + return (ISC_R_ADDRNOTAVAIL); + case ECONNREFUSED: + return (ISC_R_CONNREFUSED); + default: + if (dolog) { + isc__strerror(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/unix/errno2result.h b/lib/isc/unix/errno2result.h new file mode 100644 index 0000000..7733ab3 --- /dev/null +++ b/lib/isc/unix/errno2result.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +/* XXXDCL this should be moved to lib/isc/include/isc/errno2result.h. */ + +#include <errno.h> /* Provides errno. */ +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +#define isc__errno2result(x) isc___errno2result(x, true, __FILE__, __LINE__) + +isc_result_t +isc___errno2result(int posixerrno, bool dolog, + const char *file, unsigned int line); + +ISC_LANG_ENDDECLS + +#endif /* UNIX_ERRNO2RESULT_H */ diff --git a/lib/isc/unix/file.c b/lib/isc/unix/file.c new file mode 100644 index 0000000..64d9596 --- /dev/null +++ b/lib/isc/unix/file.c @@ -0,0 +1,781 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Portions Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +/*! \file */ + +#include <config.h> + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <time.h> /* Required for utimes on some platforms. */ +#include <unistd.h> /* Required for mkstemp on NetBSD. */ + + +#include <sys/stat.h> +#include <sys/time.h> + +#ifdef HAVE_SYS_MMAN_H +#include <sys/mman.h> +#endif + +#include <isc/dir.h> +#include <isc/file.h> +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/random.h> +#include <isc/sha2.h> +#include <isc/string.h> +#include <isc/time.h> +#include <isc/util.h> + +#include "errno2result.h" + +/* + * 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_result_t +isc_file_getmodtime(const char *file, isc_time_t *modtime) { + isc_result_t result; + struct stat stats; + + REQUIRE(file != NULL); + REQUIRE(modtime != NULL); + + result = file_stats(file, &stats); + + if (result == ISC_R_SUCCESS) +#ifdef ISC_PLATFORM_HAVESTATNSEC + isc_time_set(modtime, stats.st_mtime, stats.st_mtim.tv_nsec); +#else + isc_time_set(modtime, stats.st_mtime, 0); +#endif + + return (result); +} + +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 *when) { + struct timeval times[2]; + + REQUIRE(file != NULL && when != NULL); + + /* + * tv_sec is at least a 32 bit quantity on all platforms we're + * dealing with, but it is signed on most (all?) of them, + * so we need to make sure the high bit isn't set. This unfortunately + * loses when either: + * * tv_sec becomes a signed 64 bit integer but long is 32 bits + * and isc_time_seconds > LONG_MAX, or + * * isc_time_seconds is changed to be > 32 bits but long is 32 bits + * and isc_time_seconds has at least 33 significant bits. + */ + times[0].tv_sec = times[1].tv_sec = (long)isc_time_seconds(when); + + /* + * Here is the real check for the high bit being set. + */ + if ((times[0].tv_sec & + (1ULL << (sizeof(times[0].tv_sec) * CHAR_BIT - 1))) != 0) + return (ISC_R_RANGE); + + /* + * isc_time_nanoseconds guarantees a value that divided by 1000 will + * fit into the minimum possible size tv_usec field. Unfortunately, + * we don't know what that type is so can't cast directly ... but + * we can at least cast to signed so the IRIX compiler shuts up. + */ + times[0].tv_usec = times[1].tv_usec = + (int32_t)(isc_time_nanoseconds(when) / 1000); + + if (utimes(file, times) < 0) + return (isc__errno2result(errno)); + + return (ISC_R_SUCCESS); +} + +#undef TEMPLATE +#define TEMPLATE "tmp-XXXXXXXXXX" /*%< 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) +{ + const 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); +} + +static const char alphnum[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +isc_result_t +isc_file_renameunique(const char *file, char *templet) { + char *x; + char *cp; + + REQUIRE(file != NULL); + REQUIRE(templet != NULL); + + cp = templet; + while (*cp != '\0') + cp++; + if (cp == templet) + return (ISC_R_FAILURE); + + x = cp--; + while (cp >= templet && *cp == 'X') { + uint32_t which; + + isc_random_get(&which); + *cp = alphnum[which % (sizeof(alphnum) - 1)]; + x = cp--; + } + while (link(file, templet) == -1) { + if (errno != EEXIST) + return (isc__errno2result(errno)); + for (cp = x;;) { + const char *t; + if (*cp == '\0') + return (ISC_R_FAILURE); + t = strchr(alphnum, *cp); + if (t == NULL || *++t == '\0') + *cp++ = alphnum[0]; + else { + *cp = *t; + break; + } + } + } + if (unlink(file) < 0) + if (errno != ENOENT) + return (isc__errno2result(errno)); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_openunique(char *templet, FILE **fp) { + int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; + return (isc_file_openuniquemode(templet, mode, fp)); +} + +isc_result_t +isc_file_openuniqueprivate(char *templet, FILE **fp) { + int mode = S_IWUSR|S_IRUSR; + return (isc_file_openuniquemode(templet, mode, fp)); +} + +isc_result_t +isc_file_openuniquemode(char *templet, int mode, FILE **fp) { + int fd; + FILE *f; + isc_result_t result = ISC_R_SUCCESS; + char *x; + char *cp; + + REQUIRE(templet != NULL); + REQUIRE(fp != NULL && *fp == NULL); + + cp = templet; + while (*cp != '\0') + cp++; + if (cp == templet) + return (ISC_R_FAILURE); + + x = cp--; + while (cp >= templet && *cp == 'X') { + uint32_t which; + + isc_random_get(&which); + *cp = alphnum[which % (sizeof(alphnum) - 1)]; + x = cp--; + } + + + while ((fd = open(templet, O_RDWR|O_CREAT|O_EXCL, mode)) == -1) { + if (errno != EEXIST) + return (isc__errno2result(errno)); + for (cp = x;;) { + char *t; + if (*cp == '\0') + return (ISC_R_FAILURE); + t = strchr(alphnum, *cp); + if (t == NULL || *++t == '\0') + *cp++ = alphnum[0]; + else { + *cp = *t; + break; + } + } + } + f = fdopen(fd, "w+"); + if (f == NULL) { + result = isc__errno2result(errno); + if (remove(templet) < 0) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_FILE, ISC_LOG_ERROR, + "remove '%s': failed", templet); + } + (void)close(fd); + } else + *fp = f; + + return (result); +} + +isc_result_t +isc_file_bopenunique(char *templet, FILE **fp) { + int mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; + return (isc_file_openuniquemode(templet, mode, fp)); +} + +isc_result_t +isc_file_bopenuniqueprivate(char *templet, FILE **fp) { + int mode = S_IWUSR|S_IRUSR; + return (isc_file_openuniquemode(templet, mode, fp)); +} + +isc_result_t +isc_file_bopenuniquemode(char *templet, int mode, FILE **fp) { + return (isc_file_openuniquemode(templet, mode, 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 = rename(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 exists and 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); + return (filename[0] == '/'); +} + +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 (isc_file_iscurrentdir(filename)) + return (true); + return (false); +} + +const char * +isc_file_basename(const char *filename) { + const 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 *buf, size_t buflen) { + const char *base; + size_t len; + + REQUIRE(filename != NULL); + REQUIRE(buf != NULL); + + base = isc_file_basename(filename); + len = strlen(base) + 1; + + if (len > buflen) + return (ISC_R_NOSPACE); + memmove(buf, base, len); + + return (ISC_R_SUCCESS); +} + +/* + * Put the absolute name of the current directory into 'dirname', which is + * a buffer of at least 'length' characters. End the string with the + * appropriate path separator, such that the final product could be + * concatenated with a relative pathname to make a valid pathname string. + */ +static isc_result_t +dir_current(char *dirname, size_t length) { + char *cwd; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(dirname != NULL); + REQUIRE(length > 0U); + + cwd = getcwd(dirname, length); + + if (cwd == NULL) { + if (errno == ERANGE) { + result = ISC_R_NOSPACE; + } else { + result = isc__errno2result(errno); + } + } else { + if (strlen(dirname) + 1 == length) { + result = ISC_R_NOSPACE; + } else if (dirname[1] != '\0') { + strlcat(dirname, "/", length); + } + } + + return (result); +} + +isc_result_t +isc_file_absolutepath(const char *filename, char *path, size_t pathlen) { + isc_result_t result; + result = dir_current(path, pathlen); + if (result != ISC_R_SUCCESS) + return (result); + if (strlen(path) + strlen(filename) + 1 > pathlen) + return (ISC_R_NOSPACE); + strlcat(path, filename, pathlen); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_file_truncate(const char *filename, isc_offset_t size) { + isc_result_t result = ISC_R_SUCCESS; + + if (truncate(filename, size) < 0) + result = isc__errno2result(errno); + return (result); +} + +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 **bname) +{ + char *dir; + const char *file, *slash; + + if (path == NULL) + return (ISC_R_INVALIDFILE); + + slash = strrchr(path, '/'); + + if (slash == path) { + file = ++slash; + dir = isc_mem_strdup(mctx, "/"); + } else if (slash != NULL) { + file = ++slash; + dir = isc_mem_allocate(mctx, slash - path); + if (dir != NULL) + 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; + *bname = file; + + return (ISC_R_SUCCESS); +} + +void * +isc_file_mmap(void *addr, size_t len, int prot, + int flags, int fd, off_t offset) +{ +#ifdef HAVE_MMAP + return (mmap(addr, len, prot, flags, fd, offset)); +#else + 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, len); + if (ret != (ssize_t) len) { + free(buf); + buf = NULL; + } + + return (buf); +#endif +} + +int +isc_file_munmap(void *addr, size_t len) { +#ifdef HAVE_MMAP + return (munmap(addr, len)); +#else + UNUSED(len); + + free(addr); + return (0); +#endif +} + +#define DISALLOW "\\/ABCDEFGHIJKLMNOPQRSTUVWXYZ" +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +isc_result_t +isc_file_sanitize(const char *dir, const char *base, const char *ext, + char *path, size_t length) +{ + char buf[PATH_MAX], hash[ISC_SHA256_DIGESTSTRINGLENGTH]; + size_t l = 0; + + REQUIRE(base != NULL); + REQUIRE(path != NULL); + + l = strlen(base) + 1; + + /* + * allow room for a full sha256 hash (64 chars + * plus null terminator) + */ + if (l < 65U) + l = 65; + + if (dir != NULL) + l += strlen(dir) + 1; + if (ext != NULL) + l += strlen(ext) + 1; + + if (l > length || l > (unsigned)PATH_MAX) + return (ISC_R_NOSPACE); + + /* Check whether the full-length SHA256 hash filename exists */ + isc_sha256_data((const void *) base, strlen(base), hash); + 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); +} + +bool +isc_file_isdirwritable(const char *path) { + return (access(path, W_OK|X_OK) == 0); +} diff --git a/lib/isc/unix/fsaccess.c b/lib/isc/unix/fsaccess.c new file mode 100644 index 0000000..e3cd0bf --- /dev/null +++ b/lib/isc/unix/fsaccess.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdbool.h> +#include <errno.h> + +#include "errno2result.h" + +/*! \file + * \brief + * The OS-independent part of the API is in lib/isc. + */ +#include "../fsaccess.c" + +isc_result_t +isc_fsaccess_set(const char *path, isc_fsaccess_t access) { + struct stat statb; + mode_t mode; + bool is_dir = false; + isc_fsaccess_t bits; + 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); + + /* + * 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); + + bits = ISC_FSACCESS_EXECUTE | + ISC_FSACCESS_ACCESSCHILD; + + SET_AND_CLEAR(S_IXUSR, S_IXGRP, S_IXOTH); + + INSIST(access == 0); + + if (chmod(path, mode) < 0) + return (isc__errno2result(errno)); + + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/unix/ifiter_getifaddrs.c b/lib/isc/unix/ifiter_getifaddrs.c new file mode 100644 index 0000000..de6f060 --- /dev/null +++ b/lib/isc/unix/ifiter_getifaddrs.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file + * \brief + * Obtain the list of network interfaces using the getifaddrs(3) library. + */ + +#include <stdbool.h> +#include <ifaddrs.h> + +/*% Iterator Magic */ +#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'G') +/*% Valid Iterator */ +#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) + +#ifdef __linux +static bool seenv6 = false; +#endif + +/*% Iterator structure */ +struct isc_interfaceiter { + unsigned int magic; /*%< Magic number. */ + isc_mem_t *mctx; + void *buf; /*%< (unused) */ + unsigned int bufsize; /*%< (always 0) */ + struct ifaddrs *ifaddrs; /*%< List of ifaddrs */ + struct ifaddrs *pos; /*%< Ptr to current ifaddr */ + isc_interface_t current; /*%< Current interface data. */ + isc_result_t result; /*%< Last result code. */ +#ifdef __linux + FILE * proc; + char entry[ISC_IF_INET6_SZ]; + isc_result_t valid; +#endif +}; + +isc_result_t +isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { + isc_interfaceiter_t *iter; + isc_result_t result; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(mctx != NULL); + REQUIRE(iterp != NULL); + REQUIRE(*iterp == NULL); + + iter = isc_mem_get(mctx, sizeof(*iter)); + if (iter == NULL) + return (ISC_R_NOMEMORY); + + iter->mctx = mctx; + iter->buf = NULL; + iter->bufsize = 0; + iter->ifaddrs = NULL; +#ifdef __linux + /* + * Only open "/proc/net/if_inet6" if we have never seen a IPv6 + * address returned by getifaddrs(). + */ + if (!seenv6) + iter->proc = fopen("/proc/net/if_inet6", "r"); + else + iter->proc = NULL; + iter->valid = ISC_R_FAILURE; +#endif + + if (getifaddrs(&iter->ifaddrs) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERGETIFADDRS, + ISC_MSG_GETIFADDRS, + "getting interface " + "addresses: getifaddrs: %s"), + strbuf); + result = ISC_R_UNEXPECTED; + goto failure; + } + + /* + * A newly created iterator has an undefined position + * until isc_interfaceiter_first() is called. + */ + iter->pos = NULL; + iter->result = ISC_R_FAILURE; + + iter->magic = IFITER_MAGIC; + *iterp = iter; + return (ISC_R_SUCCESS); + + failure: +#ifdef __linux + if (iter->proc != NULL) + fclose(iter->proc); +#endif + if (iter->ifaddrs != NULL) /* just in case */ + freeifaddrs(iter->ifaddrs); + 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, + * return ISC_R_IGNORE. + */ + +static isc_result_t +internal_current(isc_interfaceiter_t *iter) { + struct ifaddrs *ifa; + int family; + unsigned int namelen; + + REQUIRE(VALID_IFITER(iter)); + + ifa = iter->pos; + +#ifdef __linux + if (iter->pos == NULL) + return (linux_if_inet6_current(iter)); +#endif + + INSIST(ifa != NULL); + INSIST(ifa->ifa_name != NULL); + + if (ifa->ifa_addr == NULL) + return (ISC_R_IGNORE); + + family = ifa->ifa_addr->sa_family; + if (family != AF_INET && family != AF_INET6) + return (ISC_R_IGNORE); + +#ifdef __linux + if (family == AF_INET6) + seenv6 = true; +#endif + + memset(&iter->current, 0, sizeof(iter->current)); + + namelen = strlen(ifa->ifa_name); + if (namelen > sizeof(iter->current.name) - 1) + namelen = sizeof(iter->current.name) - 1; + + memset(iter->current.name, 0, sizeof(iter->current.name)); + memmove(iter->current.name, ifa->ifa_name, namelen); + + iter->current.flags = 0; + + if ((ifa->ifa_flags & IFF_UP) != 0) + iter->current.flags |= INTERFACE_F_UP; + + if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0) + iter->current.flags |= INTERFACE_F_POINTTOPOINT; + + if ((ifa->ifa_flags & IFF_LOOPBACK) != 0) + iter->current.flags |= INTERFACE_F_LOOPBACK; + + iter->current.af = family; + + get_addr(family, &iter->current.address, ifa->ifa_addr, ifa->ifa_name); + + if (ifa->ifa_netmask != NULL) + get_addr(family, &iter->current.netmask, ifa->ifa_netmask, + ifa->ifa_name); + + if (ifa->ifa_dstaddr != NULL && + (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) + get_addr(family, &iter->current.dstaddress, ifa->ifa_dstaddr, + ifa->ifa_name); + + 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->pos != NULL) + iter->pos = iter->pos->ifa_next; + if (iter->pos == NULL) { +#ifdef __linux + if (!seenv6) + return (linux_if_inet6_next(iter)); +#endif + return (ISC_R_NOMORE); + } + + return (ISC_R_SUCCESS); +} + +static void +internal_destroy(isc_interfaceiter_t *iter) { + +#ifdef __linux + if (iter->proc != NULL) + fclose(iter->proc); + iter->proc = NULL; +#endif + if (iter->ifaddrs) + freeifaddrs(iter->ifaddrs); + iter->ifaddrs = NULL; +} + +static +void internal_first(isc_interfaceiter_t *iter) { + +#ifdef __linux + linux_if_inet6_first(iter); +#endif + iter->pos = iter->ifaddrs; +} diff --git a/lib/isc/unix/ifiter_ioctl.c b/lib/isc/unix/ifiter_ioctl.c new file mode 100644 index 0000000..9702fae --- /dev/null +++ b/lib/isc/unix/ifiter_ioctl.c @@ -0,0 +1,931 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <stdbool.h> + +#include <isc/print.h> + +/*! \file + * \brief + * Obtain the list of network interfaces using the SIOCGLIFCONF ioctl. + * See netintro(4). + */ + +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) +#ifdef ISC_PLATFORM_HAVEIF_LADDRCONF +#define lifc_len iflc_len +#define lifc_buf iflc_buf +#define lifc_req iflc_req +#define LIFCONF if_laddrconf +#else +#define ISC_HAVE_LIFC_FAMILY 1 +#define ISC_HAVE_LIFC_FLAGS 1 +#define LIFCONF lifconf +#endif + +#ifdef ISC_PLATFORM_HAVEIF_LADDRREQ +#define lifr_addr iflr_addr +#define lifr_name iflr_name +#define lifr_dstaddr iflr_dstaddr +#define lifr_flags iflr_flags +#define ss_family sa_family +#define LIFREQ if_laddrreq +#else +#define LIFREQ lifreq +#endif +#endif + +#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'T') +#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) + +struct isc_interfaceiter { + unsigned int magic; /* Magic number. */ + isc_mem_t *mctx; + int mode; + int socket; + struct ifconf ifc; + void *buf; /* Buffer for sysctl data. */ + unsigned int bufsize; /* Bytes allocated. */ + unsigned int pos; /* Current offset in + SIOCGIFCONF data */ +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) + int socket6; + struct LIFCONF lifc; + void *buf6; /* Buffer for sysctl data. */ + unsigned int bufsize6; /* Bytes allocated. */ + unsigned int pos6; /* Current offset in + SIOCGLIFCONF data */ + isc_result_t result6; /* Last result code. */ + bool first6; +#endif +#ifdef HAVE_TRUCLUSTER + int clua_context; /* Cluster alias context */ + bool clua_done; + struct sockaddr clua_sa; +#endif +#ifdef __linux + FILE * proc; + char entry[ISC_IF_INET6_SZ]; + isc_result_t valid; +#endif + isc_interface_t current; /* Current interface data. */ + isc_result_t result; /* Last result code. */ +}; + +#ifdef HAVE_TRUCLUSTER +#include <clua/clua.h> +#include <sys/socket.h> +#endif + + +/*% + * Size of buffer for SIOCGLIFCONF, in bytes. We assume no sane system + * will have more than a megabyte of interface configuration data. + */ +#define IFCONF_BUFSIZE_INITIAL 4096 +#define IFCONF_BUFSIZE_MAX 1048576 + +#ifdef __linux +#ifndef IF_NAMESIZE +# ifdef IFNAMSIZ +# define IF_NAMESIZE IFNAMSIZ +# else +# define IF_NAMESIZE 16 +# endif +#endif +#endif + +static isc_result_t +getbuf4(isc_interfaceiter_t *iter) { + char strbuf[ISC_STRERRORSIZE]; + + iter->bufsize = IFCONF_BUFSIZE_INITIAL; + + for (;;) { + iter->buf = isc_mem_get(iter->mctx, iter->bufsize); + if (iter->buf == NULL) + return (ISC_R_NOMEMORY); + + memset(&iter->ifc.ifc_len, 0, sizeof(iter->ifc.ifc_len)); + iter->ifc.ifc_len = iter->bufsize; + iter->ifc.ifc_buf = iter->buf; + /* + * Ignore the HP/UX warning about "integer overflow during + * conversion". It comes from its own macro definition, + * and is really hard to shut up. + */ + if (ioctl(iter->socket, SIOCGIFCONF, (char *)&iter->ifc) + == -1) { + if (errno != EINVAL) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_GETIFCONFIG, + "get interface " + "configuration: %s"), + strbuf); + goto unexpected; + } + /* + * EINVAL. Retry with a bigger buffer. + */ + } else { + /* + * The ioctl succeeded. + * Some OS's just return what will fit rather + * than set EINVAL if the buffer is too small + * to fit all the interfaces in. If + * ifc.lifc_len is too near to the end of the + * buffer we will grow it just in case and + * retry. + */ + if (iter->ifc.ifc_len + 2 * sizeof(struct ifreq) + < iter->bufsize) + break; + } + if (iter->bufsize >= IFCONF_BUFSIZE_MAX) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_BUFFERMAX, + "get interface " + "configuration: " + "maximum buffer " + "size exceeded")); + goto unexpected; + } + isc_mem_put(iter->mctx, iter->buf, iter->bufsize); + + iter->bufsize *= 2; + } + return (ISC_R_SUCCESS); + + unexpected: + isc_mem_put(iter->mctx, iter->buf, iter->bufsize); + iter->buf = NULL; + return (ISC_R_UNEXPECTED); +} + +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) +static isc_result_t +getbuf6(isc_interfaceiter_t *iter) { + char strbuf[ISC_STRERRORSIZE]; + isc_result_t result; + + iter->bufsize6 = IFCONF_BUFSIZE_INITIAL; + + for (;;) { + iter->buf6 = isc_mem_get(iter->mctx, iter->bufsize6); + if (iter->buf6 == NULL) + return (ISC_R_NOMEMORY); + + memset(&iter->lifc, 0, sizeof(iter->lifc)); +#ifdef ISC_HAVE_LIFC_FAMILY + iter->lifc.lifc_family = AF_INET6; +#endif +#ifdef ISC_HAVE_LIFC_FLAGS + iter->lifc.lifc_flags = 0; +#endif + iter->lifc.lifc_len = iter->bufsize6; + iter->lifc.lifc_buf = iter->buf6; + /* + * Ignore the HP/UX warning about "integer overflow during + * conversion". It comes from its own macro definition, + * and is really hard to shut up. + */ + if (ioctl(iter->socket6, SIOCGLIFCONF, (char *)&iter->lifc) + == -1) { +#ifdef __hpux + /* + * IPv6 interface scanning is not available on all + * kernels w/ IPv6 sockets. + */ + if (errno == ENOENT) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_INTERFACE, + ISC_LOG_DEBUG(1), + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_GETIFCONFIG, + "get interface " + "configuration: %s"), + strbuf); + result = ISC_R_FAILURE; + goto cleanup; + } +#endif + if (errno != EINVAL) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_GETIFCONFIG, + "get interface " + "configuration: %s"), + strbuf); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + /* + * EINVAL. Retry with a bigger buffer. + */ + } else { + /* + * The ioctl succeeded. + * Some OS's just return what will fit rather + * than set EINVAL if the buffer is too small + * to fit all the interfaces in. If + * ifc.ifc_len is too near to the end of the + * buffer we will grow it just in case and + * retry. + */ + if (iter->lifc.lifc_len + 2 * sizeof(struct LIFREQ) + < iter->bufsize6) + break; + } + if (iter->bufsize6 >= IFCONF_BUFSIZE_MAX) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_BUFFERMAX, + "get interface " + "configuration: " + "maximum buffer " + "size exceeded")); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + isc_mem_put(iter->mctx, iter->buf6, iter->bufsize6); + + iter->bufsize6 *= 2; + } + + if (iter->lifc.lifc_len != 0) + iter->mode = 6; + return (ISC_R_SUCCESS); + + cleanup: + isc_mem_put(iter->mctx, iter->buf6, iter->bufsize6); + iter->buf6 = NULL; + return (result); +} +#endif + +isc_result_t +isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { + isc_interfaceiter_t *iter; + isc_result_t result; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(mctx != NULL); + REQUIRE(iterp != NULL); + REQUIRE(*iterp == NULL); + + iter = isc_mem_get(mctx, sizeof(*iter)); + if (iter == NULL) + return (ISC_R_NOMEMORY); + + iter->mctx = mctx; + iter->mode = 4; + iter->buf = NULL; + iter->pos = (unsigned int) -1; +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) + iter->buf6 = NULL; + iter->pos6 = (unsigned int) -1; + iter->result6 = ISC_R_NOMORE; + iter->socket6 = -1; + iter->first6 = false; +#endif + + /* + * Get the interface configuration, allocating more memory if + * necessary. + */ + +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) + result = isc_net_probeipv6(); + if (result == ISC_R_SUCCESS) { + /* + * Create an unbound datagram socket to do the SIOCGLIFCONF + * ioctl on. HP/UX requires an AF_INET6 socket for + * SIOCGLIFCONF to get IPv6 addresses. + */ + if ((iter->socket6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_MAKESCANSOCKET, + "making interface " + "scan socket: %s"), + strbuf); + result = ISC_R_UNEXPECTED; + goto socket6_failure; + } + result = iter->result6 = getbuf6(iter); + if (result != ISC_R_NOTIMPLEMENTED && result != ISC_R_SUCCESS) + goto ioctl6_failure; + } +#endif + if ((iter->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_MAKESCANSOCKET, + "making interface " + "scan socket: %s"), + strbuf); + result = ISC_R_UNEXPECTED; + goto socket_failure; + } + result = getbuf4(iter); + if (result != ISC_R_SUCCESS) + goto ioctl_failure; + + /* + * A newly created iterator has an undefined position + * until isc_interfaceiter_first() is called. + */ +#ifdef HAVE_TRUCLUSTER + iter->clua_context = -1; + iter->clua_done = true; +#endif +#ifdef __linux + iter->proc = fopen("/proc/net/if_inet6", "r"); + iter->valid = ISC_R_FAILURE; +#endif + iter->result = ISC_R_FAILURE; + + iter->magic = IFITER_MAGIC; + *iterp = iter; + return (ISC_R_SUCCESS); + + ioctl_failure: + if (iter->buf != NULL) + isc_mem_put(mctx, iter->buf, iter->bufsize); + (void) close(iter->socket); + + socket_failure: +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) + if (iter->buf6 != NULL) + isc_mem_put(mctx, iter->buf6, iter->bufsize6); + ioctl6_failure: + if (iter->socket6 != -1) + (void) close(iter->socket6); + socket6_failure: +#endif + + isc_mem_put(mctx, iter, sizeof(*iter)); + return (result); +} + +#ifdef HAVE_TRUCLUSTER +static void +get_inaddr(isc_netaddr_t *dst, struct in_addr *src) { + dst->family = AF_INET; + memmove(&dst->type.in, src, sizeof(struct in_addr)); +} + +static isc_result_t +internal_current_clusteralias(isc_interfaceiter_t *iter) { + struct clua_info ci; + if (clua_getaliasinfo(&iter->clua_sa, &ci) != CLUA_SUCCESS) + return (ISC_R_IGNORE); + memset(&iter->current, 0, sizeof(iter->current)); + iter->current.af = iter->clua_sa.sa_family; + memset(iter->current.name, 0, sizeof(iter->current.name)); + snprintf(iter->current.name, sizeof(iter->current.name), + "clua%d", ci.aliasid); + iter->current.flags = INTERFACE_F_UP; + get_inaddr(&iter->current.address, &ci.addr); + get_inaddr(&iter->current.netmask, &ci.netmask); + return (ISC_R_SUCCESS); +} +#endif + +/* + * 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_current4(isc_interfaceiter_t *iter) { + struct ifreq *ifrp; + struct ifreq ifreq; + int family; + char strbuf[ISC_STRERRORSIZE]; +#if !defined(ISC_PLATFORM_HAVEIF_LADDRREQ) && defined(SIOCGLIFADDR) + struct lifreq lifreq; +#else + char sabuf[256]; +#endif + int i, bits, prefixlen; + + REQUIRE(VALID_IFITER(iter)); + + if (iter->ifc.ifc_len == 0 || + iter->pos == (unsigned int)iter->ifc.ifc_len) { +#ifdef __linux + return (linux_if_inet6_current(iter)); +#else + return (ISC_R_NOMORE); +#endif + } + + INSIST( iter->pos < (unsigned int) iter->ifc.ifc_len); + + ifrp = (struct ifreq *)((char *) iter->ifc.ifc_req + iter->pos); + + memset(&ifreq, 0, sizeof(ifreq)); + memmove(&ifreq, ifrp, sizeof(ifreq)); + + family = ifreq.ifr_addr.sa_family; +#if defined(ISC_PLATFORM_HAVEIPV6) + if (family != AF_INET && family != AF_INET6) +#else + if (family != AF_INET) +#endif + return (ISC_R_IGNORE); + + memset(&iter->current, 0, sizeof(iter->current)); + iter->current.af = family; + + INSIST(sizeof(ifreq.ifr_name) <= sizeof(iter->current.name)); + memset(iter->current.name, 0, sizeof(iter->current.name)); + memmove(iter->current.name, ifreq.ifr_name, sizeof(ifreq.ifr_name)); + + get_addr(family, &iter->current.address, + (struct sockaddr *)&ifrp->ifr_addr, ifreq.ifr_name); + + /* + * If the interface does not have a address ignore it. + */ + switch (family) { + case AF_INET: + if (iter->current.address.type.in.s_addr == htonl(INADDR_ANY)) + return (ISC_R_IGNORE); + break; + case AF_INET6: + if (memcmp(&iter->current.address.type.in6, &in6addr_any, + sizeof(in6addr_any)) == 0) + return (ISC_R_IGNORE); + break; + } + + /* + * Get interface flags. + */ + + iter->current.flags = 0; + + /* + * Ignore the HP/UX warning about "integer overflow during + * conversion. It comes from its own macro definition, + * and is really hard to shut up. + */ + if (ioctl(iter->socket, SIOCGIFFLAGS, (char *) &ifreq) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "%s: getting interface flags: %s", + ifreq.ifr_name, strbuf); + return (ISC_R_IGNORE); + } + + if ((ifreq.ifr_flags & IFF_UP) != 0) + iter->current.flags |= INTERFACE_F_UP; + +#ifdef IFF_POINTOPOINT + if ((ifreq.ifr_flags & IFF_POINTOPOINT) != 0) + iter->current.flags |= INTERFACE_F_POINTTOPOINT; +#endif + + if ((ifreq.ifr_flags & IFF_LOOPBACK) != 0) + iter->current.flags |= INTERFACE_F_LOOPBACK; + + if (family == AF_INET) + goto inet; + +#if !defined(ISC_PLATFORM_HAVEIF_LADDRREQ) && defined(SIOCGLIFADDR) + memset(&lifreq, 0, sizeof(lifreq)); + memmove(lifreq.lifr_name, iter->current.name, sizeof(lifreq.lifr_name)); + memmove(&lifreq.lifr_addr, &iter->current.address.type.in6, + sizeof(iter->current.address.type.in6)); + + if (ioctl(iter->socket, SIOCGLIFADDR, &lifreq) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "%s: getting interface address: %s", + ifreq.ifr_name, strbuf); + return (ISC_R_IGNORE); + } + prefixlen = lifreq.lifr_addrlen; +#else + isc_netaddr_format(&iter->current.address, sabuf, sizeof(sabuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_INTERFACE, + ISC_LOG_INFO, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_GETIFCONFIG, + "prefix length for %s is unknown " + "(assume 128)"), sabuf); + prefixlen = 128; +#endif + + /* + * Netmask already zeroed. + */ + iter->current.netmask.family = family; + for (i = 0; i < 16; i++) { + if (prefixlen > 8) { + bits = 0; + prefixlen -= 8; + } else { + bits = 8 - prefixlen; + prefixlen = 0; + } + iter->current.netmask.type.in6.s6_addr[i] = + (~0U << bits) & 0xff; + } + return (ISC_R_SUCCESS); + + inet: + if (family != AF_INET) + return (ISC_R_IGNORE); +#ifdef IFF_POINTOPOINT + /* + * If the interface is point-to-point, get the destination address. + */ + if ((iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) { + /* + * Ignore the HP/UX warning about "integer overflow during + * conversion. It comes from its own macro definition, + * and is really hard to shut up. + */ + if (ioctl(iter->socket, SIOCGIFDSTADDR, (char *)&ifreq) + < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_GETDESTADDR, + "%s: getting " + "destination address: %s"), + ifreq.ifr_name, strbuf); + return (ISC_R_IGNORE); + } + get_addr(family, &iter->current.dstaddress, + (struct sockaddr *)&ifreq.ifr_dstaddr, ifreq.ifr_name); + } +#endif + + /* + * Get the network mask. + */ + memset(&ifreq, 0, sizeof(ifreq)); + memmove(&ifreq, ifrp, sizeof(ifreq)); + /* + * Ignore the HP/UX warning about "integer overflow during + * conversion. It comes from its own macro definition, + * and is really hard to shut up. + */ + if (ioctl(iter->socket, SIOCGIFNETMASK, (char *)&ifreq) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_GETNETMASK, + "%s: getting netmask: %s"), + ifreq.ifr_name, strbuf); + return (ISC_R_IGNORE); + } + get_addr(family, &iter->current.netmask, + (struct sockaddr *)&ifreq.ifr_addr, ifreq.ifr_name); + return (ISC_R_SUCCESS); +} + +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) +static isc_result_t +internal_current6(isc_interfaceiter_t *iter) { + struct LIFREQ *ifrp; + struct LIFREQ lifreq; + int family; + char strbuf[ISC_STRERRORSIZE]; + int fd; + + REQUIRE(VALID_IFITER(iter)); + if (iter->result6 != ISC_R_SUCCESS) + return (iter->result6); + REQUIRE(iter->pos6 < (unsigned int) iter->lifc.lifc_len); + + ifrp = (struct LIFREQ *)((char *) iter->lifc.lifc_req + iter->pos6); + + memset(&lifreq, 0, sizeof(lifreq)); + memmove(&lifreq, ifrp, sizeof(lifreq)); + + family = lifreq.lifr_addr.ss_family; +#ifdef ISC_PLATFORM_HAVEIPV6 + if (family != AF_INET && family != AF_INET6) +#else + if (family != AF_INET) +#endif + return (ISC_R_IGNORE); + + memset(&iter->current, 0, sizeof(iter->current)); + iter->current.af = family; + + INSIST(sizeof(lifreq.lifr_name) <= sizeof(iter->current.name)); + memset(iter->current.name, 0, sizeof(iter->current.name)); + memmove(iter->current.name, lifreq.lifr_name, sizeof(lifreq.lifr_name)); + + get_addr(family, &iter->current.address, + (struct sockaddr *)&lifreq.lifr_addr, lifreq.lifr_name); + + /* + * If the interface does not have a address ignore it. + */ + switch (family) { + case AF_INET: + if (iter->current.address.type.in.s_addr == htonl(INADDR_ANY)) + return (ISC_R_IGNORE); + break; + case AF_INET6: + if (memcmp(&iter->current.address.type.in6, &in6addr_any, + sizeof(in6addr_any)) == 0) + return (ISC_R_IGNORE); + break; + } + + /* + * Get interface flags. + */ + + iter->current.flags = 0; + + if (family == AF_INET6) + fd = iter->socket6; + else + fd = iter->socket; + + /* + * Ignore the HP/UX warning about "integer overflow during + * conversion. It comes from its own macro definition, + * and is really hard to shut up. + */ + if (ioctl(fd, SIOCGLIFFLAGS, (char *) &lifreq) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "%s: getting interface flags: %s", + lifreq.lifr_name, strbuf); + return (ISC_R_IGNORE); + } + + if ((lifreq.lifr_flags & IFF_UP) != 0) + iter->current.flags |= INTERFACE_F_UP; + +#ifdef IFF_POINTOPOINT + if ((lifreq.lifr_flags & IFF_POINTOPOINT) != 0) + iter->current.flags |= INTERFACE_F_POINTTOPOINT; +#endif + + if ((lifreq.lifr_flags & IFF_LOOPBACK) != 0) + iter->current.flags |= INTERFACE_F_LOOPBACK; + +#ifdef IFF_POINTOPOINT + /* + * If the interface is point-to-point, get the destination address. + */ + if ((iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) { + /* + * Ignore the HP/UX warning about "integer overflow during + * conversion. It comes from its own macro definition, + * and is really hard to shut up. + */ + if (ioctl(fd, SIOCGLIFDSTADDR, (char *)&lifreq) + < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_GETDESTADDR, + "%s: getting " + "destination address: %s"), + lifreq.lifr_name, strbuf); + return (ISC_R_IGNORE); + } + get_addr(family, &iter->current.dstaddress, + (struct sockaddr *)&lifreq.lifr_dstaddr, + lifreq.lifr_name); + } +#endif + + /* + * Get the network mask. Netmask already zeroed. + */ + memset(&lifreq, 0, sizeof(lifreq)); + memmove(&lifreq, ifrp, sizeof(lifreq)); + +#ifdef lifr_addrlen + /* + * Special case: if the system provides lifr_addrlen member, the + * netmask of an IPv6 address can be derived from the length, since + * an IPv6 address always has a contiguous mask. + */ + if (family == AF_INET6) { + int i, bits; + + iter->current.netmask.family = family; + for (i = 0; i < lifreq.lifr_addrlen; i += 8) { + bits = lifreq.lifr_addrlen - i; + bits = (bits < 8) ? (8 - bits) : 0; + iter->current.netmask.type.in6.s6_addr[i / 8] = + (~0U << bits) & 0xff; + } + + return (ISC_R_SUCCESS); + } +#endif + + /* + * Ignore the HP/UX warning about "integer overflow during + * conversion. It comes from its own macro definition, + * and is really hard to shut up. + */ + if (ioctl(fd, SIOCGLIFNETMASK, (char *)&lifreq) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERIOCTL, + ISC_MSG_GETNETMASK, + "%s: getting netmask: %s"), + lifreq.lifr_name, strbuf); + return (ISC_R_IGNORE); + } + get_addr(family, &iter->current.netmask, + (struct sockaddr *)&lifreq.lifr_addr, lifreq.lifr_name); + + return (ISC_R_SUCCESS); +} +#endif + +static isc_result_t +internal_current(isc_interfaceiter_t *iter) { +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) + if (iter->mode == 6) { + iter->result6 = internal_current6(iter); + if (iter->result6 != ISC_R_NOMORE) + return (iter->result6); + } +#endif +#ifdef HAVE_TRUCLUSTER + if (!iter->clua_done) + return(internal_current_clusteralias(iter)); +#endif + return (internal_current4(iter)); +} + +/* + * 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_next4(isc_interfaceiter_t *iter) { +#ifdef ISC_PLATFORM_HAVESALEN + struct ifreq *ifrp; +#endif + + if (iter->pos < (unsigned int) iter->ifc.ifc_len) { +#ifdef ISC_PLATFORM_HAVESALEN + ifrp = (struct ifreq *)((char *) iter->ifc.ifc_req + iter->pos); + + if (ifrp->ifr_addr.sa_len > sizeof(struct sockaddr)) + iter->pos += sizeof(ifrp->ifr_name) + + ifrp->ifr_addr.sa_len; + else +#endif + iter->pos += sizeof(struct ifreq); + + } else { + INSIST(iter->pos == (unsigned int) iter->ifc.ifc_len); +#ifdef __linux + return (linux_if_inet6_next(iter)); +#else + return (ISC_R_NOMORE); +#endif + } + return (ISC_R_SUCCESS); +} + +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) +static isc_result_t +internal_next6(isc_interfaceiter_t *iter) { +#ifdef ISC_PLATFORM_HAVESALEN + struct LIFREQ *ifrp; +#endif + + if (iter->result6 != ISC_R_SUCCESS && iter->result6 != ISC_R_IGNORE) + return (iter->result6); + + REQUIRE(iter->pos6 < (unsigned int) iter->lifc.lifc_len); + +#ifdef ISC_PLATFORM_HAVESALEN + ifrp = (struct LIFREQ *)((char *) iter->lifc.lifc_req + iter->pos6); + + if (ifrp->lifr_addr.sa_len > sizeof(struct sockaddr)) + iter->pos6 += sizeof(ifrp->lifr_name) + ifrp->lifr_addr.sa_len; + else +#endif + iter->pos6 += sizeof(struct LIFREQ); + + if (iter->pos6 >= (unsigned int) iter->lifc.lifc_len) + return (ISC_R_NOMORE); + + return (ISC_R_SUCCESS); +} +#endif + +static isc_result_t +internal_next(isc_interfaceiter_t *iter) { +#ifdef HAVE_TRUCLUSTER + int clua_result; +#endif +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) + if (iter->mode == 6) { + iter->result6 = internal_next6(iter); + if (iter->result6 != ISC_R_NOMORE) + return (iter->result6); + if (iter->first6) { + iter->first6 = false; + return (ISC_R_SUCCESS); + } + } +#endif +#ifdef HAVE_TRUCLUSTER + if (!iter->clua_done) { + clua_result = clua_getaliasaddress(&iter->clua_sa, + &iter->clua_context); + if (clua_result != CLUA_SUCCESS) + iter->clua_done = true; + return (ISC_R_SUCCESS); + } +#endif + return (internal_next4(iter)); +} + +static void +internal_destroy(isc_interfaceiter_t *iter) { + (void) close(iter->socket); +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) + if (iter->socket6 != -1) + (void) close(iter->socket6); + if (iter->buf6 != NULL) { + isc_mem_put(iter->mctx, iter->buf6, iter->bufsize6); + } +#endif +#ifdef __linux + if (iter->proc != NULL) + fclose(iter->proc); +#endif +} + +static +void internal_first(isc_interfaceiter_t *iter) { +#ifdef HAVE_TRUCLUSTER + int clua_result; +#endif + iter->pos = 0; +#if defined(SIOCGLIFCONF) && defined(SIOCGLIFADDR) + iter->pos6 = 0; + if (iter->result6 == ISC_R_NOMORE) + iter->result6 = ISC_R_SUCCESS; + iter->first6 = true; +#endif +#ifdef HAVE_TRUCLUSTER + iter->clua_context = 0; + clua_result = clua_getaliasaddress(&iter->clua_sa, + &iter->clua_context); + iter->clua_done = (clua_result != CLUA_SUCCESS); +#endif +#ifdef __linux + linux_if_inet6_first(iter); +#endif +} diff --git a/lib/isc/unix/ifiter_sysctl.c b/lib/isc/unix/ifiter_sysctl.c new file mode 100644 index 0000000..ebbefe9 --- /dev/null +++ b/lib/isc/unix/ifiter_sysctl.c @@ -0,0 +1,300 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file + * \brief + * Obtain the list of network interfaces using sysctl. + * See TCP/IP Illustrated Volume 2, sections 19.8, 19.14, + * and 19.16. + */ + +#include <inttypes.h> + +#include <sys/param.h> +#include <sys/sysctl.h> + +#include <net/route.h> +#include <net/if_dl.h> + +#include <isc/print.h> + +/* XXX what about Alpha? */ +#ifdef sgi +#define ROUNDUP(a) ((a) > 0 ? \ + (1 + (((a) - 1) | (sizeof(__uint64_t) - 1))) : \ + sizeof(__uint64_t)) +#else +#define ROUNDUP(a) ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) \ + : sizeof(long)) +#endif + +#define IFITER_MAGIC ISC_MAGIC('I', 'F', 'I', 'S') +#define VALID_IFITER(t) ISC_MAGIC_VALID(t, IFITER_MAGIC) + +struct isc_interfaceiter { + unsigned int magic; /* Magic number. */ + isc_mem_t *mctx; + void *buf; /* Buffer for sysctl data. */ + unsigned int bufsize; /* Bytes allocated. */ + unsigned int bufused; /* Bytes used. */ + unsigned int pos; /* Current offset in + sysctl data. */ + isc_interface_t current; /* Current interface data. */ + isc_result_t result; /* Last result code. */ +}; + +static int mib[6] = { + CTL_NET, + PF_ROUTE, + 0, + 0, /* Any address family. */ + NET_RT_IFLIST, + 0 /* Flags. */ +}; + +isc_result_t +isc_interfaceiter_create(isc_mem_t *mctx, isc_interfaceiter_t **iterp) { + isc_interfaceiter_t *iter; + isc_result_t result; + size_t bufsize; + size_t bufused; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(mctx != NULL); + REQUIRE(iterp != NULL); + REQUIRE(*iterp == NULL); + + iter = isc_mem_get(mctx, sizeof(*iter)); + if (iter == NULL) + return (ISC_R_NOMEMORY); + + iter->mctx = mctx; + iter->buf = 0; + + /* + * Determine the amount of memory needed. + */ + bufsize = 0; + if (sysctl(mib, 6, NULL, &bufsize, NULL, (size_t) 0) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERSYSCTL, + ISC_MSG_GETIFLISTSIZE, + "getting interface " + "list size: sysctl: %s"), + strbuf); + result = ISC_R_UNEXPECTED; + goto failure; + } + iter->bufsize = bufsize; + + iter->buf = isc_mem_get(iter->mctx, iter->bufsize); + if (iter->buf == NULL) { + result = ISC_R_NOMEMORY; + goto failure; + } + + bufused = bufsize; + if (sysctl(mib, 6, iter->buf, &bufused, NULL, (size_t) 0) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_IFITERSYSCTL, + ISC_MSG_GETIFLIST, + "getting interface list: " + "sysctl: %s"), + strbuf); + result = ISC_R_UNEXPECTED; + goto failure; + } + iter->bufused = bufused; + INSIST(iter->bufused <= iter->bufsize); + + /* + * A newly created iterator has an undefined position + * until isc_interfaceiter_first() is called. + */ + iter->pos = (unsigned int) -1; + iter->result = ISC_R_FAILURE; + + iter->magic = IFITER_MAGIC; + *iterp = iter; + return (ISC_R_SUCCESS); + + failure: + if (iter->buf != NULL) + isc_mem_put(mctx, iter->buf, iter->bufsize); + 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, + * return ISC_R_IGNORE. In case of other failure, + * return ISC_R_UNEXPECTED. + */ + +static isc_result_t +internal_current(isc_interfaceiter_t *iter) { + struct ifa_msghdr *ifam, *ifam_end; + + REQUIRE(VALID_IFITER(iter)); + REQUIRE (iter->pos < (unsigned int) iter->bufused); + + ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); + ifam_end = (struct ifa_msghdr *) ((char *) iter->buf + iter->bufused); + + if (ifam->ifam_type == RTM_IFINFO) { + struct if_msghdr *ifm = (struct if_msghdr *) ifam; + struct sockaddr_dl *sdl = (struct sockaddr_dl *) (ifm + 1); + unsigned int namelen; + + memset(&iter->current, 0, sizeof(iter->current)); + + namelen = sdl->sdl_nlen; + if (namelen > sizeof(iter->current.name) - 1) + namelen = sizeof(iter->current.name) - 1; + + memset(iter->current.name, 0, sizeof(iter->current.name)); + memmove(iter->current.name, sdl->sdl_data, namelen); + + iter->current.flags = 0; + + if ((ifam->ifam_flags & IFF_UP) != 0) + iter->current.flags |= INTERFACE_F_UP; + + if ((ifam->ifam_flags & IFF_POINTOPOINT) != 0) + iter->current.flags |= INTERFACE_F_POINTTOPOINT; + + if ((ifam->ifam_flags & IFF_LOOPBACK) != 0) + iter->current.flags |= INTERFACE_F_LOOPBACK; + + /* + * This is not an interface address. + * Force another iteration. + */ + return (ISC_R_IGNORE); + } else if (ifam->ifam_type == RTM_NEWADDR) { + int i; + int family; + struct sockaddr *mask_sa = NULL; + struct sockaddr *addr_sa = NULL; + struct sockaddr *dst_sa = NULL; + + struct sockaddr *sa = (struct sockaddr *)(ifam + 1); + family = sa->sa_family; + + for (i = 0; i < RTAX_MAX; i++) + { + if ((ifam->ifam_addrs & (1 << i)) == 0) + continue; + + INSIST(sa < (struct sockaddr *) ifam_end); + + switch (i) { + case RTAX_NETMASK: /* Netmask */ + mask_sa = sa; + break; + case RTAX_IFA: /* Interface address */ + addr_sa = sa; + break; + case RTAX_BRD: /* Broadcast or destination address */ + dst_sa = sa; + break; + } +#ifdef ISC_PLATFORM_HAVESALEN + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(sa->sa_len)); +#else +#ifdef sgi + /* + * Do as the contributed SGI code does. + */ + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(_FAKE_SA_LEN_DST(sa))); +#else + /* XXX untested. */ + sa = (struct sockaddr *)((char*)(sa) + + ROUNDUP(sizeof(struct sockaddr))); +#endif +#endif + } + + if (addr_sa == NULL) + return (ISC_R_IGNORE); + + family = addr_sa->sa_family; + if (family != AF_INET && family != AF_INET6) + return (ISC_R_IGNORE); + + iter->current.af = family; + + get_addr(family, &iter->current.address, addr_sa, + iter->current.name); + + if (mask_sa != NULL) + get_addr(family, &iter->current.netmask, mask_sa, + iter->current.name); + + if (dst_sa != NULL && + (iter->current.flags & INTERFACE_F_POINTTOPOINT) != 0) + get_addr(family, &iter->current.dstaddress, dst_sa, + iter->current.name); + + return (ISC_R_SUCCESS); + } else { + printf("%s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_IFITERSYSCTL, + ISC_MSG_UNEXPECTEDTYPE, + "warning: unexpected interface list " + "message type\n")); + return (ISC_R_IGNORE); + } +} + +/* + * 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) { + struct ifa_msghdr *ifam; + REQUIRE (iter->pos < (unsigned int) iter->bufused); + + ifam = (struct ifa_msghdr *) ((char *) iter->buf + iter->pos); + + iter->pos += ifam->ifam_msglen; + + if (iter->pos >= iter->bufused) + return (ISC_R_NOMORE); + + return (ISC_R_SUCCESS); +} + +static void +internal_destroy(isc_interfaceiter_t *iter) { + UNUSED(iter); /* Unused. */ + /* + * Do nothing. + */ +} + +static +void internal_first(isc_interfaceiter_t *iter) { + iter->pos = 0; +} diff --git a/lib/isc/unix/include/Makefile.in b/lib/isc/unix/include/Makefile.in new file mode 100644 index 0000000..2cbf021 --- /dev/null +++ b/lib/isc/unix/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 pkcs11 +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/unix/include/isc/Makefile.in b/lib/isc/unix/include/isc/Makefile.in new file mode 100644 index 0000000..977defe --- /dev/null +++ b/lib/isc/unix/include/isc/Makefile.in @@ -0,0 +1,35 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = dir.h keyboard.h net.h netdb.h offset.h stat.h \ + stdtime.h strerror.h syslog.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/unix/include/isc/dir.h b/lib/isc/unix/include/isc/dir.h new file mode 100644 index 0000000..36d0aad --- /dev/null +++ b/lib/isc/unix/include/isc/dir.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +#include <sys/types.h> /* Required on some systems. */ +#include <dirent.h> + +#include <isc/lang.h> +#include <isc/result.h> + +#define ISC_DIR_NAMEMAX 256 +#define ISC_DIR_PATHMAX 1024 + +/*% Directory Entry */ +typedef struct isc_direntry { + /*! + * Ideally, this should be NAME_MAX, but AIX does not define it by + * default and dynamically allocating the space based on pathconf() + * complicates things undesirably, as does adding special conditionals + * just for AIX. So a comfortably sized buffer is chosen instead. + */ + char name[ISC_DIR_NAMEMAX]; + unsigned int length; +} isc_direntry_t; + +/*% Directory */ +typedef struct isc_dir { + unsigned int magic; + /*! + * As with isc_direntry_t->name, making this "right" for all systems + * is slightly problematic because AIX does not define PATH_MAX. + */ + char dirname[ISC_DIR_PATHMAX]; + isc_direntry_t entry; + DIR * 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/unix/include/isc/keyboard.h b/lib/isc/unix/include/isc/keyboard.h new file mode 100644 index 0000000..4dc8745 --- /dev/null +++ b/lib/isc/unix/include/isc/keyboard.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_KEYBOARD_H +#define ISC_KEYBOARD_H 1 + +/*! \file */ + +#include <stdbool.h> +#include <termios.h> + +#include <isc/lang.h> +#include <isc/result.h> + +ISC_LANG_BEGINDECLS + +typedef struct { + int fd; + struct termios saved_mode; + isc_result_t result; +} isc_keyboard_t; + +isc_result_t +isc_keyboard_open(isc_keyboard_t *keyboard); + +isc_result_t +isc_keyboard_close(isc_keyboard_t *keyboard, unsigned int sleepseconds); + +isc_result_t +isc_keyboard_getchar(isc_keyboard_t *keyboard, unsigned char *cp); + +bool +isc_keyboard_canceled(isc_keyboard_t *keyboard); + +ISC_LANG_ENDDECLS + +#endif /* ISC_KEYBOARD_H */ diff --git a/lib/isc/unix/include/isc/net.h b/lib/isc/unix/include/isc/net.h new file mode 100644 index 0000000..08be590 --- /dev/null +++ b/lib/isc/unix/include/isc/net.h @@ -0,0 +1,417 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + *****/ + +/*! \file + * \brief + * Basic Networking Types + * + * This module is responsible for defining the following basic networking + * types: + * + *\li struct in_addr + *\li struct in6_addr + *\li struct in6_pktinfo + *\li struct sockaddr + *\li struct sockaddr_in + *\li struct sockaddr_in6 + *\li struct sockaddr_storage + *\li in_port_t + * + * It ensures that the AF_ and PF_ macros are defined. + * + * It declares ntoh[sl]() and hton[sl](). + * + * It declares inet_aton(), inet_ntop(), and inet_pton(). + * + * It ensures that #INADDR_LOOPBACK, #INADDR_ANY, #IN6ADDR_ANY_INIT, + * IN6ADDR_V4MAPPED_INIT, in6addr_any, and in6addr_loopback are available. + * + * It ensures that IN_MULTICAST() is available to check for multicast + * addresses. + * + * MP: + *\li No impact. + * + * Reliability: + *\li No anticipated impact. + * + * Resources: + *\li N/A. + * + * Security: + *\li No anticipated impact. + * + * Standards: + *\li BSD Socket API + *\li RFC2553 + */ + +/*** + *** Imports. + ***/ +#include <isc/platform.h> + +#include <inttypes.h> + +#include <sys/types.h> +#include <sys/socket.h> /* Contractual promise. */ + +#include <net/if.h> + +#include <netinet/in.h> /* Contractual promise. */ +#include <arpa/inet.h> /* Contractual promise. */ +#ifdef ISC_PLATFORM_NEEDNETINETIN6H +#include <netinet/in6.h> /* Required on UnixWare. */ +#endif +#ifdef ISC_PLATFORM_NEEDNETINET6IN6H +#include <netinet6/in6.h> /* Required on BSD/OS for in6_pktinfo. */ +#endif + +#ifndef ISC_PLATFORM_HAVEIPV6 +#include <isc/ipv6.h> /* Contractual promise. */ +#endif + +#include <isc/lang.h> +#include <isc/types.h> + +#ifdef ISC_PLATFORM_HAVEINADDR6 +#define in6_addr in_addr6 /*%< Required for pre RFC2133 implementations. */ +#endif + +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifndef IN6ADDR_ANY_INIT +#ifdef s6_addr +/*% + * Required for some pre RFC2133 implementations. + * IN6ADDR_ANY_INIT and IN6ADDR_LOOPBACK_INIT were added in + * draft-ietf-ipngwg-bsd-api-04.txt or draft-ietf-ipngwg-bsd-api-05.txt. + * If 's6_addr' is defined then assume that there is a union and three + * levels otherwise assume two levels required. + */ +#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } } +#else +#define IN6ADDR_ANY_INIT { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } +#endif +#endif + +#ifndef IN6ADDR_LOOPBACK_INIT +#ifdef s6_addr +/*% IPv6 address loopback init */ +#define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } } +#else +#define IN6ADDR_LOOPBACK_INIT { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } +#endif +#endif + +#ifndef IN6ADDR_V4MAPPED_INIT +#ifdef s6_addr +/*% IPv6 v4mapped prefix init */ +#define IN6ADDR_V4MAPPED_INIT { { { 0,0,0,0,0,0,0,0,0,0,0xff,0xff,0,0,0,0 } } } +#else +#define IN6ADDR_V4MAPPED_INIT { { 0,0,0,0,0,0,0,0,0,0,0xff,0xff,0,0,0,0 } } +#endif +#endif + +#ifndef IN6_IS_ADDR_V4MAPPED +/*% Is IPv6 address V4 mapped? */ +#define IN6_IS_ADDR_V4MAPPED(x) \ + (memcmp((x)->s6_addr, in6addr_any.s6_addr, 10) == 0 && \ + (x)->s6_addr[10] == 0xff && (x)->s6_addr[11] == 0xff) +#endif + +#ifndef IN6_IS_ADDR_V4COMPAT +/*% Is IPv6 address V4 compatible? */ +#define IN6_IS_ADDR_V4COMPAT(x) \ + (memcmp((x)->s6_addr, in6addr_any.s6_addr, 12) == 0 && \ + ((x)->s6_addr[12] != 0 || (x)->s6_addr[13] != 0 || \ + (x)->s6_addr[14] != 0 || \ + ((x)->s6_addr[15] != 0 && (x)->s6_addr[15] != 1))) +#endif + +#ifndef IN6_IS_ADDR_MULTICAST +/*% Is IPv6 address multicast? */ +#define IN6_IS_ADDR_MULTICAST(a) ((a)->s6_addr[0] == 0xff) +#endif + +#ifndef IN6_IS_ADDR_LINKLOCAL +/*% Is IPv6 address linklocal? */ +#define IN6_IS_ADDR_LINKLOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0x80)) +#endif + +#ifndef IN6_IS_ADDR_SITELOCAL +/*% is IPv6 address sitelocal? */ +#define IN6_IS_ADDR_SITELOCAL(a) \ + (((a)->s6_addr[0] == 0xfe) && (((a)->s6_addr[1] & 0xc0) == 0xc0)) +#endif + + +#ifndef IN6_IS_ADDR_LOOPBACK +/*% is IPv6 address loopback? */ +#define IN6_IS_ADDR_LOOPBACK(x) \ + (memcmp((x)->s6_addr, in6addr_loopback.s6_addr, 16) == 0) +#endif +#endif + +#ifndef AF_INET6 +/*% IPv6 */ +#define AF_INET6 99 +#endif + +#ifndef PF_INET6 +/*% IPv6 */ +#define PF_INET6 AF_INET6 +#endif + +#ifndef INADDR_ANY +/*% inaddr any */ +#define INADDR_ANY 0x00000000UL +#endif + +#ifndef INADDR_LOOPBACK +/*% inaddr loopback */ +#define INADDR_LOOPBACK 0x7f000001UL +#endif + +#ifndef ISC_PLATFORM_HAVEIN6PKTINFO +/*% IPv6 packet info */ +struct in6_pktinfo { + struct in6_addr ipi6_addr; /*%< src/dst IPv6 address */ + unsigned int ipi6_ifindex; /*%< send/recv interface index */ +}; +#endif + + +#ifndef ISC_PLATFORM_HAVESOCKADDRSTORAGE +#define _SS_MAXSIZE 128 +#define _SS_ALIGNSIZE (sizeof (uint64_t)) +#ifdef ISC_PLATFORM_HAVESALEN +#define _SS_PAD1SIZE (_SS_ALIGNSIZE - (2 * sizeof(uint8_t))) +#define _SS_PAD2SIZE (_SS_MAXSIZE - (_SS_ALIGNSIZE + _SS_PAD1SIZE \ + + 2 * sizeof(uint8_t))) +#else +#define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof(uint16_t)) +#define _SS_PAD2SIZE (_SS_MAXSIZE - (_SS_ALIGNSIZE + _SS_PAD1SIZE \ + + sizeof(uint16_t))) +#endif + +struct sockaddr_storage { +#ifdef ISC_PLATFORM_HAVESALEN + uint8_t ss_len; + uint8_t ss_family; +#else + uint16_t ss_family; +#endif + char __ss_pad1[_SS_PAD1SIZE]; + uint64_t __ss_align; /* field to force desired structure */ + char __ss_pad2[_SS_PAD2SIZE]; +}; +#endif + +#if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRANY) +extern const struct in6_addr isc_net_in6addrany; +/*% + * Cope with a missing in6addr_any and in6addr_loopback. + */ +#define in6addr_any isc_net_in6addrany +#endif + +#if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK) +extern const struct in6_addr isc_net_in6addrloop; +#define in6addr_loopback isc_net_in6addrloop +#endif + +#ifdef ISC_PLATFORM_FIXIN6ISADDR +#undef IN6_IS_ADDR_GEOGRAPHIC +/*! + * \brief + * Fix UnixWare 7.1.1's broken IN6_IS_ADDR_* definitions. + */ +#define IN6_IS_ADDR_GEOGRAPHIC(a) (((a)->S6_un.S6_l[0] & 0xE0) == 0x80) +#undef IN6_IS_ADDR_IPX +#define IN6_IS_ADDR_IPX(a) (((a)->S6_un.S6_l[0] & 0xFE) == 0x04) +#undef IN6_IS_ADDR_LINKLOCAL +#define IN6_IS_ADDR_LINKLOCAL(a) (((a)->S6_un.S6_l[0] & 0xC0FF) == 0x80FE) +#undef IN6_IS_ADDR_MULTICAST +#define IN6_IS_ADDR_MULTICAST(a) (((a)->S6_un.S6_l[0] & 0xFF) == 0xFF) +#undef IN6_IS_ADDR_NSAP +#define IN6_IS_ADDR_NSAP(a) (((a)->S6_un.S6_l[0] & 0xFE) == 0x02) +#undef IN6_IS_ADDR_PROVIDER +#define IN6_IS_ADDR_PROVIDER(a) (((a)->S6_un.S6_l[0] & 0xE0) == 0x40) +#undef IN6_IS_ADDR_SITELOCAL +#define IN6_IS_ADDR_SITELOCAL(a) (((a)->S6_un.S6_l[0] & 0xC0FF) == 0xC0FE) +#endif /* ISC_PLATFORM_FIXIN6ISADDR */ + +#ifdef ISC_PLATFORM_NEEDPORTT +/*% + * Ensure type in_port_t is defined. + */ +typedef uint16_t in_port_t; +#endif + +#ifndef MSG_TRUNC +/*% + * 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. + */ +#define ISC_PLATFORM_RECVOVERFLOW +#endif + +/*% IP address. */ +#define ISC__IPADDR(x) ((uint32_t)htonl((uint32_t)(x))) + +/*% Is IP address multicast? */ +#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)) + +/*** + *** Functions. + ***/ + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_net_probeipv4(void); +/*%< + * Check if the system's kernel supports IPv4. + * + * Returns: + * + *\li #ISC_R_SUCCESS IPv4 is supported. + *\li #ISC_R_NOTFOUND IPv4 is not supported. + *\li #ISC_R_DISABLED IPv4 is disabled. + *\li #ISC_R_UNEXPECTED + */ + +isc_result_t +isc_net_probeipv6(void); +/*%< + * Check if the system's kernel supports IPv6. + * + * Returns: + * + *\li #ISC_R_SUCCESS IPv6 is supported. + *\li #ISC_R_NOTFOUND IPv6 is not supported. + *\li #ISC_R_DISABLED IPv6 is disabled. + *\li #ISC_R_UNEXPECTED + */ + +isc_result_t +isc_net_probe_ipv6only(void); +/*%< + * Check if the system's kernel supports the IPV6_V6ONLY socket option. + * + * Returns: + * + *\li #ISC_R_SUCCESS the option is supported for both TCP and UDP. + *\li #ISC_R_NOTFOUND IPv6 itself or the option is not supported. + *\li #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: + * + * \li #ISC_R_SUCCESS the option is supported. + * \li #ISC_R_NOTFOUND IPv6 itself or the option is not supported. + * \li #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_probeunix(void); +/* + * Returns whether UNIX domain sockets are supported. + */ + +#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_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. + */ + +#ifdef ISC_PLATFORM_NEEDNTOP +const char * +isc_net_ntop(int af, const void *src, char *dst, size_t size); +#undef inet_ntop +#define inet_ntop isc_net_ntop +#endif + +#ifdef ISC_PLATFORM_NEEDPTON +int +isc_net_pton(int af, const char *src, void *dst); +#undef inet_pton +#define inet_pton isc_net_pton +#endif + +int +isc_net_aton(const char *cp, struct in_addr *addr); +#undef inet_aton +#define inet_aton isc_net_aton + +ISC_LANG_ENDDECLS + +#endif /* ISC_NET_H */ diff --git a/lib/isc/unix/include/isc/netdb.h b/lib/isc/unix/include/isc/netdb.h new file mode 100644 index 0000000..5aaa9f6 --- /dev/null +++ b/lib/isc/unix/include/isc/netdb.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + *****/ + +/*! \file + * \brief + * Portable netdb.h support. + * + * This module is responsible for defining the get<x>by<y> APIs. + * + * MP: + *\li No impact. + * + * Reliability: + *\li No anticipated impact. + * + * Resources: + *\li N/A. + * + * Security: + *\li No anticipated impact. + * + * Standards: + *\li BSD API + */ + +/*** + *** Imports. + ***/ + +#include <isc/net.h> + +#include <netdb.h> + +#endif /* ISC_NETDB_H */ diff --git a/lib/isc/unix/include/isc/offset.h b/lib/isc/unix/include/isc/offset.h new file mode 100644 index 0000000..cf57292 --- /dev/null +++ b/lib/isc/unix/include/isc/offset.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + * \brief + * File offsets are operating-system dependent. + */ +#include <limits.h> /* Required for CHAR_BIT. */ +#include <sys/types.h> +#include <stddef.h> /* For Linux Standard Base. */ + +typedef off_t isc_offset_t; + +#endif /* ISC_OFFSET_H */ diff --git a/lib/isc/unix/include/isc/stat.h b/lib/isc/unix/include/isc/stat.h new file mode 100644 index 0000000..24c4ce5 --- /dev/null +++ b/lib/isc/unix/include/isc/stat.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/***** + ***** Module Info + *****/ + +/* + * Portable <sys/stat.h> support. + * + * This module is responsible for defining S_IS??? macros. + * + * MP: + * No impact. + * + * Reliability: + * No anticipated impact. + * + * Resources: + * N/A. + * + * Security: + * No anticipated impact. + * + */ + +/*** + *** Imports. + ***/ + +#include <sys/types.h> +#include <sys/stat.h> + +#endif /* ISC_STAT_H */ diff --git a/lib/isc/unix/include/isc/stdtime.h b/lib/isc/unix/include/isc/stdtime.h new file mode 100644 index 0000000..1a3da82 --- /dev/null +++ b/lib/isc/unix/include/isc/stdtime.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +#include <isc/lang.h> +#include <inttypes.h> + +/*% + * 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; + +/* but this flag helps... */ +#define STDTIME_ON_32BITS 1 + +/* + * isc_stdtime32_t is a 32-bit version of isc_stdtime_t. A variable of this + * type should only be used as an opaque integer (e.g.,) to compare two + * time values. + */ +typedef uint32_t isc_stdtime32_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: + * + *\li 't' is a valid pointer. + */ + +#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/unix/include/isc/strerror.h b/lib/isc/unix/include/isc/strerror.h new file mode 100644 index 0000000..fa0e429 --- /dev/null +++ b/lib/isc/unix/include/isc/strerror.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_STRERROR_H +#define ISC_STRERROR_H + +/*! \file */ + +#include <sys/types.h> + +#include <isc/lang.h> + +ISC_LANG_BEGINDECLS + +/*% String Error Size */ +#define ISC_STRERRORSIZE 128 + +/*% + * Provide a thread safe wrapper to strerror(). + * + * Requires: + * 'buf' to be non NULL. + */ +void +isc__strerror(int num, char *buf, size_t bufsize); + +ISC_LANG_ENDDECLS + +#endif /* ISC_STRERROR_H */ diff --git a/lib/isc/unix/include/isc/syslog.h b/lib/isc/unix/include/isc/syslog.h new file mode 100644 index 0000000..e0b8e85 --- /dev/null +++ b/lib/isc/unix/include/isc/syslog.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +#include <isc/lang.h> +#include <isc/types.h> + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_syslog_facilityfromstring(const char *str, int *facilityp); +/*%< + * Convert 'str' to the appropriate syslog facility constant. + * + * Requires: + * + *\li 'str' is not NULL + *\li 'facilityp' is not NULL + * + * Returns: + * \li #ISC_R_SUCCESS + * \li #ISC_R_NOTFOUND + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_SYSLOG_H */ diff --git a/lib/isc/unix/include/isc/time.h b/lib/isc/unix/include/isc/time.h new file mode 100644 index 0000000..b864c29 --- /dev/null +++ b/lib/isc/unix/include/isc/time.h @@ -0,0 +1,361 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +/*! \file */ + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/types.h> + +/*** + *** Intervals + ***/ + +/*! + * \brief + * 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 { + unsigned int seconds; + unsigned int nanoseconds; +}; + +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: + * + *\li 't' is a valid pointer. + *\li nanoseconds < 1000000000. + */ + +bool +isc_interval_iszero(const isc_interval_t *i); +/*%< + * Returns true iff. 'i' is the zero interval. + * + * Requires: + * + *\li '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 { + unsigned int seconds; + unsigned int nanoseconds; +}; + +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. + * + * Notes: + *\li The Unix version of this call is equivalent to: + *\code + * isc_time_settoepoch(t); + * isc_interval_set(i, seconds, nanoseconds); + * isc_time_add(t, i, t); + *\endcode + * + * 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: + *\li The date of the epoch is platform-dependent. + * + * Requires: + * + *\li 't' is a valid pointer. + */ + +bool +isc_time_isepoch(const isc_time_t *t); +/*%< + * Returns true iff. 't' is the epoch ("time zero"). + * + * Requires: + * + *\li 't' is a valid pointer. + */ + +isc_result_t +isc_time_now(isc_time_t *t); +/*%< + * Set 't' to the current absolute time. + * + * 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: + *\li This call is equivalent to: + * + *\code + * isc_time_now(t); + * isc_time_add(t, i, t); + *\endcode + * + * Requires: + * + *\li 't' and 'i' are valid pointers. + * + * Returns: + * + *\li Success + *\li Unexpected error + * Getting the time from the system failed. + *\li 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: + * + *\li 't1' and 't2' are valid pointers. + * + * Returns: + * + *\li -1 t1 < t2 (comparing times, not pointers) + *\li 0 t1 = t2 + *\li 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: + * + *\li 't', 'i', and 'result' are valid pointers. + * + * Returns: + *\li Success + *\li 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: + * + *\li 't', 'i', and 'result' are valid pointers. + * + * Returns: + *\li Success + *\li 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 microseconds between time t1 and time t2. + * t2 is the subtrahend of t1; ie, difference = t1 - t2. + * + * Requires: + * + *\li 't1' and 't2' are valid pointers. + * + * Returns: + *\li The difference of t1 - t2, or 0 if t1 <= t2. + */ + +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 + */ + +uint32_t +isc_time_nanoseconds(const isc_time_t *t); +/*%< + * Return the number of nanoseconds stored in a time structure. + * + * Notes: + *\li 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: + *\li 't' is a valid pointer. + * + * Ensures: + *\li 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: + *\li 'len' > 0 + *\li '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: + *\li 'len' > 0 + *\li '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_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 + * + */ + +ISC_LANG_ENDDECLS + +#endif /* ISC_TIME_H */ diff --git a/lib/isc/unix/include/pkcs11/Makefile.in b/lib/isc/unix/include/pkcs11/Makefile.in new file mode 100644 index 0000000..420fec1 --- /dev/null +++ b/lib/isc/unix/include/pkcs11/Makefile.in @@ -0,0 +1,31 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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@ + +HEADERS = cryptoki.h +SUBDIRS = +TARGETS = + +@BIND9_MAKE_RULES@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/pkcs11 + +install:: installdirs + for i in ${HEADERS}; do \ + ${INSTALL_DATA} $(srcdir)/$$i ${DESTDIR}${includedir}/pkcs11 || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/pkcs11/$$i || exit 1; \ + done diff --git a/lib/isc/unix/include/pkcs11/cryptoki.h b/lib/isc/unix/include/pkcs11/cryptoki.h new file mode 100644 index 0000000..7dc48b0 --- /dev/null +++ b/lib/isc/unix/include/pkcs11/cryptoki.h @@ -0,0 +1,66 @@ +/* cryptoki.h include file for PKCS #11. */ +/* + * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +/* $Revision: 1.3 $ */ + +/* + * Portions Copyright RSA Security Inc. + * + * License to copy and use this software is granted provided that it is + * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface + * (Cryptoki)" in all material mentioning or referencing this software. + + * License is also granted to make and use derivative works provided that + * such works are identified as "derived from the RSA Security Inc. PKCS #11 + * Cryptographic Token Interface (Cryptoki)" in all material mentioning or + * referencing the derived work. + + * RSA Security Inc. makes no representations concerning either the + * merchantability of this software or the suitability of this software for + * any particular purpose. It is provided "as is" without express or implied + * warranty of any kind. + */ + +/* This is a sample file containing the top level include directives + * for building Unix Cryptoki libraries and applications. + */ + +#ifndef ___CRYPTOKI_H_INC___ +#define ___CRYPTOKI_H_INC___ + +#define CK_PTR * + +#define CK_DEFINE_FUNCTION(returnType, name) \ + returnType name + +#define CK_DECLARE_FUNCTION(returnType, name) \ + returnType name + +#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \ + returnType (* name) + +#define CK_CALLBACK_FUNCTION(returnType, name) \ + returnType (* name) + +/* NULL is in unistd.h */ +#include <unistd.h> +#define NULL_PTR NULL + +#undef CK_PKCS11_FUNCTION_INFO + +#include <pkcs11/pkcs11.h> + +#endif /* ___CRYPTOKI_H_INC___ */ diff --git a/lib/isc/unix/interfaceiter.c b/lib/isc/unix/interfaceiter.c new file mode 100644 index 0000000..4df4d34 --- /dev/null +++ b/lib/isc/unix/interfaceiter.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/ioctl.h> +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> /* Required for ifiter_ioctl.c. */ +#endif + +#include <stdio.h> +#include <inttypes.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + +#include <isc/interfaceiter.h> +#include <isc/log.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/net.h> +#include <isc/print.h> +#include <isc/result.h> +#include <isc/strerror.h> +#include <isc/string.h> +#include <isc/types.h> +#include <isc/util.h> + +/* Must follow <isc/net.h>. */ +#ifdef HAVE_NET_IF6_H +#include <net/if6.h> +#endif +#include <net/if.h> + +/* Common utility functions */ + +/*% + * Extract the network address part from a "struct sockaddr". + * \brief + * 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). + */ + +static void +get_addr(unsigned int family, isc_netaddr_t *dst, struct sockaddr *src, + char *ifname) +{ + struct sockaddr_in6 *sa6; + +#if !defined(ISC_PLATFORM_HAVEIFNAMETOINDEX) || \ + !defined(ISC_PLATFORM_HAVESCOPEID) + UNUSED(ifname); +#endif + + /* clear any remaining value for safety */ + memset(dst, 0, sizeof(*dst)); + + 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: + sa6 = (struct sockaddr_in6 *)src; + memmove(&dst->type.in6, &sa6->sin6_addr, + sizeof(struct in6_addr)); +#ifdef ISC_PLATFORM_HAVESCOPEID + if (sa6->sin6_scope_id != 0) + isc_netaddr_setzone(dst, sa6->sin6_scope_id); + else { + /* + * BSD variants embed scope zone IDs in the 128bit + * address as a kernel internal form. Unfortunately, + * the embedded IDs are not hidden from applications + * when getting access to them by sysctl or ioctl. + * We convert the internal format to the pure address + * part and the zone ID part. + * Since multicast addresses should not appear here + * and they cannot be distinguished from netmasks, + * we only consider unicast link-local addresses. + */ + if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) { + uint16_t zone16; + + memmove(&zone16, &sa6->sin6_addr.s6_addr[2], + sizeof(zone16)); + zone16 = ntohs(zone16); + if (zone16 != 0) { + /* the zone ID is embedded */ + isc_netaddr_setzone(dst, + (uint32_t)zone16); + dst->type.in6.s6_addr[2] = 0; + dst->type.in6.s6_addr[3] = 0; +#ifdef ISC_PLATFORM_HAVEIFNAMETOINDEX + } else if (ifname != NULL) { + unsigned int zone; + + /* + * sin6_scope_id is still not provided, + * but the corresponding interface name + * is know. Use the interface ID as + * the link ID. + */ + zone = if_nametoindex(ifname); + if (zone != 0) { + isc_netaddr_setzone(dst, + (uint32_t)zone); + } +#endif + } + } + } +#endif + break; + default: + INSIST(0); + break; + } +} + +/* + * Include system-dependent code. + */ + +#ifdef __linux +#define ISC_IF_INET6_SZ \ + sizeof("00000000000000000000000000000001 01 80 10 80 XXXXXXloXXXXXXXX\n") +static isc_result_t linux_if_inet6_next(isc_interfaceiter_t *); +static isc_result_t linux_if_inet6_current(isc_interfaceiter_t *); +static void linux_if_inet6_first(isc_interfaceiter_t *iter); +#endif + +#if HAVE_GETIFADDRS +#include "ifiter_getifaddrs.c" +#elif HAVE_IFLIST_SYSCTL +#include "ifiter_sysctl.c" +#else +#include "ifiter_ioctl.c" +#endif + +#ifdef __linux +static void +linux_if_inet6_first(isc_interfaceiter_t *iter) { + if (iter->proc != NULL) { + rewind(iter->proc); + (void)linux_if_inet6_next(iter); + } else + iter->valid = ISC_R_NOMORE; +} + +static isc_result_t +linux_if_inet6_next(isc_interfaceiter_t *iter) { + if (iter->proc != NULL && + fgets(iter->entry, sizeof(iter->entry), iter->proc) != NULL) + iter->valid = ISC_R_SUCCESS; + else + iter->valid = ISC_R_NOMORE; + return (iter->valid); +} + +static isc_result_t +linux_if_inet6_current(isc_interfaceiter_t *iter) { + char address[33]; + char name[IF_NAMESIZE+1]; + struct in6_addr addr6; + unsigned int ifindex, prefix, flag3, flag4; + int res; + unsigned int i; + + if (iter->valid != ISC_R_SUCCESS) + return (iter->valid); + if (iter->proc == NULL) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR, + "/proc/net/if_inet6:iter->proc == NULL"); + return (ISC_R_FAILURE); + } + + res = sscanf(iter->entry, "%32[a-f0-9] %x %x %x %x %16s\n", + address, &ifindex, &prefix, &flag3, &flag4, name); + if (res != 6) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR, + "/proc/net/if_inet6:sscanf() -> %d (expected 6)", + res); + return (ISC_R_FAILURE); + } + if (strlen(address) != 32) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_INTERFACE, ISC_LOG_ERROR, + "/proc/net/if_inet6:strlen(%s) != 32", address); + return (ISC_R_FAILURE); + } + for (i = 0; i < 16; i++) { + unsigned char byte; + static const char hex[] = "0123456789abcdef"; + byte = ((strchr(hex, address[i * 2]) - hex) << 4) | + (strchr(hex, address[i * 2 + 1]) - hex); + addr6.s6_addr[i] = byte; + } + iter->current.af = AF_INET6; + iter->current.flags = INTERFACE_F_UP; + isc_netaddr_fromin6(&iter->current.address, &addr6); + if (isc_netaddr_islinklocal(&iter->current.address)) { + isc_netaddr_setzone(&iter->current.address, + (uint32_t)ifindex); + } + for (i = 0; i < 16; i++) { + if (prefix > 8) { + addr6.s6_addr[i] = 0xff; + prefix -= 8; + } else { + addr6.s6_addr[i] = (0xff << (8 - prefix)) & 0xff; + prefix = 0; + } + } + isc_netaddr_fromin6(&iter->current.netmask, &addr6); + strlcpy(iter->current.name, name, sizeof(iter->current.name)); + return (ISC_R_SUCCESS); +} +#endif + +/* + * The remaining code is common to the sysctl and ioctl case. + */ + +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) { + isc_result_t result; + + REQUIRE(VALID_IFITER(iter)); + + internal_first(iter); + for (;;) { + result = internal_current(iter); + if (result != ISC_R_IGNORE) + break; + result = internal_next(iter); + if (result != ISC_R_SUCCESS) + break; + } + iter->result = result; + return (result); +} + +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_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; + REQUIRE(VALID_IFITER(iter)); + + internal_destroy(iter); + if (iter->buf != NULL) + isc_mem_put(iter->mctx, iter->buf, iter->bufsize); + + iter->magic = 0; + isc_mem_put(iter->mctx, iter, sizeof(*iter)); + *iterp = NULL; +} diff --git a/lib/isc/unix/ipv6.c b/lib/isc/unix/ipv6.c new file mode 100644 index 0000000..eb77377 --- /dev/null +++ b/lib/isc/unix/ipv6.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <isc/ipv6.h> + +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; diff --git a/lib/isc/unix/keyboard.c b/lib/isc/unix/keyboard.c new file mode 100644 index 0000000..cb5f116 --- /dev/null +++ b/lib/isc/unix/keyboard.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <stdbool.h> + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/uio.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <fcntl.h> + +#include <isc/keyboard.h> +#include <isc/util.h> + +isc_result_t +isc_keyboard_open(isc_keyboard_t *keyboard) { + int fd; + isc_result_t ret; + struct termios current_mode; + + REQUIRE(keyboard != NULL); + + fd = open("/dev/tty", O_RDONLY, 0); + if (fd < 0) + return (ISC_R_IOERROR); + + keyboard->fd = fd; + + if (tcgetattr(fd, &keyboard->saved_mode) < 0) { + ret = ISC_R_IOERROR; + goto errout; + } + + current_mode = keyboard->saved_mode; + + current_mode.c_iflag &= + ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); + current_mode.c_oflag &= ~OPOST; + current_mode.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); + current_mode.c_cflag &= ~(CSIZE|PARENB); + current_mode.c_cflag |= CS8; + + current_mode.c_cc[VMIN] = 1; + current_mode.c_cc[VTIME] = 0; + if (tcsetattr(fd, TCSAFLUSH, ¤t_mode) < 0) { + ret = ISC_R_IOERROR; + goto errout; + } + + keyboard->result = ISC_R_SUCCESS; + + return (ISC_R_SUCCESS); + + errout: + close (fd); + + return (ret); +} + +isc_result_t +isc_keyboard_close(isc_keyboard_t *keyboard, unsigned int sleeptime) { + REQUIRE(keyboard != NULL); + + if (sleeptime > 0 && keyboard->result != ISC_R_CANCELED) + (void)sleep(sleeptime); + + (void)tcsetattr(keyboard->fd, TCSAFLUSH, &keyboard->saved_mode); + (void)close(keyboard->fd); + + keyboard->fd = -1; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_keyboard_getchar(isc_keyboard_t *keyboard, unsigned char *cp) { + ssize_t cc; + unsigned char c; + cc_t *controlchars; + + REQUIRE(keyboard != NULL); + REQUIRE(cp != NULL); + + cc = read(keyboard->fd, &c, 1); + if (cc < 0) { + keyboard->result = ISC_R_IOERROR; + return (keyboard->result); + } + + controlchars = keyboard->saved_mode.c_cc; + if (c == controlchars[VINTR] || c == controlchars[VQUIT]) { + keyboard->result = ISC_R_CANCELED; + return (keyboard->result); + } + + *cp = c; + + return (ISC_R_SUCCESS); +} + +bool +isc_keyboard_canceled(isc_keyboard_t *keyboard) { + return (keyboard->result == ISC_R_CANCELED); +} diff --git a/lib/isc/unix/meminfo.c b/lib/isc/unix/meminfo.c new file mode 100644 index 0000000..ad750a7 --- /dev/null +++ b/lib/isc/unix/meminfo.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <isc/meminfo.h> +#include <inttypes.h> +#include <unistd.h> +#ifdef HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#endif + +uint64_t +isc_meminfo_totalphys(void) { +#if defined(CTL_HW) && (defined(HW_PHYSMEM64) || defined(HW_MEMSIZE)) + int mib[2]; + mib[0] = CTL_HW; +#if defined(HW_MEMSIZE) + mib[1] = HW_MEMSIZE; +#elif defined(HW_PHYSMEM64) + mib[1] = HW_PHYSMEM64; +#endif + uint64_t size = 0; + size_t len = sizeof(size); + if (sysctl(mib, 2, &size, &len, NULL, 0) == 0) + return (size); +#endif +#if defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE) + return ((size_t) (sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE))); +#endif + return (0); +} diff --git a/lib/isc/unix/net.c b/lib/isc/unix/net.c new file mode 100644 index 0000000..efe17bd --- /dev/null +++ b/lib/isc/unix/net.c @@ -0,0 +1,913 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <sys/types.h> + +#if defined(HAVE_SYS_SYSCTL_H) +#if defined(HAVE_SYS_PARAM_H) +#include <sys/param.h> +#endif +#include <sys/sysctl.h> +#endif +#include <sys/uio.h> + +#include <errno.h> +#include <stdbool.h> +#include <unistd.h> +#include <fcntl.h> + +#include <isc/log.h> +#include <isc/msgs.h> +#include <isc/net.h> +#include <isc/netdb.h> +#include <isc/once.h> +#include <isc/strerror.h> +#include <isc/string.h> +#include <isc/util.h> + +#ifndef ISC_SOCKADDR_LEN_T +#define ISC_SOCKADDR_LEN_T unsigned int +#endif + +/*% + * 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 1024 +#endif /* ISC_NET_PORTRANGELOW */ +#ifndef ISC_NET_PORTRANGEHIGH +#define ISC_NET_PORTRANGEHIGH 65535 +#endif /* ISC_NET_PORTRANGEHIGH */ + +#ifdef HAVE_SYSCTLBYNAME + +/*% + * sysctl variants + */ +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__) +#define USE_SYSCTL_PORTRANGE +#define SYSCTL_V4PORTRANGE_LOW "net.inet.ip.portrange.hifirst" +#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.portrange.hilast" +#define SYSCTL_V6PORTRANGE_LOW "net.inet.ip.portrange.hifirst" +#define SYSCTL_V6PORTRANGE_HIGH "net.inet.ip.portrange.hilast" +#endif + +#ifdef __NetBSD__ +#define USE_SYSCTL_PORTRANGE +#define SYSCTL_V4PORTRANGE_LOW "net.inet.ip.anonportmin" +#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.anonportmax" +#define SYSCTL_V6PORTRANGE_LOW "net.inet6.ip6.anonportmin" +#define SYSCTL_V6PORTRANGE_HIGH "net.inet6.ip6.anonportmax" +#endif + +#else /* !HAVE_SYSCTLBYNAME */ + +#ifdef __OpenBSD__ +#define USE_SYSCTL_PORTRANGE +#define SYSCTL_V4PORTRANGE_LOW { CTL_NET, PF_INET, IPPROTO_IP, \ + IPCTL_IPPORT_HIFIRSTAUTO } +#define SYSCTL_V4PORTRANGE_HIGH { CTL_NET, PF_INET, IPPROTO_IP, \ + IPCTL_IPPORT_HILASTAUTO } +/* Same for IPv6 */ +#define SYSCTL_V6PORTRANGE_LOW SYSCTL_V4PORTRANGE_LOW +#define SYSCTL_V6PORTRANGE_HIGH SYSCTL_V4PORTRANGE_HIGH +#endif + +#endif /* HAVE_SYSCTLBYNAME */ + +#if defined(ISC_PLATFORM_HAVEIPV6) +# if defined(ISC_PLATFORM_NEEDIN6ADDRANY) +const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT; +# endif + +# if defined(ISC_PLATFORM_NEEDIN6ADDRLOOPBACK) +const struct in6_addr isc_net_in6addrloop = IN6ADDR_LOOPBACK_INIT; +# endif + +# if defined(WANT_IPV6) +static isc_once_t once_ipv6only = ISC_ONCE_INIT; + +# if defined(ISC_PLATFORM_HAVEIN6PKTINFO) +static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT; +# endif +# endif /* WANT_IPV6 */ +#endif /* ISC_PLATFORM_HAVEIPV6 */ + +#ifndef ISC_CMSG_IP_TOS +#ifdef __APPLE__ +#define ISC_CMSG_IP_TOS 0 /* As of 10.8.2. */ +#else /* ! __APPLE__ */ +#define ISC_CMSG_IP_TOS 1 +#endif /* ! __APPLE__ */ +#endif /* ! ISC_CMSG_IP_TOS */ + +static isc_once_t once = ISC_ONCE_INIT; +static isc_once_t once_dscp = 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 unix_result = ISC_R_NOTFOUND; +static isc_result_t ipv6only_result = ISC_R_NOTFOUND; +static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND; +static unsigned int dscp_result = 0; + +static isc_result_t +try_proto(int domain) { + int s; + isc_result_t result = ISC_R_SUCCESS; + char strbuf[ISC_STRERRORSIZE]; + + s = socket(domain, SOCK_STREAM, 0); + if (s == -1) { + switch (errno) { +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: +#endif +#ifdef EPFNOSUPPORT + case EPFNOSUPPORT: +#endif +#ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: +#endif +#ifdef EINVAL + case EINVAL: +#endif + return (ISC_R_NOTFOUND); + default: + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + return (ISC_R_UNEXPECTED); + } + } + +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO + if (domain == PF_INET6) { + struct sockaddr_in6 sin6; + unsigned int len; + + /* + * Check to see if IPv6 is broken, as is common on Linux. + */ + len = sizeof(sin6); + if (getsockname(s, (struct sockaddr *)&sin6, (void *)&len) < 0) + { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "retrieving the address of an IPv6 " + "socket from the kernel failed."); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "IPv6 is not supported."); + result = ISC_R_NOTFOUND; + } else { + if (len == sizeof(struct sockaddr_in6)) + result = ISC_R_SUCCESS; + else { + isc_log_write(isc_lctx, + ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, + ISC_LOG_ERROR, + "IPv6 structures in kernel and " + "user space do not match."); + isc_log_write(isc_lctx, + ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, + ISC_LOG_ERROR, + "IPv6 is not supported."); + result = ISC_R_NOTFOUND; + } + } + } +#endif +#endif +#endif + + (void)close(s); + + return (result); +} + +static void +initialize_action(void) { + ipv4_result = try_proto(PF_INET); +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO + ipv6_result = try_proto(PF_INET6); +#endif +#endif +#endif +#ifdef ISC_PLATFORM_HAVESYSUNH + unix_result = try_proto(PF_UNIX); +#endif +} + +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) { + initialize(); + return (unix_result); +} + +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 +static void +try_ipv6only(void) { +#ifdef IPV6_V6ONLY + int s, on; + char strbuf[ISC_STRERRORSIZE]; +#endif + 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 + /* check for TCP sockets */ + s = socket(PF_INET6, SOCK_STREAM, 0); + if (s == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + close(s); + + /* check for UDP sockets */ + s = socket(PF_INET6, SOCK_DGRAM, 0); + if (s == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + ipv6only_result = ISC_R_UNEXPECTED; + return; + } + + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) { + ipv6only_result = ISC_R_NOTFOUND; + goto close; + } + + ipv6only_result = ISC_R_SUCCESS; + +close: + close(s); + return; +#endif /* IPV6_V6ONLY */ +} + +static void +initialize_ipv6only(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6only, + try_ipv6only) == ISC_R_SUCCESS); +} +#endif /* WANT_IPV6 */ + +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO +#ifdef WANT_IPV6 +static void +try_ipv6pktinfo(void) { + int s, 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 == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + ipv6pktinfo_result = ISC_R_UNEXPECTED; + return; + } + +#ifdef IPV6_RECVPKTINFO + optname = IPV6_RECVPKTINFO; +#else + optname = IPV6_PKTINFO; +#endif + on = 1; + if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) { + ipv6pktinfo_result = ISC_R_NOTFOUND; + goto close; + } + + ipv6pktinfo_result = ISC_R_SUCCESS; + +close: + close(s); + return; +} + +static void +initialize_ipv6pktinfo(void) { + RUNTIME_CHECK(isc_once_do(&once_ipv6pktinfo, + try_ipv6pktinfo) == ISC_R_SUCCESS); +} +#endif /* WANT_IPV6 */ +#endif /* ISC_PLATFORM_HAVEIN6PKTINFO */ +#endif /* ISC_PLATFORM_HAVEIPV6 */ + +isc_result_t +isc_net_probe_ipv6only(void) { +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 + initialize_ipv6only(); +#else + ipv6only_result = ISC_R_NOTFOUND; +#endif +#endif + return (ipv6only_result); +} + +isc_result_t +isc_net_probe_ipv6pktinfo(void) { +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO +#ifdef WANT_IPV6 + initialize_ipv6pktinfo(); +#else + ipv6pktinfo_result = ISC_R_NOTFOUND; +#endif +#endif +#endif + return (ipv6pktinfo_result); +} + +#if ISC_CMSG_IP_TOS || \ + defined(ISC_NET_BSD44MSGHDR) && defined(IPV6_TCLASS) && defined(WANT_IPV6) + +static inline ISC_SOCKADDR_LEN_T +cmsg_len(ISC_SOCKADDR_LEN_T len) { +#ifdef CMSG_LEN + return (CMSG_LEN(len)); +#else + ISC_SOCKADDR_LEN_T hdrlen; + + /* + * Cast NULL so that any pointer arithmetic performed by CMSG_DATA + * is correct. + */ + hdrlen = (ISC_SOCKADDR_LEN_T)CMSG_DATA(((struct cmsghdr *)NULL)); + return (hdrlen + len); +#endif +} + +static inline ISC_SOCKADDR_LEN_T +cmsg_space(ISC_SOCKADDR_LEN_T len) { +#ifdef CMSG_SPACE + return (CMSG_SPACE(len)); +#else + struct msghdr msg; + struct cmsghdr *cmsgp; + /* + * XXX: The buffer length is an ad-hoc value, but should be enough + * in a practical sense. + */ + char dummybuf[sizeof(struct cmsghdr) + 1024]; + + memset(&msg, 0, sizeof(msg)); + msg.msg_control = dummybuf; + msg.msg_controllen = sizeof(dummybuf); + + cmsgp = (struct cmsghdr *)dummybuf; + cmsgp->cmsg_len = cmsg_len(len); + + cmsgp = CMSG_NXTHDR(&msg, cmsgp); + if (cmsgp != NULL) + return ((char *)cmsgp - (char *)msg.msg_control); + else + return (0); +#endif +} + +#ifdef ISC_NET_BSD44MSGHDR +/* + * Make a fd non-blocking. + */ +static isc_result_t +make_nonblock(int fd) { + int ret; + int flags; + char strbuf[ISC_STRERRORSIZE]; +#ifdef USE_FIONBIO_IOCTL + int on = 1; + + ret = ioctl(fd, FIONBIO, (char *)&on); +#else + flags = fcntl(fd, F_GETFL, 0); + flags |= PORT_NONBLOCK; + ret = fcntl(fd, F_SETFL, flags); +#endif + + if (ret == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, +#ifdef USE_FIONBIO_IOCTL + "ioctl(%d, FIONBIO, &on): %s", fd, +#else + "fcntl(%d, F_SETFL, %d): %s", fd, flags, +#endif + strbuf); + + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +static bool +cmsgsend(int s, int level, int type, struct addrinfo *res) { + char strbuf[ISC_STRERRORSIZE]; + struct sockaddr_storage ss; + ISC_SOCKADDR_LEN_T len = sizeof(ss); + struct msghdr msg; + union { + struct cmsghdr h; + unsigned char b[256]; + } control; + struct cmsghdr *cmsgp; + int dscp = (46 << 2); /* Expedited forwarding. */ + struct iovec iovec; + char buf[1] = { 0 }; + isc_result_t result; + + if (bind(s, res->ai_addr, res->ai_addrlen) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "bind: %s", strbuf); + return (false); + } + + if (getsockname(s, (struct sockaddr *)&ss, &len) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "getsockname: %s", strbuf); + return (false); + } + + iovec.iov_base = buf; + iovec.iov_len = sizeof(buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (struct sockaddr *)&ss; + msg.msg_namelen = len; + msg.msg_iov = &iovec; + msg.msg_iovlen = 1; + msg.msg_control = (void*)&control; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + cmsgp = msg.msg_control; + + switch (type) { +#ifdef IP_TOS + case IP_TOS: + memset(cmsgp, 0, cmsg_space(sizeof(char))); + cmsgp->cmsg_level = level; + cmsgp->cmsg_type = type; + cmsgp->cmsg_len = cmsg_len(sizeof(char)); + *(unsigned char*)CMSG_DATA(cmsgp) = dscp; + msg.msg_controllen += cmsg_space(sizeof(char)); + break; +#endif +#ifdef IPV6_TCLASS + case IPV6_TCLASS: + memset(cmsgp, 0, cmsg_space(sizeof(dscp))); + cmsgp->cmsg_level = level; + cmsgp->cmsg_type = type; + cmsgp->cmsg_len = cmsg_len(sizeof(dscp)); + memmove(CMSG_DATA(cmsgp), &dscp, sizeof(dscp)); + msg.msg_controllen += cmsg_space(sizeof(dscp)); + break; +#endif + default: + INSIST(0); + } + + if (sendmsg(s, &msg, 0) < 0) { + int debug = ISC_LOG_DEBUG(10); + const char *typestr; + const char *msgstr; + switch (errno) { +#ifdef ENOPROTOOPT + case ENOPROTOOPT: +#endif +#ifdef EOPNOTSUPP + case EOPNOTSUPP: +#endif + case EINVAL: + case EPERM: + break; + default: + debug = ISC_LOG_NOTICE; + } + isc__strerror(errno, strbuf, sizeof(strbuf)); + if (debug != ISC_LOG_NOTICE) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "sendmsg: %s", strbuf); + } else { + typestr = (type == IP_TOS) ? "IP_TOS" : "IPV6_TCLASS"; + msgstr = isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"); + UNEXPECTED_ERROR(__FILE__, __LINE__, "probing " + "sendmsg() with %s=%02x %s: %s", + typestr, dscp, msgstr, strbuf); + } + return (false); + } + + /* + * Make sure the message actually got sent. + */ + result = make_nonblock(s); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + + iovec.iov_base = buf; + iovec.iov_len = sizeof(buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = (struct sockaddr *)&ss; + msg.msg_namelen = sizeof(ss); + msg.msg_iov = &iovec; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + if (recvmsg(s, &msg, 0) < 0) + return (false); + + return (true); +} +#endif +#endif + +static void +try_dscp_v4(void) { +#ifdef IP_TOS + char strbuf[ISC_STRERRORSIZE]; + struct addrinfo hints, *res0; + int s, dscp = 0, n; +#ifdef IP_RECVTOS + int on = 1; +#endif /* IP_RECVTOS */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; +#ifdef AI_NUMERICHOST + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; +#else + hints.ai_flags = AI_PASSIVE; +#endif + + n = getaddrinfo("127.0.0.1", NULL, &hints, &res0); + if (n != 0 || res0 == NULL) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "getaddrinfo(127.0.0.1): %s", gai_strerror(n)); + return; + } + + s = socket(res0->ai_family, res0->ai_socktype, res0->ai_protocol); + + if (s == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "socket: %s", strbuf); + freeaddrinfo(res0); + return; + } + + if (setsockopt(s, IPPROTO_IP, IP_TOS, &dscp, sizeof(dscp)) == 0) + dscp_result |= ISC_NET_DSCPSETV4; + +#ifdef IP_RECVTOS + on = 1; + if (setsockopt(s, IPPROTO_IP, IP_RECVTOS, &on, sizeof(on)) == 0) + dscp_result |= ISC_NET_DSCPRECVV4; +#endif /* IP_RECVTOS */ + +#ifdef ISC_NET_BSD44MSGHDR + +#if ISC_CMSG_IP_TOS + if (cmsgsend(s, IPPROTO_IP, IP_TOS, res0)) + dscp_result |= ISC_NET_DSCPPKTV4; +#endif /* ISC_CMSG_IP_TOS */ + +#endif /* ISC_NET_BSD44MSGHDR */ + + freeaddrinfo(res0); + close(s); + +#endif /* IP_TOS */ +} + +static void +try_dscp_v6(void) { +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 +#ifdef IPV6_TCLASS + char strbuf[ISC_STRERRORSIZE]; + struct addrinfo hints, *res0; + int s, dscp = 0, n; +#if defined(IPV6_RECVTCLASS) + int on = 1; +#endif /* IPV6_RECVTCLASS */ + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; +#ifdef AI_NUMERICHOST + hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; +#else + hints.ai_flags = AI_PASSIVE; +#endif + + n = getaddrinfo("::1", NULL, &hints, &res0); + if (n != 0 || res0 == NULL) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "getaddrinfo(::1): %s", gai_strerror(n)); + return; + } + + s = socket(res0->ai_family, res0->ai_socktype, res0->ai_protocol); + if (s == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_DEBUG(10), + "socket: %s", strbuf); + freeaddrinfo(res0); + return; + } + if (setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &dscp, sizeof(dscp)) == 0) + dscp_result |= ISC_NET_DSCPSETV6; + +#ifdef IPV6_RECVTCLASS + on = 1; + if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVTCLASS, &on, sizeof(on)) == 0) + dscp_result |= ISC_NET_DSCPRECVV6; +#endif /* IPV6_RECVTCLASS */ + +#ifdef ISC_NET_BSD44MSGHDR + if (cmsgsend(s, IPPROTO_IPV6, IPV6_TCLASS, res0)) + dscp_result |= ISC_NET_DSCPPKTV6; +#endif /* ISC_NET_BSD44MSGHDR */ + + freeaddrinfo(res0); + close(s); + +#endif /* IPV6_TCLASS */ +#endif /* WANT_IPV6 */ +#endif /* ISC_PLATFORM_HAVEIPV6 */ +} + +static void +try_dscp(void) { + try_dscp_v4(); + try_dscp_v6(); +} + +static void +initialize_dscp(void) { + RUNTIME_CHECK(isc_once_do(&once_dscp, try_dscp) == ISC_R_SUCCESS); +} + +unsigned int +isc_net_probedscp(void) { + initialize_dscp(); + return (dscp_result); +} + +#if defined(USE_SYSCTL_PORTRANGE) +#if defined(HAVE_SYSCTLBYNAME) +static isc_result_t +getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) { + int port_low, port_high; + size_t portlen; + const char *sysctlname_lowport, *sysctlname_hiport; + + if (af == AF_INET) { + sysctlname_lowport = SYSCTL_V4PORTRANGE_LOW; + sysctlname_hiport = SYSCTL_V4PORTRANGE_HIGH; + } else { + sysctlname_lowport = SYSCTL_V6PORTRANGE_LOW; + sysctlname_hiport = SYSCTL_V6PORTRANGE_HIGH; + } + portlen = sizeof(port_low); + if (sysctlbyname(sysctlname_lowport, &port_low, &portlen, + NULL, 0) < 0) { + return (ISC_R_FAILURE); + } + portlen = sizeof(port_high); + if (sysctlbyname(sysctlname_hiport, &port_high, &portlen, + NULL, 0) < 0) { + return (ISC_R_FAILURE); + } + if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) + return (ISC_R_RANGE); + + *low = (in_port_t)port_low; + *high = (in_port_t)port_high; + + return (ISC_R_SUCCESS); +} +#else /* !HAVE_SYSCTLBYNAME */ +static isc_result_t +getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) { + int mib_lo4[4] = SYSCTL_V4PORTRANGE_LOW; + int mib_hi4[4] = SYSCTL_V4PORTRANGE_HIGH; + int mib_lo6[4] = SYSCTL_V6PORTRANGE_LOW; + int mib_hi6[4] = SYSCTL_V6PORTRANGE_HIGH; + int *mib_lo, *mib_hi, miblen; + int port_low, port_high; + size_t portlen; + + if (af == AF_INET) { + mib_lo = mib_lo4; + mib_hi = mib_hi4; + miblen = sizeof(mib_lo4) / sizeof(mib_lo4[0]); + } else { + mib_lo = mib_lo6; + mib_hi = mib_hi6; + miblen = sizeof(mib_lo6) / sizeof(mib_lo6[0]); + } + + portlen = sizeof(port_low); + if (sysctl(mib_lo, miblen, &port_low, &portlen, NULL, 0) < 0) { + return (ISC_R_FAILURE); + } + + portlen = sizeof(port_high); + if (sysctl(mib_hi, miblen, &port_high, &portlen, NULL, 0) < 0) { + return (ISC_R_FAILURE); + } + + if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) + return (ISC_R_RANGE); + + *low = (in_port_t) port_low; + *high = (in_port_t) port_high; + + return (ISC_R_SUCCESS); +} +#endif /* HAVE_SYSCTLBYNAME */ +#endif /* USE_SYSCTL_PORTRANGE */ + +isc_result_t +isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) { + int result = ISC_R_FAILURE; +#if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux) + FILE *fp; +#endif + + REQUIRE(low != NULL && high != NULL); + +#if defined(USE_SYSCTL_PORTRANGE) + result = getudpportrange_sysctl(af, low, high); +#elif defined(__linux) + + UNUSED(af); + + /* + * Linux local ports are address family agnostic. + */ + fp = fopen("/proc/sys/net/ipv4/ip_local_port_range", "r"); + if (fp != NULL) { + int n; + unsigned int l, h; + + n = fscanf(fp, "%u %u", &l, &h); + if (n == 2 && (l & ~0xffff) == 0 && (h & ~0xffff) == 0) { + *low = l; + *high = h; + result = ISC_R_SUCCESS; + } + fclose(fp); + } +#else + UNUSED(af); +#endif + + 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; +} diff --git a/lib/isc/unix/os.c b/lib/isc/unix/os.c new file mode 100644 index 0000000..8cff6b6 --- /dev/null +++ b/lib/isc/unix/os.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <isc/os.h> + + +#ifdef HAVE_SYSCONF + +#include <unistd.h> + +#ifndef __hpux +static inline long +sysconf_ncpus(void) { +#if defined(_SC_NPROCESSORS_ONLN) + return sysconf((_SC_NPROCESSORS_ONLN)); +#elif defined(_SC_NPROC_ONLN) + return sysconf((_SC_NPROC_ONLN)); +#else + return (0); +#endif +} +#endif +#endif /* HAVE_SYSCONF */ + + +#ifdef __hpux + +#include <sys/pstat.h> + +static inline int +hpux_ncpus(void) { + struct pst_dynamic psd; + if (pstat_getdynamic(&psd, sizeof(psd), 1, 0) != -1) + return (psd.psd_proc_cnt); + else + return (0); +} + +#endif /* __hpux */ + +#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME) +#include <sys/types.h> /* for FreeBSD */ +#include <sys/param.h> /* for NetBSD */ +#include <sys/sysctl.h> + +static int +sysctl_ncpus(void) { + int ncpu, result; + size_t len; + + len = sizeof(ncpu); + result = sysctlbyname("hw.ncpu", &ncpu, &len , 0, 0); + if (result != -1) + return (ncpu); + return (0); +} +#endif + +unsigned int +isc_os_ncpus(void) { + long ncpus = 0; + +#ifdef __hpux + ncpus = hpux_ncpus(); +#elif defined(HAVE_SYSCONF) + ncpus = sysconf_ncpus(); +#endif +#if defined(HAVE_SYS_SYSCTL_H) && defined(HAVE_SYSCTLBYNAME) + if (ncpus <= 0) + ncpus = sysctl_ncpus(); +#endif + if (ncpus <= 0) + ncpus = 1; + + return ((unsigned int)ncpus); +} diff --git a/lib/isc/unix/pk11_api.c b/lib/isc/unix/pk11_api.c new file mode 100644 index 0000000..bbc55af --- /dev/null +++ b/lib/isc/unix/pk11_api.c @@ -0,0 +1,681 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <string.h> +#include <dlfcn.h> + +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/once.h> +#include <isc/print.h> +#include <isc/stdio.h> +#include <isc/thread.h> +#include <isc/util.h> + +#include <pkcs11/cryptoki.h> +#include <pkcs11/pkcs11.h> + +#define KEEP_PKCS11_NAMES +#include <pk11/pk11.h> +#include <pk11/internal.h> + +static void *hPK11 = NULL; +static char loaderrmsg[1024]; + +CK_RV +pkcs_C_Initialize(CK_VOID_PTR pReserved) { + CK_C_Initialize sym; + + if (hPK11 != NULL) + return (CKR_LIBRARY_ALREADY_INITIALIZED); + + hPK11 = dlopen(pk11_get_lib_name(), RTLD_NOW); + + if (hPK11 == NULL) { + snprintf(loaderrmsg, sizeof(loaderrmsg), + "dlopen(\"%s\") failed: %s\n", + pk11_get_lib_name(), dlerror()); + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + sym = (CK_C_Initialize)dlsym(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)dlsym(hPK11, "C_Finalize"); + if (sym == NULL) + return (CKR_SYMBOL_RESOLUTION_FAILED); + rv = (*sym)(pReserved); + if ((rv == CKR_OK) && (dlclose(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_GetSlotList)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_GetTokenInfo)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_GetMechanismInfo)dlsym(hPK11, + "C_GetMechanismInfo"); + } + 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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + hPK11 = dlopen(pk11_get_lib_name(), RTLD_NOW); + if (hPK11 == NULL) { + snprintf(loaderrmsg, sizeof(loaderrmsg), + "dlopen(\"%s\") failed: %s\n", + pk11_get_lib_name(), dlerror()); + return (CKR_LIBRARY_FAILED_TO_LOAD); + } + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_OpenSession)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_CloseSession)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_Login)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_Logout)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_CreateObject)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_DestroyObject)dlsym(hPK11, "C_DestroyObject"); + } + 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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_GetAttributeValue)dlsym(hPK11, + "C_GetAttributeValue"); + } + 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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_SetAttributeValue)dlsym(hPK11, + "C_SetAttributeValue"); + } + 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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_FindObjectsInit)dlsym(hPK11, "C_FindObjectsInit"); + } + 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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_FindObjects)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_FindObjectsFinal)dlsym(hPK11, + "C_FindObjectsFinal"); + } + 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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_EncryptInit)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_Encrypt)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_DigestInit)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_DigestUpdate)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_DigestFinal)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_SignInit)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_Sign)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_SignUpdate)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_SignFinal)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_VerifyInit)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_Verify)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_VerifyUpdate)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_VerifyFinal)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_GenerateKey)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_GenerateKeyPair)dlsym(hPK11, "C_GenerateKeyPair"); + } + 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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_DeriveKey)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_SeedRandom)dlsym(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; + static void *pPK11 = NULL; + + if (hPK11 == NULL) + return (CKR_LIBRARY_FAILED_TO_LOAD); + if ((sym == NULL) || (hPK11 != pPK11)) { + pPK11 = hPK11; + sym = (CK_C_GenerateRandom)dlsym(hPK11, "C_GenerateRandom"); + } + if (sym == NULL) + return (CKR_SYMBOL_RESOLUTION_FAILED); + return (*sym)(hSession, RandomData, ulRandomLen); +} diff --git a/lib/isc/unix/resource.c b/lib/isc/unix/resource.c new file mode 100644 index 0000000..6d5c5aa --- /dev/null +++ b/lib/isc/unix/resource.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <sys/types.h> +#include <sys/time.h> /* Required on some systems for <sys/resource.h>. */ +#include <sys/resource.h> + +#include <isc/platform.h> +#include <isc/resource.h> +#include <isc/result.h> +#include <isc/util.h> + +#ifdef __linux__ +#include <linux/fs.h> /* To get the large NR_OPEN. */ +#endif + +#if defined(__hpux) && defined(HAVE_SYS_DYNTUNE_H) +#include <sys/dyntune.h> +#endif + +#include "errno2result.h" + +static isc_result_t +resource2rlim(isc_resource_t resource, int *rlim_resource) { + isc_result_t result = ISC_R_SUCCESS; + + switch (resource) { + case isc_resource_coresize: + *rlim_resource = RLIMIT_CORE; + break; + case isc_resource_cputime: + *rlim_resource = RLIMIT_CPU; + break; + case isc_resource_datasize: + *rlim_resource = RLIMIT_DATA; + break; + case isc_resource_filesize: + *rlim_resource = RLIMIT_FSIZE; + break; + case isc_resource_lockedmemory: +#ifdef RLIMIT_MEMLOCK + *rlim_resource = RLIMIT_MEMLOCK; +#else + result = ISC_R_NOTIMPLEMENTED; +#endif + break; + case isc_resource_openfiles: +#ifdef RLIMIT_NOFILE + *rlim_resource = RLIMIT_NOFILE; +#else + result = ISC_R_NOTIMPLEMENTED; +#endif + break; + case isc_resource_processes: +#ifdef RLIMIT_NPROC + *rlim_resource = RLIMIT_NPROC; +#else + result = ISC_R_NOTIMPLEMENTED; +#endif + break; + case isc_resource_residentsize: +#ifdef RLIMIT_RSS + *rlim_resource = RLIMIT_RSS; +#else + result = ISC_R_NOTIMPLEMENTED; +#endif + break; + case isc_resource_stacksize: + *rlim_resource = RLIMIT_STACK; + break; + default: + /* + * This test is not very robust if isc_resource_t + * changes, but generates a clear assertion message. + */ + REQUIRE(resource >= isc_resource_coresize && + resource <= isc_resource_stacksize); + + result = ISC_R_RANGE; + break; + } + + return (result); +} + +isc_result_t +isc_resource_setlimit(isc_resource_t resource, isc_resourcevalue_t value) { + struct rlimit rl; + ISC_PLATFORM_RLIMITTYPE rlim_value; + int unixresult; + int unixresource; + isc_result_t result; + + result = resource2rlim(resource, &unixresource); + if (result != ISC_R_SUCCESS) + return (result); + + if (value == ISC_RESOURCE_UNLIMITED) + rlim_value = RLIM_INFINITY; + + else { + /* + * isc_resourcevalue_t was chosen as an unsigned 64 bit + * integer so that it could contain the maximum range of + * reasonable values. Unfortunately, this exceeds the typical + * range on Unix systems. Ensure the range of + * ISC_PLATFORM_RLIMITTYPE is not overflowed. + */ + isc_resourcevalue_t rlim_max; + bool rlim_t_is_signed = + (((double)(ISC_PLATFORM_RLIMITTYPE)-1) < 0); + + if (rlim_t_is_signed) + rlim_max = ~((ISC_PLATFORM_RLIMITTYPE)1 << + (sizeof(ISC_PLATFORM_RLIMITTYPE) * 8 - 1)); + else + rlim_max = (ISC_PLATFORM_RLIMITTYPE)-1; + + if (value > rlim_max) + value = rlim_max; + + rlim_value = value; + } + + rl.rlim_cur = rl.rlim_max = rlim_value; + unixresult = setrlimit(unixresource, &rl); + + if (unixresult == 0) + return (ISC_R_SUCCESS); + +#if defined(OPEN_MAX) && defined(__APPLE__) + /* + * The Darwin kernel doesn't accept RLIM_INFINITY for rlim_cur; the + * maximum possible value is OPEN_MAX. BIND8 used to use + * sysconf(_SC_OPEN_MAX) for such a case, but this value is much + * smaller than OPEN_MAX and is not really effective. + */ + if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) { + rl.rlim_cur = OPEN_MAX; + unixresult = setrlimit(unixresource, &rl); + if (unixresult == 0) + return (ISC_R_SUCCESS); + } +#elif defined(__linux__) +#ifndef NR_OPEN +#define NR_OPEN (1024*1024) +#endif + + /* + * Some Linux kernels don't accept RLIM_INFINIT; the maximum + * possible value is the NR_OPEN defined in linux/fs.h. + */ + if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) { + rl.rlim_cur = rl.rlim_max = NR_OPEN; + unixresult = setrlimit(unixresource, &rl); + if (unixresult == 0) + return (ISC_R_SUCCESS); + } +#elif defined(__hpux) && defined(HAVE_SYS_DYNTUNE_H) + if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) { + uint64_t maxfiles; + if (gettune("maxfiles_lim", &maxfiles) == 0) { + rl.rlim_cur = rl.rlim_max = maxfiles; + unixresult = setrlimit(unixresource, &rl); + if (unixresult == 0) + return (ISC_R_SUCCESS); + } + } +#endif + if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) { + if (getrlimit(unixresource, &rl) == 0) { + rl.rlim_cur = rl.rlim_max; + unixresult = setrlimit(unixresource, &rl); + if (unixresult == 0) + return (ISC_R_SUCCESS); + } + } + return (isc__errno2result(errno)); +} + +isc_result_t +isc_resource_getlimit(isc_resource_t resource, isc_resourcevalue_t *value) { + int unixresult; + int unixresource; + struct rlimit rl; + isc_result_t result; + + result = resource2rlim(resource, &unixresource); + if (result == ISC_R_SUCCESS) { + unixresult = getrlimit(unixresource, &rl); + INSIST(unixresult == 0); + *value = rl.rlim_max; + } + + return (result); +} + +isc_result_t +isc_resource_getcurlimit(isc_resource_t resource, isc_resourcevalue_t *value) { + int unixresult; + int unixresource; + struct rlimit rl; + isc_result_t result; + + result = resource2rlim(resource, &unixresource); + if (result == ISC_R_SUCCESS) { + unixresult = getrlimit(unixresource, &rl); + INSIST(unixresult == 0); + *value = rl.rlim_cur; + } + + return (result); +} diff --git a/lib/isc/unix/socket.c b/lib/isc/unix/socket.c new file mode 100644 index 0000000..5a40768 --- /dev/null +++ b/lib/isc/unix/socket.c @@ -0,0 +1,6848 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_SYSCTL_H +#include <sys/sysctl.h> +#endif +#include <sys/time.h> +#include <sys/uio.h> + +#if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H) +#include <linux/netlink.h> +#include <linux/rtnetlink.h> +#endif + +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <isc/buffer.h> +#include <isc/bufferlist.h> +#include <isc/condition.h> +#include <isc/formatcheck.h> +#include <isc/json.h> +#include <isc/list.h> +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/mutex.h> +#include <isc/net.h> +#include <isc/once.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/region.h> +#include <isc/resource.h> +#include <isc/socket.h> +#include <isc/stats.h> +#include <isc/strerror.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/thread.h> +#include <isc/util.h> +#include <isc/xml.h> + +#ifdef ISC_PLATFORM_HAVESYSUNH +#include <sys/un.h> +#endif +#ifdef ISC_PLATFORM_HAVEKQUEUE +#include <sys/event.h> +#endif +#ifdef ISC_PLATFORM_HAVEEPOLL +#include <sys/epoll.h> +#endif +#ifdef ISC_PLATFORM_HAVEDEVPOLL +#if defined(HAVE_SYS_DEVPOLL_H) +#include <sys/devpoll.h> +#elif defined(HAVE_DEVPOLL_H) +#include <devpoll.h> +#endif +#endif + +#include <netinet/tcp.h> + +#include "errno2result.h" + +/* See task.c about the following definition: */ +#ifdef ISC_PLATFORM_USETHREADS +#define USE_WATCHER_THREAD +#else +#define USE_SHARED_MANAGER +#endif /* ISC_PLATFORM_USETHREADS */ + +#ifndef USE_WATCHER_THREAD +#include "socket_p.h" +#include "../task_p.h" +#endif /* USE_WATCHER_THREAD */ + +#if defined(SO_BSDCOMPAT) && defined(__linux__) +#include <sys/utsname.h> +#endif + +#ifdef ISC_PLATFORM_HAVETFO +#include <netinet/tcp.h> +#endif + +/*% + * Choose the most preferable multiplex method. + */ +#ifdef ISC_PLATFORM_HAVEKQUEUE +#define USE_KQUEUE +#elif defined (ISC_PLATFORM_HAVEEPOLL) +#define USE_EPOLL +#elif defined (ISC_PLATFORM_HAVEDEVPOLL) +#define USE_DEVPOLL +typedef struct { + unsigned int want_read : 1, + want_write : 1; +} pollinfo_t; +#else +#define USE_SELECT +#endif /* ISC_PLATFORM_HAVEKQUEUE */ + +#ifndef USE_WATCHER_THREAD +#if defined(USE_KQUEUE) || defined(USE_EPOLL) || defined(USE_DEVPOLL) +struct isc_socketwait { + int nevents; +}; +#elif defined (USE_SELECT) +struct isc_socketwait { + fd_set *readset; + fd_set *writeset; + int nfds; + int maxfd; +}; +#endif /* USE_KQUEUE */ +#endif /* !USE_WATCHER_THREAD */ + +/* + * 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. + */ +int isc_dscp_check_value = -1; + +/*% + * Maximum number of allowable open sockets. This is also the maximum + * allowable socket file descriptor. + * + * Care should be taken before modifying this value for select(): + * The API standard doesn't ensure select() accept more than (the system default + * of) FD_SETSIZE descriptors, and the default size should in fact be fine in + * the vast majority of cases. This constant should therefore be increased only + * when absolutely necessary and possible, i.e., the server is exhausting all + * available file descriptors (up to FD_SETSIZE) and the select() function + * and FD_xxx macros support larger values than FD_SETSIZE (which may not + * always by true, but we keep using some of them to ensure as much + * portability as possible). Note also that overall server performance + * may be rather worsened with a larger value of this constant due to + * inherent scalability problems of select(). + * + * As a special note, this value shouldn't have to be touched if + * this is a build for an authoritative only DNS server. + */ +#ifndef ISC_SOCKET_MAXSOCKETS +#if defined(USE_KQUEUE) || defined(USE_EPOLL) || defined(USE_DEVPOLL) +#ifdef TUNE_LARGE +#define ISC_SOCKET_MAXSOCKETS 21000 +#else +#define ISC_SOCKET_MAXSOCKETS 4096 +#endif /* TUNE_LARGE */ +#elif defined(USE_SELECT) +#define ISC_SOCKET_MAXSOCKETS FD_SETSIZE +#endif /* USE_KQUEUE... */ +#endif /* ISC_SOCKET_MAXSOCKETS */ + +#ifdef USE_SELECT +/*% + * Mac OS X needs a special definition to support larger values in select(). + * We always define this because a larger value can be specified run-time. + */ +#ifdef __APPLE__ +#define _DARWIN_UNLIMITED_SELECT +#endif /* __APPLE__ */ +#endif /* USE_SELECT */ + +#ifdef ISC_SOCKET_USE_POLLWATCH +/*% + * If this macro is defined, enable workaround for a Solaris /dev/poll kernel + * bug: DP_POLL ioctl could keep sleeping even if socket I/O is possible for + * some of the specified FD. The idea is based on the observation that it's + * likely for a busy server to keep receiving packets. It specifically works + * as follows: the socket watcher is first initialized with the state of + * "poll_idle". While it's in the idle state it keeps sleeping until a socket + * event occurs. When it wakes up for a socket I/O event, it moves to the + * poll_active state, and sets the poll timeout to a short period + * (ISC_SOCKET_POLLWATCH_TIMEOUT msec). If timeout occurs in this state, the + * watcher goes to the poll_checking state with the same timeout period. + * In this state, the watcher tries to detect whether this is a break + * during intermittent events or the kernel bug is triggered. If the next + * polling reports an event within the short period, the previous timeout is + * likely to be a kernel bug, and so the watcher goes back to the active state. + * Otherwise, it moves to the idle state again. + * + * It's not clear whether this is a thread-related bug, but since we've only + * seen this with threads, this workaround is used only when enabling threads. + */ + +typedef enum { poll_idle, poll_active, poll_checking } pollstate_t; + +#ifndef ISC_SOCKET_POLLWATCH_TIMEOUT +#define ISC_SOCKET_POLLWATCH_TIMEOUT 10 +#endif /* ISC_SOCKET_POLLWATCH_TIMEOUT */ +#endif /* ISC_SOCKET_USE_POLLWATCH */ + +/*% + * Size of per-FD lock buckets. + */ +#ifdef ISC_PLATFORM_USETHREADS +#define FDLOCK_COUNT 1024 +#define FDLOCK_ID(fd) ((fd) % FDLOCK_COUNT) +#else +#define FDLOCK_COUNT 1 +#define FDLOCK_ID(fd) 0 +#endif /* ISC_PLATFORM_USETHREADS */ + +/*% + * Maximum number of events communicated with the kernel. There should normally + * be no need for having a large number. + */ +#if defined(USE_KQUEUE) || defined(USE_EPOLL) || defined(USE_DEVPOLL) +#ifndef ISC_SOCKET_MAXEVENTS +#ifdef TUNE_LARGE +#define ISC_SOCKET_MAXEVENTS 2048 +#else +#define ISC_SOCKET_MAXEVENTS 64 +#endif /* TUNE_LARGE */ +#endif +#endif + +/*% + * Some systems define the socket length argument as an int, some as size_t, + * some as socklen_t. This is here so it can be easily changed if needed. + */ +#ifndef ISC_SOCKADDR_LEN_T +#define ISC_SOCKADDR_LEN_T unsigned int +#endif + +/*% + * Define what the possible "soft" errors can be. These are non-fatal returns + * of various network related functions, like recv() and so on. + * + * For some reason, BSDI (and perhaps others) will sometimes return <0 + * from recv() but will have errno==0. This is broken, but we have to + * work around it here. + */ +#define SOFT_ERROR(e) ((e) == EAGAIN || \ + (e) == EWOULDBLOCK || \ + (e) == ENOBUFS || \ + (e) == EINTR || \ + (e) == 0) + +#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; + +#define SOCKET_MAGIC ISC_MAGIC('I', 'O', 'i', 'o') +#define VALID_SOCKET(s) ISC_MAGIC_VALID(s, 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. + */ +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO +#ifndef USE_CMSG +#define USE_CMSG 1 +#endif +#endif + +/*% + * NetBSD and FreeBSD can timestamp packets. XXXMLG Should we have + * a setsockopt() like interface to request timestamps, and if the OS + * doesn't do it for us, call gettimeofday() on every UDP receive? + */ +#ifdef SO_TIMESTAMP +#ifndef USE_CMSG +#define USE_CMSG 1 +#endif +#endif + +/*% + * The size to raise the receive buffer to (from BIND 8). + */ +#ifdef TUNE_LARGE +#ifdef sun +#define RCVBUFSIZE (1*1024*1024) +#else +#define RCVBUFSIZE (16*1024*1024) +#endif +#else +#define RCVBUFSIZE (32*1024) +#endif /* TUNE_LARGE */ + +/*% + * Instead of calculating the cmsgbuf lengths every time we take + * a rule of thumb approach - sizes are taken from x86_64 linux, + * multiplied by 2, everything should fit. Those sizes are not + * large enough to cause any concern. + */ +#if defined(USE_CMSG) && defined(ISC_PLATFORM_HAVEIN6PKTINFO) +#define CMSG_SP_IN6PKT 40 +#else +#define CMSG_SP_IN6PKT 0 +#endif + +#if defined(USE_CMSG) && defined(SO_TIMESTAMP) +#define CMSG_SP_TIMESTAMP 32 +#else +#define CMSG_SP_TIMESTAMP 0 +#endif + +#if defined(USE_CMSG) && (defined(IPV6_TCLASS) || defined(IP_TOS)) +#define CMSG_SP_TCTOS 24 +#else +#define CMSG_SP_TCTOS 0 +#endif + +#define CMSG_SP_INT 24 + +/* Align cmsg buffers to be safe on SPARC etc. */ +#define RECVCMSGBUFLEN ISC_ALIGN(2*(CMSG_SP_IN6PKT + CMSG_SP_TIMESTAMP + CMSG_SP_TCTOS)+1, sizeof(void*)) +#define SENDCMSGBUFLEN ISC_ALIGN(2*(CMSG_SP_IN6PKT + CMSG_SP_INT + CMSG_SP_TCTOS)+1, sizeof(void*)) + +/*% + * The number of times a send operation is repeated if the result is EINTR. + */ +#define NRETRIES 10 + +typedef struct isc__socket isc__socket_t; +typedef struct isc__socketmgr isc__socketmgr_t; + +#define NEWCONNSOCK(ev) ((isc__socket_t *)(ev)->newsocket) + +struct isc__socket { + /* Not locked. */ + isc_socket_t common; + isc__socketmgr_t *manager; + isc_mutex_t lock; + isc_sockettype_t type; + const isc_statscounter_t *statsindex; + + /* Locked by socket lock. */ + ISC_LINK(isc__socket_t) link; + unsigned int references; + int fd; + int pf; + char name[16]; + void * tag; + + 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; + + /* + * Internal events. Posted when a descriptor is readable or + * writable. These are statically allocated and never freed. + * They will be set to non-purgable before use. + */ + intev_t readable_ev; + intev_t writable_ev; + + isc_sockaddr_t peer_address; /* remote address */ + + unsigned int pending_recv : 1, + pending_send : 1, + pending_accept : 1, + listener : 1, /* listener socket */ + connected : 1, + connecting : 1, /* connect pending */ + bound : 1, /* bound to local addr */ + dupped : 1, + active : 1, /* currently active */ + pktdscp : 1; /* per packet dscp */ + +#ifdef ISC_PLATFORM_RECVOVERFLOW + unsigned char overflow; /* used for MSG_TRUNC fake */ +#endif + + void *fdwatcharg; + isc_sockfdwatch_t fdwatchcb; + int fdwatchflags; + isc_task_t *fdwatchtask; + unsigned int dscp; +}; + +#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. */ + isc_socketmgr_t common; + isc_mem_t *mctx; + isc_mutex_t lock; + isc_mutex_t *fdlock; + isc_stats_t *stats; +#ifdef USE_KQUEUE + int kqueue_fd; + int nevents; + struct kevent *events; +#endif /* USE_KQUEUE */ +#ifdef USE_EPOLL + int epoll_fd; + int nevents; + struct epoll_event *events; +#endif /* USE_EPOLL */ +#ifdef USE_DEVPOLL + int devpoll_fd; + isc_resourcevalue_t open_max; + unsigned int calls; + int nevents; + struct pollfd *events; +#endif /* USE_DEVPOLL */ +#ifdef USE_SELECT + int fd_bufsize; +#endif /* USE_SELECT */ + unsigned int maxsocks; +#ifdef ISC_PLATFORM_USETHREADS + int pipe_fds[2]; +#endif + + /* Locked by fdlock. */ + isc__socket_t **fds; + int *fdstate; +#if defined(USE_EPOLL) + uint32_t *epoll_events; +#endif +#ifdef USE_DEVPOLL + pollinfo_t *fdpollinfo; +#endif + + /* Locked by manager lock. */ + ISC_LIST(isc__socket_t) socklist; +#ifdef USE_SELECT + fd_set *read_fds; + fd_set *read_fds_copy; + fd_set *write_fds; + fd_set *write_fds_copy; + int maxfd; +#endif /* USE_SELECT */ + int reserved; /* unlocked */ +#ifdef USE_WATCHER_THREAD + isc_thread_t watcher; + isc_condition_t shutdown_ok; +#else /* USE_WATCHER_THREAD */ + unsigned int refs; +#endif /* USE_WATCHER_THREAD */ + int maxudp; +}; + +#ifdef USE_SHARED_MANAGER +static isc__socketmgr_t *socketmgr = NULL; +#endif /* USE_SHARED_MANAGER */ + +#define CLOSED 0 /* this one must be zero */ +#define MANAGED 1 +#define CLOSE_PENDING 2 + +/* + * send() and recv() iovec counts + */ +#define MAXSCATTERGATHER_SEND (ISC_SOCKET_MAXSCATTERGATHER) +#ifdef ISC_PLATFORM_RECVOVERFLOW +# define MAXSCATTERGATHER_RECV (ISC_SOCKET_MAXSCATTERGATHER + 1) +#else +# define MAXSCATTERGATHER_RECV (ISC_SOCKET_MAXSCATTERGATHER) +#endif + +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 void send_recvdone_event(isc__socket_t *, isc_socketevent_t **); +static void send_senddone_event(isc__socket_t *, isc_socketevent_t **); +static void send_connectdone_event(isc__socket_t *, isc_socket_connev_t **); +static void free_socket(isc__socket_t **); +static isc_result_t allocate_socket(isc__socketmgr_t *, isc_sockettype_t, + isc__socket_t **); +static void destroy(isc__socket_t **); +static void internal_accept(isc_task_t *, isc_event_t *); +static void internal_connect(isc_task_t *, isc_event_t *); +static void internal_recv(isc_task_t *, isc_event_t *); +static void internal_send(isc_task_t *, isc_event_t *); +static void internal_fdwatch_write(isc_task_t *, isc_event_t *); +static void internal_fdwatch_read(isc_task_t *, isc_event_t *); +static void process_cmsg(isc__socket_t *, struct msghdr *, isc_socketevent_t *); +static void build_msghdr_send(isc__socket_t *, char *, isc_socketevent_t *, + struct msghdr *, struct iovec *, size_t *); +static void build_msghdr_recv(isc__socket_t *, char *, isc_socketevent_t *, + struct msghdr *, struct iovec *, size_t *); +#ifdef USE_WATCHER_THREAD +static bool process_ctlfd(isc__socketmgr_t *manager); +#endif +static void setdscp(isc__socket_t *sock, isc_dscp_t dscp); + +/*% + * The following are intended for internal use (indicated by "isc__" + * prefix) but are not declared as static, allowing direct access from + * unit tests etc. + */ + +isc_result_t +isc__socket_open(isc_socket_t *sock0); +isc_result_t +isc__socket_close(isc_socket_t *sock0); +isc_result_t +isc__socket_create(isc_socketmgr_t *manager, int pf, isc_sockettype_t type, + isc_socket_t **socketp); +void +isc__socket_attach(isc_socket_t *sock, isc_socket_t **socketp); +void +isc__socket_detach(isc_socket_t **socketp); +isc_result_t +isc__socket_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, + unsigned int minimum, isc_task_t *task, + isc_taskaction_t action, void *arg); +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_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 +isc__socket_send(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, isc_taskaction_t action, void *arg); +isc_result_t +isc__socket_sendto(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo); +isc_result_t +isc__socket_sendv(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg); +isc_result_t +isc__socket_sendtov(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo); +isc_result_t +isc__socket_sendtov2(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + unsigned int flags); +isc_result_t +isc__socket_sendto2(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + isc_socketevent_t *event, unsigned int flags); +isc_socketevent_t * +isc_socket_socketevent(isc_mem_t *mctx, void *sender, + isc_eventtype_t eventtype, isc_taskaction_t action, + void *arg); + +void +isc__socket_cleanunix(isc_sockaddr_t *sockaddr, bool active); +isc_result_t +isc__socket_permunix(isc_sockaddr_t *sockaddr, uint32_t perm, + uint32_t owner, uint32_t group); +isc_result_t +isc__socket_bind(isc_socket_t *sock, isc_sockaddr_t *sockaddr, + unsigned int options); +isc_result_t +isc__socket_filter(isc_socket_t *sock, const char *filter); +isc_result_t +isc__socket_listen(isc_socket_t *sock, unsigned int backlog); +isc_result_t +isc__socket_accept(isc_socket_t *sock, + isc_task_t *task, isc_taskaction_t action, void *arg); +isc_result_t +isc__socket_connect(isc_socket_t *sock, isc_sockaddr_t *addr, + isc_task_t *task, isc_taskaction_t action, + void *arg); +isc_result_t +isc__socket_getpeername(isc_socket_t *sock, isc_sockaddr_t *addressp); +isc_result_t +isc__socket_getsockname(isc_socket_t *sock, isc_sockaddr_t *addressp); +void +isc__socket_cancel(isc_socket_t *sock, isc_task_t *task, unsigned int how); +isc_sockettype_t +isc__socket_gettype(isc_socket_t *sock); +bool +isc__socket_isbound(isc_socket_t *sock); +void +isc__socket_ipv6only(isc_socket_t *sock, bool yes); +void +isc__socket_dscp(isc_socket_t *sock, isc_dscp_t dscp); +isc_result_t +isc__socket_fdwatchcreate(isc_socketmgr_t *manager, int fd, int flags, + isc_sockfdwatch_t callback, void *cbarg, + isc_task_t *task, isc_socket_t **socketp); +isc_result_t +isc__socket_fdwatchpoke(isc_socket_t *sock, int flags); +isc_result_t +isc__socket_dup(isc_socket_t *sock, isc_socket_t **socketp); +int +isc__socket_getfd(isc_socket_t *sock); + +isc_result_t +isc__socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp); +isc_result_t +isc__socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp, + unsigned int maxsocks); +isc_result_t +isc_socketmgr_getmaxsockets(isc_socketmgr_t *manager0, unsigned int *nsockp); +void +isc_socketmgr_setstats(isc_socketmgr_t *manager0, isc_stats_t *stats); +void +isc__socketmgr_destroy(isc_socketmgr_t **managerp); +void +isc__socket_setname(isc_socket_t *socket0, const char *name, void *tag); +const char * +isc__socket_getname(isc_socket_t *socket0); +void * +isc__socket_gettag(isc_socket_t *socket0); + +#ifdef HAVE_LIBXML2 +void +isc__socketmgr_renderxml(isc_socketmgr_t *mgr0, xmlTextWriterPtr writer); +#endif +#ifdef HAVE_JSON +isc_result_t +isc__socketmgr_renderjson(isc_socketmgr_t *mgr0, json_object *stats); +#endif + +static struct { + isc_socketmethods_t methods; + + /*% + * The following are defined just for avoiding unused static functions. + */ + void *recvv, *send, *sendv, *sendto2, *cleanunix, *permunix, *filter, + *listen, *accept, *getpeername, *isbound; +} socketmethods = { + { + isc__socket_attach, + isc__socket_detach, + isc__socket_bind, + isc__socket_sendto, + isc__socket_sendto2, + isc__socket_connect, + isc__socket_recv, + isc__socket_recv2, + isc__socket_cancel, + isc__socket_getsockname, + isc__socket_gettype, + isc__socket_ipv6only, + isc__socket_fdwatchpoke, + isc__socket_dup, + isc__socket_getfd, + isc__socket_dscp + }, + (void *)isc__socket_recvv, (void *)isc__socket_send, + (void *)isc__socket_sendv, (void *)isc__socket_sendto2, + (void *)isc__socket_cleanunix, (void *)isc__socket_permunix, + (void *)isc__socket_filter, (void *)isc__socket_listen, + (void *)isc__socket_accept, (void *)isc__socket_getpeername, + (void *)isc__socket_isbound +}; + +static isc_socketmgrmethods_t socketmgrmethods = { + isc__socketmgr_destroy, + isc__socket_create, + isc__socket_fdwatchcreate +}; + +#define SELECT_POKE_SHUTDOWN (-1) +#define SELECT_POKE_NOTHING (-2) +#define SELECT_POKE_READ (-3) +#define SELECT_POKE_ACCEPT (-3) /*%< Same as _READ */ +#define SELECT_POKE_WRITE (-4) +#define SELECT_POKE_CONNECT (-4) /*%< Same as _WRITE */ +#define SELECT_POKE_CLOSE (-5) + +#define SOCK_DEAD(s) ((s)->references == 0) + +/*% + * Shortcut index arrays to get access to statistics counters. + */ +enum { + STATID_OPEN = 0, + STATID_OPENFAIL = 1, + STATID_CLOSE = 2, + STATID_BINDFAIL = 3, + STATID_CONNECTFAIL = 4, + STATID_CONNECT = 5, + STATID_ACCEPTFAIL = 6, + STATID_ACCEPT = 7, + STATID_SENDFAIL = 8, + STATID_RECVFAIL = 9, + STATID_ACTIVE = 10 +}; +static const isc_statscounter_t udp4statsindex[] = { + isc_sockstatscounter_udp4open, + isc_sockstatscounter_udp4openfail, + isc_sockstatscounter_udp4close, + isc_sockstatscounter_udp4bindfail, + isc_sockstatscounter_udp4connectfail, + isc_sockstatscounter_udp4connect, + -1, + -1, + isc_sockstatscounter_udp4sendfail, + isc_sockstatscounter_udp4recvfail, + isc_sockstatscounter_udp4active +}; +static const isc_statscounter_t udp6statsindex[] = { + isc_sockstatscounter_udp6open, + isc_sockstatscounter_udp6openfail, + isc_sockstatscounter_udp6close, + isc_sockstatscounter_udp6bindfail, + isc_sockstatscounter_udp6connectfail, + isc_sockstatscounter_udp6connect, + -1, + -1, + isc_sockstatscounter_udp6sendfail, + isc_sockstatscounter_udp6recvfail, + isc_sockstatscounter_udp6active +}; +static const isc_statscounter_t tcp4statsindex[] = { + isc_sockstatscounter_tcp4open, + isc_sockstatscounter_tcp4openfail, + isc_sockstatscounter_tcp4close, + isc_sockstatscounter_tcp4bindfail, + isc_sockstatscounter_tcp4connectfail, + isc_sockstatscounter_tcp4connect, + isc_sockstatscounter_tcp4acceptfail, + isc_sockstatscounter_tcp4accept, + isc_sockstatscounter_tcp4sendfail, + isc_sockstatscounter_tcp4recvfail, + isc_sockstatscounter_tcp4active +}; +static const isc_statscounter_t tcp6statsindex[] = { + isc_sockstatscounter_tcp6open, + isc_sockstatscounter_tcp6openfail, + isc_sockstatscounter_tcp6close, + isc_sockstatscounter_tcp6bindfail, + isc_sockstatscounter_tcp6connectfail, + isc_sockstatscounter_tcp6connect, + isc_sockstatscounter_tcp6acceptfail, + isc_sockstatscounter_tcp6accept, + isc_sockstatscounter_tcp6sendfail, + isc_sockstatscounter_tcp6recvfail, + isc_sockstatscounter_tcp6active +}; +static const isc_statscounter_t unixstatsindex[] = { + isc_sockstatscounter_unixopen, + isc_sockstatscounter_unixopenfail, + isc_sockstatscounter_unixclose, + isc_sockstatscounter_unixbindfail, + isc_sockstatscounter_unixconnectfail, + isc_sockstatscounter_unixconnect, + isc_sockstatscounter_unixacceptfail, + isc_sockstatscounter_unixaccept, + isc_sockstatscounter_unixsendfail, + isc_sockstatscounter_unixrecvfail, + isc_sockstatscounter_unixactive +}; +static const isc_statscounter_t fdwatchstatsindex[] = { + -1, + -1, + isc_sockstatscounter_fdwatchclose, + isc_sockstatscounter_fdwatchbindfail, + isc_sockstatscounter_fdwatchconnectfail, + isc_sockstatscounter_fdwatchconnect, + -1, + -1, + isc_sockstatscounter_fdwatchsendfail, + isc_sockstatscounter_fdwatchrecvfail, + -1 +}; +static const isc_statscounter_t rawstatsindex[] = { + isc_sockstatscounter_rawopen, + isc_sockstatscounter_rawopenfail, + isc_sockstatscounter_rawclose, + -1, + -1, + -1, + -1, + -1, + -1, + isc_sockstatscounter_rawrecvfail, + isc_sockstatscounter_rawactive +}; + +#if defined(USE_KQUEUE) || defined(USE_EPOLL) || defined(USE_DEVPOLL) || \ + defined(USE_WATCHER_THREAD) +static void +manager_log(isc__socketmgr_t *sockmgr, + isc_logcategory_t *category, isc_logmodule_t *module, int level, + const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6); +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); +} +#endif + +static void +socket_log(isc__socket_t *sock, isc_sockaddr_t *address, + isc_logcategory_t *category, isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int message, + const char *fmt, ...) ISC_FORMAT_PRINTF(9, 10); +static void +socket_log(isc__socket_t *sock, isc_sockaddr_t *address, + isc_logcategory_t *category, isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int message, + const char *fmt, ...) +{ + char msgbuf[2048]; + char peerbuf[ISC_SOCKADDR_FORMATSIZE]; + 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_iwrite(isc_lctx, category, module, level, + msgcat, msgset, message, + "socket %p: %s", sock, msgbuf); + } else { + isc_sockaddr_format(address, peerbuf, sizeof(peerbuf)); + isc_log_iwrite(isc_lctx, category, module, level, + msgcat, msgset, message, + "socket %p %s: %s", sock, peerbuf, msgbuf); + } +} + +#if defined(_AIX) && defined(ISC_NET_BSD44MSGHDR) && \ + defined(USE_CMSG) && defined(IPV6_RECVPKTINFO) +/* + * AIX has a kernel bug where IPV6_RECVPKTINFO gets cleared by + * setting IPV6_V6ONLY. + */ +static void +FIX_IPV6_RECVPKTINFO(isc__socket_t *sock) +{ + char strbuf[ISC_STRERRORSIZE]; + int on = 1; + + if (sock->pf != AF_INET6 || sock->type != isc_sockettype_udp) + return; + + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + (void *)&on, sizeof(on)) < 0) { + + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_RECVPKTINFO) " + "%s: %s", sock->fd, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } +} +#else +#define FIX_IPV6_RECVPKTINFO(sock) (void)0 +#endif + +/*% + * Increment socket-related statistics counters. + */ +static inline void +inc_stats(isc_stats_t *stats, isc_statscounter_t counterid) { + REQUIRE(counterid != -1); + + if (stats != NULL) + isc_stats_increment(stats, counterid); +} + +/*% + * Decrement socket-related statistics counters. + */ +static inline void +dec_stats(isc_stats_t *stats, isc_statscounter_t counterid) { + REQUIRE(counterid != -1); + + if (stats != NULL) + isc_stats_decrement(stats, counterid); +} + +static inline isc_result_t +watch_fd(isc__socketmgr_t *manager, int fd, int msg) { + isc_result_t result = ISC_R_SUCCESS; + +#ifdef USE_KQUEUE + struct kevent evchange; + + memset(&evchange, 0, sizeof(evchange)); + if (msg == SELECT_POKE_READ) + evchange.filter = EVFILT_READ; + else + evchange.filter = EVFILT_WRITE; + evchange.flags = EV_ADD; + evchange.ident = fd; + if (kevent(manager->kqueue_fd, &evchange, 1, NULL, 0, NULL) != 0) + result = isc__errno2result(errno); + + return (result); +#elif defined(USE_EPOLL) + struct epoll_event event; + uint32_t oldevents; + int ret; + int op; + + oldevents = manager->epoll_events[fd]; + if (msg == SELECT_POKE_READ) + manager->epoll_events[fd] |= EPOLLIN; + else + manager->epoll_events[fd] |= EPOLLOUT; + + event.events = manager->epoll_events[fd]; + memset(&event.data, 0, sizeof(event.data)); + event.data.fd = fd; + + op = (oldevents == 0U) ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; + ret = epoll_ctl(manager->epoll_fd, op, fd, &event); + if (ret == -1) { + if (errno == EEXIST) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "epoll_ctl(ADD/MOD) returned " + "EEXIST for fd %d", fd); + result = isc__errno2result(errno); + } + + return (result); +#elif defined(USE_DEVPOLL) + struct pollfd pfd; + int lockid = FDLOCK_ID(fd); + + memset(&pfd, 0, sizeof(pfd)); + if (msg == SELECT_POKE_READ) + pfd.events = POLLIN; + else + pfd.events = POLLOUT; + pfd.fd = fd; + pfd.revents = 0; + LOCK(&manager->fdlock[lockid]); + if (write(manager->devpoll_fd, &pfd, sizeof(pfd)) == -1) + result = isc__errno2result(errno); + else { + if (msg == SELECT_POKE_READ) + manager->fdpollinfo[fd].want_read = 1; + else + manager->fdpollinfo[fd].want_write = 1; + } + UNLOCK(&manager->fdlock[lockid]); + + return (result); +#elif defined(USE_SELECT) + LOCK(&manager->lock); + if (msg == SELECT_POKE_READ) + FD_SET(fd, manager->read_fds); + if (msg == SELECT_POKE_WRITE) + FD_SET(fd, manager->write_fds); + UNLOCK(&manager->lock); + + return (result); +#endif +} + +static inline isc_result_t +unwatch_fd(isc__socketmgr_t *manager, int fd, int msg) { + isc_result_t result = ISC_R_SUCCESS; + +#ifdef USE_KQUEUE + struct kevent evchange; + + memset(&evchange, 0, sizeof(evchange)); + if (msg == SELECT_POKE_READ) + evchange.filter = EVFILT_READ; + else + evchange.filter = EVFILT_WRITE; + evchange.flags = EV_DELETE; + evchange.ident = fd; + if (kevent(manager->kqueue_fd, &evchange, 1, NULL, 0, NULL) != 0) + result = isc__errno2result(errno); + + return (result); +#elif defined(USE_EPOLL) + struct epoll_event event; + int ret; + int op; + + if (msg == SELECT_POKE_READ) + manager->epoll_events[fd] &= ~(EPOLLIN); + else + manager->epoll_events[fd] &= ~(EPOLLOUT); + + event.events = manager->epoll_events[fd]; + memset(&event.data, 0, sizeof(event.data)); + event.data.fd = fd; + + op = (event.events == 0U) ? EPOLL_CTL_DEL : EPOLL_CTL_MOD; + ret = epoll_ctl(manager->epoll_fd, op, fd, &event); + if (ret == -1 && errno != ENOENT) { + char strbuf[ISC_STRERRORSIZE]; + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "epoll_ctl(DEL), %d: %s", fd, strbuf); + result = ISC_R_UNEXPECTED; + } + return (result); +#elif defined(USE_DEVPOLL) + struct pollfd pfds[2]; + size_t writelen = sizeof(pfds[0]); + int lockid = FDLOCK_ID(fd); + + memset(pfds, 0, sizeof(pfds)); + pfds[0].events = POLLREMOVE; + pfds[0].fd = fd; + + /* + * Canceling read or write polling via /dev/poll is tricky. Since it + * only provides a way of canceling per FD, we may need to re-poll the + * socket for the other operation. + */ + LOCK(&manager->fdlock[lockid]); + if (msg == SELECT_POKE_READ && + manager->fdpollinfo[fd].want_write == 1) { + pfds[1].events = POLLOUT; + pfds[1].fd = fd; + writelen += sizeof(pfds[1]); + } + if (msg == SELECT_POKE_WRITE && + manager->fdpollinfo[fd].want_read == 1) { + pfds[1].events = POLLIN; + pfds[1].fd = fd; + writelen += sizeof(pfds[1]); + } + + if (write(manager->devpoll_fd, pfds, writelen) == -1) + result = isc__errno2result(errno); + else { + if (msg == SELECT_POKE_READ) + manager->fdpollinfo[fd].want_read = 0; + else + manager->fdpollinfo[fd].want_write = 0; + } + UNLOCK(&manager->fdlock[lockid]); + + return (result); +#elif defined(USE_SELECT) + LOCK(&manager->lock); + if (msg == SELECT_POKE_READ) + FD_CLR(fd, manager->read_fds); + else if (msg == SELECT_POKE_WRITE) + FD_CLR(fd, manager->write_fds); + UNLOCK(&manager->lock); + + return (result); +#endif +} + +static void +wakeup_socket(isc__socketmgr_t *manager, int fd, int msg) { + isc_result_t result; + int lockid = FDLOCK_ID(fd); + + /* + * This is a wakeup on a socket. If the socket is not in the + * process of being closed, start watching it for either reads + * or writes. + */ + + INSIST(fd >= 0 && fd < (int)manager->maxsocks); + + if (msg == SELECT_POKE_CLOSE) { + /* No one should be updating fdstate, so no need to lock it */ + INSIST(manager->fdstate[fd] == CLOSE_PENDING); + manager->fdstate[fd] = CLOSED; + (void)unwatch_fd(manager, fd, SELECT_POKE_READ); + (void)unwatch_fd(manager, fd, SELECT_POKE_WRITE); + (void)close(fd); + return; + } + + LOCK(&manager->fdlock[lockid]); + if (manager->fdstate[fd] == CLOSE_PENDING) { + UNLOCK(&manager->fdlock[lockid]); + + /* + * We accept (and ignore) any error from unwatch_fd() as we are + * closing the socket, hoping it doesn't leave dangling state in + * the kernel. + * Note that unwatch_fd() must be called after releasing the + * fdlock; otherwise it could cause deadlock due to a lock order + * reversal. + */ + (void)unwatch_fd(manager, fd, SELECT_POKE_READ); + (void)unwatch_fd(manager, fd, SELECT_POKE_WRITE); + return; + } + if (manager->fdstate[fd] != MANAGED) { + UNLOCK(&manager->fdlock[lockid]); + return; + } + UNLOCK(&manager->fdlock[lockid]); + + /* + * Set requested bit. + */ + result = watch_fd(manager, fd, msg); + if (result != ISC_R_SUCCESS) { + /* + * XXXJT: what should we do? Ignoring the failure of watching + * a socket will make the application dysfunctional, but there + * seems to be no reasonable recovery process. + */ + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "failed to start watching FD (%d): %s", + fd, isc_result_totext(result)); + } +} + +#ifdef USE_WATCHER_THREAD +/* + * Poke the select loop when there is something for us to do. + * The write is required (by POSIX) to complete. That is, we + * will not get partial writes. + */ +static void +select_poke(isc__socketmgr_t *mgr, int fd, int msg) { + int cc; + int buf[2]; + char strbuf[ISC_STRERRORSIZE]; + + buf[0] = fd; + buf[1] = msg; + + do { + cc = write(mgr->pipe_fds[1], buf, sizeof(buf)); +#ifdef ENOSR + /* + * Treat ENOSR as EAGAIN but loop slowly as it is + * unlikely to clear fast. + */ + if (cc < 0 && errno == ENOSR) { + sleep(1); + errno = EAGAIN; + } +#endif + } while (cc < 0 && SOFT_ERROR(errno)); + + if (cc < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_WRITEFAILED, + "write() failed " + "during watcher poke: %s"), + strbuf); + } + + INSIST(cc == sizeof(buf)); +} + +/* + * Read a message on the internal fd. + */ +static void +select_readmsg(isc__socketmgr_t *mgr, int *fd, int *msg) { + int buf[2]; + int cc; + char strbuf[ISC_STRERRORSIZE]; + + cc = read(mgr->pipe_fds[0], buf, sizeof(buf)); + if (cc < 0) { + *msg = SELECT_POKE_NOTHING; + *fd = -1; /* Silence compiler. */ + if (SOFT_ERROR(errno)) + return; + + isc__strerror(errno, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_READFAILED, + "read() failed " + "during watcher poke: %s"), + strbuf); + } + INSIST(cc == sizeof(buf)); + + *fd = buf[0]; + *msg = buf[1]; +} +#else /* USE_WATCHER_THREAD */ +/* + * Update the state of the socketmgr when something changes. + */ +static void +select_poke(isc__socketmgr_t *manager, int fd, int msg) { + if (msg == SELECT_POKE_SHUTDOWN) + return; + else if (fd >= 0) + wakeup_socket(manager, fd, msg); + return; +} +#endif /* USE_WATCHER_THREAD */ + +/* + * Make a fd non-blocking. + */ +static isc_result_t +make_nonblock(int fd) { + int ret; + char strbuf[ISC_STRERRORSIZE]; +#ifdef USE_FIONBIO_IOCTL + int on = 1; +#else + int flags; +#endif + +#ifdef USE_FIONBIO_IOCTL + ret = ioctl(fd, FIONBIO, (char *)&on); +#else + flags = fcntl(fd, F_GETFL, 0); + flags |= PORT_NONBLOCK; + ret = fcntl(fd, F_SETFL, flags); +#endif + + if (ret == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, +#ifdef USE_FIONBIO_IOCTL + "ioctl(%d, FIONBIO, &on): %s", fd, +#else + "fcntl(%d, F_SETFL, %d): %s", fd, flags, +#endif + strbuf); + + return (ISC_R_UNEXPECTED); + } + + return (ISC_R_SUCCESS); +} + +#ifdef USE_CMSG +/* + * Not all OSes support advanced CMSG macros: CMSG_LEN and CMSG_SPACE. + * In order to ensure as much portability as possible, we provide wrapper + * functions of these macros. + * Note that cmsg_space() could run slow on OSes that do not have + * CMSG_SPACE. + */ +static inline ISC_SOCKADDR_LEN_T +cmsg_len(ISC_SOCKADDR_LEN_T len) { +#ifdef CMSG_LEN + return (CMSG_LEN(len)); +#else + ISC_SOCKADDR_LEN_T hdrlen; + + /* + * Cast NULL so that any pointer arithmetic performed by CMSG_DATA + * is correct. + */ + hdrlen = (ISC_SOCKADDR_LEN_T)CMSG_DATA(((struct cmsghdr *)NULL)); + return (hdrlen + len); +#endif +} + +static inline ISC_SOCKADDR_LEN_T +cmsg_space(ISC_SOCKADDR_LEN_T len) { +#ifdef CMSG_SPACE + return (CMSG_SPACE(len)); +#else + struct msghdr msg; + struct cmsghdr *cmsgp; + /* + * XXX: The buffer length is an ad-hoc value, but should be enough + * in a practical sense. + */ + char dummybuf[sizeof(struct cmsghdr) + 1024]; + + memset(&msg, 0, sizeof(msg)); + msg.msg_control = dummybuf; + msg.msg_controllen = sizeof(dummybuf); + + cmsgp = (struct cmsghdr *)dummybuf; + cmsgp->cmsg_len = cmsg_len(len); + + cmsgp = CMSG_NXTHDR(&msg, cmsgp); + if (cmsgp != NULL) + return ((char *)cmsgp - (char *)msg.msg_control); + else + return (0); +#endif +} +#endif /* USE_CMSG */ + +/* + * Process control messages received on a socket. + */ +static void +process_cmsg(isc__socket_t *sock, struct msghdr *msg, isc_socketevent_t *dev) { +#ifdef ISC_NET_BSD44MSGHDR +#ifdef USE_CMSG + struct cmsghdr *cmsgp; +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO + struct in6_pktinfo *pktinfop; +#endif +#ifdef SO_TIMESTAMP + void *timevalp; +#endif +#endif +#endif + + /* + * sock is used only when ISC_NET_BSD44MSGHDR and USE_CMSG are defined. + * msg and dev are used only when ISC_NET_BSD44MSGHDR is defined. + * They are all here, outside of the CPP tests, because it is + * more consistent with the usual ISC coding style. + */ + UNUSED(sock); + UNUSED(msg); + UNUSED(dev); + +#ifdef ISC_NET_BSD44MSGHDR + +#ifdef MSG_TRUNC + if ((msg->msg_flags & MSG_TRUNC) == MSG_TRUNC) + dev->attributes |= ISC_SOCKEVENTATTR_TRUNC; +#endif + +#ifdef MSG_CTRUNC + if ((msg->msg_flags & MSG_CTRUNC) == MSG_CTRUNC) + dev->attributes |= ISC_SOCKEVENTATTR_CTRUNC; +#endif + +#ifndef USE_CMSG + return; +#else + if (msg->msg_controllen == 0U || msg->msg_control == NULL) + return; + +#ifdef SO_TIMESTAMP + timevalp = NULL; +#endif +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO + pktinfop = NULL; +#endif + + cmsgp = CMSG_FIRSTHDR(msg); + while (cmsgp != NULL) { + socket_log(sock, NULL, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_PROCESSCMSG, + "processing cmsg %p", cmsgp); + +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO + if (cmsgp->cmsg_level == IPPROTO_IPV6 + && cmsgp->cmsg_type == IPV6_PKTINFO) { + + pktinfop = (struct in6_pktinfo *)CMSG_DATA(cmsgp); + memmove(&dev->pktinfo, pktinfop, + sizeof(struct in6_pktinfo)); + dev->attributes |= ISC_SOCKEVENTATTR_PKTINFO; + socket_log(sock, NULL, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_IFRECEIVED, + "interface received on ifindex %u", + dev->pktinfo.ipi6_ifindex); + if (IN6_IS_ADDR_MULTICAST(&pktinfop->ipi6_addr)) + dev->attributes |= ISC_SOCKEVENTATTR_MULTICAST; + goto next; + } +#endif + +#ifdef SO_TIMESTAMP + if (cmsgp->cmsg_level == SOL_SOCKET + && cmsgp->cmsg_type == SCM_TIMESTAMP) { + struct timeval tv; + timevalp = CMSG_DATA(cmsgp); + memmove(&tv, timevalp, sizeof(tv)); + dev->timestamp.seconds = tv.tv_sec; + dev->timestamp.nanoseconds = tv.tv_usec * 1000; + dev->attributes |= ISC_SOCKEVENTATTR_TIMESTAMP; + goto next; + } +#endif + +#ifdef IPV6_TCLASS + if (cmsgp->cmsg_level == IPPROTO_IPV6 + && cmsgp->cmsg_type == IPV6_TCLASS) { + dev->dscp = *(int *)CMSG_DATA(cmsgp); + dev->dscp >>= 2; + dev->attributes |= ISC_SOCKEVENTATTR_DSCP; + goto next; + } +#endif + +#ifdef IP_TOS + if (cmsgp->cmsg_level == IPPROTO_IP + && (cmsgp->cmsg_type == IP_TOS +#ifdef IP_RECVTOS + || cmsgp->cmsg_type == IP_RECVTOS +#endif + )) { + dev->dscp = (int) *(unsigned char *)CMSG_DATA(cmsgp); + dev->dscp >>= 2; + dev->attributes |= ISC_SOCKEVENTATTR_DSCP; + goto next; + } +#endif + next: + cmsgp = CMSG_NXTHDR(msg, cmsgp); + } +#endif /* USE_CMSG */ + +#endif /* ISC_NET_BSD44MSGHDR */ +} + +/* + * 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. + * + * If write_countp != NULL, *write_countp will hold the number of bytes + * this transaction can send. + */ +static void +build_msghdr_send(isc__socket_t *sock, char* cmsgbuf, isc_socketevent_t *dev, + struct msghdr *msg, struct iovec *iov, size_t *write_countp) +{ + unsigned int iovcount; + isc_buffer_t *buffer; + isc_region_t used; + size_t write_count; + size_t skip_count; +#ifdef ISC_NET_BSD44MSGHDR + struct cmsghdr *cmsgp; +#endif + + memset(msg, 0, sizeof(*msg)); + + if (!sock->connected) { + msg->msg_name = (void *)&dev->address.type.sa; + msg->msg_namelen = dev->address.length; + } else { + msg->msg_name = NULL; + msg->msg_namelen = 0; + } + + buffer = ISC_LIST_HEAD(dev->bufferlist); + write_count = 0; + iovcount = 0; + + /* + * Single buffer I/O? Skip what we've done so far in this region. + */ + if (buffer == NULL) { + write_count = dev->region.length - dev->n; + iov[0].iov_base = (void *)(dev->region.base + dev->n); + iov[0].iov_len = write_count; + iovcount = 1; + + goto config; + } + + /* + * Multibuffer I/O. + * Skip the data in the buffer list that we have already written. + */ + skip_count = dev->n; + while (buffer != NULL) { + REQUIRE(ISC_BUFFER_VALID(buffer)); + if (skip_count < isc_buffer_usedlength(buffer)) + break; + skip_count -= isc_buffer_usedlength(buffer); + buffer = ISC_LIST_NEXT(buffer, link); + } + + while (buffer != NULL) { + INSIST(iovcount < MAXSCATTERGATHER_SEND); + + isc_buffer_usedregion(buffer, &used); + + if (used.length > 0) { + iov[iovcount].iov_base = (void *)(used.base + + skip_count); + iov[iovcount].iov_len = used.length - skip_count; + write_count += (used.length - skip_count); + skip_count = 0; + iovcount++; + } + buffer = ISC_LIST_NEXT(buffer, link); + } + + INSIST(skip_count == 0U); + + config: + msg->msg_iov = iov; + msg->msg_iovlen = iovcount; + +#ifdef ISC_NET_BSD44MSGHDR + msg->msg_control = NULL; + msg->msg_controllen = 0; + msg->msg_flags = 0; +#if defined(USE_CMSG) + +#if defined(ISC_PLATFORM_HAVEIN6PKTINFO) + if ((sock->type == isc_sockettype_udp) && + ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0)) + { + struct in6_pktinfo *pktinfop; + + socket_log(sock, NULL, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_SENDTODATA, + "sendto pktinfo data, ifindex %u", + dev->pktinfo.ipi6_ifindex); + + msg->msg_control = (void *)cmsgbuf; + msg->msg_controllen = cmsg_space(sizeof(struct in6_pktinfo)); + INSIST(msg->msg_controllen <= SENDCMSGBUFLEN); + + cmsgp = (struct cmsghdr *)cmsgbuf; + cmsgp->cmsg_level = IPPROTO_IPV6; + cmsgp->cmsg_type = IPV6_PKTINFO; + cmsgp->cmsg_len = cmsg_len(sizeof(struct in6_pktinfo)); + pktinfop = (struct in6_pktinfo *)CMSG_DATA(cmsgp); + memmove(pktinfop, &dev->pktinfo, sizeof(struct in6_pktinfo)); + } +#endif + +#if defined(IPV6_USE_MIN_MTU) + if ((sock->type == isc_sockettype_udp) && + ((dev->attributes & ISC_SOCKEVENTATTR_USEMINMTU) != 0)) + { + int use_min_mtu = 1; /* -1, 0, 1 */ + + cmsgp = (struct cmsghdr *)(cmsgbuf + + msg->msg_controllen); + + msg->msg_control = (void *)cmsgbuf; + msg->msg_controllen += cmsg_space(sizeof(use_min_mtu)); + INSIST(msg->msg_controllen <= SENDCMSGBUFLEN); + + cmsgp->cmsg_level = IPPROTO_IPV6; + cmsgp->cmsg_type = IPV6_USE_MIN_MTU; + cmsgp->cmsg_len = cmsg_len(sizeof(use_min_mtu)); + memmove(CMSG_DATA(cmsgp), &use_min_mtu, sizeof(use_min_mtu)); + } +#endif + + if (isc_dscp_check_value > -1) { + if (sock->type == isc_sockettype_udp) + INSIST((int)dev->dscp == isc_dscp_check_value); + else if (sock->type == isc_sockettype_tcp) + INSIST((int)sock->dscp == isc_dscp_check_value); + } + +#if defined(IP_TOS) || (defined(IPPROTO_IPV6) && defined(IPV6_TCLASS)) + if ((sock->type == isc_sockettype_udp) && + ((dev->attributes & ISC_SOCKEVENTATTR_DSCP) != 0)) + { + int dscp = (dev->dscp << 2) & 0xff; + + INSIST(dev->dscp < 0x40); + +#ifdef IP_TOS + if (sock->pf == AF_INET && sock->pktdscp) { + cmsgp = (struct cmsghdr *)(cmsgbuf + + msg->msg_controllen); + msg->msg_control = (void *)cmsgbuf; + msg->msg_controllen += cmsg_space(sizeof(dscp)); + INSIST(msg->msg_controllen <= SENDCMSGBUFLEN); + + cmsgp->cmsg_level = IPPROTO_IP; + cmsgp->cmsg_type = IP_TOS; + cmsgp->cmsg_len = cmsg_len(sizeof(char)); + *(unsigned char*)CMSG_DATA(cmsgp) = dscp; + } else if (sock->pf == AF_INET && sock->dscp != dev->dscp) { + if (setsockopt(sock->fd, IPPROTO_IP, IP_TOS, + (void *)&dscp, sizeof(int)) < 0) + { + char strbuf[ISC_STRERRORSIZE]; + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IP_TOS, %.02x)" + " %s: %s", + sock->fd, dscp >> 2, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } else + sock->dscp = dscp; + } +#endif +#if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS) + if (sock->pf == AF_INET6 && sock->pktdscp) { + cmsgp = (struct cmsghdr *)(cmsgbuf + + msg->msg_controllen); + msg->msg_control = (void *)cmsgbuf; + msg->msg_controllen += cmsg_space(sizeof(dscp)); + INSIST(msg->msg_controllen <= SENDCMSGBUFLEN); + + cmsgp->cmsg_level = IPPROTO_IPV6; + cmsgp->cmsg_type = IPV6_TCLASS; + cmsgp->cmsg_len = cmsg_len(sizeof(dscp)); + memmove(CMSG_DATA(cmsgp), &dscp, sizeof(dscp)); + } else if (sock->pf == AF_INET6 && sock->dscp != dev->dscp) { + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_TCLASS, + (void *)&dscp, sizeof(int)) < 0) { + char strbuf[ISC_STRERRORSIZE]; + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_TCLASS, " + "%.02x) %s: %s", + sock->fd, dscp >> 2, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } else + sock->dscp = dscp; + } +#endif + if (msg->msg_controllen != 0 && + msg->msg_controllen < SENDCMSGBUFLEN) + { + memset(cmsgbuf + msg->msg_controllen, 0, + SENDCMSGBUFLEN - msg->msg_controllen); + } + } +#endif +#endif /* USE_CMSG */ +#else /* ISC_NET_BSD44MSGHDR */ + msg->msg_accrights = NULL; + msg->msg_accrightslen = 0; +#endif /* ISC_NET_BSD44MSGHDR */ + + if (write_countp != NULL) + *write_countp = write_count; +} + +/* + * Construct an iov array and attach it to the msghdr passed in. This is + * the RECV constructor, which will use the available 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. + * + * If read_countp != NULL, *read_countp will hold the number of bytes + * this transaction can receive. + */ +static void +build_msghdr_recv(isc__socket_t *sock, char *cmsgbuf, isc_socketevent_t *dev, + struct msghdr *msg, struct iovec *iov, size_t *read_countp) +{ + unsigned int iovcount; + isc_buffer_t *buffer; + isc_region_t available; + size_t read_count; + + memset(msg, 0, sizeof(struct msghdr)); + + if (sock->type == isc_sockettype_udp) { + memset(&dev->address, 0, sizeof(dev->address)); +#ifdef BROKEN_RECVMSG + if (sock->pf == AF_INET) { + msg->msg_name = (void *)&dev->address.type.sin; + msg->msg_namelen = sizeof(dev->address.type.sin6); + } else if (sock->pf == AF_INET6) { + msg->msg_name = (void *)&dev->address.type.sin6; + msg->msg_namelen = sizeof(dev->address.type.sin6); +#ifdef ISC_PLATFORM_HAVESYSUNH + } else if (sock->pf == AF_UNIX) { + msg->msg_name = (void *)&dev->address.type.sunix; + msg->msg_namelen = sizeof(dev->address.type.sunix); +#endif + } else { + msg->msg_name = (void *)&dev->address.type.sa; + msg->msg_namelen = sizeof(dev->address.type); + } +#else + msg->msg_name = (void *)&dev->address.type.sa; + msg->msg_namelen = sizeof(dev->address.type); +#endif + } else { /* TCP */ + msg->msg_name = NULL; + msg->msg_namelen = 0; + dev->address = sock->peer_address; + } + + buffer = ISC_LIST_HEAD(dev->bufferlist); + read_count = 0; + + /* + * Single buffer I/O? Skip what we've done so far in this region. + */ + if (buffer == NULL) { + read_count = dev->region.length - dev->n; + iov[0].iov_base = (void *)(dev->region.base + dev->n); + iov[0].iov_len = read_count; + iovcount = 1; + + goto config; + } + + /* + * Multibuffer I/O. + * Skip empty buffers. + */ + while (buffer != NULL) { + REQUIRE(ISC_BUFFER_VALID(buffer)); + if (isc_buffer_availablelength(buffer) != 0) + break; + buffer = ISC_LIST_NEXT(buffer, link); + } + + iovcount = 0; + while (buffer != NULL) { + INSIST(iovcount < MAXSCATTERGATHER_RECV); + + isc_buffer_availableregion(buffer, &available); + + if (available.length > 0) { + iov[iovcount].iov_base = (void *)(available.base); + iov[iovcount].iov_len = available.length; + read_count += available.length; + iovcount++; + } + buffer = ISC_LIST_NEXT(buffer, link); + } + + config: + + /* + * If needed, set up to receive that one extra byte. + */ +#ifdef ISC_PLATFORM_RECVOVERFLOW + if (sock->type == isc_sockettype_udp) { + INSIST(iovcount < MAXSCATTERGATHER_RECV); + iov[iovcount].iov_base = (void *)(&sock->overflow); + iov[iovcount].iov_len = 1; + iovcount++; + } +#endif + + msg->msg_iov = iov; + msg->msg_iovlen = iovcount; + +#ifdef ISC_NET_BSD44MSGHDR +#if defined(USE_CMSG) + msg->msg_control = cmsgbuf; + msg->msg_controllen = RECVCMSGBUFLEN; +#else + msg->msg_control = NULL; + msg->msg_controllen = 0; +#endif /* USE_CMSG */ + msg->msg_flags = 0; +#else /* ISC_NET_BSD44MSGHDR */ + msg->msg_accrights = NULL; + msg->msg_accrightslen = 0; +#endif /* ISC_NET_BSD44MSGHDR */ + + if (read_countp != NULL) + *read_countp = read_count; +} + +static void +set_dev_address(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->peer_address; + } else if (sock->type == isc_sockettype_tcp) { + INSIST(address == NULL); + dev->address = sock->peer_address; + } +} + +static void +destroy_socketevent(isc_event_t *event) { + isc_socketevent_t *ev = (isc_socketevent_t *)event; + + INSIST(ISC_LIST_EMPTY(ev->bufferlist)); + + (ev->destroy)(event); +} + +static isc_socketevent_t * +allocate_socketevent(isc_mem_t *mctx, void *sender, + isc_eventtype_t eventtype, isc_taskaction_t action, + void *arg) +{ + isc_socketevent_t *ev; + + ev = (isc_socketevent_t *)isc_event_allocate(mctx, sender, + eventtype, action, arg, + sizeof(*ev)); + + if (ev == NULL) + return (NULL); + + ev->result = ISC_R_UNSET; + ISC_LINK_INIT(ev, ev_link); + ISC_LIST_INIT(ev->bufferlist); + 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) { + unsigned int i; + + printf("MSGHDR %p\n", msg); + printf("\tname %p, namelen %ld\n", msg->msg_name, + (long) msg->msg_namelen); + printf("\tiov %p, iovlen %ld\n", msg->msg_iov, + (long) msg->msg_iovlen); + for (i = 0; i < (unsigned int)msg->msg_iovlen; i++) + printf("\t\t%u\tbase %p, len %ld\n", i, + msg->msg_iov[i].iov_base, + (long) msg->msg_iov[i].iov_len); +#ifdef ISC_NET_BSD44MSGHDR + printf("\tcontrol %p, controllen %ld\n", msg->msg_control, + (long) msg->msg_controllen); +#endif +} +#endif + +#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 */ + +static int +doio_recv(isc__socket_t *sock, isc_socketevent_t *dev) { + int cc; + struct iovec iov[MAXSCATTERGATHER_RECV]; + size_t read_count; + size_t actual_count; + struct msghdr msghdr; + isc_buffer_t *buffer; + int recv_errno; + char strbuf[ISC_STRERRORSIZE]; + char cmsgbuf[RECVCMSGBUFLEN] = {0}; + + build_msghdr_recv(sock, cmsgbuf, dev, &msghdr, iov, &read_count); + +#if defined(ISC_SOCKET_DEBUG) + dump_msg(&msghdr); +#endif + + cc = recvmsg(sock->fd, &msghdr, 0); + recv_errno = errno; + +#if defined(ISC_SOCKET_DEBUG) + dump_msg(&msghdr); +#endif + + if (cc < 0) { + if (SOFT_ERROR(recv_errno)) + return (DOIO_SOFT); + + if (isc_log_wouldlog(isc_lctx, IOEVENT_LEVEL)) { + isc__strerror(recv_errno, strbuf, sizeof(strbuf)); + socket_log(sock, NULL, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_DOIORECV, + "doio_recv: recvmsg(%d) %d bytes, err %d/%s", + sock->fd, cc, recv_errno, strbuf); + } + +#define SOFT_OR_HARD(_system, _isc) \ + if (recv_errno == _system) { \ + if (sock->connected) { \ + dev->result = _isc; \ + inc_stats(sock->manager->stats, \ + sock->statsindex[STATID_RECVFAIL]); \ + return (DOIO_HARD); \ + } \ + return (DOIO_SOFT); \ + } +#define ALWAYS_HARD(_system, _isc) \ + if (recv_errno == _system) { \ + dev->result = _isc; \ + inc_stats(sock->manager->stats, \ + sock->statsindex[STATID_RECVFAIL]); \ + return (DOIO_HARD); \ + } + + SOFT_OR_HARD(ECONNREFUSED, ISC_R_CONNREFUSED); + SOFT_OR_HARD(ENETUNREACH, ISC_R_NETUNREACH); + SOFT_OR_HARD(EHOSTUNREACH, ISC_R_HOSTUNREACH); + SOFT_OR_HARD(EHOSTDOWN, ISC_R_HOSTDOWN); + /* HPUX 11.11 can return EADDRNOTAVAIL. */ + SOFT_OR_HARD(EADDRNOTAVAIL, ISC_R_ADDRNOTAVAIL); + SOFT_OR_HARD(ENOBUFS, ISC_R_NORESOURCES); + /* Should never get this one but it was seen. */ +#ifdef ENOPROTOOPT + SOFT_OR_HARD(ENOPROTOOPT, ISC_R_HOSTUNREACH); +#endif + /* + * HPUX returns EPROTO and EINVAL on receiving some ICMP/ICMPv6 + * errors. + */ +#ifdef EPROTO + SOFT_OR_HARD(EPROTO, ISC_R_HOSTUNREACH); +#endif + SOFT_OR_HARD(EINVAL, ISC_R_HOSTUNREACH); + +#undef SOFT_OR_HARD +#undef ALWAYS_HARD + + dev->result = isc__errno2result(recv_errno); + inc_stats(sock->manager->stats, + sock->statsindex[STATID_RECVFAIL]); + return (DOIO_HARD); + } + + /* + * On TCP and UNIX sockets, zero length reads indicate EOF, + * while on UDP sockets, zero length reads are perfectly valid, + * although strange. + */ + switch (sock->type) { + case isc_sockettype_tcp: + case isc_sockettype_unix: + if (cc == 0) + return (DOIO_EOF); + break; + case isc_sockettype_udp: + case isc_sockettype_raw: + break; + case isc_sockettype_fdwatch: + default: + INSIST(0); + } + + if (sock->type == isc_sockettype_udp) { + dev->address.length = msghdr.msg_namelen; + if (isc_sockaddr_getport(&dev->address) == 0) { + if (isc_log_wouldlog(isc_lctx, IOEVENT_LEVEL)) { + socket_log(sock, &dev->address, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_ZEROPORT, + "dropping source port zero packet"); + } + return (DOIO_SOFT); + } + /* + * Simulate a firewall blocking UDP responses bigger than + * 'maxudp' bytes. + */ + if (sock->manager->maxudp != 0 && cc > sock->manager->maxudp) + return (DOIO_SOFT); + } + + socket_log(sock, &dev->address, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_PKTRECV, + "packet received correctly"); + + /* + * Overflow bit detection. If we received MORE bytes than we should, + * this indicates an overflow situation. Set the flag in the + * dev entry and adjust how much we read by one. + */ +#ifdef ISC_PLATFORM_RECVOVERFLOW + if ((sock->type == isc_sockettype_udp) && ((size_t)cc > read_count)) { + dev->attributes |= ISC_SOCKEVENTATTR_TRUNC; + cc--; + } +#endif + + /* + * If there are control messages attached, run through them and pull + * out the interesting bits. + */ + process_cmsg(sock, &msghdr, dev); + + /* + * update the buffers (if any) and the i/o count + */ + dev->n += cc; + actual_count = cc; + buffer = ISC_LIST_HEAD(dev->bufferlist); + while (buffer != NULL && actual_count > 0U) { + REQUIRE(ISC_BUFFER_VALID(buffer)); + if (isc_buffer_availablelength(buffer) <= actual_count) { + actual_count -= isc_buffer_availablelength(buffer); + isc_buffer_add(buffer, + isc_buffer_availablelength(buffer)); + } else { + isc_buffer_add(buffer, actual_count); + actual_count = 0; + POST(actual_count); + break; + } + buffer = ISC_LIST_NEXT(buffer, link); + if (buffer == NULL) { + INSIST(actual_count == 0U); + } + } + + /* + * If we read less than we expected, update counters, + * and let the upper layer poke the descriptor. + */ + if (((size_t)cc != read_count) && (dev->n < dev->minimum)) + return (DOIO_SOFT); + + /* + * Full reads are posted, or partials if partials are ok. + */ + dev->result = ISC_R_SUCCESS; + return (DOIO_SUCCESS); +} + +/* + * 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 +doio_send(isc__socket_t *sock, isc_socketevent_t *dev) { + int cc; + struct iovec iov[MAXSCATTERGATHER_SEND]; + size_t write_count; + struct msghdr msghdr; + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + int attempts = 0; + int send_errno; + char strbuf[ISC_STRERRORSIZE]; + char cmsgbuf[SENDCMSGBUFLEN] = {0}; + + build_msghdr_send(sock, cmsgbuf, dev, &msghdr, iov, &write_count); + + resend: + if (sock->type == isc_sockettype_udp && + sock->manager->maxudp != 0 && + write_count > (size_t)sock->manager->maxudp) + cc = write_count; + else + cc = sendmsg(sock->fd, &msghdr, 0); + send_errno = errno; + + /* + * Check for error or block condition. + */ + if (cc < 0) { + if (send_errno == EINTR && ++attempts < NRETRIES) + goto resend; + + if (SOFT_ERROR(send_errno)) { + if (errno == EWOULDBLOCK || errno == EAGAIN) + dev->result = ISC_R_WOULDBLOCK; + return (DOIO_SOFT); + } + +#define SOFT_OR_HARD(_system, _isc) \ + if (send_errno == _system) { \ + if (sock->connected) { \ + dev->result = _isc; \ + inc_stats(sock->manager->stats, \ + sock->statsindex[STATID_SENDFAIL]); \ + return (DOIO_HARD); \ + } \ + return (DOIO_SOFT); \ + } +#define ALWAYS_HARD(_system, _isc) \ + if (send_errno == _system) { \ + dev->result = _isc; \ + inc_stats(sock->manager->stats, \ + sock->statsindex[STATID_SENDFAIL]); \ + return (DOIO_HARD); \ + } + + SOFT_OR_HARD(ECONNREFUSED, ISC_R_CONNREFUSED); + ALWAYS_HARD(EACCES, ISC_R_NOPERM); + ALWAYS_HARD(EAFNOSUPPORT, ISC_R_ADDRNOTAVAIL); + ALWAYS_HARD(EADDRNOTAVAIL, ISC_R_ADDRNOTAVAIL); + ALWAYS_HARD(EHOSTUNREACH, ISC_R_HOSTUNREACH); +#ifdef EHOSTDOWN + ALWAYS_HARD(EHOSTDOWN, ISC_R_HOSTUNREACH); +#endif + ALWAYS_HARD(ENETUNREACH, ISC_R_NETUNREACH); + SOFT_OR_HARD(ENOBUFS, ISC_R_NORESOURCES); + ALWAYS_HARD(EPERM, ISC_R_HOSTUNREACH); + ALWAYS_HARD(EPIPE, ISC_R_NOTCONNECTED); + ALWAYS_HARD(ECONNRESET, ISC_R_CONNECTIONRESET); + +#undef SOFT_OR_HARD +#undef ALWAYS_HARD + + /* + * The other error types depend on whether or not the + * socket is UDP or TCP. If it is UDP, some errors + * that we expect to be fatal under TCP are merely + * annoying, and are really soft errors. + * + * However, these soft errors are still returned as + * a status. + */ + isc_sockaddr_format(&dev->address, addrbuf, sizeof(addrbuf)); + isc__strerror(send_errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "internal_send: %s: %s", + addrbuf, strbuf); + dev->result = isc__errno2result(send_errno); + inc_stats(sock->manager->stats, + sock->statsindex[STATID_SENDFAIL]); + return (DOIO_HARD); + } + + if (cc == 0) { + inc_stats(sock->manager->stats, + sock->statsindex[STATID_SENDFAIL]); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "doio_send: send() %s 0", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_RETURNED, "returned")); + } + + /* + * If we write less than we expected, update counters, poke. + */ + dev->n += cc; + if ((size_t)cc != write_count) + 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); +} + +/* + * Kill. + * + * Caller must ensure that the socket is not locked and no external + * references exist. + */ +static void +socketclose(isc__socketmgr_t *manager, isc__socket_t *sock, int fd) { + isc_sockettype_t type = sock->type; + int lockid = FDLOCK_ID(fd); + + /* + * No one has this socket open, so the watcher doesn't have to be + * poked, and the socket doesn't have to be locked. + */ + LOCK(&manager->fdlock[lockid]); + manager->fds[fd] = NULL; + if (type == isc_sockettype_fdwatch) + manager->fdstate[fd] = CLOSED; + else + manager->fdstate[fd] = CLOSE_PENDING; + UNLOCK(&manager->fdlock[lockid]); + if (type == isc_sockettype_fdwatch) { + /* + * The caller may close the socket once this function returns, + * and `fd' may be reassigned for a new socket. So we do + * unwatch_fd() here, rather than defer it via select_poke(). + * Note: this may complicate data protection among threads and + * may reduce performance due to additional locks. One way to + * solve this would be to dup() the watched descriptor, but we + * take a simpler approach at this moment. + */ + (void)unwatch_fd(manager, fd, SELECT_POKE_READ); + (void)unwatch_fd(manager, fd, SELECT_POKE_WRITE); + } else + select_poke(manager, fd, SELECT_POKE_CLOSE); + + inc_stats(manager->stats, sock->statsindex[STATID_CLOSE]); + if (sock->active == 1) { + dec_stats(manager->stats, sock->statsindex[STATID_ACTIVE]); + sock->active = 0; + } + + /* + * update manager->maxfd here (XXX: this should be implemented more + * efficiently) + */ +#ifdef USE_SELECT + LOCK(&manager->lock); + if (manager->maxfd == fd) { + int i; + + manager->maxfd = 0; + for (i = fd - 1; i >= 0; i--) { + lockid = FDLOCK_ID(i); + + LOCK(&manager->fdlock[lockid]); + if (manager->fdstate[i] == MANAGED) { + manager->maxfd = i; + UNLOCK(&manager->fdlock[lockid]); + break; + } + UNLOCK(&manager->fdlock[lockid]); + } +#ifdef ISC_PLATFORM_USETHREADS + if (manager->maxfd < manager->pipe_fds[0]) + manager->maxfd = manager->pipe_fds[0]; +#endif + } + + UNLOCK(&manager->lock); +#endif /* USE_SELECT */ +} + +static void +destroy(isc__socket_t **sockp) { + int fd; + isc__socket_t *sock = *sockp; + isc__socketmgr_t *manager = sock->manager; + + socket_log(sock, NULL, CREATION, isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_DESTROYING, "destroying"); + + INSIST(ISC_LIST_EMPTY(sock->connect_list)); + INSIST(ISC_LIST_EMPTY(sock->accept_list)); + INSIST(ISC_LIST_EMPTY(sock->recv_list)); + INSIST(ISC_LIST_EMPTY(sock->send_list)); + INSIST(sock->fd >= -1 && sock->fd < (int)manager->maxsocks); + + if (sock->fd >= 0) { + fd = sock->fd; + sock->fd = -1; + socketclose(manager, sock, fd); + } + + LOCK(&manager->lock); + + ISC_LIST_UNLINK(manager->socklist, sock, link); + +#ifdef USE_WATCHER_THREAD + if (ISC_LIST_EMPTY(manager->socklist)) + SIGNAL(&manager->shutdown_ok); +#endif /* USE_WATCHER_THREAD */ + + /* can't unlock manager as its memory context is still used */ + free_socket(sockp); + + UNLOCK(&manager->lock); +} + +static isc_result_t +allocate_socket(isc__socketmgr_t *manager, isc_sockettype_t type, + isc__socket_t **socketp) +{ + isc__socket_t *sock; + isc_result_t result; + + sock = isc_mem_get(manager->mctx, sizeof(*sock)); + + if (sock == NULL) + return (ISC_R_NOMEMORY); + + sock->common.magic = 0; + sock->common.impmagic = 0; + sock->references = 0; + + sock->manager = manager; + sock->type = type; + sock->fd = -1; + sock->dscp = 0; /* TOS/TCLASS is zero until set. */ + sock->dupped = 0; + sock->statsindex = NULL; + sock->active = 0; + + ISC_LINK_INIT(sock, link); + + + memset(sock->name, 0, sizeof(sock->name)); + sock->tag = NULL; + + /* + * 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_recv = 0; + sock->pending_send = 0; + sock->pending_accept = 0; + sock->listener = 0; + sock->connected = 0; + sock->connecting = 0; + sock->bound = 0; + sock->pktdscp = 0; + + /* + * Initialize the lock. + */ + result = isc_mutex_init(&sock->lock); + if (result != ISC_R_SUCCESS) { + sock->common.magic = 0; + sock->common.impmagic = 0; + goto error; + } + + /* + * Initialize readable and writable events. + */ + ISC_EVENT_INIT(&sock->readable_ev, sizeof(intev_t), + ISC_EVENTATTR_NOPURGE, NULL, ISC_SOCKEVENT_INTR, + NULL, sock, sock, NULL, NULL); + ISC_EVENT_INIT(&sock->writable_ev, sizeof(intev_t), + ISC_EVENTATTR_NOPURGE, NULL, ISC_SOCKEVENT_INTW, + NULL, sock, sock, NULL, NULL); + + sock->common.magic = ISCAPI_SOCKET_MAGIC; + sock->common.impmagic = SOCKET_MAGIC; + *socketp = sock; + + return (ISC_R_SUCCESS); + + error: + isc_mem_put(manager->mctx, sock, sizeof(*sock)); + + return (result); +} + +/* + * This event requires that the various lists be empty, that the reference + * count be 1, and that the magic number is valid. The other socket bits, + * like the lock, must be initialized as well. The fd associated must be + * marked as closed, by setting it to -1 on close, or this routine will + * also close the socket. + */ +static void +free_socket(isc__socket_t **socketp) { + isc__socket_t *sock = *socketp; + + INSIST(VALID_SOCKET(sock)); + INSIST(sock->references == 0); + INSIST(!sock->connecting); + INSIST(!sock->pending_recv); + INSIST(!sock->pending_send); + INSIST(!sock->pending_accept); + INSIST(ISC_LIST_EMPTY(sock->recv_list)); + INSIST(ISC_LIST_EMPTY(sock->send_list)); + INSIST(ISC_LIST_EMPTY(sock->accept_list)); + INSIST(ISC_LIST_EMPTY(sock->connect_list)); + INSIST(!ISC_LINK_LINKED(sock, link)); + + sock->common.magic = 0; + sock->common.impmagic = 0; + + DESTROYLOCK(&sock->lock); + + isc_mem_put(sock->manager->mctx, sock, sizeof(*sock)); + + *socketp = NULL; +} + +#ifdef SO_RCVBUF +static isc_once_t rcvbuf_once = ISC_ONCE_INIT; +static int rcvbuf = RCVBUFSIZE; + +static void +set_rcvbuf(void) { + int fd; + int max = rcvbuf, min; + ISC_SOCKADDR_LEN_T len; + + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); +#if defined(ISC_PLATFORM_HAVEIPV6) + if (fd == -1) { + switch (errno) { + case EPROTONOSUPPORT: + case EPFNOSUPPORT: + case EAFNOSUPPORT: + /* + * Linux 2.2 (and maybe others) return EINVAL instead of + * EAFNOSUPPORT. + */ + case EINVAL: + fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + break; + } + } +#endif + if (fd == -1) + return; + + len = sizeof(min); + if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&min, &len) == 0 && + min < rcvbuf) { + again: + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&rcvbuf, + sizeof(rcvbuf)) == -1) { + if (errno == ENOBUFS && rcvbuf > min) { + max = rcvbuf - 1; + rcvbuf = (rcvbuf + min) / 2; + goto again; + } else { + rcvbuf = min; + goto cleanup; + } + } else + min = rcvbuf; + if (min != max) { + rcvbuf = max; + goto again; + } + } + cleanup: + close (fd); +} +#endif + +#ifdef SO_BSDCOMPAT +/* + * This really should not be necessary to do. Having to workout + * which kernel version we are on at run time so that we don't cause + * the kernel to issue a warning about us using a deprecated socket option. + * Such warnings should *never* be on by default in production kernels. + * + * We can't do this a build time because executables are moved between + * machines and hence kernels. + * + * We can't just not set SO_BSDCOMAT because some kernels require it. + */ + +static isc_once_t bsdcompat_once = ISC_ONCE_INIT; +bool bsdcompat = true; + +static void +clear_bsdcompat(void) { +#ifdef __linux__ + struct utsname buf; + char *endp; + long int major; + long int minor; + + uname(&buf); /* Can only fail if buf is bad in Linux. */ + + /* Paranoia in parsing can be increased, but we trust uname(). */ + major = strtol(buf.release, &endp, 10); + if (*endp == '.') { + minor = strtol(endp+1, &endp, 10); + if ((major > 2) || ((major == 2) && (minor >= 4))) { + bsdcompat = false; + } + } +#endif /* __linux __ */ +} +#endif + +static void +use_min_mtu(isc__socket_t *sock) { +#if !defined(IPV6_USE_MIN_MTU) && !defined(IPV6_MTU) + UNUSED(sock); +#endif +#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)); + } +#endif +#if defined(IPV6_MTU) + /* + * Use minimum MTU on IPv6 sockets. + */ + if (sock->pf == AF_INET6) { + int mtu = 1280; + (void)setsockopt(sock->fd, IPPROTO_IPV6, IPV6_MTU, + &mtu, sizeof(mtu)); + } +#endif +} + +static void +set_tcp_maxseg(isc__socket_t *sock, int size) { +#ifdef TCP_MAXSEG + if (sock->type == isc_sockettype_tcp) + (void)setsockopt(sock->fd, IPPROTO_TCP, TCP_MAXSEG, + (void *)&size, sizeof(size)); +#endif +} + +static isc_result_t +opensocket(isc__socketmgr_t *manager, isc__socket_t *sock, + isc__socket_t *dup_socket) +{ + isc_result_t result; + char strbuf[ISC_STRERRORSIZE]; + const char *err = "socket"; + int tries = 0; +#if defined(USE_CMSG) || defined(SO_BSDCOMPAT) || defined(SO_NOSIGPIPE) + int on = 1; +#endif +#if defined(SO_RCVBUF) + ISC_SOCKADDR_LEN_T optlen; + int size = 0; +#endif + + again: + if (dup_socket == NULL) { + switch (sock->type) { + case isc_sockettype_udp: + sock->fd = socket(sock->pf, SOCK_DGRAM, IPPROTO_UDP); + break; + case isc_sockettype_tcp: + sock->fd = socket(sock->pf, SOCK_STREAM, IPPROTO_TCP); + break; + case isc_sockettype_unix: + sock->fd = socket(sock->pf, SOCK_STREAM, 0); + break; + case isc_sockettype_raw: + errno = EPFNOSUPPORT; + /* + * PF_ROUTE is a alias for PF_NETLINK on linux. + */ +#if defined(PF_ROUTE) + if (sock->fd == -1 && sock->pf == PF_ROUTE) { +#ifdef NETLINK_ROUTE + sock->fd = socket(sock->pf, SOCK_RAW, + NETLINK_ROUTE); +#else + sock->fd = socket(sock->pf, SOCK_RAW, 0); +#endif + if (sock->fd != -1) { +#ifdef NETLINK_ROUTE + struct sockaddr_nl sa; + int n; + + /* + * Do an implicit bind. + */ + memset(&sa, 0, sizeof(sa)); + sa.nl_family = AF_NETLINK; + sa.nl_groups = RTMGRP_IPV4_IFADDR | + RTMGRP_IPV6_IFADDR; + n = bind(sock->fd, + (struct sockaddr *) &sa, + sizeof(sa)); + if (n < 0) { + close(sock->fd); + sock->fd = -1; + } +#endif + sock->bound = 1; + } + } +#endif + break; + case isc_sockettype_fdwatch: + /* + * We should not be called for isc_sockettype_fdwatch + * sockets. + */ + INSIST(0); + break; + } + } else { + sock->fd = dup(dup_socket->fd); + sock->dupped = 1; + sock->bound = dup_socket->bound; + } + if (sock->fd == -1 && errno == EINTR && tries++ < 42) + goto again; + +#ifdef F_DUPFD + /* + * Leave a space for stdio and TCP to work in. + */ + if (manager->reserved != 0 && sock->type == isc_sockettype_udp && + sock->fd >= 0 && sock->fd < manager->reserved) { + int newfd, tmp; + newfd = fcntl(sock->fd, F_DUPFD, manager->reserved); + tmp = errno; + (void)close(sock->fd); + errno = tmp; + sock->fd = newfd; + err = "isc_socket_create: fcntl/reserved"; + } else if (sock->fd >= 0 && sock->fd < 20) { + int newfd, tmp; + newfd = fcntl(sock->fd, F_DUPFD, 20); + tmp = errno; + (void)close(sock->fd); + errno = tmp; + sock->fd = newfd; + err = "isc_socket_create: fcntl"; + } +#endif + + if (sock->fd >= (int)manager->maxsocks) { + (void)close(sock->fd); + isc_log_iwrite(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_TOOMANYFDS, + "socket: file descriptor exceeds limit (%d/%u)", + sock->fd, manager->maxsocks); + inc_stats(manager->stats, sock->statsindex[STATID_OPENFAIL]); + return (ISC_R_NORESOURCES); + } + + if (sock->fd < 0) { + switch (errno) { + case EMFILE: + case ENFILE: + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_iwrite(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_TOOMANYFDS, + "%s: %s", err, strbuf); + /* fallthrough */ + case ENOBUFS: + inc_stats(manager->stats, + sock->statsindex[STATID_OPENFAIL]); + return (ISC_R_NORESOURCES); + + case EPROTONOSUPPORT: + case EPFNOSUPPORT: + case EAFNOSUPPORT: + /* + * Linux 2.2 (and maybe others) return EINVAL instead of + * EAFNOSUPPORT. + */ + case EINVAL: + inc_stats(manager->stats, + sock->statsindex[STATID_OPENFAIL]); + return (ISC_R_FAMILYNOSUPPORT); + + default: + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "%s() %s: %s", err, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + inc_stats(manager->stats, + sock->statsindex[STATID_OPENFAIL]); + return (ISC_R_UNEXPECTED); + } + } + + if (dup_socket != NULL) + goto setup_done; + + result = make_nonblock(sock->fd); + if (result != ISC_R_SUCCESS) { + (void)close(sock->fd); + inc_stats(manager->stats, sock->statsindex[STATID_OPENFAIL]); + return (result); + } + +#ifdef SO_BSDCOMPAT + RUNTIME_CHECK(isc_once_do(&bsdcompat_once, + clear_bsdcompat) == ISC_R_SUCCESS); + if (sock->type != isc_sockettype_unix && bsdcompat && + setsockopt(sock->fd, SOL_SOCKET, SO_BSDCOMPAT, + (void *)&on, sizeof(on)) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, SO_BSDCOMPAT) %s: %s", + sock->fd, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + strbuf); + /* Press on... */ + } +#endif + +#ifdef SO_NOSIGPIPE + if (setsockopt(sock->fd, SOL_SOCKET, SO_NOSIGPIPE, + (void *)&on, sizeof(on)) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, SO_NOSIGPIPE) %s: %s", + sock->fd, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + strbuf); + /* Press on... */ + } +#endif + + /* + * Use minimum mtu if possible. + */ + if (sock->type == isc_sockettype_tcp && sock->pf == AF_INET6) { + use_min_mtu(sock); + set_tcp_maxseg(sock, 1280 - 20 - 40); /* 1280 - TCP - IPV6 */ + } + +#if defined(USE_CMSG) || defined(SO_RCVBUF) + if (sock->type == isc_sockettype_udp) { + +#if defined(USE_CMSG) +#if defined(SO_TIMESTAMP) + if (setsockopt(sock->fd, SOL_SOCKET, SO_TIMESTAMP, + (void *)&on, sizeof(on)) < 0 + && errno != ENOPROTOOPT) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, SO_TIMESTAMP) %s: %s", + sock->fd, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + /* Press on... */ + } +#endif /* SO_TIMESTAMP */ + +#if defined(ISC_PLATFORM_HAVEIPV6) +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO +#ifdef IPV6_RECVPKTINFO + /* RFC 3542 */ + if ((sock->pf == AF_INET6) + && (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + (void *)&on, sizeof(on)) < 0)) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_RECVPKTINFO) " + "%s: %s", sock->fd, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } +#else + /* RFC 2292 */ + if ((sock->pf == AF_INET6) + && (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_PKTINFO, + (void *)&on, sizeof(on)) < 0)) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_PKTINFO) %s: %s", + sock->fd, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } +#endif /* IPV6_RECVPKTINFO */ +#endif /* ISC_PLATFORM_HAVEIN6PKTINFO */ +#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DONT) + /* + * Turn off Path MTU discovery on IPv6/UDP sockets. + */ + if (sock->pf == AF_INET6) { + int action = IPV6_PMTUDISC_DONT; + (void)setsockopt(sock->fd, IPPROTO_IPV6, + IPV6_MTU_DISCOVER, &action, + sizeof(action)); + } +#endif +#endif /* ISC_PLATFORM_HAVEIPV6 */ +#endif /* defined(USE_CMSG) */ + +#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) + /* + * Turn off Path MTU discovery on IPv4/UDP sockets. + * Prefer IP_PMTUDISC_OMIT over IP_PMTUDISC_DONT + * if it available. + */ + if (sock->pf == AF_INET) { + int action; +#if defined(IP_PMTUDISC_OMIT) + action = IP_PMTUDISC_OMIT; + if (setsockopt(sock->fd, IPPROTO_IP, + IP_MTU_DISCOVER, &action, + sizeof(action)) < 0) { +#endif + action = IP_PMTUDISC_DONT; + (void)setsockopt(sock->fd, IPPROTO_IP, + IP_MTU_DISCOVER, + &action, sizeof(action)); +#if defined(IP_PMTUDISC_OMIT) + } +#endif + } +#endif +#if defined(IP_DONTFRAG) + /* + * Turn off Path MTU discovery on IPv4/UDP sockets. + */ + if (sock->pf == AF_INET) { + int off = 0; + (void)setsockopt(sock->fd, IPPROTO_IP, IP_DONTFRAG, + &off, sizeof(off)); + } +#endif + +#if defined(SO_RCVBUF) + optlen = sizeof(size); + if (getsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, + (void *)&size, &optlen) == 0 && size < rcvbuf) { + RUNTIME_CHECK(isc_once_do(&rcvbuf_once, + set_rcvbuf) == ISC_R_SUCCESS); + if (setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUF, + (void *)&rcvbuf, sizeof(rcvbuf)) == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, SO_RCVBUF, %d) %s: %s", + sock->fd, rcvbuf, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } + } +#endif + } +#ifdef IPV6_RECVTCLASS + if ((sock->pf == AF_INET6) + && (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVTCLASS, + (void *)&on, sizeof(on)) < 0)) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_RECVTCLASS) " + "%s: %s", sock->fd, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + strbuf); + } +#endif +#ifdef IP_RECVTOS + if ((sock->pf == AF_INET) + && (setsockopt(sock->fd, IPPROTO_IP, IP_RECVTOS, + (void *)&on, sizeof(on)) < 0)) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IP_RECVTOS) " + "%s: %s", sock->fd, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + strbuf); + } +#endif +#endif /* defined(USE_CMSG) || defined(SO_RCVBUF) */ + +setup_done: + inc_stats(manager->stats, sock->statsindex[STATID_OPEN]); + if (sock->active == 0) { + inc_stats(manager->stats, sock->statsindex[STATID_ACTIVE]); + sock->active = 1; + } + + return (ISC_R_SUCCESS); +} + +/* + * Create a 'type' socket or duplicate an existing 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 *manager0, int pf, isc_sockettype_t type, + isc_socket_t **socketp, isc_socket_t *dup_socket) +{ + isc__socket_t *sock = NULL; + isc__socketmgr_t *manager = (isc__socketmgr_t *)manager0; + isc_result_t result; + int lockid; + + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(socketp != NULL && *socketp == NULL); + REQUIRE(type != isc_sockettype_fdwatch); + + result = allocate_socket(manager, type, &sock); + if (result != ISC_R_SUCCESS) + return (result); + + switch (sock->type) { + case isc_sockettype_udp: + sock->statsindex = + (pf == AF_INET) ? udp4statsindex : udp6statsindex; +#define DCSPPKT(pf) ((pf == AF_INET) ? ISC_NET_DSCPPKTV4 : ISC_NET_DSCPPKTV6) + sock->pktdscp = (isc_net_probedscp() & DCSPPKT(pf)) != 0; + break; + case isc_sockettype_tcp: + sock->statsindex = + (pf == AF_INET) ? tcp4statsindex : tcp6statsindex; + break; + case isc_sockettype_unix: + sock->statsindex = unixstatsindex; + break; + case isc_sockettype_raw: + sock->statsindex = rawstatsindex; + break; + default: + INSIST(0); + } + + sock->pf = pf; + + result = opensocket(manager, sock, (isc__socket_t *)dup_socket); + if (result != ISC_R_SUCCESS) { + free_socket(&sock); + return (result); + } + + sock->common.methods = (isc_socketmethods_t *)&socketmethods; + sock->references = 1; + *socketp = (isc_socket_t *)sock; + + /* + * Note we don't have to lock the socket like we normally would because + * there are no external references to it yet. + */ + + lockid = FDLOCK_ID(sock->fd); + LOCK(&manager->fdlock[lockid]); + manager->fds[sock->fd] = sock; + manager->fdstate[sock->fd] = MANAGED; +#if defined(USE_EPOLL) + manager->epoll_events[sock->fd] = 0; +#endif +#ifdef USE_DEVPOLL + INSIST(sock->manager->fdpollinfo[sock->fd].want_read == 0 && + sock->manager->fdpollinfo[sock->fd].want_write == 0); +#endif + UNLOCK(&manager->fdlock[lockid]); + + LOCK(&manager->lock); + ISC_LIST_APPEND(manager->socklist, sock, link); +#ifdef USE_SELECT + if (manager->maxfd < sock->fd) + manager->maxfd = sock->fd; +#endif + UNLOCK(&manager->lock); + + socket_log(sock, NULL, CREATION, isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_CREATED, dup_socket != NULL ? "dupped" : "created"); + + return (ISC_R_SUCCESS); +} + +/*% + * 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'. + */ +isc_result_t +isc__socket_create(isc_socketmgr_t *manager0, int pf, isc_sockettype_t type, + isc_socket_t **socketp) +{ + return (socket_create(manager0, pf, type, socketp, NULL)); +} + +/*% + * Duplicate an existing socket. The new socket is returned + * in 'socketp'. + */ +isc_result_t +isc__socket_dup(isc_socket_t *sock0, isc_socket_t **socketp) { + isc__socket_t *sock = (isc__socket_t *)sock0; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(socketp != NULL && *socketp == NULL); + + return (socket_create((isc_socketmgr_t *) sock->manager, + sock->pf, sock->type, socketp, + sock0)); +} + +isc_result_t +isc__socket_open(isc_socket_t *sock0) { + isc_result_t result; + isc__socket_t *sock = (isc__socket_t *)sock0; + + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + REQUIRE(sock->references == 1); + REQUIRE(sock->type != isc_sockettype_fdwatch); + UNLOCK(&sock->lock); + /* + * We don't need to retain the lock hereafter, since no one else has + * this socket. + */ + REQUIRE(sock->fd == -1); + + result = opensocket(sock->manager, sock, NULL); + if (result != ISC_R_SUCCESS) + sock->fd = -1; + + if (result == ISC_R_SUCCESS) { + int lockid = FDLOCK_ID(sock->fd); + + LOCK(&sock->manager->fdlock[lockid]); + sock->manager->fds[sock->fd] = sock; + sock->manager->fdstate[sock->fd] = MANAGED; +#if defined(USE_EPOLL) + sock->manager->epoll_events[sock->fd] = 0; +#endif +#ifdef USE_DEVPOLL + INSIST(sock->manager->fdpollinfo[sock->fd].want_read == 0 && + sock->manager->fdpollinfo[sock->fd].want_write == 0); +#endif + UNLOCK(&sock->manager->fdlock[lockid]); + +#ifdef USE_SELECT + LOCK(&sock->manager->lock); + if (sock->manager->maxfd < sock->fd) + sock->manager->maxfd = sock->fd; + UNLOCK(&sock->manager->lock); +#endif + } + + return (result); +} + +/* + * 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'. + */ +isc_result_t +isc__socket_fdwatchcreate(isc_socketmgr_t *manager0, int fd, int flags, + isc_sockfdwatch_t callback, void *cbarg, + isc_task_t *task, isc_socket_t **socketp) +{ + isc__socketmgr_t *manager = (isc__socketmgr_t *)manager0; + isc__socket_t *sock = NULL; + isc_result_t result; + int lockid; + + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(socketp != NULL && *socketp == NULL); + + if (fd < 0 || (unsigned int)fd >= manager->maxsocks) + return (ISC_R_RANGE); + + result = allocate_socket(manager, isc_sockettype_fdwatch, &sock); + if (result != ISC_R_SUCCESS) + return (result); + + sock->fd = fd; + sock->fdwatcharg = cbarg; + sock->fdwatchcb = callback; + sock->fdwatchflags = flags; + sock->fdwatchtask = task; + sock->statsindex = fdwatchstatsindex; + + sock->common.methods = (isc_socketmethods_t *)&socketmethods; + sock->references = 1; + *socketp = (isc_socket_t *)sock; + + /* + * Note we don't have to lock the socket like we normally would because + * there are no external references to it yet. + */ + + lockid = FDLOCK_ID(sock->fd); + LOCK(&manager->fdlock[lockid]); + manager->fds[sock->fd] = sock; + manager->fdstate[sock->fd] = MANAGED; +#if defined(USE_EPOLL) + manager->epoll_events[sock->fd] = 0; +#endif + UNLOCK(&manager->fdlock[lockid]); + + LOCK(&manager->lock); + ISC_LIST_APPEND(manager->socklist, sock, link); +#ifdef USE_SELECT + if (manager->maxfd < sock->fd) + manager->maxfd = sock->fd; +#endif + UNLOCK(&manager->lock); + + if (flags & ISC_SOCKFDWATCH_READ) + select_poke(sock->manager, sock->fd, SELECT_POKE_READ); + if (flags & ISC_SOCKFDWATCH_WRITE) + select_poke(sock->manager, sock->fd, SELECT_POKE_WRITE); + + socket_log(sock, NULL, CREATION, isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_CREATED, "fdwatch-created"); + + return (ISC_R_SUCCESS); +} + +/* + * Indicate to the manager that it should watch the socket again. + * This can be used to restart watching if the previous event handler + * didn't indicate there was more data to be processed. Primarily + * it is for writing but could be used for reading if desired + */ + +isc_result_t +isc__socket_fdwatchpoke(isc_socket_t *sock0, int flags) +{ + isc__socket_t *sock = (isc__socket_t *)sock0; + + REQUIRE(VALID_SOCKET(sock)); + + /* + * We check both flags first to allow us to get the lock + * once but only if we need it. + */ + + if ((flags & (ISC_SOCKFDWATCH_READ | ISC_SOCKFDWATCH_WRITE)) != 0) { + LOCK(&sock->lock); + if (((flags & ISC_SOCKFDWATCH_READ) != 0) && + !sock->pending_recv) + select_poke(sock->manager, sock->fd, + SELECT_POKE_READ); + if (((flags & ISC_SOCKFDWATCH_WRITE) != 0) && + !sock->pending_send) + select_poke(sock->manager, sock->fd, + SELECT_POKE_WRITE); + UNLOCK(&sock->lock); + } + + socket_log(sock, NULL, TRACE, isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_POKED, "fdwatch-poked flags: %d", flags); + + return (ISC_R_SUCCESS); +} + +/* + * Attach to a socket. Caller must explicitly detach when it is done. + */ +void +isc__socket_attach(isc_socket_t *sock0, isc_socket_t **socketp) { + isc__socket_t *sock = (isc__socket_t *)sock0; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(socketp != NULL && *socketp == NULL); + + LOCK(&sock->lock); + sock->references++; + UNLOCK(&sock->lock); + + *socketp = (isc_socket_t *)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; + bool kill_socket = false; + + REQUIRE(socketp != NULL); + sock = (isc__socket_t *)*socketp; + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + REQUIRE(sock->references > 0); + sock->references--; + if (sock->references == 0) + kill_socket = true; + UNLOCK(&sock->lock); + + if (kill_socket) + destroy(&sock); + + *socketp = NULL; +} + +isc_result_t +isc__socket_close(isc_socket_t *sock0) { + isc__socket_t *sock = (isc__socket_t *)sock0; + int fd; + isc__socketmgr_t *manager; + + fflush(stdout); + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + + REQUIRE(sock->references == 1); + REQUIRE(sock->type != isc_sockettype_fdwatch); + REQUIRE(sock->fd >= 0 && sock->fd < (int)sock->manager->maxsocks); + + INSIST(!sock->connecting); + INSIST(!sock->pending_recv); + INSIST(!sock->pending_send); + INSIST(!sock->pending_accept); + INSIST(ISC_LIST_EMPTY(sock->recv_list)); + INSIST(ISC_LIST_EMPTY(sock->send_list)); + INSIST(ISC_LIST_EMPTY(sock->accept_list)); + INSIST(ISC_LIST_EMPTY(sock->connect_list)); + + manager = sock->manager; + fd = sock->fd; + sock->fd = -1; + sock->dupped = 0; + memset(sock->name, 0, sizeof(sock->name)); + sock->tag = NULL; + sock->listener = 0; + sock->connected = 0; + sock->connecting = 0; + sock->bound = 0; + isc_sockaddr_any(&sock->peer_address); + + UNLOCK(&sock->lock); + + socketclose(manager, sock, fd); + + return (ISC_R_SUCCESS); +} + +/* + * I/O is possible on a given socket. Schedule an event to this task that + * will call an internal function to do the I/O. This will charge the + * task with the I/O operation and let our select loop handler get back + * to doing something real as fast as possible. + * + * The socket and manager must be locked before calling this function. + */ +static void +dispatch_recv(isc__socket_t *sock) { + intev_t *iev; + isc_socketevent_t *ev; + isc_task_t *sender; + + INSIST(!sock->pending_recv); + + if (sock->type != isc_sockettype_fdwatch) { + ev = ISC_LIST_HEAD(sock->recv_list); + if (ev == NULL) + return; + socket_log(sock, NULL, EVENT, NULL, 0, 0, + "dispatch_recv: event %p -> task %p", + ev, ev->ev_sender); + sender = ev->ev_sender; + } else { + sender = sock->fdwatchtask; + } + + sock->pending_recv = 1; + iev = &sock->readable_ev; + + sock->references++; + iev->ev_sender = sock; + if (sock->type == isc_sockettype_fdwatch) + iev->ev_action = internal_fdwatch_read; + else + iev->ev_action = internal_recv; + iev->ev_arg = sock; + + isc_task_send(sender, (isc_event_t **)&iev); +} + +static void +dispatch_send(isc__socket_t *sock) { + intev_t *iev; + isc_socketevent_t *ev; + isc_task_t *sender; + + INSIST(!sock->pending_send); + + if (sock->type != isc_sockettype_fdwatch) { + ev = ISC_LIST_HEAD(sock->send_list); + if (ev == NULL) + return; + socket_log(sock, NULL, EVENT, NULL, 0, 0, + "dispatch_send: event %p -> task %p", + ev, ev->ev_sender); + sender = ev->ev_sender; + } else { + sender = sock->fdwatchtask; + } + + sock->pending_send = 1; + iev = &sock->writable_ev; + + sock->references++; + iev->ev_sender = sock; + if (sock->type == isc_sockettype_fdwatch) + iev->ev_action = internal_fdwatch_write; + else + iev->ev_action = internal_send; + iev->ev_arg = sock; + + isc_task_send(sender, (isc_event_t **)&iev); +} + +/* + * Dispatch an internal accept event. + */ +static void +dispatch_accept(isc__socket_t *sock) { + intev_t *iev; + isc_socket_newconnev_t *ev; + + INSIST(!sock->pending_accept); + + /* + * Are there any done events left, or were they all canceled + * before the manager got the socket lock? + */ + ev = ISC_LIST_HEAD(sock->accept_list); + if (ev == NULL) + return; + + sock->pending_accept = 1; + iev = &sock->readable_ev; + + sock->references++; /* keep socket around for this internal event */ + iev->ev_sender = sock; + iev->ev_action = internal_accept; + iev->ev_arg = sock; + + isc_task_send(ev->ev_sender, (isc_event_t **)&iev); +} + +static void +dispatch_connect(isc__socket_t *sock) { + intev_t *iev; + isc_socket_connev_t *ev; + + iev = &sock->writable_ev; + + ev = ISC_LIST_HEAD(sock->connect_list); + INSIST(ev != NULL); /* XXX */ + + INSIST(sock->connecting); + + sock->references++; /* keep socket around for this internal event */ + iev->ev_sender = sock; + iev->ev_action = internal_connect; + iev->ev_arg = sock; + + isc_task_send(ev->ev_sender, (isc_event_t **)&iev); +} + +/* + * 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 socket 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) + == ISC_SOCKEVENTATTR_ATTACHED) + isc_task_sendanddetach(&task, (isc_event_t **)dev); + else + isc_task_send(task, (isc_event_t **)dev); +} + +/* + * See comments for send_recvdone_event() above. + * + * Caller must have the socket locked if the event is attached to the socket. + */ +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) + == ISC_SOCKEVENTATTR_ATTACHED) + isc_task_sendanddetach(&task, (isc_event_t **)dev); + else + isc_task_send(task, (isc_event_t **)dev); +} + +/* + * See comments for send_recvdone_event() above. + * + * Caller must have the socket locked if the event is attached to the socket. + */ +static void +send_connectdone_event(isc__socket_t *sock, isc_socket_connev_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->connect_list, *dev, ev_link); + + isc_task_sendanddetach(&task, (isc_event_t **)dev); +} + +/* + * Call accept() on a socket, to get the new file descriptor. The listen + * socket is used as a prototype to create a new isc_socket_t. The new + * socket has one outstanding reference. The task receiving the event + * will be detached from just after the event is delivered. + * + * 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 unlock and return. + */ +static void +internal_accept(isc_task_t *me, isc_event_t *ev) { + isc__socket_t *sock; + isc__socketmgr_t *manager; + isc_socket_newconnev_t *dev; + isc_task_t *task; + ISC_SOCKADDR_LEN_T addrlen; + int fd; + isc_result_t result = ISC_R_SUCCESS; + char strbuf[ISC_STRERRORSIZE]; + const char *err = "accept"; + + UNUSED(me); + + sock = ev->ev_sender; + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + socket_log(sock, NULL, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTLOCK, + "internal_accept called, locked socket"); + + manager = sock->manager; + INSIST(VALID_MANAGER(manager)); + + INSIST(sock->listener); + INSIST(sock->pending_accept == 1); + sock->pending_accept = 0; + + INSIST(sock->references > 0); + sock->references--; /* the internal event is done with this socket */ + if (sock->references == 0) { + UNLOCK(&sock->lock); + destroy(&sock); + return; + } + + /* + * Get the first item off the accept list. + * If it is empty, unlock the socket and return. + */ + dev = ISC_LIST_HEAD(sock->accept_list); + if (dev == NULL) { + UNLOCK(&sock->lock); + return; + } + + /* + * Try to accept the new connection. If the accept fails with + * EAGAIN or EINTR, simply poke the watcher to watch this socket + * again. Also ignore ECONNRESET, which has been reported to + * be spuriously returned on Linux 2.2.19 although it is not + * a documented error for accept(). ECONNABORTED has been + * reported for Solaris 8. The rest are thrown in not because + * we have seen them but because they are ignored by other + * daemons such as BIND 8 and Apache. + */ + + addrlen = sizeof(NEWCONNSOCK(dev)->peer_address.type); + memset(&NEWCONNSOCK(dev)->peer_address.type, 0, addrlen); + fd = accept(sock->fd, &NEWCONNSOCK(dev)->peer_address.type.sa, + (void *)&addrlen); + +#ifdef F_DUPFD + /* + * Leave a space for stdio to work in. + */ + if (fd >= 0 && fd < 20) { + int newfd, tmp; + newfd = fcntl(fd, F_DUPFD, 20); + tmp = errno; + (void)close(fd); + errno = tmp; + fd = newfd; + err = "accept/fcntl"; + } +#endif + + if (fd < 0) { + if (SOFT_ERROR(errno)) + goto soft_error; + switch (errno) { + case ENFILE: + case EMFILE: + isc_log_iwrite(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_TOOMANYFDS, + "%s: too many open file descriptors", + err); + goto soft_error; + + case ENOBUFS: + case ENOMEM: + case ECONNRESET: + case ECONNABORTED: + case EHOSTUNREACH: + case EHOSTDOWN: + case ENETUNREACH: + case ENETDOWN: + case ECONNREFUSED: +#ifdef EPROTO + case EPROTO: +#endif +#ifdef ENONET + case ENONET: +#endif + goto soft_error; + default: + break; + } + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "internal_accept: %s() %s: %s", err, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + fd = -1; + result = ISC_R_UNEXPECTED; + } else { + if (addrlen == 0U) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "internal_accept(): " + "accept() failed to return " + "remote address"); + + (void)close(fd); + goto soft_error; + } else if (NEWCONNSOCK(dev)->peer_address.type.sa.sa_family != + sock->pf) + { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "internal_accept(): " + "accept() returned peer address " + "family %u (expected %u)", + NEWCONNSOCK(dev)->peer_address. + type.sa.sa_family, + sock->pf); + (void)close(fd); + goto soft_error; + } else if (fd >= (int)manager->maxsocks) { + isc_log_iwrite(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_TOOMANYFDS, + "accept: " + "file descriptor exceeds limit (%d/%u)", + fd, manager->maxsocks); + (void)close(fd); + goto soft_error; + } + } + + if (fd != -1) { + NEWCONNSOCK(dev)->peer_address.length = addrlen; + NEWCONNSOCK(dev)->pf = sock->pf; + } + + /* + * Pull off the done event. + */ + ISC_LIST_UNLINK(sock->accept_list, dev, ev_link); + + /* + * Poke watcher if there are more pending accepts. + */ + if (!ISC_LIST_EMPTY(sock->accept_list)) + select_poke(sock->manager, sock->fd, SELECT_POKE_ACCEPT); + + UNLOCK(&sock->lock); + + if (fd != -1) { + result = make_nonblock(fd); + if (result != ISC_R_SUCCESS) { + (void)close(fd); + fd = -1; + } + } + + /* + * -1 means the new socket didn't happen. + */ + if (fd != -1) { + int lockid = FDLOCK_ID(fd); + + NEWCONNSOCK(dev)->fd = fd; + NEWCONNSOCK(dev)->bound = 1; + NEWCONNSOCK(dev)->connected = 1; + + /* + * Use minimum mtu if possible. + */ + use_min_mtu(NEWCONNSOCK(dev)); + set_tcp_maxseg(NEWCONNSOCK(dev), 1280 - 20 - 40); + + /* + * Ensure DSCP settings are inherited across accept. + */ + setdscp(NEWCONNSOCK(dev), sock->dscp); + + /* + * Save away the remote address + */ + dev->address = NEWCONNSOCK(dev)->peer_address; + + if (NEWCONNSOCK(dev)->active == 0) { + inc_stats(manager->stats, + NEWCONNSOCK(dev)->statsindex[STATID_ACTIVE]); + NEWCONNSOCK(dev)->active = 1; + } + + LOCK(&manager->fdlock[lockid]); + manager->fds[fd] = NEWCONNSOCK(dev); + manager->fdstate[fd] = MANAGED; +#if defined(USE_EPOLL) + manager->epoll_events[fd] = 0; +#endif + UNLOCK(&manager->fdlock[lockid]); + + LOCK(&manager->lock); + +#ifdef USE_SELECT + if (manager->maxfd < fd) + manager->maxfd = fd; +#endif + + socket_log(sock, &NEWCONNSOCK(dev)->peer_address, CREATION, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTEDCXN, + "accepted connection, new socket %p", + dev->newsocket); + + ISC_LIST_APPEND(manager->socklist, NEWCONNSOCK(dev), link); + + UNLOCK(&manager->lock); + + inc_stats(manager->stats, sock->statsindex[STATID_ACCEPT]); + } else { + inc_stats(manager->stats, sock->statsindex[STATID_ACCEPTFAIL]); + NEWCONNSOCK(dev)->references--; + free_socket((isc__socket_t **)&dev->newsocket); + } + + /* + * Fill in the done event details and send it off. + */ + dev->result = result; + task = dev->ev_sender; + dev->ev_sender = sock; + + isc_task_sendanddetach(&task, ISC_EVENT_PTR(&dev)); + return; + + soft_error: + select_poke(sock->manager, sock->fd, SELECT_POKE_ACCEPT); + UNLOCK(&sock->lock); + + inc_stats(manager->stats, sock->statsindex[STATID_ACCEPTFAIL]); + return; +} + +static void +internal_recv(isc_task_t *me, isc_event_t *ev) { + isc_socketevent_t *dev; + isc__socket_t *sock; + + INSIST(ev->ev_type == ISC_SOCKEVENT_INTR); + + sock = ev->ev_sender; + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + socket_log(sock, NULL, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALRECV, + "internal_recv: task %p got event %p", me, ev); + + INSIST(sock->pending_recv == 1); + sock->pending_recv = 0; + + INSIST(sock->references > 0); + sock->references--; /* the internal event is done with this socket */ + if (sock->references == 0) { + UNLOCK(&sock->lock); + destroy(&sock); + return; + } + + /* + * Try to do as much I/O as possible on this socket. There are no + * limits here, currently. + */ + dev = ISC_LIST_HEAD(sock->recv_list); + while (dev != NULL) { + switch (doio_recv(sock, dev)) { + case DOIO_SOFT: + goto poke; + + case DOIO_EOF: + /* + * read of 0 means the remote end was closed. + * Run through the event queue and dispatch all + * the events with an EOF result code. + */ + do { + dev->result = ISC_R_EOF; + send_recvdone_event(sock, &dev); + dev = ISC_LIST_HEAD(sock->recv_list); + } while (dev != NULL); + goto poke; + + case DOIO_SUCCESS: + case DOIO_HARD: + send_recvdone_event(sock, &dev); + break; + } + + dev = ISC_LIST_HEAD(sock->recv_list); + } + + poke: + if (!ISC_LIST_EMPTY(sock->recv_list)) + select_poke(sock->manager, sock->fd, SELECT_POKE_READ); + + UNLOCK(&sock->lock); +} + +static void +internal_send(isc_task_t *me, isc_event_t *ev) { + isc_socketevent_t *dev; + isc__socket_t *sock; + + INSIST(ev->ev_type == ISC_SOCKEVENT_INTW); + + /* + * Find out what socket this is and lock it. + */ + sock = (isc__socket_t *)ev->ev_sender; + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + socket_log(sock, NULL, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALSEND, + "internal_send: task %p got event %p", me, ev); + + INSIST(sock->pending_send == 1); + sock->pending_send = 0; + + INSIST(sock->references > 0); + sock->references--; /* the internal event is done with this socket */ + if (sock->references == 0) { + UNLOCK(&sock->lock); + destroy(&sock); + return; + } + + /* + * Try to do as much I/O as possible on this socket. There are no + * limits here, currently. + */ + dev = ISC_LIST_HEAD(sock->send_list); + while (dev != NULL) { + switch (doio_send(sock, dev)) { + case DOIO_SOFT: + goto poke; + + case DOIO_HARD: + case DOIO_SUCCESS: + send_senddone_event(sock, &dev); + break; + } + + dev = ISC_LIST_HEAD(sock->send_list); + } + + poke: + if (!ISC_LIST_EMPTY(sock->send_list)) + select_poke(sock->manager, sock->fd, SELECT_POKE_WRITE); + + UNLOCK(&sock->lock); +} + +static void +internal_fdwatch_write(isc_task_t *me, isc_event_t *ev) { + isc__socket_t *sock; + int more_data; + + INSIST(ev->ev_type == ISC_SOCKEVENT_INTW); + + /* + * Find out what socket this is and lock it. + */ + sock = (isc__socket_t *)ev->ev_sender; + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + socket_log(sock, NULL, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALSEND, + "internal_fdwatch_write: task %p got event %p", me, ev); + + INSIST(sock->pending_send == 1); + + UNLOCK(&sock->lock); + more_data = (sock->fdwatchcb)(me, (isc_socket_t *)sock, + sock->fdwatcharg, ISC_SOCKFDWATCH_WRITE); + LOCK(&sock->lock); + + sock->pending_send = 0; + + INSIST(sock->references > 0); + sock->references--; /* the internal event is done with this socket */ + if (sock->references == 0) { + UNLOCK(&sock->lock); + destroy(&sock); + return; + } + + if (more_data) + select_poke(sock->manager, sock->fd, SELECT_POKE_WRITE); + + UNLOCK(&sock->lock); +} + +static void +internal_fdwatch_read(isc_task_t *me, isc_event_t *ev) { + isc__socket_t *sock; + int more_data; + + INSIST(ev->ev_type == ISC_SOCKEVENT_INTR); + + /* + * Find out what socket this is and lock it. + */ + sock = (isc__socket_t *)ev->ev_sender; + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + socket_log(sock, NULL, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALRECV, + "internal_fdwatch_read: task %p got event %p", me, ev); + + INSIST(sock->pending_recv == 1); + + UNLOCK(&sock->lock); + more_data = (sock->fdwatchcb)(me, (isc_socket_t *)sock, + sock->fdwatcharg, ISC_SOCKFDWATCH_READ); + LOCK(&sock->lock); + + sock->pending_recv = 0; + + INSIST(sock->references > 0); + sock->references--; /* the internal event is done with this socket */ + if (sock->references == 0) { + UNLOCK(&sock->lock); + destroy(&sock); + return; + } + + if (more_data) + select_poke(sock->manager, sock->fd, SELECT_POKE_READ); + + UNLOCK(&sock->lock); +} + +/* + * Process read/writes on each fd here. Avoid locking + * and unlocking twice if both reads and writes are possible. + */ +static void +process_fd(isc__socketmgr_t *manager, int fd, bool readable, + bool writeable) +{ + isc__socket_t *sock; + bool unlock_sock; + bool unwatch_read = false, unwatch_write = false; + int lockid = FDLOCK_ID(fd); + + /* + * If the socket is going to be closed, don't do more I/O. + */ + LOCK(&manager->fdlock[lockid]); + if (manager->fdstate[fd] == CLOSE_PENDING) { + UNLOCK(&manager->fdlock[lockid]); + + (void)unwatch_fd(manager, fd, SELECT_POKE_READ); + (void)unwatch_fd(manager, fd, SELECT_POKE_WRITE); + return; + } + + sock = manager->fds[fd]; + unlock_sock = false; + if (readable) { + if (sock == NULL) { + unwatch_read = true; + goto check_write; + } + unlock_sock = true; + LOCK(&sock->lock); + if (!SOCK_DEAD(sock)) { + if (sock->listener) + dispatch_accept(sock); + else + dispatch_recv(sock); + } + unwatch_read = true; + } +check_write: + if (writeable) { + if (sock == NULL) { + unwatch_write = true; + goto unlock_fd; + } + if (!unlock_sock) { + unlock_sock = true; + LOCK(&sock->lock); + } + if (!SOCK_DEAD(sock)) { + if (sock->connecting) + dispatch_connect(sock); + else + dispatch_send(sock); + } + unwatch_write = true; + } + if (unlock_sock) + UNLOCK(&sock->lock); + + unlock_fd: + UNLOCK(&manager->fdlock[lockid]); + if (unwatch_read) + (void)unwatch_fd(manager, fd, SELECT_POKE_READ); + if (unwatch_write) + (void)unwatch_fd(manager, fd, SELECT_POKE_WRITE); + +} + +#ifdef USE_KQUEUE +static bool +process_fds(isc__socketmgr_t *manager, struct kevent *events, int nevents) { + int i; + bool readable, writable; + bool done = false; +#ifdef USE_WATCHER_THREAD + bool have_ctlevent = false; +#endif + + if (nevents == manager->nevents) { + /* + * This is not an error, but something unexpected. If this + * happens, it may indicate the need for increasing + * ISC_SOCKET_MAXEVENTS. + */ + manager_log(manager, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_INFO, + "maximum number of FD events (%d) received", + nevents); + } + + for (i = 0; i < nevents; i++) { + REQUIRE(events[i].ident < manager->maxsocks); +#ifdef USE_WATCHER_THREAD + if (events[i].ident == (uintptr_t)manager->pipe_fds[0]) { + have_ctlevent = true; + continue; + } +#endif + readable = (events[i].filter == EVFILT_READ); + writable = (events[i].filter == EVFILT_WRITE); + process_fd(manager, events[i].ident, readable, writable); + } + +#ifdef USE_WATCHER_THREAD + if (have_ctlevent) + done = process_ctlfd(manager); +#endif + + return (done); +} +#elif defined(USE_EPOLL) +static bool +process_fds(isc__socketmgr_t *manager, struct epoll_event *events, int nevents) +{ + int i; + bool done = false; +#ifdef USE_WATCHER_THREAD + bool have_ctlevent = false; +#endif + + if (nevents == manager->nevents) { + manager_log(manager, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_INFO, + "maximum number of FD events (%d) received", + nevents); + } + + for (i = 0; i < nevents; i++) { + REQUIRE(events[i].data.fd < (int)manager->maxsocks); +#ifdef USE_WATCHER_THREAD + if (events[i].data.fd == manager->pipe_fds[0]) { + have_ctlevent = true; + continue; + } +#endif + if ((events[i].events & EPOLLERR) != 0 || + (events[i].events & EPOLLHUP) != 0) { + /* + * epoll does not set IN/OUT bits on an erroneous + * condition, so we need to try both anyway. This is a + * bit inefficient, but should be okay for such rare + * events. Note also that the read or write attempt + * won't block because we use non-blocking sockets. + */ + int fd = events[i].data.fd; + events[i].events |= manager->epoll_events[fd]; + } + process_fd(manager, events[i].data.fd, + (events[i].events & EPOLLIN) != 0, + (events[i].events & EPOLLOUT) != 0); + } + +#ifdef USE_WATCHER_THREAD + if (have_ctlevent) + done = process_ctlfd(manager); +#endif + + return (done); +} +#elif defined(USE_DEVPOLL) +static bool +process_fds(isc__socketmgr_t *manager, struct pollfd *events, int nevents) { + int i; + bool done = false; +#ifdef USE_WATCHER_THREAD + bool have_ctlevent = false; +#endif + + if (nevents == manager->nevents) { + manager_log(manager, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_INFO, + "maximum number of FD events (%d) received", + nevents); + } + + for (i = 0; i < nevents; i++) { + REQUIRE(events[i].fd < (int)manager->maxsocks); +#ifdef USE_WATCHER_THREAD + if (events[i].fd == manager->pipe_fds[0]) { + have_ctlevent = true; + continue; + } +#endif + process_fd(manager, events[i].fd, + (events[i].events & POLLIN) != 0, + (events[i].events & POLLOUT) != 0); + } + +#ifdef USE_WATCHER_THREAD + if (have_ctlevent) + done = process_ctlfd(manager); +#endif + + return (done); +} +#elif defined(USE_SELECT) +static void +process_fds(isc__socketmgr_t *manager, int maxfd, fd_set *readfds, + fd_set *writefds) +{ + int i; + + REQUIRE(maxfd <= (int)manager->maxsocks); + + for (i = 0; i < maxfd; i++) { +#ifdef USE_WATCHER_THREAD + if (i == manager->pipe_fds[0] || i == manager->pipe_fds[1]) + continue; +#endif /* USE_WATCHER_THREAD */ + process_fd(manager, i, FD_ISSET(i, readfds), + FD_ISSET(i, writefds)); + } +} +#endif + +#ifdef USE_WATCHER_THREAD +static bool +process_ctlfd(isc__socketmgr_t *manager) { + int msg, fd; + + for (;;) { + select_readmsg(manager, &fd, &msg); + + manager_log(manager, IOEVENT, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_WATCHERMSG, + "watcher got message %d " + "for socket %d"), msg, fd); + + /* + * Nothing to read? + */ + if (msg == SELECT_POKE_NOTHING) + break; + + /* + * Handle shutdown message. We really should + * jump out of this loop right away, but + * it doesn't matter if we have to do a little + * more work first. + */ + if (msg == SELECT_POKE_SHUTDOWN) + return (true); + + /* + * This is a wakeup on a socket. Look + * at the event queue for both read and write, + * and decide if we need to watch on it now + * or not. + */ + wakeup_socket(manager, fd, msg); + } + + return (false); +} + +/* + * This is the thread that will loop forever, always in a select or poll + * call. + * + * When select returns something to do, track down what thread gets to do + * this I/O and post the event to it. + */ +static isc_threadresult_t +watcher(void *uap) { + isc__socketmgr_t *manager = uap; + bool done; + int cc; +#ifdef USE_KQUEUE + const char *fnname = "kevent()"; +#elif defined (USE_EPOLL) + const char *fnname = "epoll_wait()"; +#elif defined(USE_DEVPOLL) + isc_result_t result; + const char *fnname = "ioctl(DP_POLL)"; + struct dvpoll dvp; + int pass; +#if defined(ISC_SOCKET_USE_POLLWATCH) + pollstate_t pollstate = poll_idle; +#endif +#elif defined (USE_SELECT) + const char *fnname = "select()"; + int maxfd; + int ctlfd; +#endif + char strbuf[ISC_STRERRORSIZE]; + +#if defined (USE_SELECT) + /* + * Get the control fd here. This will never change. + */ + ctlfd = manager->pipe_fds[0]; +#endif + done = false; + while (!done) { + do { +#ifdef USE_KQUEUE + cc = kevent(manager->kqueue_fd, NULL, 0, + manager->events, manager->nevents, NULL); +#elif defined(USE_EPOLL) + cc = epoll_wait(manager->epoll_fd, manager->events, + manager->nevents, -1); +#elif defined(USE_DEVPOLL) + /* + * Re-probe every thousand calls. + */ + if (manager->calls++ > 1000U) { + result = isc_resource_getcurlimit( + isc_resource_openfiles, + &manager->open_max); + if (result != ISC_R_SUCCESS) + manager->open_max = 64; + manager->calls = 0; + } + for (pass = 0; pass < 2; pass++) { + dvp.dp_fds = manager->events; + dvp.dp_nfds = manager->nevents; + if (dvp.dp_nfds >= manager->open_max) + dvp.dp_nfds = manager->open_max - 1; +#ifndef ISC_SOCKET_USE_POLLWATCH + dvp.dp_timeout = -1; +#else + if (pollstate == poll_idle) + dvp.dp_timeout = -1; + else + dvp.dp_timeout = + ISC_SOCKET_POLLWATCH_TIMEOUT; +#endif /* ISC_SOCKET_USE_POLLWATCH */ + cc = ioctl(manager->devpoll_fd, DP_POLL, &dvp); + if (cc == -1 && errno == EINVAL) { + /* + * {OPEN_MAX} may have dropped. Look + * up the current value and try again. + */ + result = isc_resource_getcurlimit( + isc_resource_openfiles, + &manager->open_max); + if (result != ISC_R_SUCCESS) + manager->open_max = 64; + } else + break; + } +#elif defined(USE_SELECT) + LOCK(&manager->lock); + memmove(manager->read_fds_copy, manager->read_fds, + manager->fd_bufsize); + memmove(manager->write_fds_copy, manager->write_fds, + manager->fd_bufsize); + maxfd = manager->maxfd + 1; + UNLOCK(&manager->lock); + + cc = select(maxfd, manager->read_fds_copy, + manager->write_fds_copy, NULL, NULL); +#endif /* USE_KQUEUE */ + + if (cc < 0 && !SOFT_ERROR(errno)) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + "%s %s: %s", fnname, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), strbuf); + } + +#if defined(USE_DEVPOLL) && defined(ISC_SOCKET_USE_POLLWATCH) + if (cc == 0) { + if (pollstate == poll_active) + pollstate = poll_checking; + else if (pollstate == poll_checking) + pollstate = poll_idle; + } else if (cc > 0) { + if (pollstate == poll_checking) { + /* + * XXX: We'd like to use a more + * verbose log level as it's actually an + * unexpected event, but the kernel bug + * reportedly happens pretty frequently + * (and it can also be a false positive) + * so it would be just too noisy. + */ + manager_log(manager, + ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, + ISC_LOG_DEBUG(1), + "unexpected POLL timeout"); + } + pollstate = poll_active; + } +#endif + } while (cc < 0); + +#if defined(USE_KQUEUE) || defined (USE_EPOLL) || defined (USE_DEVPOLL) + done = process_fds(manager, manager->events, cc); +#elif defined(USE_SELECT) + process_fds(manager, maxfd, manager->read_fds_copy, + manager->write_fds_copy); + + /* + * Process reads on internal, control fd. + */ + if (FD_ISSET(ctlfd, manager->read_fds_copy)) + done = process_ctlfd(manager); +#endif + } + + manager_log(manager, TRACE, "%s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_EXITING, "watcher exiting")); + + return ((isc_threadresult_t)0); +} +#endif /* USE_WATCHER_THREAD */ + +void +isc__socketmgr_setreserved(isc_socketmgr_t *manager0, uint32_t reserved) { + isc__socketmgr_t *manager = (isc__socketmgr_t *)manager0; + + REQUIRE(VALID_MANAGER(manager)); + + manager->reserved = reserved; +} + +void +isc__socketmgr_maxudp(isc_socketmgr_t *manager0, int maxudp) { + isc__socketmgr_t *manager = (isc__socketmgr_t *)manager0; + + REQUIRE(VALID_MANAGER(manager)); + + manager->maxudp = maxudp; +} + +/* + * Create a new socket manager. + */ + +static isc_result_t +setup_watcher(isc_mem_t *mctx, isc__socketmgr_t *manager) { + isc_result_t result; +#if defined(USE_KQUEUE) || defined(USE_EPOLL) || defined(USE_DEVPOLL) + char strbuf[ISC_STRERRORSIZE]; +#endif + +#ifdef USE_KQUEUE + manager->nevents = ISC_SOCKET_MAXEVENTS; + manager->events = isc_mem_get(mctx, sizeof(struct kevent) * + manager->nevents); + if (manager->events == NULL) + return (ISC_R_NOMEMORY); + manager->kqueue_fd = kqueue(); + if (manager->kqueue_fd == -1) { + result = isc__errno2result(errno); + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "kqueue %s: %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + strbuf); + isc_mem_put(mctx, manager->events, + sizeof(struct kevent) * manager->nevents); + return (result); + } + +#ifdef USE_WATCHER_THREAD + result = watch_fd(manager, manager->pipe_fds[0], SELECT_POKE_READ); + if (result != ISC_R_SUCCESS) { + close(manager->kqueue_fd); + isc_mem_put(mctx, manager->events, + sizeof(struct kevent) * manager->nevents); + return (result); + } +#endif /* USE_WATCHER_THREAD */ +#elif defined(USE_EPOLL) + manager->nevents = ISC_SOCKET_MAXEVENTS; + manager->events = isc_mem_get(mctx, sizeof(struct epoll_event) * + manager->nevents); + if (manager->events == NULL) + return (ISC_R_NOMEMORY); + manager->epoll_fd = epoll_create(manager->nevents); + if (manager->epoll_fd == -1) { + result = isc__errno2result(errno); + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "epoll_create %s: %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + strbuf); + isc_mem_put(mctx, manager->events, + sizeof(struct epoll_event) * manager->nevents); + return (result); + } +#ifdef USE_WATCHER_THREAD + result = watch_fd(manager, manager->pipe_fds[0], SELECT_POKE_READ); + if (result != ISC_R_SUCCESS) { + close(manager->epoll_fd); + isc_mem_put(mctx, manager->events, + sizeof(struct epoll_event) * manager->nevents); + return (result); + } +#endif /* USE_WATCHER_THREAD */ +#elif defined(USE_DEVPOLL) + manager->nevents = ISC_SOCKET_MAXEVENTS; + result = isc_resource_getcurlimit(isc_resource_openfiles, + &manager->open_max); + if (result != ISC_R_SUCCESS) + manager->open_max = 64; + manager->calls = 0; + manager->events = isc_mem_get(mctx, sizeof(struct pollfd) * + manager->nevents); + if (manager->events == NULL) + return (ISC_R_NOMEMORY); + /* + * Note: fdpollinfo should be able to support all possible FDs, so + * it must have maxsocks entries (not nevents). + */ + manager->fdpollinfo = isc_mem_get(mctx, sizeof(pollinfo_t) * + manager->maxsocks); + if (manager->fdpollinfo == NULL) { + isc_mem_put(mctx, manager->events, + sizeof(struct pollfd) * manager->nevents); + return (ISC_R_NOMEMORY); + } + memset(manager->fdpollinfo, 0, sizeof(pollinfo_t) * manager->maxsocks); + manager->devpoll_fd = open("/dev/poll", O_RDWR); + if (manager->devpoll_fd == -1) { + result = isc__errno2result(errno); + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "open(/dev/poll) %s: %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + strbuf); + isc_mem_put(mctx, manager->events, + sizeof(struct pollfd) * manager->nevents); + isc_mem_put(mctx, manager->fdpollinfo, + sizeof(pollinfo_t) * manager->maxsocks); + return (result); + } +#ifdef USE_WATCHER_THREAD + result = watch_fd(manager, manager->pipe_fds[0], SELECT_POKE_READ); + if (result != ISC_R_SUCCESS) { + close(manager->devpoll_fd); + isc_mem_put(mctx, manager->events, + sizeof(struct pollfd) * manager->nevents); + isc_mem_put(mctx, manager->fdpollinfo, + sizeof(pollinfo_t) * manager->maxsocks); + return (result); + } +#endif /* USE_WATCHER_THREAD */ +#elif defined(USE_SELECT) + UNUSED(result); + +#if ISC_SOCKET_MAXSOCKETS > FD_SETSIZE + /* + * Note: this code should also cover the case of MAXSOCKETS <= + * FD_SETSIZE, but we separate the cases to avoid possible portability + * issues regarding howmany() and the actual representation of fd_set. + */ + manager->fd_bufsize = howmany(manager->maxsocks, NFDBITS) * + sizeof(fd_mask); +#else + manager->fd_bufsize = sizeof(fd_set); +#endif + + manager->read_fds = NULL; + manager->read_fds_copy = NULL; + manager->write_fds = NULL; + manager->write_fds_copy = NULL; + + manager->read_fds = isc_mem_get(mctx, manager->fd_bufsize); + if (manager->read_fds != NULL) + manager->read_fds_copy = isc_mem_get(mctx, manager->fd_bufsize); + if (manager->read_fds_copy != NULL) + manager->write_fds = isc_mem_get(mctx, manager->fd_bufsize); + if (manager->write_fds != NULL) { + manager->write_fds_copy = isc_mem_get(mctx, + manager->fd_bufsize); + } + if (manager->write_fds_copy == NULL) { + if (manager->write_fds != NULL) { + isc_mem_put(mctx, manager->write_fds, + manager->fd_bufsize); + } + if (manager->read_fds_copy != NULL) { + isc_mem_put(mctx, manager->read_fds_copy, + manager->fd_bufsize); + } + if (manager->read_fds != NULL) { + isc_mem_put(mctx, manager->read_fds, + manager->fd_bufsize); + } + return (ISC_R_NOMEMORY); + } + memset(manager->read_fds, 0, manager->fd_bufsize); + memset(manager->write_fds, 0, manager->fd_bufsize); + +#ifdef USE_WATCHER_THREAD + (void)watch_fd(manager, manager->pipe_fds[0], SELECT_POKE_READ); + manager->maxfd = manager->pipe_fds[0]; +#else /* USE_WATCHER_THREAD */ + manager->maxfd = 0; +#endif /* USE_WATCHER_THREAD */ +#endif /* USE_KQUEUE */ + + return (ISC_R_SUCCESS); +} + +static void +cleanup_watcher(isc_mem_t *mctx, isc__socketmgr_t *manager) { +#ifdef USE_WATCHER_THREAD + isc_result_t result; + + result = unwatch_fd(manager, manager->pipe_fds[0], SELECT_POKE_READ); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "epoll_ctl(DEL) %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + } +#endif /* USE_WATCHER_THREAD */ + +#ifdef USE_KQUEUE + close(manager->kqueue_fd); + isc_mem_put(mctx, manager->events, + sizeof(struct kevent) * manager->nevents); +#elif defined(USE_EPOLL) + close(manager->epoll_fd); + isc_mem_put(mctx, manager->events, + sizeof(struct epoll_event) * manager->nevents); +#elif defined(USE_DEVPOLL) + close(manager->devpoll_fd); + isc_mem_put(mctx, manager->events, + sizeof(struct pollfd) * manager->nevents); + isc_mem_put(mctx, manager->fdpollinfo, + sizeof(pollinfo_t) * manager->maxsocks); +#elif defined(USE_SELECT) + if (manager->read_fds != NULL) + isc_mem_put(mctx, manager->read_fds, manager->fd_bufsize); + if (manager->read_fds_copy != NULL) + isc_mem_put(mctx, manager->read_fds_copy, manager->fd_bufsize); + if (manager->write_fds != NULL) + isc_mem_put(mctx, manager->write_fds, manager->fd_bufsize); + if (manager->write_fds_copy != NULL) + isc_mem_put(mctx, manager->write_fds_copy, manager->fd_bufsize); +#endif /* USE_KQUEUE */ +} + +isc_result_t +isc__socketmgr_create(isc_mem_t *mctx, isc_socketmgr_t **managerp) { + return (isc__socketmgr_create2(mctx, managerp, 0)); +} + +isc_result_t +isc__socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp, + unsigned int maxsocks) +{ + int i; + isc__socketmgr_t *manager; +#ifdef USE_WATCHER_THREAD + char strbuf[ISC_STRERRORSIZE]; +#endif + isc_result_t result; + + REQUIRE(managerp != NULL && *managerp == NULL); + +#ifdef USE_SHARED_MANAGER + if (socketmgr != NULL) { + /* Don't allow maxsocks to be updated */ + if (maxsocks > 0 && socketmgr->maxsocks != maxsocks) + return (ISC_R_EXISTS); + + socketmgr->refs++; + *managerp = (isc_socketmgr_t *)socketmgr; + return (ISC_R_SUCCESS); + } +#endif /* USE_SHARED_MANAGER */ + + if (maxsocks == 0) + maxsocks = ISC_SOCKET_MAXSOCKETS; + + manager = isc_mem_get(mctx, sizeof(*manager)); + if (manager == NULL) + return (ISC_R_NOMEMORY); + + /* zero-clear so that necessary cleanup on failure will be easy */ + memset(manager, 0, sizeof(*manager)); + manager->maxsocks = maxsocks; + manager->reserved = 0; + manager->maxudp = 0; + manager->fds = isc_mem_get(mctx, + manager->maxsocks * sizeof(isc__socket_t *)); + if (manager->fds == NULL) { + result = ISC_R_NOMEMORY; + goto free_manager; + } + manager->fdstate = isc_mem_get(mctx, manager->maxsocks * sizeof(int)); + if (manager->fdstate == NULL) { + result = ISC_R_NOMEMORY; + goto free_manager; + } +#if defined(USE_EPOLL) + manager->epoll_events = isc_mem_get(mctx, (manager->maxsocks * + sizeof(uint32_t))); + if (manager->epoll_events == NULL) { + result = ISC_R_NOMEMORY; + goto free_manager; + } + memset(manager->epoll_events, 0, manager->maxsocks * sizeof(uint32_t)); +#endif + manager->stats = NULL; + + manager->common.methods = &socketmgrmethods; + manager->common.magic = ISCAPI_SOCKETMGR_MAGIC; + manager->common.impmagic = SOCKET_MANAGER_MAGIC; + manager->mctx = NULL; + memset(manager->fds, 0, manager->maxsocks * sizeof(isc_socket_t *)); + ISC_LIST_INIT(manager->socklist); + result = isc_mutex_init(&manager->lock); + if (result != ISC_R_SUCCESS) + goto free_manager; + manager->fdlock = isc_mem_get(mctx, FDLOCK_COUNT * sizeof(isc_mutex_t)); + if (manager->fdlock == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_lock; + } + for (i = 0; i < FDLOCK_COUNT; i++) { + result = isc_mutex_init(&manager->fdlock[i]); + if (result != ISC_R_SUCCESS) { + while (--i >= 0) + DESTROYLOCK(&manager->fdlock[i]); + isc_mem_put(mctx, manager->fdlock, + FDLOCK_COUNT * sizeof(isc_mutex_t)); + manager->fdlock = NULL; + goto cleanup_lock; + } + } + +#ifdef USE_WATCHER_THREAD + if (isc_condition_init(&manager->shutdown_ok) != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + result = ISC_R_UNEXPECTED; + goto cleanup_lock; + } + + /* + * Create the special fds that will be used to wake up the + * select/poll loop when something internal needs to be done. + */ + if (pipe(manager->pipe_fds) != 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "pipe() %s: %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + strbuf); + result = ISC_R_UNEXPECTED; + goto cleanup_condition; + } + + RUNTIME_CHECK(make_nonblock(manager->pipe_fds[0]) == ISC_R_SUCCESS); +#if 0 + RUNTIME_CHECK(make_nonblock(manager->pipe_fds[1]) == ISC_R_SUCCESS); +#endif +#endif /* USE_WATCHER_THREAD */ + +#ifdef USE_SHARED_MANAGER + manager->refs = 1; +#endif /* USE_SHARED_MANAGER */ + + /* + * Set up initial state for the select loop + */ + result = setup_watcher(mctx, manager); + if (result != ISC_R_SUCCESS) + goto cleanup; + + memset(manager->fdstate, 0, manager->maxsocks * sizeof(int)); + +#ifdef USE_WATCHER_THREAD + /* + * Start up the select/poll thread. + */ + if (isc_thread_create(watcher, manager, &manager->watcher) != + ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_thread_create() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + cleanup_watcher(mctx, manager); + result = ISC_R_UNEXPECTED; + goto cleanup; + } + isc_thread_setname(manager->watcher, "isc-socket"); +#endif /* USE_WATCHER_THREAD */ + isc_mem_attach(mctx, &manager->mctx); + +#ifdef USE_SHARED_MANAGER + socketmgr = manager; +#endif /* USE_SHARED_MANAGER */ + *managerp = (isc_socketmgr_t *)manager; + + return (ISC_R_SUCCESS); + +cleanup: +#ifdef USE_WATCHER_THREAD + (void)close(manager->pipe_fds[0]); + (void)close(manager->pipe_fds[1]); +#endif /* USE_WATCHER_THREAD */ + +#ifdef USE_WATCHER_THREAD +cleanup_condition: + (void)isc_condition_destroy(&manager->shutdown_ok); +#endif /* USE_WATCHER_THREAD */ + + +cleanup_lock: + if (manager->fdlock != NULL) { + for (i = 0; i < FDLOCK_COUNT; i++) + DESTROYLOCK(&manager->fdlock[i]); + } + DESTROYLOCK(&manager->lock); + +free_manager: + if (manager->fdlock != NULL) { + isc_mem_put(mctx, manager->fdlock, + FDLOCK_COUNT * sizeof(isc_mutex_t)); + } +#if defined(USE_EPOLL) + if (manager->epoll_events != NULL) { + isc_mem_put(mctx, manager->epoll_events, + manager->maxsocks * sizeof(uint32_t)); + } +#endif + if (manager->fdstate != NULL) { + isc_mem_put(mctx, manager->fdstate, + manager->maxsocks * sizeof(int)); + } + if (manager->fds != NULL) { + isc_mem_put(mctx, manager->fds, + manager->maxsocks * sizeof(isc_socket_t *)); + } + isc_mem_put(mctx, manager, sizeof(*manager)); + + return (result); +} + +isc_result_t +isc_socketmgr_getmaxsockets(isc_socketmgr_t *manager0, unsigned int *nsockp) { + isc__socketmgr_t *manager = (isc__socketmgr_t *)manager0; + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(nsockp != NULL); + + *nsockp = manager->maxsocks; + + return (ISC_R_SUCCESS); +} + +void +isc_socketmgr_setstats(isc_socketmgr_t *manager0, isc_stats_t *stats) { + isc__socketmgr_t *manager = (isc__socketmgr_t *)manager0; + + 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; + int i; + isc_mem_t *mctx; + + /* + * Destroy a socket manager. + */ + + REQUIRE(managerp != NULL); + manager = (isc__socketmgr_t *)*managerp; + REQUIRE(VALID_MANAGER(manager)); + +#ifdef USE_SHARED_MANAGER + manager->refs--; + if (manager->refs > 0) { + *managerp = NULL; + return; + } + socketmgr = NULL; +#endif /* USE_SHARED_MANAGER */ + + LOCK(&manager->lock); + + /* + * Wait for all sockets to be destroyed. + */ + while (!ISC_LIST_EMPTY(manager->socklist)) { +#ifdef USE_WATCHER_THREAD + manager_log(manager, CREATION, "%s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_SOCKETSREMAIN, + "sockets exist")); + WAIT(&manager->shutdown_ok, &manager->lock); +#else /* USE_WATCHER_THREAD */ + UNLOCK(&manager->lock); + isc__taskmgr_dispatch(NULL); + LOCK(&manager->lock); +#endif /* USE_WATCHER_THREAD */ + } + + UNLOCK(&manager->lock); + + /* + * Here, poke our select/poll thread. Do this by closing the write + * half of the pipe, which will send EOF to the read half. + * This is currently a no-op in the non-threaded case. + */ + select_poke(manager, 0, SELECT_POKE_SHUTDOWN); + +#ifdef USE_WATCHER_THREAD + /* + * Wait for thread to exit. + */ + if (isc_thread_join(manager->watcher, NULL) != ISC_R_SUCCESS) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_thread_join() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); +#endif /* USE_WATCHER_THREAD */ + + /* + * Clean up. + */ + cleanup_watcher(manager->mctx, manager); + +#ifdef USE_WATCHER_THREAD + (void)close(manager->pipe_fds[0]); + (void)close(manager->pipe_fds[1]); + (void)isc_condition_destroy(&manager->shutdown_ok); +#endif /* USE_WATCHER_THREAD */ + + for (i = 0; i < (int)manager->maxsocks; i++) + if (manager->fdstate[i] == CLOSE_PENDING) /* no need to lock */ + (void)close(i); + +#if defined(USE_EPOLL) + isc_mem_put(manager->mctx, manager->epoll_events, + manager->maxsocks * sizeof(uint32_t)); +#endif + isc_mem_put(manager->mctx, manager->fds, + manager->maxsocks * sizeof(isc__socket_t *)); + isc_mem_put(manager->mctx, manager->fdstate, + manager->maxsocks * sizeof(int)); + + if (manager->stats != NULL) + isc_stats_detach(&manager->stats); + + if (manager->fdlock != NULL) { + for (i = 0; i < FDLOCK_COUNT; i++) + DESTROYLOCK(&manager->fdlock[i]); + isc_mem_put(manager->mctx, manager->fdlock, + FDLOCK_COUNT * sizeof(isc_mutex_t)); + } + DESTROYLOCK(&manager->lock); + manager->common.magic = 0; + manager->common.impmagic = 0; + mctx= manager->mctx; + isc_mem_put(mctx, manager, sizeof(*manager)); + + isc_mem_detach(&mctx); + + *managerp = NULL; + +#ifdef USE_SHARED_MANAGER + socketmgr = NULL; +#endif +} + +static isc_result_t +socket_recv(isc__socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, + unsigned int flags) +{ + int io_state; + bool have_lock = false; + isc_task_t *ntask = NULL; + isc_result_t result = ISC_R_SUCCESS; + + dev->ev_sender = task; + + if (sock->type == isc_sockettype_udp) { + io_state = doio_recv(sock, dev); + } else { + LOCK(&sock->lock); + have_lock = true; + + if (ISC_LIST_EMPTY(sock->recv_list)) + io_state = doio_recv(sock, dev); + else + io_state = DOIO_SOFT; + } + + switch (io_state) { + case DOIO_SOFT: + /* + * We couldn't read all or part of the request right now, so + * queue it. + * + * Attach to socket and to task + */ + isc_task_attach(task, &ntask); + dev->attributes |= ISC_SOCKEVENTATTR_ATTACHED; + + if (!have_lock) { + LOCK(&sock->lock); + have_lock = true; + } + + /* + * Enqueue the request. If the socket was previously not being + * watched, poke the watcher to start paying attention to it. + */ + if (ISC_LIST_EMPTY(sock->recv_list) && !sock->pending_recv) + select_poke(sock->manager, sock->fd, SELECT_POKE_READ); + ISC_LIST_ENQUEUE(sock->recv_list, dev, ev_link); + + socket_log(sock, NULL, EVENT, NULL, 0, 0, + "socket_recv: event %p -> task %p", + dev, ntask); + + if ((flags & ISC_SOCKFLAG_IMMEDIATE) != 0) + result = ISC_R_INPROGRESS; + break; + + case DOIO_EOF: + dev->result = ISC_R_EOF; + /* fallthrough */ + + case DOIO_HARD: + case DOIO_SUCCESS: + if ((flags & ISC_SOCKFLAG_IMMEDIATE) == 0) + send_recvdone_event(sock, &dev); + break; + } + + if (have_lock) + UNLOCK(&sock->lock); + + return (result); +} + +isc_result_t +isc__socket_recvv(isc_socket_t *sock0, isc_bufferlist_t *buflist, + unsigned int minimum, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + isc__socket_t *sock = (isc__socket_t *)sock0; + isc_socketevent_t *dev; + isc__socketmgr_t *manager; + unsigned int iocount; + isc_buffer_t *buffer; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(buflist != NULL); + REQUIRE(!ISC_LIST_EMPTY(*buflist)); + REQUIRE(task != NULL); + REQUIRE(action != NULL); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + iocount = isc_bufferlist_availablecount(buflist); + REQUIRE(iocount > 0); + + INSIST(sock->bound); + + dev = allocate_socketevent(manager->mctx, sock, + ISC_SOCKEVENT_RECVDONE, action, arg); + if (dev == NULL) + return (ISC_R_NOMEMORY); + + /* + * UDP sockets are always partial read + */ + if (sock->type == isc_sockettype_udp) + dev->minimum = 1; + else { + if (minimum == 0) + dev->minimum = iocount; + else + dev->minimum = minimum; + } + + /* + * Move each buffer from the passed in list to our internal one. + */ + buffer = ISC_LIST_HEAD(*buflist); + while (buffer != NULL) { + ISC_LIST_DEQUEUE(*buflist, buffer, link); + ISC_LIST_ENQUEUE(dev->bufferlist, buffer, link); + buffer = ISC_LIST_HEAD(*buflist); + } + + return (socket_recv(sock, dev, task, 0)); +} + +isc_result_t +isc__socket_recv(isc_socket_t *sock0, isc_region_t *region, + unsigned int minimum, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + isc__socket_t *sock = (isc__socket_t *)sock0; + isc_socketevent_t *dev; + isc__socketmgr_t *manager; + + REQUIRE(VALID_SOCKET(sock)); + 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) + return (ISC_R_NOMEMORY); + + return (isc__socket_recv2(sock0, region, minimum, task, dev, 0)); +} + +isc_result_t +isc__socket_recv2(isc_socket_t *sock0, isc_region_t *region, + unsigned int minimum, isc_task_t *task, + isc_socketevent_t *event, unsigned int flags) +{ + isc__socket_t *sock = (isc__socket_t *)sock0; + + event->ev_sender = sock; + event->result = ISC_R_UNSET; + ISC_LIST_INIT(event->bufferlist); + 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; + } + + return (socket_recv(sock, event, task, flags)); +} + +static isc_result_t +socket_send(isc__socket_t *sock, isc_socketevent_t *dev, isc_task_t *task, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + unsigned int flags) +{ + int io_state; + bool have_lock = false; + 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) { + dev->attributes |= ISC_SOCKEVENTATTR_PKTINFO; + dev->pktinfo = *pktinfo; + + if (!isc_sockaddr_issitelocal(&dev->address) && + !isc_sockaddr_islinklocal(&dev->address)) { + socket_log(sock, NULL, TRACE, isc_msgcat, + ISC_MSGSET_SOCKET, ISC_MSG_PKTINFOPROVIDED, + "pktinfo structure provided, ifindex %u " + "(set to 0)", pktinfo->ipi6_ifindex); + + /* + * Set the pktinfo index to 0 here, to let the + * kernel decide what interface it should send on. + */ + dev->pktinfo.ipi6_ifindex = 0; + } + } + + if (sock->type == isc_sockettype_udp) + io_state = doio_send(sock, dev); + else { + LOCK(&sock->lock); + have_lock = true; + + if (ISC_LIST_EMPTY(sock->send_list)) + io_state = doio_send(sock, dev); + else + io_state = DOIO_SOFT; + } + + switch (io_state) { + 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) { + isc_task_attach(task, &ntask); + dev->attributes |= ISC_SOCKEVENTATTR_ATTACHED; + + if (!have_lock) { + LOCK(&sock->lock); + have_lock = true; + } + + /* + * Enqueue the request. If the socket was previously + * not being watched, poke the watcher to start + * paying attention to it. + */ + if (ISC_LIST_EMPTY(sock->send_list) && + !sock->pending_send) + select_poke(sock->manager, sock->fd, + SELECT_POKE_WRITE); + ISC_LIST_ENQUEUE(sock->send_list, dev, ev_link); + + socket_log(sock, NULL, EVENT, NULL, 0, 0, + "socket_send: event %p -> task %p", + dev, ntask); + + if ((flags & ISC_SOCKFLAG_IMMEDIATE) != 0) + result = ISC_R_INPROGRESS; + break; + } + + /* FALLTHROUGH */ + + case DOIO_HARD: + case DOIO_SUCCESS: + if ((flags & ISC_SOCKFLAG_IMMEDIATE) == 0) + send_senddone_event(sock, &dev); + break; + } + + if (have_lock) + UNLOCK(&sock->lock); + + 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 *sock0, isc_region_t *region, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo) +{ + isc__socket_t *sock = (isc__socket_t *)sock0; + isc_socketevent_t *dev; + isc__socketmgr_t *manager; + + REQUIRE(VALID_SOCKET(sock)); + 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) + return (ISC_R_NOMEMORY); + + dev->region = *region; + + return (socket_send(sock, dev, task, address, pktinfo, 0)); +} + +isc_result_t +isc__socket_sendv(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg) +{ + return (isc__socket_sendtov2(sock, buflist, task, action, arg, NULL, + NULL, 0)); +} + +isc_result_t +isc__socket_sendtov(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo) +{ + return (isc__socket_sendtov2(sock, buflist, task, action, arg, address, + pktinfo, 0)); +} + +isc_result_t +isc__socket_sendtov2(isc_socket_t *sock0, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + unsigned int flags) +{ + isc__socket_t *sock = (isc__socket_t *)sock0; + isc_socketevent_t *dev; + isc__socketmgr_t *manager; + unsigned int iocount; + isc_buffer_t *buffer; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(buflist != NULL); + REQUIRE(!ISC_LIST_EMPTY(*buflist)); + REQUIRE(task != NULL); + REQUIRE(action != NULL); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + iocount = isc_bufferlist_usedcount(buflist); + REQUIRE(iocount > 0); + + dev = allocate_socketevent(manager->mctx, sock, + ISC_SOCKEVENT_SENDDONE, action, arg); + if (dev == NULL) + return (ISC_R_NOMEMORY); + + /* + * Move each buffer from the passed in list to our internal one. + */ + buffer = ISC_LIST_HEAD(*buflist); + while (buffer != NULL) { + ISC_LIST_DEQUEUE(*buflist, buffer, link); + ISC_LIST_ENQUEUE(dev->bufferlist, buffer, link); + buffer = ISC_LIST_HEAD(*buflist); + } + + return (socket_send(sock, dev, task, address, pktinfo, flags)); +} + +isc_result_t +isc__socket_sendto2(isc_socket_t *sock0, isc_region_t *region, + isc_task_t *task, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + isc_socketevent_t *event, unsigned int flags) +{ + isc__socket_t *sock = (isc__socket_t *)sock0; + + REQUIRE(VALID_SOCKET(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_UNSET; + ISC_LIST_INIT(event->bufferlist); + event->region = *region; + event->n = 0; + event->offset = 0; + event->attributes &= ~ISC_SOCKEVENTATTR_ATTACHED; + + return (socket_send(sock, event, task, address, pktinfo, flags)); +} + +void +isc__socket_cleanunix(isc_sockaddr_t *sockaddr, bool active) { +#ifdef ISC_PLATFORM_HAVESYSUNH + int s; + struct stat sb; + char strbuf[ISC_STRERRORSIZE]; + + if (sockaddr->type.sa.sa_family != AF_UNIX) + return; + +#ifndef S_ISSOCK +#if defined(S_IFMT) && defined(S_IFSOCK) +#define S_ISSOCK(mode) ((mode & S_IFMT)==S_IFSOCK) +#elif defined(_S_IFMT) && defined(S_IFSOCK) +#define S_ISSOCK(mode) ((mode & _S_IFMT)==S_IFSOCK) +#endif +#endif + +#ifndef S_ISFIFO +#if defined(S_IFMT) && defined(S_IFIFO) +#define S_ISFIFO(mode) ((mode & S_IFMT)==S_IFIFO) +#elif defined(_S_IFMT) && defined(S_IFIFO) +#define S_ISFIFO(mode) ((mode & _S_IFMT)==S_IFIFO) +#endif +#endif + +#if !defined(S_ISFIFO) && !defined(S_ISSOCK) +#error You need to define S_ISFIFO and S_ISSOCK as appropriate for your platform. See <sys/stat.h>. +#endif + +#ifndef S_ISFIFO +#define S_ISFIFO(mode) 0 +#endif + +#ifndef S_ISSOCK +#define S_ISSOCK(mode) 0 +#endif + + if (active) { + if (stat(sockaddr->type.sunix.sun_path, &sb) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "isc_socket_cleanunix: stat(%s): %s", + sockaddr->type.sunix.sun_path, strbuf); + return; + } + if (!(S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "isc_socket_cleanunix: %s: not a socket", + sockaddr->type.sunix.sun_path); + return; + } + if (unlink(sockaddr->type.sunix.sun_path) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "isc_socket_cleanunix: unlink(%s): %s", + sockaddr->type.sunix.sun_path, strbuf); + } + return; + } + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING, + "isc_socket_cleanunix: socket(%s): %s", + sockaddr->type.sunix.sun_path, strbuf); + return; + } + + if (stat(sockaddr->type.sunix.sun_path, &sb) < 0) { + switch (errno) { + case ENOENT: /* We exited cleanly last time */ + break; + default: + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING, + "isc_socket_cleanunix: stat(%s): %s", + sockaddr->type.sunix.sun_path, strbuf); + break; + } + goto cleanup; + } + + if (!(S_ISSOCK(sb.st_mode) || S_ISFIFO(sb.st_mode))) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING, + "isc_socket_cleanunix: %s: not a socket", + sockaddr->type.sunix.sun_path); + goto cleanup; + } + + if (connect(s, (struct sockaddr *)&sockaddr->type.sunix, + sizeof(sockaddr->type.sunix)) < 0) { + switch (errno) { + case ECONNREFUSED: + case ECONNRESET: + if (unlink(sockaddr->type.sunix.sun_path) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, + ISC_LOG_WARNING, + "isc_socket_cleanunix: " + "unlink(%s): %s", + sockaddr->type.sunix.sun_path, + strbuf); + } + break; + default: + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_WARNING, + "isc_socket_cleanunix: connect(%s): %s", + sockaddr->type.sunix.sun_path, strbuf); + break; + } + } + cleanup: + close(s); +#else + UNUSED(sockaddr); + UNUSED(active); +#endif +} + +isc_result_t +isc__socket_permunix(isc_sockaddr_t *sockaddr, uint32_t perm, + uint32_t owner, uint32_t group) +{ +#ifdef ISC_PLATFORM_HAVESYSUNH + isc_result_t result = ISC_R_SUCCESS; + char strbuf[ISC_STRERRORSIZE]; + char path[sizeof(sockaddr->type.sunix.sun_path)]; +#ifdef NEED_SECURE_DIRECTORY + char *slash; +#endif + + REQUIRE(sockaddr->type.sa.sa_family == AF_UNIX); + INSIST(strlen(sockaddr->type.sunix.sun_path) < sizeof(path)); + strlcpy(path, sockaddr->type.sunix.sun_path, sizeof(path)); + +#ifdef NEED_SECURE_DIRECTORY + slash = strrchr(path, '/'); + if (slash != NULL) { + if (slash != path) { + *slash = '\0'; + } else { + strlcpy(path, "/", sizeof(path)); + } + } else { + strlcpy(path, ".", sizeof(path)); + } +#endif + + if (chmod(path, perm) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "isc_socket_permunix: chmod(%s, %d): %s", + path, perm, strbuf); + result = ISC_R_FAILURE; + } + if (chown(path, owner, group) < 0) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + "isc_socket_permunix: chown(%s, %d, %d): %s", + path, owner, group, + strbuf); + result = ISC_R_FAILURE; + } + return (result); +#else + UNUSED(sockaddr); + UNUSED(perm); + UNUSED(owner); + UNUSED(group); + return (ISC_R_NOTIMPLEMENTED); +#endif +} + +isc_result_t +isc__socket_bind(isc_socket_t *sock0, isc_sockaddr_t *sockaddr, + unsigned int options) { + isc__socket_t *sock = (isc__socket_t *)sock0; + char strbuf[ISC_STRERRORSIZE]; + int on = 1; + + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + + 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. + */ +#ifdef AF_UNIX + if (sock->pf == AF_UNIX) + goto bind_socket; +#endif + if ((options & ISC_SOCKET_REUSEADDRESS) != 0 && + isc_sockaddr_getport(sockaddr) != (in_port_t)0 && + setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, + sizeof(on)) < 0) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d) %s", sock->fd, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + /* Press on... */ + } +#ifdef AF_UNIX + bind_socket: +#endif + if (bind(sock->fd, &sockaddr->type.sa, sockaddr->length) < 0) { + inc_stats(sock->manager->stats, + sock->statsindex[STATID_BINDFAIL]); + + UNLOCK(&sock->lock); + switch (errno) { + case EACCES: + return (ISC_R_NOPERM); + case EADDRNOTAVAIL: + return (ISC_R_ADDRNOTAVAIL); + case EADDRINUSE: + return (ISC_R_ADDRINUSE); + case EINVAL: + return (ISC_R_BOUND); + default: + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "bind: %s", + strbuf); + return (ISC_R_UNEXPECTED); + } + } + + socket_log(sock, sockaddr, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_BOUND, "bound"); + sock->bound = 1; + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); +} + +/* + * Enable this only for specific OS versions, and only when they have repaired + * their problems with it. Until then, this is is broken and needs to be + * diabled by default. See RT22589 for details. + */ +#undef ENABLE_ACCEPTFILTER + +isc_result_t +isc__socket_filter(isc_socket_t *sock0, const char *filter) { + isc__socket_t *sock = (isc__socket_t *)sock0; +#if defined(SO_ACCEPTFILTER) && defined(ENABLE_ACCEPTFILTER) + char strbuf[ISC_STRERRORSIZE]; + struct accept_filter_arg afa; +#else + UNUSED(sock); + UNUSED(filter); +#endif + + REQUIRE(VALID_SOCKET(sock)); + +#if defined(SO_ACCEPTFILTER) && defined(ENABLE_ACCEPTFILTER) + bzero(&afa, sizeof(afa)); + strlcpy(afa.af_name, filter, sizeof(afa.af_name)); + if (setsockopt(sock->fd, SOL_SOCKET, SO_ACCEPTFILTER, + &afa, sizeof(afa)) == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + socket_log(sock, NULL, CREATION, isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_FILTER, "setsockopt(SO_ACCEPTFILTER): %s", + strbuf); + return (ISC_R_FAILURE); + } + return (ISC_R_SUCCESS); +#else + return (ISC_R_NOTIMPLEMENTED); +#endif +} + +/* + * Try enabling TCP Fast Open for a given socket if the OS supports it. + */ +static void +set_tcp_fastopen(isc__socket_t *sock, unsigned int backlog) { +#if defined(ISC_PLATFORM_HAVETFO) && defined(TCP_FASTOPEN) + char strbuf[ISC_STRERRORSIZE]; + +/* + * FreeBSD, as of versions 10.3 and 11.0, defines TCP_FASTOPEN while also + * shipping a default kernel without TFO support, so we special-case it by + * performing an additional runtime check for TFO support using sysctl to + * prevent setsockopt() errors from being logged. + */ +#if defined(__FreeBSD__) && defined(HAVE_SYSCTLBYNAME) +#define SYSCTL_TFO "net.inet.tcp.fastopen.enabled" + unsigned int enabled; + size_t enabledlen = sizeof(enabled); + static bool tfo_notice_logged = false; + + if (sysctlbyname(SYSCTL_TFO, &enabled, &enabledlen, NULL, 0) < 0) { + /* + * This kernel does not support TCP Fast Open. There is + * nothing more we can do. + */ + return; + } else if (enabled == 0) { + /* + * This kernel does support TCP Fast Open, but it is disabled + * by sysctl. Notify the user, but do not nag. + */ + if (!tfo_notice_logged) { + isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_NOTICE, + "TCP_FASTOPEN support is disabled by " + "sysctl (" SYSCTL_TFO " = 0)"); + tfo_notice_logged = true; + } + return; + } +#endif + +#ifdef __APPLE__ + backlog = 1; +#else + backlog = backlog / 2; + if (backlog == 0) + backlog = 1; +#endif + if (setsockopt(sock->fd, IPPROTO_TCP, TCP_FASTOPEN, + (void *)&backlog, sizeof(backlog)) < 0) { + isc__strerror(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 */ + } +#else + UNUSED(sock); + UNUSED(backlog); +#endif +} + +/* + * 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 *sock0, unsigned int backlog) { + isc__socket_t *sock = (isc__socket_t *)sock0; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + + REQUIRE(!sock->listener); + REQUIRE(sock->bound); + REQUIRE(sock->type == isc_sockettype_tcp || + sock->type == isc_sockettype_unix); + + if (backlog == 0) + backlog = SOMAXCONN; + + if (listen(sock->fd, (int)backlog) < 0) { + UNLOCK(&sock->lock); + isc__strerror(errno, strbuf, sizeof(strbuf)); + + UNEXPECTED_ERROR(__FILE__, __LINE__, "listen: %s", strbuf); + + return (ISC_R_UNEXPECTED); + } + + set_tcp_fastopen(sock, backlog); + + sock->listener = 1; + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); +} + +/* + * This should try to do aggressive accept() XXXMLG + */ +isc_result_t +isc__socket_accept(isc_socket_t *sock0, + isc_task_t *task, isc_taskaction_t action, void *arg) +{ + isc__socket_t *sock = (isc__socket_t *)sock0; + isc_socket_newconnev_t *dev; + isc__socketmgr_t *manager; + isc_task_t *ntask = NULL; + isc__socket_t *nsock; + isc_result_t result; + bool do_poke = false; + + REQUIRE(VALID_SOCKET(sock)); + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + LOCK(&sock->lock); + + 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. + */ + dev = (isc_socket_newconnev_t *) + isc_event_allocate(manager->mctx, task, ISC_SOCKEVENT_NEWCONN, + action, arg, sizeof(*dev)); + if (dev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + ISC_LINK_INIT(dev, ev_link); + + result = allocate_socket(manager, sock->type, &nsock); + if (result != ISC_R_SUCCESS) { + isc_event_free(ISC_EVENT_PTR(&dev)); + UNLOCK(&sock->lock); + return (result); + } + + /* + * Attach to socket and to task. + */ + isc_task_attach(task, &ntask); + if (isc_task_exiting(ntask)) { + free_socket(&nsock); + isc_task_detach(&ntask); + isc_event_free(ISC_EVENT_PTR(&dev)); + UNLOCK(&sock->lock); + return (ISC_R_SHUTTINGDOWN); + } + nsock->references++; + nsock->statsindex = sock->statsindex; + + dev->ev_sender = ntask; + dev->newsocket = (isc_socket_t *)nsock; + + /* + * Poke watcher here. We still have the socket locked, so there + * is no race condition. We will keep the lock for such a short + * bit of time waking it up now or later won't matter all that much. + */ + if (ISC_LIST_EMPTY(sock->accept_list)) + do_poke = true; + + ISC_LIST_ENQUEUE(sock->accept_list, dev, ev_link); + + if (do_poke) + select_poke(manager, sock->fd, SELECT_POKE_ACCEPT); + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__socket_connect(isc_socket_t *sock0, isc_sockaddr_t *addr, + isc_task_t *task, isc_taskaction_t action, void *arg) +{ + isc__socket_t *sock = (isc__socket_t *)sock0; + isc_socket_connev_t *dev; + isc_task_t *ntask = NULL; + isc__socketmgr_t *manager; + int cc; + char strbuf[ISC_STRERRORSIZE]; + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + + 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); + + dev = (isc_socket_connev_t *)isc_event_allocate(manager->mctx, sock, + ISC_SOCKEVENT_CONNECT, + action, arg, + sizeof(*dev)); + if (dev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + ISC_LINK_INIT(dev, ev_link); + + if (sock->connecting) { + INSIST(isc_sockaddr_equal(&sock->peer_address, addr)); + goto queue; + } + + if (sock->connected) { + INSIST(isc_sockaddr_equal(&sock->peer_address, addr)); + dev->result = ISC_R_SUCCESS; + isc_task_send(task, ISC_EVENT_PTR(&dev)); + + UNLOCK(&sock->lock); + + return (ISC_R_SUCCESS); + } + + /* + * Try to do the connect right away, as there can be only one + * outstanding, and it might happen to complete. + */ + sock->peer_address = *addr; + cc = connect(sock->fd, &addr->type.sa, addr->length); + if (cc < 0) { + /* + * HP-UX "fails" to connect a UDP socket and sets errno to + * EINPROGRESS if it's non-blocking. We'd rather regard this as + * a success and let the user detect it if it's really an error + * at the time of sending a packet on the socket. + */ + if (sock->type == isc_sockettype_udp && errno == EINPROGRESS) { + cc = 0; + goto success; + } + if (SOFT_ERROR(errno) || errno == EINPROGRESS) + goto queue; + + switch (errno) { +#define ERROR_MATCH(a, b) case a: dev->result = b; goto err_exit; + ERROR_MATCH(EACCES, ISC_R_NOPERM); + ERROR_MATCH(EADDRNOTAVAIL, ISC_R_ADDRNOTAVAIL); + ERROR_MATCH(EAFNOSUPPORT, ISC_R_ADDRNOTAVAIL); + ERROR_MATCH(ECONNREFUSED, ISC_R_CONNREFUSED); + ERROR_MATCH(EHOSTUNREACH, ISC_R_HOSTUNREACH); +#ifdef EHOSTDOWN + ERROR_MATCH(EHOSTDOWN, ISC_R_HOSTUNREACH); +#endif + ERROR_MATCH(ENETUNREACH, ISC_R_NETUNREACH); + ERROR_MATCH(ENOBUFS, ISC_R_NORESOURCES); + ERROR_MATCH(EPERM, ISC_R_HOSTUNREACH); + ERROR_MATCH(EPIPE, ISC_R_NOTCONNECTED); + ERROR_MATCH(ECONNRESET, ISC_R_CONNECTIONRESET); +#undef ERROR_MATCH + } + + sock->connected = 0; + + isc__strerror(errno, strbuf, sizeof(strbuf)); + isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "connect(%s) %d/%s", + addrbuf, errno, strbuf); + + UNLOCK(&sock->lock); + inc_stats(sock->manager->stats, + sock->statsindex[STATID_CONNECTFAIL]); + isc_event_free(ISC_EVENT_PTR(&dev)); + return (ISC_R_UNEXPECTED); + + err_exit: + sock->connected = 0; + isc_task_send(task, ISC_EVENT_PTR(&dev)); + + UNLOCK(&sock->lock); + inc_stats(sock->manager->stats, + sock->statsindex[STATID_CONNECTFAIL]); + return (ISC_R_SUCCESS); + } + + /* + * If connect completed, fire off the done event. + */ + success: + if (cc == 0) { + sock->connected = 1; + sock->bound = 1; + dev->result = ISC_R_SUCCESS; + isc_task_send(task, ISC_EVENT_PTR(&dev)); + + UNLOCK(&sock->lock); + + inc_stats(sock->manager->stats, + sock->statsindex[STATID_CONNECT]); + + return (ISC_R_SUCCESS); + } + + queue: + + /* + * Attach to task. + */ + isc_task_attach(task, &ntask); + + dev->ev_sender = ntask; + + /* + * Poke watcher here. We still have the socket locked, so there + * is no race condition. We will keep the lock for such a short + * bit of time waking it up now or later won't matter all that much. + */ + if (ISC_LIST_EMPTY(sock->connect_list) && !sock->connecting) + select_poke(manager, sock->fd, SELECT_POKE_CONNECT); + + sock->connecting = 1; + + ISC_LIST_ENQUEUE(sock->connect_list, dev, ev_link); + + UNLOCK(&sock->lock); + return (ISC_R_SUCCESS); +} + +/* + * Called when a socket with a pending connect() finishes. + */ +static void +internal_connect(isc_task_t *me, isc_event_t *ev) { + isc__socket_t *sock; + isc_socket_connev_t *dev; + int cc; + isc_result_t result; + ISC_SOCKADDR_LEN_T optlen; + char strbuf[ISC_STRERRORSIZE]; + char peerbuf[ISC_SOCKADDR_FORMATSIZE]; + + UNUSED(me); + INSIST(ev->ev_type == ISC_SOCKEVENT_INTW); + + sock = ev->ev_sender; + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + + /* + * When the internal event was sent the reference count was bumped + * to keep the socket around for us. Decrement the count here. + */ + INSIST(sock->references > 0); + sock->references--; + if (sock->references == 0) { + UNLOCK(&sock->lock); + destroy(&sock); + return; + } + + /* + * Get the first item off the connect list. + * If it is empty, unlock the socket and return. + */ + dev = ISC_LIST_HEAD(sock->connect_list); + if (dev == NULL) { + INSIST(!sock->connecting); + UNLOCK(&sock->lock); + return; + } + + INSIST(sock->connecting); + sock->connecting = 0; + + /* + * Get any possible error status here. + */ + optlen = sizeof(cc); + if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, + (void *)&cc, (void *)&optlen) != 0) + cc = errno; + else + errno = cc; + + if (errno != 0) { + /* + * If the error is EAGAIN, just re-select on this + * fd and pretend nothing strange happened. + */ + if (SOFT_ERROR(errno) || errno == EINPROGRESS) { + sock->connecting = 1; + select_poke(sock->manager, sock->fd, + SELECT_POKE_CONNECT); + UNLOCK(&sock->lock); + + return; + } + + inc_stats(sock->manager->stats, + sock->statsindex[STATID_CONNECTFAIL]); + + /* + * Translate other errors into ISC_R_* flavors. + */ + switch (errno) { +#define ERROR_MATCH(a, b) case a: result = b; break; + ERROR_MATCH(EACCES, ISC_R_NOPERM); + ERROR_MATCH(EADDRNOTAVAIL, ISC_R_ADDRNOTAVAIL); + ERROR_MATCH(EAFNOSUPPORT, ISC_R_ADDRNOTAVAIL); + ERROR_MATCH(ECONNREFUSED, ISC_R_CONNREFUSED); + ERROR_MATCH(EHOSTUNREACH, ISC_R_HOSTUNREACH); +#ifdef EHOSTDOWN + ERROR_MATCH(EHOSTDOWN, ISC_R_HOSTUNREACH); +#endif + ERROR_MATCH(ENETUNREACH, ISC_R_NETUNREACH); + ERROR_MATCH(ENOBUFS, ISC_R_NORESOURCES); + ERROR_MATCH(EPERM, ISC_R_HOSTUNREACH); + ERROR_MATCH(EPIPE, ISC_R_NOTCONNECTED); + ERROR_MATCH(ETIMEDOUT, ISC_R_TIMEDOUT); + ERROR_MATCH(ECONNRESET, ISC_R_CONNECTIONRESET); +#undef ERROR_MATCH + default: + result = ISC_R_UNEXPECTED; + isc_sockaddr_format(&sock->peer_address, peerbuf, + sizeof(peerbuf)); + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "internal_connect: connect(%s) %s", + peerbuf, strbuf); + } + } else { + inc_stats(sock->manager->stats, + sock->statsindex[STATID_CONNECT]); + result = ISC_R_SUCCESS; + sock->connected = 1; + sock->bound = 1; + } + + do { + dev->result = result; + send_connectdone_event(sock, &dev); + dev = ISC_LIST_HEAD(sock->connect_list); + } while (dev != NULL); + + UNLOCK(&sock->lock); +} + +isc_result_t +isc__socket_getpeername(isc_socket_t *sock0, isc_sockaddr_t *addressp) { + isc__socket_t *sock = (isc__socket_t *)sock0; + isc_result_t result; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(addressp != NULL); + + LOCK(&sock->lock); + + if (sock->connected) { + *addressp = sock->peer_address; + result = ISC_R_SUCCESS; + } else { + result = ISC_R_NOTCONNECTED; + } + + UNLOCK(&sock->lock); + + return (result); +} + +isc_result_t +isc__socket_getsockname(isc_socket_t *sock0, isc_sockaddr_t *addressp) { + isc__socket_t *sock = (isc__socket_t *)sock0; + ISC_SOCKADDR_LEN_T len; + isc_result_t result; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(addressp != NULL); + + LOCK(&sock->lock); + + 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) { + isc__strerror(errno, 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 *sock0, isc_task_t *task, unsigned int how) { + isc__socket_t *sock = (isc__socket_t *)sock0; + + 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); + + /* + * 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) == ISC_SOCKCANCEL_RECV) + && !ISC_LIST_EMPTY(sock->recv_list)) { + 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; + } + } + + if (((how & ISC_SOCKCANCEL_SEND) == ISC_SOCKCANCEL_SEND) + && !ISC_LIST_EMPTY(sock->send_list)) { + 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; + } + } + + if (((how & ISC_SOCKCANCEL_ACCEPT) == ISC_SOCKCANCEL_ACCEPT) + && !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_LIST_UNLINK(sock->accept_list, dev, + ev_link); + + NEWCONNSOCK(dev)->references--; + free_socket((isc__socket_t **)&dev->newsocket); + + dev->result = ISC_R_CANCELED; + dev->ev_sender = sock; + isc_task_sendanddetach(¤t_task, + ISC_EVENT_PTR(&dev)); + } + + dev = next; + } + } + + if (((how & ISC_SOCKCANCEL_CONNECT) == ISC_SOCKCANCEL_CONNECT) + && !ISC_LIST_EMPTY(sock->connect_list)) { + isc_socket_connev_t *dev; + isc_socket_connev_t *next; + isc_task_t *current_task; + + INSIST(sock->connecting); + sock->connecting = 0; + + 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; + } + } + + UNLOCK(&sock->lock); +} + +isc_sockettype_t +isc__socket_gettype(isc_socket_t *sock0) { + isc__socket_t *sock = (isc__socket_t *)sock0; + + REQUIRE(VALID_SOCKET(sock)); + + return (sock->type); +} + +bool +isc__socket_isbound(isc_socket_t *sock0) { + isc__socket_t *sock = (isc__socket_t *)sock0; + bool val; + + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + val = ((sock->bound) ? true : false); + UNLOCK(&sock->lock); + + return (val); +} + +void +isc__socket_ipv6only(isc_socket_t *sock0, bool yes) { + isc__socket_t *sock = (isc__socket_t *)sock0; +#if defined(IPV6_V6ONLY) + int onoff = yes ? 1 : 0; +#else + UNUSED(yes); + UNUSED(sock); +#endif + + REQUIRE(VALID_SOCKET(sock)); + INSIST(!sock->dupped); + +#ifdef IPV6_V6ONLY + if (sock->pf == AF_INET6) { + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_V6ONLY, + (void *)&onoff, sizeof(int)) < 0) { + char strbuf[ISC_STRERRORSIZE]; + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_V6ONLY) " + "%s: %s", sock->fd, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } + } + FIX_IPV6_RECVPKTINFO(sock); /* AIX */ +#endif +} + +static void +setdscp(isc__socket_t *sock, isc_dscp_t dscp) { +#if defined(IP_TOS) || defined(IPV6_TCLASS) + int value = dscp << 2; +#endif + + sock->dscp = dscp; + +#ifdef IP_TOS + if (sock->pf == AF_INET) { + if (setsockopt(sock->fd, IPPROTO_IP, IP_TOS, + (void *)&value, sizeof(value)) < 0) { + char strbuf[ISC_STRERRORSIZE]; + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IP_TOS, %.02x) " + "%s: %s", sock->fd, value >> 2, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } + } +#endif +#ifdef IPV6_TCLASS + if (sock->pf == AF_INET6) { + if (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_TCLASS, + (void *)&value, sizeof(value)) < 0) { + char strbuf[ISC_STRERRORSIZE]; + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_TCLASS, %.02x) " + "%s: %s", sock->fd, dscp >> 2, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } + } +#endif +} + +void +isc__socket_dscp(isc_socket_t *sock0, isc_dscp_t dscp) { + isc__socket_t *sock = (isc__socket_t *)sock0; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(dscp < 0x40); + +#if !defined(IP_TOS) && !defined(IPV6_TCLASS) + UNUSED(dscp); +#else + if (dscp < 0) + return; + + /* The DSCP value must not be changed once it has been set. */ + if (isc_dscp_check_value != -1) + INSIST(dscp == isc_dscp_check_value); +#endif + + +#ifdef notyet + REQUIRE(!sock->dupped); +#endif + + setdscp(sock, dscp); +} + +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)); +} + +#ifndef USE_WATCHER_THREAD +/* + * In our assumed scenario, we can simply use a single static object. + * XXX: this is not true if the application uses multiple threads with + * 'multi-context' mode. Fixing this is a future TODO item. + */ +static isc_socketwait_t swait_private; + +int +isc__socketmgr_waitevents(isc_socketmgr_t *manager0, struct timeval *tvp, + isc_socketwait_t **swaitp) +{ + isc__socketmgr_t *manager = (isc__socketmgr_t *)manager0; + int n; +#ifdef USE_KQUEUE + struct timespec ts, *tsp; +#endif +#ifdef USE_EPOLL + int timeout; +#endif +#ifdef USE_DEVPOLL + isc_result_t result; + int pass; + struct dvpoll dvp; +#endif + + REQUIRE(swaitp != NULL && *swaitp == NULL); + +#ifdef USE_SHARED_MANAGER + if (manager == NULL) + manager = socketmgr; +#endif + if (manager == NULL) + return (0); + +#ifdef USE_KQUEUE + if (tvp != NULL) { + ts.tv_sec = tvp->tv_sec; + ts.tv_nsec = tvp->tv_usec * 1000; + tsp = &ts; + } else + tsp = NULL; + swait_private.nevents = kevent(manager->kqueue_fd, NULL, 0, + manager->events, manager->nevents, + tsp); + n = swait_private.nevents; +#elif defined(USE_EPOLL) + if (tvp != NULL) + timeout = tvp->tv_sec * 1000 + (tvp->tv_usec + 999) / 1000; + else + timeout = -1; + swait_private.nevents = epoll_wait(manager->epoll_fd, + manager->events, + manager->nevents, timeout); + n = swait_private.nevents; +#elif defined(USE_DEVPOLL) + /* + * Re-probe every thousand calls. + */ + if (manager->calls++ > 1000U) { + result = isc_resource_getcurlimit(isc_resource_openfiles, + &manager->open_max); + if (result != ISC_R_SUCCESS) + manager->open_max = 64; + manager->calls = 0; + } + for (pass = 0; pass < 2; pass++) { + dvp.dp_fds = manager->events; + dvp.dp_nfds = manager->nevents; + if (dvp.dp_nfds >= manager->open_max) + dvp.dp_nfds = manager->open_max - 1; + if (tvp != NULL) { + dvp.dp_timeout = tvp->tv_sec * 1000 + + (tvp->tv_usec + 999) / 1000; + } else + dvp.dp_timeout = -1; + n = ioctl(manager->devpoll_fd, DP_POLL, &dvp); + if (n == -1 && errno == EINVAL) { + /* + * {OPEN_MAX} may have dropped. Look + * up the current value and try again. + */ + result = isc_resource_getcurlimit( + isc_resource_openfiles, + &manager->open_max); + if (result != ISC_R_SUCCESS) + manager->open_max = 64; + } else + break; + } + swait_private.nevents = n; +#elif defined(USE_SELECT) + memmove(manager->read_fds_copy, manager->read_fds, manager->fd_bufsize); + memmove(manager->write_fds_copy, manager->write_fds, + manager->fd_bufsize); + + swait_private.readset = manager->read_fds_copy; + swait_private.writeset = manager->write_fds_copy; + swait_private.maxfd = manager->maxfd + 1; + + n = select(swait_private.maxfd, swait_private.readset, + swait_private.writeset, NULL, tvp); +#endif + + *swaitp = &swait_private; + return (n); +} + +isc_result_t +isc__socketmgr_dispatch(isc_socketmgr_t *manager0, isc_socketwait_t *swait) { + isc__socketmgr_t *manager = (isc__socketmgr_t *)manager0; + + REQUIRE(swait == &swait_private); + +#ifdef USE_SHARED_MANAGER + if (manager == NULL) + manager = socketmgr; +#endif + if (manager == NULL) + return (ISC_R_NOTFOUND); + +#if defined(USE_KQUEUE) || defined(USE_EPOLL) || defined(USE_DEVPOLL) + (void)process_fds(manager, manager->events, swait->nevents); + return (ISC_R_SUCCESS); +#elif defined(USE_SELECT) + process_fds(manager, swait->maxfd, swait->readset, swait->writeset); + return (ISC_R_SUCCESS); +#endif +} +#endif /* USE_WATCHER_THREAD */ + +void +isc__socket_setname(isc_socket_t *socket0, const char *name, void *tag) { + isc__socket_t *sock = (isc__socket_t *)socket0; + + /* + * Name 'sock'. + */ + + REQUIRE(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + strlcpy(sock->name, name, sizeof(sock->name)); + sock->tag = tag; + UNLOCK(&sock->lock); +} + +const char * +isc__socket_getname(isc_socket_t *socket0) { + isc__socket_t *sock = (isc__socket_t *)socket0; + + return (sock->name); +} + +void * +isc__socket_gettag(isc_socket_t *socket0) { + isc__socket_t *sock = (isc__socket_t *)socket0; + + return (sock->tag); +} + +isc_result_t +isc__socket_register(void) { + return (isc_socket_register(isc__socketmgr_create)); +} + +int +isc__socket_getfd(isc_socket_t *socket0) { + isc__socket_t *sock = (isc__socket_t *)socket0; + + return ((short) sock->fd); +} + +#if defined(HAVE_LIBXML2) || defined(HAVE_JSON) +static const char * +_socktype(isc_sockettype_t type) +{ + if (type == isc_sockettype_udp) + return ("udp"); + else if (type == isc_sockettype_tcp) + return ("tcp"); + else if (type == isc_sockettype_unix) + return ("unix"); + else if (type == isc_sockettype_fdwatch) + return ("fdwatch"); + else + return ("not-initialized"); +} +#endif + +#ifdef HAVE_LIBXML2 +#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0) +int +isc_socketmgr_renderxml(isc_socketmgr_t *mgr0, xmlTextWriterPtr writer) { + isc__socketmgr_t *mgr = (isc__socketmgr_t *)mgr0; + isc__socket_t *sock = NULL; + char peerbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t addr; + ISC_SOCKADDR_LEN_T len; + int xmlrc; + + LOCK(&mgr->lock); + +#ifdef USE_SHARED_MANAGER + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->refs)); + TRY0(xmlTextWriterEndElement(writer)); +#endif /* USE_SHARED_MANAGER */ + + 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, "%d", + sock->references)); + TRY0(xmlTextWriterEndElement(writer)); + + TRY0(xmlTextWriterWriteElement(writer, ISC_XMLCHAR "type", + ISC_XMLCHAR _socktype(sock->type))); + + if (sock->connected) { + isc_sockaddr_format(&sock->peer_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->connecting) + 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 +#define CHECKMEM(m) do { \ + if (m == NULL) { \ + result = ISC_R_NOMEMORY;\ + goto error;\ + } \ +} while(0) + +isc_result_t +isc_socketmgr_renderjson(isc_socketmgr_t *mgr0, json_object *stats) { + isc_result_t result = ISC_R_SUCCESS; + isc__socketmgr_t *mgr = (isc__socketmgr_t *)mgr0; + isc__socket_t *sock = NULL; + char peerbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t addr; + ISC_SOCKADDR_LEN_T len; + json_object *obj, *array = json_object_new_array(); + + 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(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->peer_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->connecting) { + 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 */ + +#include "../socket_api.c" diff --git a/lib/isc/unix/socket_p.h b/lib/isc/unix/socket_p.h new file mode 100644 index 0000000..fb4fdb8 --- /dev/null +++ b/lib/isc/unix/socket_p.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_SOCKET_P_H +#define ISC_SOCKET_P_H + +/*! \file */ + +#ifdef ISC_PLATFORM_NEEDSYSSELECTH +#include <sys/select.h> +#endif + +typedef struct isc_socketwait isc_socketwait_t; +int isc__socketmgr_waitevents(isc_socketmgr_t *, struct timeval *, + isc_socketwait_t **); +isc_result_t isc__socketmgr_dispatch(isc_socketmgr_t *, isc_socketwait_t *); +#endif /* ISC_SOCKET_P_H */ diff --git a/lib/isc/unix/stdio.c b/lib/isc/unix/stdio.c new file mode 100644 index 0000000..e60fa65 --- /dev/null +++ b/lib/isc/unix/stdio.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <errno.h> +#include <unistd.h> + +#include <isc/stdio.h> +#include <isc/stat.h> +#include <isc/util.h> + +#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; + +#ifdef HAVE_FSEEKO + r = fseeko(f, offset, whence); +#else + r = fseek(f, offset, whence); +#endif + if (r == 0) + return (ISC_R_SUCCESS); + else + return (isc__errno2result(errno)); +} + +isc_result_t +isc_stdio_tell(FILE *f, off_t *offsetp) { + off_t r; + + REQUIRE(offsetp != NULL); + +#ifdef HAVE_FTELLO + r = ftello(f); +#else + r = ftell(f); +#endif + 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)); +} + +/* + * OpenBSD has deprecated ENOTSUP in favor of EOPNOTSUPP. + */ +#if defined(EOPNOTSUPP) && !defined(ENOTSUP) +#define ENOTSUP EOPNOTSUPP +#endif + +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 fsync() on regular files. + */ + if ((buf.st_mode & S_IFMT) != S_IFREG) + return (ISC_R_SUCCESS); + + r = fsync(fileno(f)); + if (r == 0) + return (ISC_R_SUCCESS); + else + return (isc__errno2result(errno)); +} + diff --git a/lib/isc/unix/stdtime.c b/lib/isc/unix/stdtime.c new file mode 100644 index 0000000..989bde4 --- /dev/null +++ b/lib/isc/unix/stdtime.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> +#include <stddef.h> /* NULL */ +#include <stdlib.h> /* NULL */ +#include <syslog.h> + +#include <sys/time.h> + +#include <isc/stdtime.h> +#include <isc/util.h> + +#ifndef ISC_FIX_TV_USEC +#define ISC_FIX_TV_USEC 1 +#endif + +#define US_PER_S 1000000 + +#if ISC_FIX_TV_USEC +static inline void +fix_tv_usec(struct timeval *tv) { + bool fixed = false; + + if (tv->tv_usec < 0) { + fixed = true; + do { + tv->tv_sec -= 1; + tv->tv_usec += US_PER_S; + } while (tv->tv_usec < 0); + } else if (tv->tv_usec >= US_PER_S) { + fixed = true; + do { + tv->tv_sec += 1; + tv->tv_usec -= US_PER_S; + } while (tv->tv_usec >=US_PER_S); + } + /* + * Call syslog directly as we are called from the logging functions. + */ + if (fixed) + (void)syslog(LOG_ERR, "gettimeofday returned bad tv_usec: corrected"); +} +#endif + +void +isc_stdtime_get(isc_stdtime_t *t) { + struct timeval tv; + + /* + * Set 't' to the number of seconds since 00:00:00 UTC, January 1, + * 1970. + */ + + REQUIRE(t != NULL); + + RUNTIME_CHECK(gettimeofday(&tv, NULL) != -1); + +#if ISC_FIX_TV_USEC + fix_tv_usec(&tv); + INSIST(tv.tv_usec >= 0); +#else + INSIST(tv.tv_usec >= 0 && tv.tv_usec < US_PER_S); +#endif + + *t = (unsigned int)tv.tv_sec; +} diff --git a/lib/isc/unix/strerror.c b/lib/isc/unix/strerror.c new file mode 100644 index 0000000..e4fb793 --- /dev/null +++ b/lib/isc/unix/strerror.c @@ -0,0 +1,67 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdio.h> +#include <string.h> + +#include <isc/mutex.h> +#include <isc/once.h> +#include <isc/print.h> +#include <isc/strerror.h> +#include <isc/util.h> + +#ifdef HAVE_STRERROR +/*% + * We need to do this this way for profiled locks. + */ +static isc_mutex_t isc_strerror_lock; +static void init_lock(void) { + RUNTIME_CHECK(isc_mutex_init(&isc_strerror_lock) == ISC_R_SUCCESS); +} +#else +extern const char * const sys_errlist[]; +extern const int sys_nerr; +#endif + +void +isc__strerror(int num, char *buf, size_t size) { +#ifdef HAVE_STRERROR + char *msg; + unsigned int unum = (unsigned int)num; + static isc_once_t once = ISC_ONCE_INIT; + + REQUIRE(buf != NULL); + + RUNTIME_CHECK(isc_once_do(&once, init_lock) == ISC_R_SUCCESS); + + LOCK(&isc_strerror_lock); + msg = strerror(num); + if (msg != NULL) + snprintf(buf, size, "%s", msg); + else + snprintf(buf, size, "Unknown error: %u", unum); + UNLOCK(&isc_strerror_lock); +#else + unsigned int unum = (unsigned int)num; + + REQUIRE(buf != NULL); + + if (num >= 0 && num < sys_nerr) + snprintf(buf, size, "%s", sys_errlist[num]); + else + snprintf(buf, size, "Unknown error: %u", unum); +#endif +} diff --git a/lib/isc/unix/syslog.c b/lib/isc/unix/syslog.c new file mode 100644 index 0000000..4ce3272 --- /dev/null +++ b/lib/isc/unix/syslog.c @@ -0,0 +1,77 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <stdlib.h> +#include <syslog.h> + +#include <isc/result.h> +#include <isc/string.h> +#include <isc/syslog.h> +#include <isc/util.h> + +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_UUCP + { LOG_UUCP, "uucp" }, +#endif +#ifdef LOG_CRON + { LOG_CRON, "cron" }, +#endif +#ifdef LOG_AUTHPRIV + { LOG_AUTHPRIV, "authpriv" }, +#endif +#ifdef LOG_FTP + { LOG_FTP, "ftp" }, +#endif + { 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); + +} diff --git a/lib/isc/unix/time.c b/lib/isc/unix/time.c new file mode 100644 index 0000000..8edc9df --- /dev/null +++ b/lib/isc/unix/time.c @@ -0,0 +1,500 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <config.h> + +#include <errno.h> +#include <limits.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <syslog.h> +#include <time.h> + +#include <sys/time.h> /* Required for struct timeval on some platforms. */ + +#include <isc/log.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/strerror.h> +#include <isc/string.h> +#include <isc/time.h> +#include <isc/tm.h> +#include <isc/util.h> + +#define NS_PER_S 1000000000 /*%< Nanoseconds per second. */ +#define NS_PER_US 1000 /*%< Nanoseconds per microsecond. */ +#define NS_PER_MS 1000000 /*%< Nanoseconds per millisecond. */ +#define US_PER_S 1000000 /*%< Microseconds per second. */ + +/* + * All of the INSIST()s checks of nanoseconds < NS_PER_S are for + * consistency checking of the type. In lieu of magic numbers, it + * is the best we've got. The check is only performed on functions which + * need an initialized type. + */ + +#ifndef ISC_FIX_TV_USEC +#define ISC_FIX_TV_USEC 1 +#endif + +/*% + *** Intervals + ***/ + +static const isc_interval_t zero_interval = { 0, 0 }; +const isc_interval_t * const isc_interval_zero = &zero_interval; + +#if ISC_FIX_TV_USEC +static inline void +fix_tv_usec(struct timeval *tv) { + bool fixed = false; + + if (tv->tv_usec < 0) { + fixed = true; + do { + tv->tv_sec -= 1; + tv->tv_usec += US_PER_S; + } while (tv->tv_usec < 0); + } else if (tv->tv_usec >= US_PER_S) { + fixed = true; + do { + tv->tv_sec += 1; + tv->tv_usec -= US_PER_S; + } while (tv->tv_usec >=US_PER_S); + } + /* + * Call syslog directly as was are called from the logging functions. + */ + if (fixed) + (void)syslog(LOG_ERR, "gettimeofday returned bad tv_usec: corrected"); +} +#endif + +void +isc_interval_set(isc_interval_t *i, + unsigned int seconds, unsigned int nanoseconds) +{ + REQUIRE(i != NULL); + REQUIRE(nanoseconds < NS_PER_S); + + i->seconds = seconds; + i->nanoseconds = nanoseconds; +} + +bool +isc_interval_iszero(const isc_interval_t *i) { + REQUIRE(i != NULL); + INSIST(i->nanoseconds < NS_PER_S); + + if (i->seconds == 0 && i->nanoseconds == 0) + return (true); + + return (false); +} + + +/*** + *** Absolute Times + ***/ + +static const isc_time_t epoch = { 0, 0 }; +const isc_time_t * const isc_time_epoch = &epoch; + +void +isc_time_set(isc_time_t *t, unsigned int seconds, unsigned int nanoseconds) { + REQUIRE(t != NULL); + REQUIRE(nanoseconds < NS_PER_S); + + t->seconds = seconds; + t->nanoseconds = nanoseconds; +} + +void +isc_time_settoepoch(isc_time_t *t) { + REQUIRE(t != NULL); + + t->seconds = 0; + t->nanoseconds = 0; +} + +bool +isc_time_isepoch(const isc_time_t *t) { + REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); + + if (t->seconds == 0 && t->nanoseconds == 0) + return (true); + + return (false); +} + + +isc_result_t +isc_time_now(isc_time_t *t) { + struct timeval tv; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(t != NULL); + + if (gettimeofday(&tv, NULL) == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "%s", strbuf); + return (ISC_R_UNEXPECTED); + } + + /* + * Does POSIX guarantee the signedness of tv_sec and tv_usec? If not, + * then this test will generate warnings for platforms on which it is + * unsigned. In any event, the chances of any of these problems + * happening are pretty much zero, but since the libisc library ensures + * certain things to be true ... + */ +#if ISC_FIX_TV_USEC + fix_tv_usec(&tv); + if (tv.tv_sec < 0) + return (ISC_R_UNEXPECTED); +#else + if (tv.tv_sec < 0 || tv.tv_usec < 0 || tv.tv_usec >= US_PER_S) + return (ISC_R_UNEXPECTED); +#endif + + /* + * Ensure the tv_sec value fits in t->seconds. + */ + if (sizeof(tv.tv_sec) > sizeof(t->seconds) && + ((tv.tv_sec | (unsigned int)-1) ^ (unsigned int)-1) != 0U) + return (ISC_R_RANGE); + + t->seconds = tv.tv_sec; + t->nanoseconds = tv.tv_usec * NS_PER_US; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i) { + struct timeval tv; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(t != NULL); + REQUIRE(i != NULL); + INSIST(i->nanoseconds < NS_PER_S); + + if (gettimeofday(&tv, NULL) == -1) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "%s", strbuf); + return (ISC_R_UNEXPECTED); + } + + /* + * Does POSIX guarantee the signedness of tv_sec and tv_usec? If not, + * then this test will generate warnings for platforms on which it is + * unsigned. In any event, the chances of any of these problems + * happening are pretty much zero, but since the libisc library ensures + * certain things to be true ... + */ +#if ISC_FIX_TV_USEC + fix_tv_usec(&tv); + if (tv.tv_sec < 0) + return (ISC_R_UNEXPECTED); +#else + if (tv.tv_sec < 0 || tv.tv_usec < 0 || tv.tv_usec >= US_PER_S) + return (ISC_R_UNEXPECTED); +#endif + + /* + * Ensure the resulting seconds value fits in the size of an + * unsigned int. (It is written this way as a slight optimization; + * note that even if both values == INT_MAX, then when added + * and getting another 1 added below the result is UINT_MAX.) + */ + if ((tv.tv_sec > INT_MAX || i->seconds > INT_MAX) && + ((long long)tv.tv_sec + i->seconds > UINT_MAX)) + return (ISC_R_RANGE); + + t->seconds = tv.tv_sec + i->seconds; + t->nanoseconds = tv.tv_usec * NS_PER_US + i->nanoseconds; + if (t->nanoseconds >= NS_PER_S) { + t->seconds++; + t->nanoseconds -= NS_PER_S; + } + + return (ISC_R_SUCCESS); +} + +int +isc_time_compare(const isc_time_t *t1, const isc_time_t *t2) { + REQUIRE(t1 != NULL && t2 != NULL); + INSIST(t1->nanoseconds < NS_PER_S && t2->nanoseconds < NS_PER_S); + + if (t1->seconds < t2->seconds) + return (-1); + if (t1->seconds > t2->seconds) + return (1); + if (t1->nanoseconds < t2->nanoseconds) + return (-1); + if (t1->nanoseconds > t2->nanoseconds) + return (1); + return (0); +} + +isc_result_t +isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result) +{ + REQUIRE(t != NULL && i != NULL && result != NULL); + INSIST(t->nanoseconds < NS_PER_S && i->nanoseconds < NS_PER_S); + + /* + * Ensure the resulting seconds value fits in the size of an + * unsigned int. (It is written this way as a slight optimization; + * note that even if both values == INT_MAX, then when added + * and getting another 1 added below the result is UINT_MAX.) + */ + if ((t->seconds > INT_MAX || i->seconds > INT_MAX) && + ((long long)t->seconds + i->seconds > UINT_MAX)) + return (ISC_R_RANGE); + + result->seconds = t->seconds + i->seconds; + result->nanoseconds = t->nanoseconds + i->nanoseconds; + if (result->nanoseconds >= NS_PER_S) { + result->seconds++; + result->nanoseconds -= NS_PER_S; + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_time_subtract(const isc_time_t *t, const isc_interval_t *i, + isc_time_t *result) +{ + REQUIRE(t != NULL && i != NULL && result != NULL); + INSIST(t->nanoseconds < NS_PER_S && i->nanoseconds < NS_PER_S); + + if ((unsigned int)t->seconds < i->seconds || + ((unsigned int)t->seconds == i->seconds && + t->nanoseconds < i->nanoseconds)) + return (ISC_R_RANGE); + + result->seconds = t->seconds - i->seconds; + if (t->nanoseconds >= i->nanoseconds) + result->nanoseconds = t->nanoseconds - i->nanoseconds; + else { + result->nanoseconds = NS_PER_S - i->nanoseconds + + t->nanoseconds; + result->seconds--; + } + + return (ISC_R_SUCCESS); +} + +uint64_t +isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2) { + uint64_t i1, i2, i3; + + REQUIRE(t1 != NULL && t2 != NULL); + INSIST(t1->nanoseconds < NS_PER_S && t2->nanoseconds < NS_PER_S); + + i1 = (uint64_t)t1->seconds * NS_PER_S + t1->nanoseconds; + i2 = (uint64_t)t2->seconds * NS_PER_S + t2->nanoseconds; + + if (i1 <= i2) + return (0); + + i3 = i1 - i2; + + /* + * Convert to microseconds. + */ + i3 /= NS_PER_US; + + return (i3); +} + +uint32_t +isc_time_seconds(const isc_time_t *t) { + REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); + + return ((uint32_t)t->seconds); +} + +isc_result_t +isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp) { + time_t seconds; + + REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); + + /* + * Ensure that the number of seconds represented by t->seconds + * can be represented by a time_t. Since t->seconds is an unsigned + * int and since time_t is mostly opaque, this is trickier than + * it seems. (This standardized opaqueness of time_t is *very* + * frustrating; time_t is not even limited to being an integral + * type.) + * + * The mission, then, is to avoid generating any kind of warning + * about "signed versus unsigned" while trying to determine if the + * the unsigned int t->seconds is out range for tv_sec, which is + * pretty much only true if time_t is a signed integer of the same + * size as the return value of isc_time_seconds. + * + * If the paradox in the if clause below is true, t->seconds is out + * of range for time_t. + */ + seconds = (time_t)t->seconds; + + INSIST(sizeof(unsigned int) == sizeof(uint32_t)); + INSIST(sizeof(time_t) >= sizeof(uint32_t)); + + if (t->seconds > (~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) { + REQUIRE(t != NULL); + + ENSURE(t->nanoseconds < NS_PER_S); + + return ((uint32_t)t->nanoseconds); +} + +void +isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len) { + time_t now; + unsigned int flen; +#ifdef ISC_PLATFORM_USETHREADS + struct tm tm; +#endif + + REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + now = (time_t) t->seconds; +#ifdef ISC_PLATFORM_USETHREADS + flen = strftime(buf, len, "%d-%b-%Y %X", localtime_r(&now, &tm)); +#else + flen = strftime(buf, len, "%d-%b-%Y %X", localtime(&now)); +#endif + INSIST(flen < len); + if (flen != 0) + snprintf(buf + flen, len - flen, + ".%03u", t->nanoseconds / NS_PER_MS); + 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) { + time_t now; + unsigned int flen; +#ifdef ISC_PLATFORM_USETHREADS + struct tm tm; +#endif + + REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + /* + * 5 spaces, 1 comma, 3 GMT, 2 %d, 4 %Y, 8 %H:%M:%S, 3+ %a, 3+ %b (29+) + */ + now = (time_t)t->seconds; +#ifdef ISC_PLATFORM_USETHREADS + flen = strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", + gmtime_r(&now, &tm)); +#else + flen = strftime(buf, len, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&now)); +#endif + INSIST(flen < len); +} + +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, when, 0); + return (ISC_R_SUCCESS); +} + +void +isc_time_formatISO8601(const isc_time_t *t, char *buf, unsigned int len) { + time_t now; + unsigned int flen; +#ifdef ISC_PLATFORM_USETHREADS + struct tm tm; +#endif + + REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + now = (time_t)t->seconds; +#ifdef ISC_PLATFORM_USETHREADS + flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm)); +#else + flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); +#endif + INSIST(flen < len); +} + +void +isc_time_formatISO8601ms(const isc_time_t *t, char *buf, unsigned int len) { + time_t now; + unsigned int flen; +#ifdef ISC_PLATFORM_USETHREADS + struct tm tm; +#endif + + REQUIRE(t != NULL); + INSIST(t->nanoseconds < NS_PER_S); + REQUIRE(buf != NULL); + REQUIRE(len > 0); + + now = (time_t)t->seconds; +#ifdef ISC_PLATFORM_USETHREADS + flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime_r(&now, &tm)); +#else + flen = strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", gmtime(&now)); +#endif + INSIST(flen < len); + if (flen > 0U && len - flen >= 5) { + flen -= 1; /* rewind one character (Z) */ + snprintf(buf + flen, len - flen, ".%03uZ", + t->nanoseconds / NS_PER_MS); + } +} diff --git a/lib/isc/version.c b/lib/isc/version.c new file mode 100644 index 0000000..3a83a39 --- /dev/null +++ b/lib/isc/version.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/*! \file */ + +#include <isc/version.h> + +const char isc_version[] = VERSION; + +const unsigned int isc_libinterface = LIBINTERFACE; +const unsigned int isc_librevision = LIBREVISION; +const unsigned int isc_libage = LIBAGE; diff --git a/lib/isc/win32/DLLMain.c b/lib/isc/win32/DLLMain.c new file mode 100644 index 0000000..f14260e --- /dev/null +++ b/lib/isc/win32/DLLMain.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <windows.h> +#include <stdio.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: + break; + + /* The attached process creates a new thread. */ + case DLL_THREAD_ATTACH: + break; + + /* The thread of the attached process terminates. */ + case DLL_THREAD_DETACH: + break; + + /* + * The DLL is unloading from a process due to + * process termination or a call to FreeLibrary. + */ + case DLL_PROCESS_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..19b46bd --- /dev/null +++ b/lib/isc/win32/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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@ @ISC_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 @ISC_PK11_API_C@ + +SUBDIRS = include +TARGETS = ${OBJS} + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/win32/app.c b/lib/isc/win32/app.c new file mode 100644 index 0000000..35274a2 --- /dev/null +++ b/lib/isc/win32/app.c @@ -0,0 +1,481 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <sys/types.h> + +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <process.h> + +#include <isc/app.h> +#include <isc/condition.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/mutex.h> +#include <isc/event.h> +#include <isc/platform.h> +#include <isc/string.h> +#include <isc/task.h> +#include <isc/time.h> +#include <isc/util.h> +#include <isc/thread.h> + +/*% + * For BIND9 internal applications built with threads, we use a single app + * context and let multiple worker, I/O, timer threads do actual jobs. + */ + +static isc_thread_t blockedthread; + +/*% + * The following are intended for internal use (indicated by "isc__" + * prefix) but are not declared as static, allowing direct access from + * unit tests etc. + */ +isc_result_t isc__app_start(void); +isc_result_t isc__app_ctxstart(isc_appctx_t *ctx); +isc_result_t isc__app_onrun(isc_mem_t *mctx, isc_task_t *task, + isc_taskaction_t action, void *arg); +isc_result_t isc__app_ctxrun(isc_appctx_t *ctx); +isc_result_t isc__app_run(void); +isc_result_t isc__app_ctxshutdown(isc_appctx_t *ctx); +isc_result_t isc__app_shutdown(void); +isc_result_t isc__app_reload(void); +isc_result_t isc__app_ctxsuspend(isc_appctx_t *ctx); +void isc__app_ctxfinish(isc_appctx_t *ctx); +void isc__app_finish(void); +void isc__app_block(void); +void isc__app_unblock(void); +isc_result_t isc__appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp); +void isc__appctx_destroy(isc_appctx_t **ctxp); +void isc__appctx_settaskmgr(isc_appctx_t *ctx, isc_taskmgr_t *taskmgr); +void isc__appctx_setsocketmgr(isc_appctx_t *ctx, isc_socketmgr_t *socketmgr); +void isc__appctx_settimermgr(isc_appctx_t *ctx, isc_timermgr_t *timermgr); +isc_result_t isc__app_ctxonrun(isc_appctx_t *ctx, isc_mem_t *mctx, + isc_task_t *task, isc_taskaction_t action, + void *arg); + +/* + * The application context of this module. This implementation actually + * doesn't use it. (This may change in the future). + */ +#define APPCTX_MAGIC ISC_MAGIC('A', 'p', 'c', 'x') +#define VALID_APPCTX(c) ISC_MAGIC_VALID(c, APPCTX_MAGIC) + +/* Events to wait for */ + +#define NUM_EVENTS 2 + +enum { + RELOAD_EVENT, + SHUTDOWN_EVENT +}; + +typedef struct isc__appctx { + isc_appctx_t common; + isc_mem_t *mctx; + isc_eventlist_t on_run; + isc_mutex_t lock; + bool shutdown_requested; + bool running; + /* + * We assume that 'want_shutdown' can be read and written atomically. + */ + bool want_shutdown; + /* + * We assume that 'want_reload' can be read and written atomically. + */ + bool want_reload; + + bool blocked; + + HANDLE hEvents[NUM_EVENTS]; + + isc_taskmgr_t *taskmgr; + isc_socketmgr_t *socketmgr; + isc_timermgr_t *timermgr; +} isc__appctx_t; + +static isc__appctx_t isc_g_appctx; + +static struct { + isc_appmethods_t methods; + + /*% + * The following are defined just for avoiding unused static functions. + */ + void *run, *shutdown, *start, *reload, *finish, *block, *unblock; +} appmethods = { + { + isc__appctx_destroy, + isc__app_ctxstart, + isc__app_ctxrun, + isc__app_ctxsuspend, + isc__app_ctxshutdown, + isc__app_ctxfinish, + isc__appctx_settaskmgr, + isc__appctx_setsocketmgr, + isc__appctx_settimermgr, + isc__app_ctxonrun + }, + (void *)isc__app_run, + (void *)isc__app_shutdown, + (void *)isc__app_start, + (void *)isc__app_reload, + (void *)isc__app_finish, + (void *)isc__app_block, + (void *)isc__app_unblock +}; + +/* + * We need to remember which thread is the main thread... + */ +static isc_thread_t main_thread; + +isc_result_t +isc__app_ctxstart(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + isc_result_t result; + + REQUIRE(VALID_APPCTX(ctx)); + + /* + * Start an ISC library application. + */ + + main_thread = GetCurrentThread(); + + result = isc_mutex_init(&ctx->lock); + if (result != ISC_R_SUCCESS) + return (result); + + ctx->shutdown_requested = false; + ctx->running = false; + ctx->want_shutdown = false; + ctx->want_reload = false; + ctx->blocked = false; + + /* Create the reload event in a non-signaled state */ + ctx->hEvents[RELOAD_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL); + + /* Create the shutdown event in a non-signaled state */ + ctx->hEvents[SHUTDOWN_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL); + + ISC_LIST_INIT(ctx->on_run); + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__app_start(void) { + isc_g_appctx.common.impmagic = APPCTX_MAGIC; + isc_g_appctx.common.magic = ISCAPI_APPCTX_MAGIC; + isc_g_appctx.common.methods = &appmethods.methods; + isc_g_appctx.mctx = NULL; + /* The remaining members will be initialized in ctxstart() */ + + return (isc__app_ctxstart((isc_appctx_t *)&isc_g_appctx)); +} + +isc_result_t +isc__app_onrun(isc_mem_t *mctx, isc_task_t *task, isc_taskaction_t action, + void *arg) +{ + return (isc__app_ctxonrun((isc_appctx_t *)&isc_g_appctx, mctx, + task, action, arg)); +} + +isc_result_t +isc__app_ctxonrun(isc_appctx_t *ctx0, isc_mem_t *mctx, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + isc_event_t *event; + isc_task_t *cloned_task = NULL; + isc_result_t result; + + LOCK(&ctx->lock); + + if (ctx->running) { + result = ISC_R_ALREADYRUNNING; + goto unlock; + } + + /* + * Note that we store the task to which we're going to send the event + * in the event's "sender" field. + */ + isc_task_attach(task, &cloned_task); + event = isc_event_allocate(mctx, cloned_task, ISC_APPEVENT_SHUTDOWN, + action, arg, sizeof(*event)); + if (event == NULL) { + result = ISC_R_NOMEMORY; + goto unlock; + } + + ISC_LINK_INIT(event, ev_link); + ISC_LIST_APPEND(ctx->on_run, event, ev_link); + + result = ISC_R_SUCCESS; + + unlock: + UNLOCK(&ctx->lock); + + return (result); +} + +isc_result_t +isc__app_ctxrun(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + isc_event_t *event, *next_event; + isc_task_t *task; + HANDLE *pHandles = NULL; + DWORD dwWaitResult; + + REQUIRE(VALID_APPCTX(ctx)); + + REQUIRE(main_thread == GetCurrentThread()); + + LOCK(&ctx->lock); + + if (!ctx->running) { + ctx->running = true; + + /* + * Post any on-run events (in FIFO order). + */ + for (event = ISC_LIST_HEAD(ctx->on_run); + event != NULL; + event = next_event) { + next_event = ISC_LIST_NEXT(event, ev_link); + ISC_LIST_UNLINK(ctx->on_run, event, ev_link); + task = event->ev_sender; + event->ev_sender = NULL; + isc_task_sendanddetach(&task, &event); + } + + } + + UNLOCK(&ctx->lock); + + /* + * There is no danger if isc_app_shutdown() is called before we wait + * for events. + */ + + while (!ctx->want_shutdown) { + dwWaitResult = WaitForMultipleObjects(NUM_EVENTS, ctx->hEvents, + FALSE, INFINITE); + + /* See why we returned */ + + if (WaitSucceeded(dwWaitResult, NUM_EVENTS)) { + /* + * The return was due to one of the events + * being signaled + */ + switch (WaitSucceededIndex(dwWaitResult)) { + case RELOAD_EVENT: + ctx->want_reload = true; + break; + + case SHUTDOWN_EVENT: + ctx->want_shutdown = true; + break; + } + } + + if (ctx->want_reload) { + ctx->want_reload = false; + return (ISC_R_RELOAD); + } + + if (ctx->want_shutdown && ctx->blocked) + exit(-1); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__app_run(void) { + return (isc__app_ctxrun((isc_appctx_t *)&isc_g_appctx)); +} + +isc_result_t +isc__app_ctxshutdown(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + bool want_kill = true; + + REQUIRE(VALID_APPCTX(ctx)); + + LOCK(&ctx->lock); + + REQUIRE(ctx->running); + + if (ctx->shutdown_requested) + want_kill = false; /* We're only signaling once */ + else + ctx->shutdown_requested = true; + + UNLOCK(&ctx->lock); + + if (want_kill) + SetEvent(ctx->hEvents[SHUTDOWN_EVENT]); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__app_shutdown(void) { + return (isc__app_ctxshutdown((isc_appctx_t *)&isc_g_appctx)); +} + +isc_result_t +isc__app_ctxsuspend(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + bool want_kill = true; + + REQUIRE(VALID_APPCTX(ctx)); + + LOCK(&ctx->lock); + + REQUIRE(ctx->running); + + /* + * Don't send the reload signal if we're shutting down. + */ + if (ctx->shutdown_requested) + want_kill = false; + + UNLOCK(&ctx->lock); + + if (want_kill) + SetEvent(ctx->hEvents[RELOAD_EVENT]); + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc__app_reload(void) { + return (isc__app_ctxsuspend((isc_appctx_t *)&isc_g_appctx)); +} + +void +isc__app_ctxfinish(isc_appctx_t *ctx0) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + + REQUIRE(VALID_APPCTX(ctx)); + + DESTROYLOCK(&ctx->lock); +} + +void +isc__app_finish(void) { + isc__app_ctxfinish((isc_appctx_t *)&isc_g_appctx); +} + +void +isc__app_block(void) { + REQUIRE(isc_g_appctx.running); + REQUIRE(!isc_g_appctx.blocked); + + isc_g_appctx.blocked = true; + blockedthread = GetCurrentThread(); +} + +void +isc__app_unblock(void) { + REQUIRE(isc_g_appctx.running); + REQUIRE(isc_g_appctx.blocked); + + isc_g_appctx.blocked = false; + REQUIRE(blockedthread == GetCurrentThread()); +} + +isc_result_t +isc__appctx_create(isc_mem_t *mctx, isc_appctx_t **ctxp) { + isc__appctx_t *ctx; + + REQUIRE(mctx != NULL); + REQUIRE(ctxp != NULL && *ctxp == NULL); + + ctx = isc_mem_get(mctx, sizeof(*ctx)); + if (ctx == NULL) + return (ISC_R_NOMEMORY); + + ctx->common.impmagic = APPCTX_MAGIC; + ctx->common.magic = ISCAPI_APPCTX_MAGIC; + ctx->common.methods = &appmethods.methods; + + ctx->mctx = NULL; + isc_mem_attach(mctx, &ctx->mctx); + + ctx->taskmgr = NULL; + ctx->socketmgr = NULL; + ctx->timermgr = NULL; + + *ctxp = (isc_appctx_t *)ctx; + + return (ISC_R_SUCCESS); +} + +void +isc__appctx_destroy(isc_appctx_t **ctxp) { + isc__appctx_t *ctx; + + REQUIRE(ctxp != NULL); + ctx = (isc__appctx_t *)*ctxp; + REQUIRE(VALID_APPCTX(ctx)); + + isc_mem_putanddetach(&ctx->mctx, ctx, sizeof(*ctx)); + + *ctxp = NULL; +} + +void +isc__appctx_settaskmgr(isc_appctx_t *ctx0, isc_taskmgr_t *taskmgr) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + + REQUIRE(VALID_APPCTX(ctx)); + + ctx->taskmgr = taskmgr; +} + +void +isc__appctx_setsocketmgr(isc_appctx_t *ctx0, isc_socketmgr_t *socketmgr) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + + REQUIRE(VALID_APPCTX(ctx)); + + ctx->socketmgr = socketmgr; +} + +void +isc__appctx_settimermgr(isc_appctx_t *ctx0, isc_timermgr_t *timermgr) { + isc__appctx_t *ctx = (isc__appctx_t *)ctx0; + + REQUIRE(VALID_APPCTX(ctx)); + + ctx->timermgr = timermgr; +} + +isc_result_t +isc__app_register(void) { + return (isc_app_register(isc__appctx_create)); +} + +#include "../app_api.c" diff --git a/lib/isc/win32/condition.c b/lib/isc/win32/condition.c new file mode 100644 index 0000000..7887508 --- /dev/null +++ b/lib/isc/win32/condition.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <inttypes.h> +#include <stdbool.h> + +#include <isc/condition.h> +#include <isc/assertions.h> +#include <isc/util.h> +#include <isc/thread.h> +#include <isc/time.h> + +#define LSIGNAL 0 +#define LBROADCAST 1 + +isc_result_t +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) { + /* XXX */ + return (ISC_R_UNEXPECTED); + } + cond->events[LSIGNAL] = h; + + /* + * The threadlist will hold the actual events needed + * for the wait condition + */ + ISC_LIST_INIT(cond->threadlist); + + return (ISC_R_SUCCESS); +} + +/* + * 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(unsigned long 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..5db2c23 --- /dev/null +++ b/lib/isc/win32/dir.c @@ -0,0 +1,307 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <string.h> +#include <direct.h> +#include <process.h> +#include <io.h> + +#include <sys/stat.h> + +#include <isc/assertions.h> +#include <isc/dir.h> +#include <isc/magic.h> +#include <isc/print.h> +#include <isc/string.h> +#include <isc/util.h> + +#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(*p & 0xff)) + *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/entropy.c b/lib/isc/win32/entropy.c new file mode 100644 index 0000000..2480c02 --- /dev/null +++ b/lib/isc/win32/entropy.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +/* + * This is the system dependent part of the ISC entropy API. + */ + +#include <config.h> + +#include <stdbool.h> + +#include <windows.h> +#include <wincrypt.h> + +#include <process.h> +#include <io.h> +#include <share.h> + +/* + * There is only one variable in the entropy data structures that is not + * system independent, but pulling the structure that uses it into this file + * ultimately means pulling several other independent structures here also to + * resolve their interdependencies. Thus only the problem variable's type + * is defined here. + */ +#define FILESOURCE_HANDLE_TYPE HCRYPTPROV + +typedef struct { + int dummy; +} isc_entropyusocketsource_t; + +#include "../entropy.c" + +static unsigned int +get_from_filesource(isc_entropysource_t *source, uint32_t desired) { + isc_entropy_t *ent = source->ent; + unsigned char buf[128]; + HCRYPTPROV hcryptprov = source->sources.file.handle; + ssize_t ndesired; + unsigned int added; + + if (source->bad) + return (0); + + desired = desired / 8 + (((desired & 0x07) > 0) ? 1 : 0); + + added = 0; + while (desired > 0) { + ndesired = ISC_MIN(desired, sizeof(buf)); + if (!CryptGenRandom(hcryptprov, (DWORD)ndesired, buf)) { + CryptReleaseContext(hcryptprov, 0); + source->bad = true; + goto out; + } + + entropypool_adddata(ent, buf, + (unsigned int)ndesired, + (unsigned int)ndesired * 8); + added += (unsigned int)ndesired * 8; + desired -= (uint32_t)ndesired; + } + + out: + return (added); +} + +/* + * Poll each source, trying to get data from it to stuff into the entropy + * pool. + */ +static void +fillpool(isc_entropy_t *ent, unsigned int desired, bool blocking) { + unsigned int added; + unsigned int remaining; + unsigned int needed; + unsigned int nsource; + isc_entropysource_t *source; + isc_entropysource_t *firstsource; + + REQUIRE(VALID_ENTROPY(ent)); + + needed = desired; + + /* + * This logic is a little strange, so an explanation is in order. + * + * If needed is 0, it means we are being asked to "fill to whatever + * we think is best." This means that if we have at least a + * partially full pool (say, > 1/4th of the pool) we probably don't + * need to add anything. + * + * Also, we will check to see if the "pseudo" count is too high. + * If it is, try to mix in better data. Too high is currently + * defined as 1/4th of the pool. + * + * Next, if we are asked to add a specific bit of entropy, make + * certain that we will do so. Clamp how much we try to add to + * (DIGEST_SIZE * 8 < needed < POOLBITS - entropy). + * + * Note that if we are in a blocking mode, we will only try to + * get as much data as we need, not as much as we might want + * to build up. + */ + if (needed == 0) { + REQUIRE(!blocking); + + if ((ent->pool.entropy >= RND_POOLBITS / 4) + && (ent->pool.pseudo <= RND_POOLBITS / 4)) + return; + + needed = THRESHOLD_BITS * 4; + } else { + needed = ISC_MAX(needed, THRESHOLD_BITS); + needed = ISC_MIN(needed, RND_POOLBITS); + } + + /* + * In any case, clamp how much we need to how much we can add. + */ + needed = ISC_MIN(needed, RND_POOLBITS - ent->pool.entropy); + + /* + * But wait! If we're not yet initialized, we need at least + * THRESHOLD_BITS + * of randomness. + */ + if (ent->initialized < THRESHOLD_BITS) + needed = ISC_MAX(needed, THRESHOLD_BITS - ent->initialized); + + /* + * Poll each file source to see if we can read anything useful from + * it. XXXMLG When where are multiple sources, we should keep a + * record of which one we last used so we can start from it (or the + * next one) to avoid letting some sources build up entropy while + * others are always drained. + */ + + added = 0; + remaining = needed; + if (ent->nextsource == NULL) { + ent->nextsource = ISC_LIST_HEAD(ent->sources); + if (ent->nextsource == NULL) + return; + } + source = ent->nextsource; + /* + * Remember the first source so we can break if we have looped back to + * the beginning and still have nothing + */ + firstsource = source; + again_file: + for (nsource = 0; nsource < ent->nsources; nsource++) { + unsigned int got; + + if (remaining == 0) + break; + + got = 0; + + if (source->type == ENTROPY_SOURCETYPE_FILE) + got = get_from_filesource(source, remaining); + + added += got; + + remaining -= ISC_MIN(remaining, got); + + source = ISC_LIST_NEXT(source, link); + if (source == NULL) + source = ISC_LIST_HEAD(ent->sources); + } + ent->nextsource = source; + + /* + * Go again only if there's been progress and we've not + * gone back to the beginning + */ + if (!(ent->nextsource == firstsource && added == 0)) { + if (blocking && remaining != 0) { + goto again_file; + } + } + + /* + * Here, if there are bits remaining to be had and we can block, + * check to see if we have a callback source. If so, call them. + */ + source = ISC_LIST_HEAD(ent->sources); + while ((remaining != 0) && (source != NULL)) { + unsigned int got; + + got = 0; + + if (source->type == ENTROPY_SOURCETYPE_CALLBACK) + got = get_from_callback(source, remaining, blocking); + + added += got; + remaining -= ISC_MIN(remaining, got); + + if (added >= needed) + break; + + source = ISC_LIST_NEXT(source, link); + } + + /* + * Mark as initialized if we've added enough data. + */ + if (ent->initialized < THRESHOLD_BITS) + ent->initialized += added; +} + + + +/* + * Requires "ent" be locked. + */ +static void +destroyfilesource(isc_entropyfilesource_t *source) { + CryptReleaseContext(source->handle, 0); +} + +static void +destroyusocketsource(isc_entropyusocketsource_t *source) { + UNUSED(source); +} + + +isc_result_t +isc_entropy_createfilesource(isc_entropy_t *ent, const char *fname) { + isc_result_t ret; + isc_entropysource_t *source; + HCRYPTPROV hcryptprov; + BOOL err; + + REQUIRE(VALID_ENTROPY(ent)); + REQUIRE(fname != NULL); + + LOCK(&ent->lock); + + source = NULL; + + /* + * The first time we just try to acquire the context + */ + err = CryptAcquireContext(&hcryptprov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT); + if (!err){ + (void)GetLastError(); + ret = ISC_R_IOERROR; + goto errout; + } + + source = isc_mem_get(ent->mctx, sizeof(isc_entropysource_t)); + if (source == NULL) { + ret = ISC_R_NOMEMORY; + goto closecontext; + } + + /* + * From here down, no failures can occur. + */ + source->magic = SOURCE_MAGIC; + source->type = ENTROPY_SOURCETYPE_FILE; + source->ent = ent; + source->total = 0; + source->bad = false; + memset(source->name, 0, sizeof(source->name)); + ISC_LINK_INIT(source, link); + source->sources.file.handle = hcryptprov; + + /* + * Hook it into the entropy system. + */ + ISC_LIST_APPEND(ent->sources, source, link); + ent->nsources++; + + UNLOCK(&ent->lock); + return (ISC_R_SUCCESS); + + closecontext: + CryptReleaseContext(hcryptprov, 0); + + errout: + if (source != NULL) + isc_mem_put(ent->mctx, source, sizeof(isc_entropysource_t)); + + UNLOCK(&ent->lock); + + return (ret); +} diff --git a/lib/isc/win32/errno.c b/lib/isc/win32/errno.c new file mode 100644 index 0000000..74ebf0c --- /dev/null +++ b/lib/isc/win32/errno.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <config.h> + +#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..83dbb4e --- /dev/null +++ b/lib/isc/win32/errno2result.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <stdbool.h> + +#include <winsock2.h> +#include "errno2result.h" +#include <isc/result.h> +#include <isc/strerror.h> +#include <isc/util.h> + +/* + * 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 + case ENFILE: + case EMFILE: + case WSAEMFILE: + return (ISC_R_TOOMANYOPENFILES); + 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) { + isc__strerror(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..99752ae --- /dev/null +++ b/lib/isc/win32/errno2result.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <errno.h> /* Provides errno. */ +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/types.h> + +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..d281ba1 --- /dev/null +++ b/lib/isc/win32/file.c @@ -0,0 +1,928 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#undef rename +#include <errno.h> +#include <limits.h> +#include <stdbool.h> +#include <stdlib.h> +#include <io.h> +#include <process.h> + +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/utime.h> + +#include <isc/file.h> +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/random.h> +#include <isc/result.h> +#include <isc/sha2.h> +#include <isc/stat.h> +#include <isc/string.h> +#include <isc/time.h> +#include <isc/util.h> + +#include "errno2result.h" + +static const char alphnum[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +/* + * 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_get(&which); + *trv = alphnum[which % (sizeof(alphnum) - 1)]; + } + /* + * 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(*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; + int res = 0; + 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) { + 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 + (void)fchmod(fd, mode); +#endif + 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); + if (dir != NULL) + 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" +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +isc_result_t +isc_file_sanitize(const char *dir, const char *base, const char *ext, + char *path, size_t length) +{ + char buf[PATH_MAX], hash[ISC_SHA256_DIGESTSTRINGLENGTH]; + size_t l = 0; + + 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 */ + isc_sha256_data((const void *) base, strlen(base), hash); + 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..164985d --- /dev/null +++ b/lib/isc/win32/fsaccess.c @@ -0,0 +1,365 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <config.h> + +#include <aclapi.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <io.h> +#include <errno.h> +#include <stdbool.h> + +#include <isc/file.h> +#include <isc/stat.h> +#include <isc/string.h> + +#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]; + + 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(tmpbuf, "\\"); + sharename = strtok(NULL, "\\"); + 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) + NTFSbits |= FILE_GENERIC_READ; + if (caccess & ISC_FSACCESS_WRITE) + NTFSbits |= FILE_GENERIC_WRITE; + if (caccess & ISC_FSACCESS_EXECUTE) + NTFSbits |= FILE_GENERIC_EXECUTE; + + /* For directories check the directory-specific bits */ + if (isdir == true) { + if (caccess & ISC_FSACCESS_CREATECHILD) + NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE; + if (caccess & ISC_FSACCESS_DELETECHILD) + NTFSbits |= FILE_DELETE_CHILD; + if (caccess & ISC_FSACCESS_LISTDIRECTORY) + NTFSbits |= FILE_LIST_DIRECTORY; + if (caccess & ISC_FSACCESS_ACCESSCHILD) + 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) + NTFSbits |= FILE_GENERIC_READ; + if (caccess & ISC_FSACCESS_WRITE) + NTFSbits |= FILE_GENERIC_WRITE; + if (caccess & ISC_FSACCESS_EXECUTE) + NTFSbits |= FILE_GENERIC_EXECUTE; + + /* For directories check the directory-specific bits */ + if (isdir == TRUE) { + if (caccess & ISC_FSACCESS_CREATECHILD) + NTFSbits |= FILE_ADD_SUBDIRECTORY | FILE_ADD_FILE; + if (caccess & ISC_FSACCESS_DELETECHILD) + NTFSbits |= FILE_DELETE_CHILD; + if (caccess & ISC_FSACCESS_LISTDIRECTORY) + NTFSbits |= FILE_LIST_DIRECTORY; + if (caccess & ISC_FSACCESS_ACCESSCHILD) + 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/Makefile.in b/lib/isc/win32/include/Makefile.in new file mode 100644 index 0000000..2cbf021 --- /dev/null +++ b/lib/isc/win32/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 pkcs11 +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..a7c0243 --- /dev/null +++ b/lib/isc/win32/include/isc/Makefile.in @@ -0,0 +1,30 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = dir.h keyboard.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/atomic.h b/lib/isc/win32/include/isc/atomic.h new file mode 100644 index 0000000..4b3d92d --- /dev/null +++ b/lib/isc/win32/include/isc/atomic.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_ATOMIC_H +#define ISC_ATOMIC_H 1 + +#include <inttypes.h> + +#include <isc/platform.h> +#include <isc/types.h> + +/* + * This routine atomically increments the value stored in 'p' by 'val', and + * returns the previous value. + */ +#ifdef ISC_PLATFORM_HAVEXADD +static __inline int32_t +isc_atomic_xadd(int32_t *p, int32_t val) { + return (int32_t) _InterlockedExchangeAdd((long *)p, (long)val); +} +#endif + +#ifdef ISC_PLATFORM_HAVEXADDQ +static __inline int64_t +isc_atomic_xaddq(int64_t *p, int64_t val) { + return (int64_t) _InterlockedExchangeAdd64((__int64 *)p, + (__int64) val); +} +#endif + +/* + * This routine atomically stores the value 'val' in 'p' (32-bit version). + */ +#ifdef ISC_PLATFORM_HAVEATOMICSTORE +static __inline void +isc_atomic_store(int32_t *p, int32_t val) { + (void) _InterlockedExchange((long *)p, (long)val); +} +#endif + +/* + * This routine atomically stores the value 'val' in 'p' (64-bit version). + */ +#ifdef ISC_PLATFORM_HAVEATOMICSTOREQ +static __inline void +isc_atomic_storeq(int64_t *p, int64_t val) { + (void) _InterlockedExchange64((__int64 *)p, (__int64)val); +} +#endif + +/* + * This routine atomically replaces the value in 'p' with 'val', if the + * original value is equal to 'cmpval'. The original value is returned in any + * case. + */ +#ifdef ISC_PLATFORM_HAVECMPXCHG +static __inline int32_t +isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val) { + /* beware: swap arguments */ + return (int32_t) _InterlockedCompareExchange((long *)p, + (long)val, + (long)cmpval); +} +#endif + +#endif /* ISC_ATOMIC_H */ 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..57970be --- /dev/null +++ b/lib/isc/win32/include/isc/bind_registry.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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..d4088e8 --- /dev/null +++ b/lib/isc/win32/include/isc/bindevt.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 layed 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..1f5b2d6 --- /dev/null +++ b/lib/isc/win32/include/isc/condition.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <windows.h> + +#include <isc/lang.h> +#include <isc/mutex.h> +#include <isc/thread.h> +#include <isc/types.h> + +typedef struct isc_condition_thread isc_condition_thread_t; + +struct isc_condition_thread { + unsigned long 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 + +isc_result_t +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..7370fa6 --- /dev/null +++ b/lib/isc/win32/include/isc/dir.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <windows.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <isc/lang.h> +#include <isc/result.h> + +#define ISC_DIR_NAMEMAX _MAX_FNAME +#define ISC_DIR_PATHMAX _MAX_PATH + +typedef struct { + char name[ISC_DIR_NAMEMAX]; + unsigned int length; + WIN32_FIND_DATA find_data; +} isc_direntry_t; + +typedef struct { + unsigned int magic; + char dirname[ISC_DIR_PATHMAX]; + 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..bee1bd0 --- /dev/null +++ b/lib/isc/win32/include/isc/ipv6.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +#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_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_V4MAPPED_INIT +#define IN6ADDR_V4MAPPED_INIT {{ 0,0,0,0,0,0,0,0,0,0,0xff,0xff,0,0,0,0 }} +#endif + +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 + +/* + * 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 + +/* + * 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_SITELOCAL +#define IN6_IS_ADDR_SITELOCAL(a) (\ + ((a)->s6_addr[0] == 0xfe) && \ + (((a)->s6_addr[1] & 0xc0) == 0xc0)) +#endif + +#endif /* ISC_IPV6_H */ diff --git a/lib/isc/win32/include/isc/keyboard.h b/lib/isc/win32/include/isc/keyboard.h new file mode 100644 index 0000000..009523a --- /dev/null +++ b/lib/isc/win32/include/isc/keyboard.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_KEYBOARD_H +#define ISC_KEYBOARD_H 1 + +#include <stdbool.h> + +#include <isc/lang.h> +#include <isc/result.h> + +ISC_LANG_BEGINDECLS + +typedef struct { + int fd; + isc_result_t result; +} isc_keyboard_t; + +isc_result_t +isc_keyboard_open(isc_keyboard_t *keyboard); + +isc_result_t +isc_keyboard_close(isc_keyboard_t *keyboard, unsigned int sleepseconds); + +isc_result_t +isc_keyboard_getchar(isc_keyboard_t *keyboard, unsigned char *cp); + +bool +isc_keyboard_canceled(isc_keyboard_t *keyboard); + +ISC_LANG_ENDDECLS + +#endif /* ISC_KEYBOARD_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..6815741 --- /dev/null +++ b/lib/isc/win32/include/isc/mutex.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <isc/net.h> +#include <windows.h> + +#include <isc/result.h> + +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)), ISC_R_SUCCESS) +#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)), ISC_R_SUCCESS) + +/* + * 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..9712a6a --- /dev/null +++ b/lib/isc/win32/include/isc/net.h @@ -0,0 +1,431 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* $Id$ */ + +#ifndef ISC_NET_H +#define ISC_NET_H 1 + +/* + * Also define LWRES_IPV6_H to keep it from being included if liblwres is + * being used, or redefinition errors will occur. + */ +#define LWRES_IPV6_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_aton(), 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 <inttypes.h> + +#include <isc/platform.h> + +/* + * Because of some sort of problem in the MS header files, this cannot + * be simple "#include <winsock2.h>", 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 + +#include <winsock2.h> + +#include <sys/types.h> + +#include <isc/lang.h> +#include <isc/types.h> + +#include <ws2tcpip.h> +#include <isc/ipv6.h> + +/* + * 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_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001UL +#endif + +#ifndef ISC_PLATFORM_HAVEIN6PKTINFO +struct in6_pktinfo { + struct in6_addr ipi6_addr; /* src/dst IPv6 address */ + unsigned int ipi6_ifindex; /* send/recv interface index */ +}; +#endif + +#if _MSC_VER < 1300 +#define in6addr_any isc_in6addr_any +#define in6addr_loopback isc_in6addr_loopback +#endif + +/* + * Ensure type in_port_t is defined. + */ +#ifdef ISC_PLATFORM_NEEDPORTT +typedef uint16_t in_port_t; +#endif + +/* + * 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 + +#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 <errno.h> + +#ifndef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#ifndef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#endif +#ifndef EALREADY +#define EALREADY WSAEALREADY +#endif +#ifndef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#endif +#ifndef EDESTADDRREQ +#define EDESTADDRREQ WSAEDESTADDRREQ +#endif +#ifndef EMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#endif +#ifndef EPROTOTYPE +#define EPROTOTYPE WSAEPROTOTYPE +#endif +#ifndef ENOPROTOOPT +#define ENOPROTOOPT WSAENOPROTOOPT +#endif +#ifndef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#endif +#ifndef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#endif +#ifndef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#endif +#ifndef EPFNOSUPPORT +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#endif +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif +#ifndef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#endif +#ifndef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#endif +#ifndef ENETDOWN +#define ENETDOWN WSAENETDOWN +#endif +#ifndef ENETUNREACH +#define ENETUNREACH WSAENETUNREACH +#endif +#ifndef ENETRESET +#define ENETRESET WSAENETRESET +#endif +#ifndef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#endif +#ifndef ECONNRESET +#define ECONNRESET WSAECONNRESET +#endif +#ifndef ENOBUFS +#define ENOBUFS WSAENOBUFS +#endif +#ifndef EISCONN +#define EISCONN WSAEISCONN +#endif +#ifndef ENOTCONN +#define ENOTCONN WSAENOTCONN +#endif +#ifndef ESHUTDOWN +#define ESHUTDOWN WSAESHUTDOWN +#endif +#ifndef ETOOMANYREFS +#define ETOOMANYREFS WSAETOOMANYREFS +#endif +#ifndef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#endif +#ifndef ELOOP +#define ELOOP WSAELOOP +#endif +#ifndef EHOSTDOWN +#define EHOSTDOWN WSAEHOSTDOWN +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#endif +#ifndef EPROCLIM +#define EPROCLIM WSAEPROCLIM +#endif +#ifndef EUSERS +#define EUSERS WSAEUSERS +#endif +#ifndef EDQUOT +#define EDQUOT WSAEDQUOT +#endif +#ifndef ESTALE +#define ESTALE WSAESTALE +#endif +#ifndef EREMOTE +#define EREMOTE WSAEREMOTE +#endif + + +/*** + *** 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. + */ + +#ifdef ISC_PLATFORM_NEEDNTOP +const char * +isc_net_ntop(int af, const void *src, char *dst, size_t size); +#undef inet_ntop +#define inet_ntop isc_net_ntop +#endif + +#ifdef ISC_PLATFORM_NEEDPTON +int +isc_net_pton(int af, const char *src, void *dst); +#define inet_pton isc_net_pton +#endif + +int +isc_net_aton(const char *cp, struct in_addr *addr); +#define inet_aton isc_net_aton + +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..c07194d --- /dev/null +++ b/lib/isc/win32/include/isc/netdb.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 get<x>by<y> APIs. + * + * MP: + * No impact. + * + * Reliability: + * No anticipated impact. + * + * Resources: + * N/A. + * + * Security: + * No anticipated impact. + * + * Standards: + * BSD API + */ + +/*** + *** Imports. + ***/ + +#include <isc/net.h> + +#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..e8352fc --- /dev/null +++ b/lib/isc/win32/include/isc/ntgroups.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <isc/lang.h> +#include <isc/result.h> + +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..c0010fc --- /dev/null +++ b/lib/isc/win32/include/isc/ntpaths.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <isc/lang.h> + +/* + * Index of paths needed + */ +enum NtPaths { + NAMED_CONF_PATH, + LWRES_CONF_PATH, + RESOLV_CONF_PATH, + RNDC_CONF_PATH, + NAMED_PID_PATH, + LWRESD_PID_PATH, + NAMED_LOCK_PATH, + LOCAL_STATE_DIR, + SYS_CONF_DIR, + RNDC_KEY_PATH, + SESSION_KEY_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 NS_LOCALSTATEDIR "/dns/bin" +#define NS_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..14ef06f --- /dev/null +++ b/lib/isc/win32/include/isc/offset.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <limits.h> /* Required for CHAR_BIT. */ +#include <sys/types.h> + +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..c005b0b --- /dev/null +++ b/lib/isc/win32/include/isc/once.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <isc/lang.h> +#include <isc/result.h> + +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..5b8a2c9 --- /dev/null +++ b/lib/isc/win32/include/isc/platform.h.in @@ -0,0 +1,157 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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. + *****/ + +#define ISC_PLATFORM_USETHREADS 1 + +/*** + *** Network. + ***/ + +#ifndef PATH_MAX +#define PATH_MAX _MAX_PATH +#endif + +#define ISC_PLATFORM_HAVEIPV6 +#if _MSC_VER > 1200 +#define ISC_PLATFORM_HAVEIN6PKTINFO +#endif +#define ISC_PLATFORM_HAVESCOPEID +#define ISC_PLATFORM_NEEDPORTT +#undef MSG_TRUNC +#define ISC_PLATFORM_NEEDNTOP +#define ISC_PLATFORM_NEEDPTON +#define ISC_PLATFORM_HAVESOCKADDRSTORAGE + +#define ISC_PLATFORM_NEEDSTRSEP +#define ISC_PLATFORM_NEEDSTRLCPY +#define ISC_PLATFORM_NEEDSTRLCAT +#define ISC_PLATFORM_NEEDSTRLCPY + +/* + * Used to control how extern data is linked; needed for Win32 platforms. + */ +#define ISC_PLATFORM_USEDECLSPEC 1 + +/* + * Define this here for now as winsock2.h defines h_errno + * and we don't want to redeclare it. + */ +#define ISC_PLATFORM_NONSTDHERRNO + +/* + * Define if the platform has <sys/un.h>. + */ +#undef ISC_PLATFORM_HAVESYSUNH + +/* + * Define if we want to log backtrace + */ +@ISC_PLATFORM_USEBACKTRACE@ + +/* + * Defines for the noreturn attribute. + */ +#define ISC_PLATFORM_NORETURN_PRE __declspec(noreturn) +#define ISC_PLATFORM_NORETURN_POST + +/* + * Define if the hash functions must be provided by OpenSSL. + */ +@ISC_PLATFORM_OPENSSLHASH@ + +/* + * Define if AES support is wanted + */ +@ISC_PLATFORM_WANTAES@ + +/* + * If the "xadd" operation is available on this architecture, + * ISC_PLATFORM_HAVEXADD will be defined. + */ +@ISC_PLATFORM_HAVEXADD@ + +/* + * If the "xaddq" operation (64bit xadd) is available on this architecture, + * ISC_PLATFORM_HAVEXADDQ will be defined. + */ +@ISC_PLATFORM_HAVEXADDQ@ + +/* + * If the "atomic swap" operation is available on this architecture, + * ISC_PLATFORM_HAVEATOMICSTORE" will be defined. + */ +@ISC_PLATFORM_HAVEATOMICSTORE@ + +/* + * If the "compare-and-exchange" operation is available on this architecture, + * ISC_PLATFORM_HAVECMPXCHG will be defined. + */ +@ISC_PLATFORM_HAVECMPXCHG@ + +/* + * Define with the busy wait nop asm or function call. + */ +@ISC_PLATFORM_BUSYWAITNOP@ + +/* + * If the strcasestr() operation is not available on this platform, + * ISC_PLATFORM_NEEDSTRCASESTR will be defined. + */ +@ISC_PLATFORM_NEEDSTRCASESTR@ + +/* + * 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 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..8b7d685 --- /dev/null +++ b/lib/isc/win32/include/isc/stat.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <sys/stat.h> + +/* + * 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_IFDIR +# define S_IFDIR _S_IFDIR +#endif +#ifndef S_IFCHR +# define S_IFCHR _S_IFCHR +#endif +#ifndef S_IFREG +# define S_IFREG _S_IFREG +#endif + +#ifndef S_ISDIR +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#ifndef S_ISREG +# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif + +#endif /* ISC_STAT_H */ diff --git a/lib/isc/win32/include/isc/stdtime.h b/lib/isc/win32/include/isc/stdtime.h new file mode 100644 index 0000000..e11647f --- /dev/null +++ b/lib/isc/win32/include/isc/stdtime.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <isc/lang.h> +#include <inttypes.h> + +/* + * 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; + +/* but this flag helps... */ +#define STDTIME_ON_32BITS 1 + +/* + * isc_stdtime32_t is a 32-bit version of isc_stdtime_t. A variable of this + * type should only be used as an opaque integer (e.g.,) to compare two + * time values. + */ +typedef uint32_t isc_stdtime32_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. + */ + +#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/strerror.h b/lib/isc/win32/include/isc/strerror.h new file mode 100644 index 0000000..ae040a4 --- /dev/null +++ b/lib/isc/win32/include/isc/strerror.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_STRERROR_H +#define ISC_STRERROR_H + +#include <sys/types.h> + +#include <isc/lang.h> + +ISC_LANG_BEGINDECLS + +#define ISC_STRERRORSIZE 128 + +/* + * Provide a thread safe wrapper to strerrror(). + * + * Requires: + * 'buf' to be non NULL. + */ +void +isc__strerror(int num, char *buf, size_t bufsize); + +ISC_LANG_ENDDECLS + +#endif /* ISC_STRERROR_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..66431e5 --- /dev/null +++ b/lib/isc/win32/include/isc/syslog.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <isc/lang.h> +#include <isc/types.h> + +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..be1343a --- /dev/null +++ b/lib/isc/win32/include/isc/thread.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <windows.h> + +#include <isc/lang.h> +#include <isc/result.h> + +/* + * 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); +typedef DWORD isc_thread_key_t; + +#define isc_thread_self (unsigned long)GetCurrentThreadId + +ISC_LANG_BEGINDECLS + +isc_result_t +isc_thread_create(isc_threadfunc_t, isc_threadarg_t, isc_thread_t *); + +isc_result_t +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 *); + +int +isc_thread_key_create(isc_thread_key_t *key, void (*func)(void *)); + +int +isc_thread_key_delete(isc_thread_key_t key); + +void * +isc_thread_key_getspecific(isc_thread_key_t); + +int +isc_thread_key_setspecific(isc_thread_key_t key, void *value); + +#define isc_thread_yield() Sleep(0) + +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..486f527 --- /dev/null +++ b/lib/isc/win32/include/isc/time.h @@ -0,0 +1,357 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <inttypes.h> +#include <stdbool.h> +#include <windows.h> + +#include <isc/lang.h> +#include <isc/types.h> + +/*** + *** 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_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_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 + * + */ + +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..bc18c40 --- /dev/null +++ b/lib/isc/win32/include/isc/win32os.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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.h> + +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/include/pkcs11/Makefile.in b/lib/isc/win32/include/pkcs11/Makefile.in new file mode 100644 index 0000000..e9a85d4 --- /dev/null +++ b/lib/isc/win32/include/pkcs11/Makefile.in @@ -0,0 +1,26 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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@ + +HEADERS = cryptoki.h +SUBDIRS = +TARGETS = + +@BIND9_MAKE_RULES@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/pkcs11 + +install:: installdirs + for i in ${HEADERS}; do \ + ${INSTALL_DATA} $(srcdir)/$$i ${DESTDIR}${includedir}/pkcs11 || exit 1; \ + done diff --git a/lib/isc/win32/include/pkcs11/cryptoki.h b/lib/isc/win32/include/pkcs11/cryptoki.h new file mode 100644 index 0000000..2a681c4 --- /dev/null +++ b/lib/isc/win32/include/pkcs11/cryptoki.h @@ -0,0 +1,66 @@ +/* cryptoki.h include file for PKCS #11. */
+/* $Revision: 1.2 $ */
+
+/* License to copy and use this software is granted provided that it is
+ * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ * (Cryptoki)" in all material mentioning or referencing this software.
+
+ * License is also granted to make and use derivative works provided that
+ * such works are identified as "derived from the RSA Security Inc. PKCS #11
+ * Cryptographic Token Interface (Cryptoki)" in all material mentioning or
+ * referencing the derived work.
+
+ * RSA Security Inc. makes no representations concerning either the
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ */
+
+/* This is a sample file containing the top level include directives
+ * for building Win32 Cryptoki libraries and applications.
+ */
+
+#ifndef ___CRYPTOKI_H_INC___
+#define ___CRYPTOKI_H_INC___
+
+#pragma pack(push, cryptoki, 1)
+
+/* Specifies that the function is a DLL entry point. */
+#define CK_IMPORT_SPEC __declspec(dllimport)
+
+/* Define CRYPTOKI_EXPORTS during the build of cryptoki libraries. Do
+ * not define it in applications.
+ */
+#ifdef CRYPTOKI_EXPORTS
+/* Specified that the function is an exported DLL entry point. */
+#define CK_EXPORT_SPEC __declspec(dllexport)
+#else
+#define CK_EXPORT_SPEC CK_IMPORT_SPEC
+#endif
+
+/* Ensures the calling convention for Win32 builds */
+#define CK_CALL_SPEC __cdecl
+
+#define CK_PTR *
+
+#define CK_DEFINE_FUNCTION(returnType, name) \
+ returnType CK_EXPORT_SPEC CK_CALL_SPEC name
+
+#define CK_DECLARE_FUNCTION(returnType, name) \
+ returnType CK_EXPORT_SPEC CK_CALL_SPEC name
+
+#define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ returnType CK_IMPORT_SPEC (CK_CALL_SPEC CK_PTR name)
+
+#define CK_CALLBACK_FUNCTION(returnType, name) \
+ returnType (CK_CALL_SPEC CK_PTR name)
+
+#ifndef NULL_PTR
+#define NULL_PTR 0
+#endif
+
+#include <pkcs11/pkcs11.h>
+
+#pragma pack(pop, cryptoki)
+
+#endif /* ___CRYPTOKI_H_INC___ */
diff --git a/lib/isc/win32/interfaceiter.c b/lib/isc/win32/interfaceiter.c new file mode 100644 index 0000000..1bbd0f8 --- /dev/null +++ b/lib/isc/win32/interfaceiter.c @@ -0,0 +1,543 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <config.h> +#include <winsock2.h> +#include <ws2tcpip.h> +#include <sys/types.h> + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <isc/interfaceiter.h> +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/result.h> +#include <isc/string.h> +#include <isc/strerror.h> +#include <isc/types.h> +#include <isc/util.h> + +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: + INSIST(0); + break; + } +} + +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)); + if (iter == NULL) + return (ISC_R_NOMEMORY); + + 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; + isc__strerror(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 (iter->buf4 == NULL) { + result = ISC_R_NOMEMORY; + goto alloc_failure; + } + + 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; + isc__strerror(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; + isc__strerror(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 (iter->buf6 == NULL) { + result = ISC_R_NOMEMORY; + goto ioctl_failure; + } + + 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; + isc__strerror(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); + + alloc_failure: + 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; + 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)); + *iterp = NULL; +} diff --git a/lib/isc/win32/ipv6.c b/lib/isc/win32/ipv6.c new file mode 100644 index 0000000..c2a4ac5 --- /dev/null +++ b/lib/isc/win32/ipv6.c @@ -0,0 +1,20 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <isc/net.h> +#include <isc/platform.h> + +LIBISC_EXTERNAL_DATA const struct in6_addr isc_in6addr_any = + IN6ADDR_ANY_INIT; + +LIBISC_EXTERNAL_DATA const struct in6_addr isc_in6addr_loopback = + IN6ADDR_LOOPBACK_INIT; diff --git a/lib/isc/win32/keyboard.c b/lib/isc/win32/keyboard.c new file mode 100644 index 0000000..6e6f800 --- /dev/null +++ b/lib/isc/win32/keyboard.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <sys/types.h> + +#include <windows.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +#include <io.h> + +#include <isc/keyboard.h> +#include <isc/util.h> + +isc_result_t +isc_keyboard_open(isc_keyboard_t *keyboard) { + int fd; + + REQUIRE(keyboard != NULL); + + fd = _fileno(stdin); + if (fd < 0) + return (ISC_R_IOERROR); + + keyboard->fd = fd; + + keyboard->result = ISC_R_SUCCESS; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_keyboard_close(isc_keyboard_t *keyboard, unsigned int sleeptime) { + REQUIRE(keyboard != NULL); + + if (sleeptime > 0 && keyboard->result != ISC_R_CANCELED) + (void)Sleep(sleeptime*1000); + + keyboard->fd = -1; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_keyboard_getchar(isc_keyboard_t *keyboard, unsigned char *cp) { + ssize_t cc; + unsigned char c; + + REQUIRE(keyboard != NULL); + REQUIRE(cp != NULL); + + cc = read(keyboard->fd, &c, 1); + if (cc < 0) { + keyboard->result = ISC_R_IOERROR; + return (keyboard->result); + } + + *cp = c; + + return (ISC_R_SUCCESS); +} + +bool +isc_keyboard_canceled(isc_keyboard_t *keyboard) { + return (keyboard->result == ISC_R_CANCELED); +} diff --git a/lib/isc/win32/libgen.h b/lib/isc/win32/libgen.h new file mode 100644 index 0000000..e74740f --- /dev/null +++ b/lib/isc/win32/libgen.h @@ -0,0 +1,19 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 diff --git a/lib/isc/win32/libisc.def.exclude b/lib/isc/win32/libisc.def.exclude new file mode 100644 index 0000000..41e8562 --- /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_print_fprintf +isc_print_printf +isc_print_snprintf +isc_print_sprintf +isc_print_vsnprintf +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 diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in new file mode 100644 index 0000000..a82face --- /dev/null +++ b/lib/isc/win32/libisc.def.in @@ -0,0 +1,836 @@ +LIBRARY libisc + +; Exported Functions +EXPORTS + +NTReportError +closelog +@IF PKCS11 +getpassphrase +@END PKCS11 +isc___socketmgr_maxudp +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__appctx_setsocketmgr +isc__appctx_settaskmgr +isc__appctx_settimermgr +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__hash_setvec +isc__mem_allocate +isc__mem_free +isc__mem_get +isc__mem_put +isc__mem_putanddetach +isc__mem_reallocate +isc__mem_strdup +isc__mempool_get +isc__mempool_put +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_ipv6only +isc__socket_isbound +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__socket_setname +isc__socketmgr_create +isc__socketmgr_create2 +isc__socketmgr_destroy +isc__socketmgr_getmaxsockets +isc__socketmgr_setreserved +isc__socketmgr_setstats +isc__strerror +isc__task_getname +isc__task_gettag +isc__task_unsendrange +isc__taskmgr_mode +@IF AES +isc_aes128_crypt +isc_aes192_crypt +isc_aes256_crypt +@END AES +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_register +isc_app_reload +isc_app_run +isc_app_shutdown +isc_app_start +isc_app_unblock +isc_appctx_create +isc_appctx_destroy +isc_appctx_setsocketmgr +isc_appctx_settaskmgr +isc_appctx_settimermgr +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_putdecint +isc_buffer_reallocate +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_entropy_addcallbacksample +isc_entropy_addsample +isc_entropy_attach +isc_entropy_create +isc_entropy_createcallbacksource +isc_entropy_createfilesource +isc_entropy_createsamplesource +isc_entropy_destroysource +isc_entropy_detach +isc_entropy_getdata +isc_entropy_putdata +isc_entropy_stats +isc_entropy_status +isc_entropy_stopcallbacksources +isc_entropy_usebestsource +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_hash_calc +isc_hash_create +isc_hash_ctxattach +isc_hash_ctxcalc +isc_hash_ctxcreate +isc_hash_ctxdetach +isc_hash_ctxinit +isc_hash_destroy +isc_hash_function +isc_hash_function_reverse +isc_hash_get_initializer +isc_hash_init +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_hmacmd5_check +isc_hmacmd5_init +isc_hmacmd5_invalidate +isc_hmacmd5_sign +isc_hmacmd5_update +isc_hmacmd5_verify +isc_hmacmd5_verify2 +isc_hmacsha1_check +isc_hmacsha1_init +isc_hmacsha1_invalidate +isc_hmacsha1_sign +isc_hmacsha1_update +isc_hmacsha1_verify +isc_hmacsha224_init +isc_hmacsha224_invalidate +isc_hmacsha224_sign +isc_hmacsha224_update +isc_hmacsha224_verify +isc_hmacsha256_init +isc_hmacsha256_invalidate +isc_hmacsha256_sign +isc_hmacsha256_update +isc_hmacsha256_verify +isc_hmacsha384_init +isc_hmacsha384_invalidate +isc_hmacsha384_sign +isc_hmacsha384_update +isc_hmacsha384_verify +isc_hmacsha512_init +isc_hmacsha512_invalidate +isc_hmacsha512_sign +isc_hmacsha512_update +isc_hmacsha512_verify +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_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_keyboard_canceled +isc_keyboard_close +isc_keyboard_getchar +isc_keyboard_open +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_initmsgcat +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_ivwrite +isc_log_ivwrite1 +isc_log_iwrite +isc_log_iwrite1 +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_get +isc_logconfig_use +isc_logfile_roll +isc_md5_check +isc_md5_final +isc_md5_init +isc_md5_invalidate +isc_md5_update +isc_mem_attach +isc_mem_checkdestroyed +isc_mem_create +isc_mem_create2 +isc_mem_createx +isc_mem_createx2 +isc_mem_destroy +isc_mem_detach +isc_mem_getname +isc_mem_getquota +isc_mem_gettag +isc_mem_inuse +isc_mem_isovermem +isc_mem_maxinuse +isc_mem_ondestroy +isc_mem_printallactive +isc_mem_references +isc_mem_register +@IF NOTYET +isc_mem_renderjson +@END NOTYET +@IF LIBXML2 +isc_mem_renderxml +@END LIBXML2 +isc_mem_setdestroycheck +isc_mem_setname +isc_mem_setquota +isc_mem_setwater +isc_mem_stats +isc_mem_total +isc_mem_waterack +isc_meminfo_totalphys +isc_mempool_associatelock +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_msgcat_close +isc_msgcat_get +isc_msgcat_open +isc_mutexblock_destroy +isc_mutexblock_init +isc_net_aton +isc_net_disableipv4 +isc_net_disableipv6 +isc_net_enableipv4 +isc_net_enableipv6 +isc_net_getudpportrange +isc_net_ntop +isc_net_probe_ipv6only +isc_net_probe_ipv6pktinfo +isc_net_probedscp +isc_net_probeipv4 +isc_net_probeipv6 +isc_net_probeunix +isc_net_pton +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_netscope_pton +isc_ntpaths_get +isc_ntpaths_init +isc_once_do +isc_ondestroy_init +isc_ondestroy_notify +isc_ondestroy_register +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_destroy +isc_quota_detach +isc_quota_init +isc_quota_max +isc_quota_release +isc_quota_reserve +isc_quota_soft +isc_radix_create +isc_radix_destroy +isc_radix_insert +isc_radix_process +isc_radix_remove +isc_radix_search +isc_random_get +isc_random_jitter +isc_random_seed +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_refcount_init +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_rng_attach +isc_rng_create +isc_rng_detach +isc_rng_random +isc_rng_uniformrandom +isc_rwlock_destroy +isc_rwlock_downgrade +isc_rwlock_init +isc_rwlock_lock +isc_rwlock_trylock +isc_rwlock_tryupgrade +isc_rwlock_unlock +isc_safe_memcompare +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_sha1_check +isc_sha1_final +isc_sha1_init +isc_sha1_invalidate +isc_sha1_update +isc_sha224_data +isc_sha224_end +isc_sha224_final +isc_sha224_init +isc_sha224_invalidate +isc_sha224_update +isc_sha256_data +isc_sha256_end +isc_sha256_final +isc_sha256_init +isc_sha256_invalidate +isc_sha256_update +isc_sha384_data +isc_sha384_end +isc_sha384_final +isc_sha384_init +isc_sha384_invalidate +isc_sha384_update +isc_sha512_data +isc_sha512_end +isc_sha512_final +isc_sha512_init +isc_sha512_invalidate +isc_sha512_update +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_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_createinctx +@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_increment +isc_stats_ncounters +isc_stats_set +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_string_append +isc_string_append_truncate +isc_string_copy +isc_string_copy_truncate +isc_string_printf +isc_string_printf_truncate +isc_string_regiondup +isc_string_separate +isc_string_strcasestr +isc_string_strlcat +isc_string_strlcpy +isc_string_touint64 +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_destroy +isc_task_detach +isc_task_endexclusive +isc_task_exiting +isc_task_getcurrenttime +isc_task_getcurrenttimex +isc_task_onshutdown +isc_task_privilege +isc_task_purge +isc_task_purgeevent +isc_task_purgerange +isc_task_register +isc_task_send +isc_task_sendanddetach +isc_task_setname +isc_task_setprivilege +isc_task_shutdown +isc_task_unsend +isc_taskmgr_create +isc_taskmgr_createinctx +isc_taskmgr_destroy +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_setprivilege +isc_taskpool_size +isc_thread_create +isc_thread_join +isc_thread_key_create +isc_thread_key_delete +isc_thread_key_getspecific +isc_thread_key_setspecific +isc_thread_setconcurrency +isc_thread_setname +isc_time_add +isc_time_compare +isc_time_formatISO8601 +isc_time_formatISO8601ms +isc_time_formathttptimestamp +isc_time_formattimestamp +isc_time_isepoch +isc_time_microdiff +isc_time_nanoseconds +isc_time_now +isc_time_nowplusinterval +isc_time_parsehttptimestamp +isc_time_secondsastimet +isc_time_seconds +isc_time_set +isc_time_settoepoch +isc_time_subtract +isc_timer_attach +isc_timer_create +isc_timer_detach +isc_timer_gettype +isc_timer_register +isc_timer_reset +isc_timer_touch +isc_timermgr_create +isc_timermgr_createinctx +isc_timermgr_destroy +isc_timermgr_poke +isc_tm_timegm +isc_tm_strptime +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_initmsgcat +pk11_mem_get +pk11_mem_put +pk11_numbits +pk11_parse_uri +pk11_rand_bytes +pk11_rand_seed_fromfile +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 +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_msgcat DATA +@IF PKCS11 +pk11_msgcat DATA +pk11_verbose_init DATA +@END PKCS11 +@END NOLONGER diff --git a/lib/isc/win32/libisc.dsp.in b/lib/isc/win32/libisc.dsp.in new file mode 100644 index 0000000..d21edb8 --- /dev/null +++ b/lib/isc/win32/libisc.dsp.in @@ -0,0 +1,949 @@ +# Microsoft Developer Studio Project File - Name="libisc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "@PLATFORM@ (x86) Dynamic-Link Library" 0x0102 + +CFG=libisc - @PLATFORM@ Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libisc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libisc.mak" CFG="libisc - @PLATFORM@ Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libisc - @PLATFORM@ Release" (based on "@PLATFORM@ (x86) Dynamic-Link Library") +!MESSAGE "libisc - @PLATFORM@ Debug" (based on "@PLATFORM@ (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 @COPTX@ @COPTI@ /O2 /D "BIND9" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" @COPTY@ /FD /c +@IF PKCS11 +# ADD CPP /nologo /MD /W3 @COPTX@ @COPTI@ /O2 /I "./" /I "../../../" @LIBXML2_INC@ @OPENSSL_INC@ @ZLIB_INC@ /I "include" /I "../include" /I "win32" /I "../../isccfg/include" /I "../../../lib/dns/win32/include" /I "../../../lib/dns/include" /D "BIND9" @CRYPTO@ @PK11_LIB_LOCATION@ /D "WIN32" /D "NDEBUG" /D "__STDC__" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" @COPTY@ /FD /c +@ELSE PKCS11 +# ADD CPP /nologo /MD /W3 @COPTX@ @COPTI@ /O2 /I "./" /I "../../../" @LIBXML2_INC@ @OPENSSL_INC@ @ZLIB_INC@ /I "include" /I "../include" /I "win32" /I "../../isccfg/include" /D "BIND9" /D "WIN32" /D "NDEBUG" /D "__STDC__" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" @COPTY@ /FD /c +@END PKCS11 +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll @MACHINE@ +# ADD LINK32 @LIBXML2_LIB@ @OPENSSL_LIB@ @ZLIB_LIB@ user32.lib advapi32.lib ws2_32.lib /nologo /dll @MACHINE@ /out:"../../../Build/Release/libisc.dll" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /D "BIND9" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" @COPTY@ /FD /GZ /c +@IF PKCS11 +# ADD CPP /nologo /MDd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /I "./" /I "../../../" @LIBXML2_INC@ @OPENSSL_INC@ @ZLIB_INC@ /I "include" /I "../include" /I "win32" /I "../../isccfg/include" /I "../../../lib/dns/win32/include" /I "../../../lib/dns/include" /D "BIND9" @CRYPTO@ @PK11_LIB_LOCATION@ /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" /FR @COPTY@ /FD /GZ /c +@ELSE PKCS11 +# ADD CPP /nologo /MDd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /I "./" /I "../../../" @LIBXML2_INC@ @OPENSSL_INC@ @ZLIB_INC@ /I "include" /I "../include" /I "win32" /I "../../isccfg/include" /D "BIND9" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" /FR @COPTY@ /FD /GZ /c +@END PKCS11 +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug @MACHINE@ /pdbtype:sept +# ADD LINK32 @LIBXML2_LIB@ @OPENSSL_LIB@ @ZLIB_LIB@ user32.lib advapi32.lib ws2_32.lib /nologo /dll /map /debug @MACHINE@ /out:"../../../Build/Debug/libisc.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "libisc - @PLATFORM@ Release" +# Name "libisc - @PLATFORM@ Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\app.c +# End Source File +# Begin Source File + +SOURCE=.\condition.c +# End Source File +# Begin Source File + +SOURCE=.\dir.c +# End Source File +# Begin Source File + +SOURCE=.\DLLMain.c +# End Source File +# Begin Source File + +SOURCE=.\entropy.c +# End Source File +# Begin Source File + +SOURCE=.\errno.c +# End Source File +# Begin Source File + +SOURCE=.\errno2result.c +# End Source File +# Begin Source File + +SOURCE=.\file.c +# End Source File +# Begin Source File + +SOURCE=.\fsaccess.c +# End Source File +# Begin Source File + +SOURCE=.\interfaceiter.c +# End Source File +# Begin Source File + +SOURCE=.\ipv6.c +# End Source File +# Begin Source File + +SOURCE=.\keyboard.c +# End Source File +# Begin Source File + +SOURCE=.\meminfo.c +# End Source File +# Begin Source File + +SOURCE=.\net.c +# End Source File +# Begin Source File + +SOURCE=.\ntpaths.c +# End Source File +# Begin Source File + +SOURCE=.\once.c +# End Source File +# Begin Source File + +SOURCE=.\os.c +# End Source File +@IF PKCS11 +# Begin Source File + +SOURCE=.\pk11_api.c +# End Source File +@END PKCS11 +# Begin Source File + +SOURCE=.\resource.c +# End Source File +# Begin Source File + +SOURCE=.\socket.c +# End Source File +# Begin Source File + +SOURCE=.\stdio.c +# End Source File +# Begin Source File + +SOURCE=.\stdtime.c +# End Source File +# Begin Source File + +SOURCE=.\strerror.c +# End Source File +# Begin Source File + +SOURCE=.\syslog.c +# End Source File +# Begin Source File + +SOURCE=.\thread.c +# End Source File +# Begin Source File + +SOURCE=.\time.c +# End Source File +# Begin Source File + +SOURCE=.\version.c +# End Source File +# Begin Source File + +SOURCE=.\win32os.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +@IF AES +# Begin Source File + +SOURCE=..\include\isc\aes.h +# End Source File +@END AES +# Begin Source File + +SOURCE=..\include\isc\app.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\assertions.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\backtrace.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\base32.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\base64.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\bind9.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\bind_registry.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\bindevt.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\boolean.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\buffer.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\bufferlist.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\commandline.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\counter.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\condition.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\config.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\crc64.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\dir.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\entropy.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\errno.h +# End Source File +# Begin Source File + +SOURCE=.\errno2result.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\error.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\event.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\eventclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\file.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\formatcheck.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\fsaccess.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\hash.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\heap.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\hex.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\hmacmd5.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\hmacsha.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\ht.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\httpd.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\int.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\interfaceiter.h +# End Source File +@IF PKCS11 +# Begin Source File + +SOURCE=..\include\pk11\internal.h +# End Source File +@END PKCS11 +# Begin Source File + +SOURCE=.\include\isc\ipv6.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\iterated_hash.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\keyboard.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\json.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\lang.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\lex.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\lfsr.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\lib.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\list.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\log.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\magic.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\md5.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\mem.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\meminfo.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\msgcat.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\mutex.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\mutexblock.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\net.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\netaddr.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\netscope.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\netdb.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\ntgroups.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\ntpaths.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\offset.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\once.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\ondestroy.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\os.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\parseint.h +# End Source File +@IF PKCS11 +# Begin Source File + +SOURCE=..\include\pk11\pk11.h +# End Source File +@END PKCS11 +# Begin Source File + +SOURCE=..\include\isc\pool.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\portset.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\platform.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\print.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\queue.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\quota.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\radix.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\random.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\ratelimiter.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\refcount.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\regex.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\region.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\resource.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\result.h +# End Source File +@IF PKCS11 +# Begin Source File + +SOURCE=..\include\pk11\result.h +# End Source File +@END PKCS11 +# Begin Source File + +SOURCE=..\include\isc\resultclass.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\rwlock.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\safe.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\serial.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\sha1.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\sha2.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\sockaddr.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\socket.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\stat.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\stats.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\stdio.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\stdlib.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\stdtime.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\strerror.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\string.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\symtab.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\syslog.h +# End Source File +# Begin Source File + +SOURCE=.\syslog.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\task.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\taskpool.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\thread.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\time.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\timer.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\tm.h +# End Source File +# Begin Source File + +SOURCE=.\include\isc\win32os.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\types.h +# End Source File +# Begin Source File + +SOURCE=.\unistd.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\util.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\versions.h +# End Source File +# Begin Source File + +SOURCE=..\include\isc\xml.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Group "Main Isc Lib" + +# PROP Default_Filter "c" +@IF AES +# Begin Source File + +SOURCE=..\aes.c +# End Source File +@END AES +# Begin Source File + +SOURCE=..\assertions.c +# End Source File +# Begin Source File + +SOURCE=..\backtrace.c +# End Source File +# Begin Source File + +SOURCE=..\backtrace-emptytbl.c +# End Source File +# Begin Source File + +SOURCE=..\base32.c +# End Source File +# Begin Source File + +SOURCE=..\base64.c +# End Source File +# Begin Source File + +SOURCE=..\bind9.c +# End Source File +# Begin Source File + +SOURCE=..\buffer.c +# End Source File +# Begin Source File + +SOURCE=..\bufferlist.c +# End Source File +# Begin Source File + +SOURCE=..\commandline.c +# End Source File +# Begin Source File + +SOURCE=..\counter.c +# End Source File +# Begin Source File + +SOURCE=..\crc64.c +# End Source File +# Begin Source File + +SOURCE=..\error.c +# End Source File +# Begin Source File + +SOURCE=..\event.c +# End Source File +# Begin Source File + +SOURCE=..\hash.c +# End Source File +# Begin Source File + +SOURCE=..\heap.c +# End Source File +# Begin Source File + +SOURCE=..\hex.c +# End Source File +# Begin Source File + +SOURCE=..\hmacmd5.c +# End Source File +# Begin Source File + +SOURCE=..\hmacsha.c +# End Source File +# Begin Source File + +SOURCE=..\ht.c +# End Source File +# Begin Source File + +SOURCE=..\httpd.c +# End Source File +# Begin Source File + +SOURCE=..\inet_aton.c +# End Source File +# Begin Source File + +SOURCE=..\inet_ntop.c +# End Source File +# Begin Source File + +SOURCE=..\inet_pton.c +# End Source File +# Begin Source File + +SOURCE=..\iterated_hash.c +# End Source File +# Begin Source File + +SOURCE=..\lex.c +# End Source File +# Begin Source File + +SOURCE=..\lfsr.c +# End Source File +# Begin Source File + +SOURCE=..\lib.c +# End Source File +# Begin Source File + +SOURCE=..\log.c +# End Source File +# Begin Source File + +SOURCE=..\md5.c +# End Source File +# Begin Source File + +SOURCE=..\mem.c +# End Source File +# Begin Source File + +SOURCE=..\nls\msgcat.c +# End Source File +# Begin Source File + +SOURCE=..\mutexblock.c +# End Source File +# Begin Source File + +SOURCE=..\netaddr.c +# End Source File +# Begin Source File + +SOURCE=..\netscope.c +# End Source File +# Begin Source File + +SOURCE=..\ondestroy.c +# End Source File +# Begin Source File + +SOURCE=..\parseint.c +# End Source File +@IF PKCS11 +# Begin Source File + +SOURCE=..\pk11.c +# End Source File +# Begin Source File + +SOURCE=..\pk11_result.c +# End Source File +@END PKCS11 +# Begin Source File + +SOURCE=..\pool.c +# End Source File +# Begin Source File + +SOURCE=..\portset.c +# End Source File +# Begin Source File + +SOURCE=..\quota.c +# End Source File +# Begin Source File + +SOURCE=..\radix.c +# End Source File +# Begin Source File + +SOURCE=..\random.c +# End Source File +# Begin Source File + +SOURCE=..\ratelimiter.c +# End Source File +# Begin Source File + +SOURCE=..\refcount.c +# End Source File +# Begin Source File + +SOURCE=..\regex.c +# End Source File +# Begin Source File + +SOURCE=..\region.c +# End Source File +# Begin Source File + +SOURCE=..\result.c +# End Source File +# Begin Source File + +SOURCE=..\rwlock.c +# End Source File +# Begin Source File + +SOURCE=..\safe.c +# End Source File +# Begin Source File + +SOURCE=..\serial.c +# End Source File +# Begin Source File + +SOURCE=..\sha1.c +# End Source File +# Begin Source File + +SOURCE=..\sha2.c +# End Source File +# Begin Source File + +SOURCE=..\sockaddr.c +# End Source File +# Begin Source File + +SOURCE=..\stats.c +# End Source File +# Begin Source File + +SOURCE=..\string.c +# End Source File +# Begin Source File + +SOURCE=..\symtab.c +# End Source File +# Begin Source File + +SOURCE=..\task.c +# End Source File +# Begin Source File + +SOURCE=..\taskpool.c +# End Source File +# Begin Source File + +SOURCE=..\timer.c +# End Source File +# Begin Source File + +SOURCE=..\tm.c +# End Source File +# End Group +@IF ATOMIC +# Begin Source File + +SOURCE=.\include\atomic.h +# End Source File +@ELSE ATOMIC +# Begin Source File + +SOURCE=..\noatomic\include\atomic.h +# End Source File +@END ATOMIC +# Begin Source File + +SOURCE=.\libisc.def +# End Source File +# End Target +# End Project diff --git a/lib/isc/win32/libisc.dsw b/lib/isc/win32/libisc.dsw new file mode 100644 index 0000000..49c089c --- /dev/null +++ b/lib/isc/win32/libisc.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "libisc"=".\libisc.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/lib/isc/win32/libisc.mak.in b/lib/isc/win32/libisc.mak.in new file mode 100644 index 0000000..9a6acd5 --- /dev/null +++ b/lib/isc/win32/libisc.mak.in @@ -0,0 +1,2398 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on libisc.dsp +!IF "$(CFG)" == "" +CFG=libisc - @PLATFORM@ Debug +!MESSAGE No configuration specified. Defaulting to libisc - @PLATFORM@ Debug. +!ENDIF + +!IF "$(CFG)" != "libisc - @PLATFORM@ Release" && "$(CFG)" != "libisc - @PLATFORM@ Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libisc.mak" CFG="libisc - @PLATFORM@ Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libisc - @PLATFORM@ Release" (based on "@PLATFORM@ (x86) Dynamic-Link Library") +!MESSAGE "libisc - @PLATFORM@ Debug" (based on "@PLATFORM@ (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe +LIBXML=@LIBXML2_LIB@ + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" +_VC_MANIFEST_INC=0 +_VC_MANIFEST_BASENAME=__VC80 +!ELSE +_VC_MANIFEST_INC=1 +_VC_MANIFEST_BASENAME=__VC80.Debug +!ENDIF + +#################################################### +# Specifying name of temporary resource file used only in incremental builds: + +!if "$(_VC_MANIFEST_INC)" == "1" +_VC_MANIFEST_AUTO_RES=$(_VC_MANIFEST_BASENAME).auto.res +!else +_VC_MANIFEST_AUTO_RES= +!endif + +#################################################### +# _VC_MANIFEST_EMBED_EXE - command to embed manifest in EXE: + +!if "$(_VC_MANIFEST_INC)" == "1" + +#MT_SPECIAL_RETURN=1090650113 +#MT_SPECIAL_SWITCH=-notify_resource_update +MT_SPECIAL_RETURN=0 +MT_SPECIAL_SWITCH= +_VC_MANIFEST_EMBED_EXE= \ +if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \ +if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \ +rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \ +link $** /out:$@ $(LFLAGS) + +!else + +_VC_MANIFEST_EMBED_EXE= \ +if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;1 + +!endif + +#################################################### +# _VC_MANIFEST_EMBED_DLL - command to embed manifest in DLL: + +!if "$(_VC_MANIFEST_INC)" == "1" + +#MT_SPECIAL_RETURN=1090650113 +#MT_SPECIAL_SWITCH=-notify_resource_update +MT_SPECIAL_RETURN=0 +MT_SPECIAL_SWITCH= +_VC_MANIFEST_EMBED_EXE= \ +if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \ +if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \ +rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \ +link $** /out:$@ $(LFLAGS) + +!else + +_VC_MANIFEST_EMBED_EXE= \ +if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;2 + +!endif +#################################################### +# _VC_MANIFEST_CLEAN - command to clean resources files generated temporarily: + +!if "$(_VC_MANIFEST_INC)" == "1" + +_VC_MANIFEST_CLEAN=-del $(_VC_MANIFEST_BASENAME).auto.res \ + $(_VC_MANIFEST_BASENAME).auto.rc \ + $(_VC_MANIFEST_BASENAME).auto.manifest + +!else + +_VC_MANIFEST_CLEAN= + +!endif + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "..\..\..\Build\Release\libisc.dll" + + +CLEAN : +@IF AES + -@erase "$(INTDIR)\aes.obj" +@END AES + -@erase "$(INTDIR)\app.obj" + -@erase "$(INTDIR)\assertions.obj" + -@erase "$(INTDIR)\backtrace.obj" + -@erase "$(INTDIR)\backtrace-emptytbl.obj" + -@erase "$(INTDIR)\base32.obj" + -@erase "$(INTDIR)\base64.obj" + -@erase "$(INTDIR)\bind9.obj" + -@erase "$(INTDIR)\buffer.obj" + -@erase "$(INTDIR)\bufferlist.obj" + -@erase "$(INTDIR)\commandline.obj" + -@erase "$(INTDIR)\counter.obj" + -@erase "$(INTDIR)\condition.obj" + -@erase "$(INTDIR)\crc64.obj" + -@erase "$(INTDIR)\dir.obj" + -@erase "$(INTDIR)\DLLMain.obj" + -@erase "$(INTDIR)\entropy.obj" + -@erase "$(INTDIR)\errno.obj" + -@erase "$(INTDIR)\errno2result.obj" + -@erase "$(INTDIR)\error.obj" + -@erase "$(INTDIR)\event.obj" + -@erase "$(INTDIR)\file.obj" + -@erase "$(INTDIR)\fsaccess.obj" + -@erase "$(INTDIR)\hash.obj" + -@erase "$(INTDIR)\heap.obj" + -@erase "$(INTDIR)\hex.obj" + -@erase "$(INTDIR)\hmacmd5.obj" + -@erase "$(INTDIR)\hmacsha.obj" + -@erase "$(INTDIR)\ht.obj" + -@erase "$(INTDIR)\httpd.obj" + -@erase "$(INTDIR)\inet_aton.obj" + -@erase "$(INTDIR)\inet_ntop.obj" + -@erase "$(INTDIR)\inet_pton.obj" + -@erase "$(INTDIR)\interfaceiter.obj" + -@erase "$(INTDIR)\ipv6.obj" + -@erase "$(INTDIR)\iterated_hash.obj" + -@erase "$(INTDIR)\keyboard.obj" + -@erase "$(INTDIR)\lex.obj" + -@erase "$(INTDIR)\lfsr.obj" + -@erase "$(INTDIR)\lib.obj" + -@erase "$(INTDIR)\log.obj" + -@erase "$(INTDIR)\md5.obj" + -@erase "$(INTDIR)\mem.obj" + -@erase "$(INTDIR)\msgcat.obj" + -@erase "$(INTDIR)\mutexblock.obj" + -@erase "$(INTDIR)\meminfo.obj" + -@erase "$(INTDIR)\net.obj" + -@erase "$(INTDIR)\netaddr.obj" + -@erase "$(INTDIR)\netscope.obj" + -@erase "$(INTDIR)\ntpaths.obj" + -@erase "$(INTDIR)\once.obj" + -@erase "$(INTDIR)\ondestroy.obj" + -@erase "$(INTDIR)\os.obj" + -@erase "$(INTDIR)\parseint.obj" +@IF PKCS11 + -@erase "$(INTDIR)\pk11.obj" + -@erase "$(INTDIR)\pk11_api.obj" + -@erase "$(INTDIR)\pk11_result.obj" +@END PKCS11 + -@erase "$(INTDIR)\pool.obj" + -@erase "$(INTDIR)\portset.obj" + -@erase "$(INTDIR)\quota.obj" + -@erase "$(INTDIR)\radix.obj" + -@erase "$(INTDIR)\random.obj" + -@erase "$(INTDIR)\ratelimiter.obj" + -@erase "$(INTDIR)\refcount.obj" + -@erase "$(INTDIR)\regex.obj" + -@erase "$(INTDIR)\region.obj" + -@erase "$(INTDIR)\resource.obj" + -@erase "$(INTDIR)\result.obj" + -@erase "$(INTDIR)\rwlock.obj" + -@erase "$(INTDIR)\safe.obj" + -@erase "$(INTDIR)\serial.obj" + -@erase "$(INTDIR)\sha1.obj" + -@erase "$(INTDIR)\sha2.obj" + -@erase "$(INTDIR)\sockaddr.obj" + -@erase "$(INTDIR)\socket.obj" + -@erase "$(INTDIR)\stats.obj" + -@erase "$(INTDIR)\stdio.obj" + -@erase "$(INTDIR)\stdtime.obj" + -@erase "$(INTDIR)\strerror.obj" + -@erase "$(INTDIR)\string.obj" + -@erase "$(INTDIR)\symtab.obj" + -@erase "$(INTDIR)\syslog.obj" + -@erase "$(INTDIR)\task.obj" + -@erase "$(INTDIR)\taskpool.obj" + -@erase "$(INTDIR)\thread.obj" + -@erase "$(INTDIR)\time.obj" + -@erase "$(INTDIR)\timer.obj" + -@erase "$(INTDIR)\tm.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\version.obj" + -@erase "$(INTDIR)\win32os.obj" + -@erase "$(OUTDIR)\libisc.exp" + -@erase "$(OUTDIR)\libisc.lib" + -@erase "..\..\..\Build\Release\libisc.dll" + -@$(_VC_MANIFEST_CLEAN) + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +@IF PKCS11 +CPP_PROJ=/nologo /MD /W3 @COPTX@ @COPTI@ /O2 /I "./" /I "../../../" /I "include" /I "../include" /I "win32" /I "../../isccfg/include" /I "../../dns/win32/include" /I "../../dns/include" @LIBXML2_INC@ @OPENSSL_INC@ @ZLIB_INC@ /D "BIND9" @CRYPTO@ @PK11_LIB_LOCATION@ /D "WIN32" /D "NDEBUG" /D "__STDC__" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" /Fp"$(INTDIR)\libisc.pch" @COPTY@ /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +@ELSE PKCS11 +CPP_PROJ=/nologo /MD /W3 @COPTX@ @COPTI@ /O2 /I "./" /I "../../../" /I "include" /I "../include" /I "win32" /I "../../isccfg/include" @LIBXML2_INC@ @OPENSSL_INC@ @ZLIB_INC@ /D "BIND9" /D "WIN32" /D "NDEBUG" /D "__STDC__" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" /Fp"$(INTDIR)\libisc.pch" @COPTY@ /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +@END PKCS11 +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\libisc.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=user32.lib advapi32.lib ws2_32.lib $(LIBXML) @OPENSSL_LIB@ @ZLIB_LIB@ /nologo /dll /incremental:no /pdb:"$(OUTDIR)\libisc.pdb" @MACHINE@ /def:".\libisc.def" /out:"../../../Build/Release/libisc.dll" /implib:"$(OUTDIR)\libisc.lib" +DEF_FILE= \ + ".\libisc.def" +LINK32_OBJS= \ + "$(INTDIR)\app.obj" \ + "$(INTDIR)\condition.obj" \ + "$(INTDIR)\dir.obj" \ + "$(INTDIR)\DLLMain.obj" \ + "$(INTDIR)\entropy.obj" \ + "$(INTDIR)\errno.obj" \ + "$(INTDIR)\errno2result.obj" \ + "$(INTDIR)\file.obj" \ + "$(INTDIR)\fsaccess.obj" \ + "$(INTDIR)\interfaceiter.obj" \ + "$(INTDIR)\ipv6.obj" \ + "$(INTDIR)\iterated_hash.obj" \ + "$(INTDIR)\keyboard.obj" \ + "$(INTDIR)\meminfo.obj" \ + "$(INTDIR)\net.obj" \ + "$(INTDIR)\ntpaths.obj" \ + "$(INTDIR)\once.obj" \ + "$(INTDIR)\os.obj" \ +@IF PKCS11 + "$(INTDIR)\pk11_api.obj" \ +@END PKCS11 + "$(INTDIR)\resource.obj" \ + "$(INTDIR)\socket.obj" \ + "$(INTDIR)\stdio.obj" \ + "$(INTDIR)\stdtime.obj" \ + "$(INTDIR)\strerror.obj" \ + "$(INTDIR)\syslog.obj" \ + "$(INTDIR)\thread.obj" \ + "$(INTDIR)\time.obj" \ + "$(INTDIR)\version.obj" \ + "$(INTDIR)\win32os.obj" \ +@IF AES + "$(INTDIR)\aes.obj" \ +@END AES + "$(INTDIR)\assertions.obj" \ + "$(INTDIR)\backtrace.obj" \ + "$(INTDIR)\backtrace-emptytbl.obj" \ + "$(INTDIR)\base32.obj" \ + "$(INTDIR)\base64.obj" \ + "$(INTDIR)\bind9.obj" \ + "$(INTDIR)\buffer.obj" \ + "$(INTDIR)\bufferlist.obj" \ + "$(INTDIR)\commandline.obj" \ + "$(INTDIR)\counter.obj" \ + "$(INTDIR)\crc64.obj" \ + "$(INTDIR)\error.obj" \ + "$(INTDIR)\event.obj" \ + "$(INTDIR)\hash.obj" \ + "$(INTDIR)\heap.obj" \ + "$(INTDIR)\hex.obj" \ + "$(INTDIR)\hmacmd5.obj" \ + "$(INTDIR)\hmacsha.obj" \ + "$(INTDIR)\ht.obj" \ + "$(INTDIR)\httpd.obj" \ + "$(INTDIR)\inet_aton.obj" \ + "$(INTDIR)\inet_ntop.obj" \ + "$(INTDIR)\inet_pton.obj" \ + "$(INTDIR)\lex.obj" \ + "$(INTDIR)\lfsr.obj" \ + "$(INTDIR)\lib.obj" \ + "$(INTDIR)\log.obj" \ + "$(INTDIR)\md5.obj" \ + "$(INTDIR)\mem.obj" \ + "$(INTDIR)\msgcat.obj" \ + "$(INTDIR)\mutexblock.obj" \ + "$(INTDIR)\netaddr.obj" \ + "$(INTDIR)\netscope.obj" \ + "$(INTDIR)\ondestroy.obj" \ +@IF PKCS11 + "$(INTDIR)\pk11.obj" \ + "$(INTDIR)\pk11_result.obj" \ +@END PKCS11 + "$(INTDIR)\quota.obj" \ + "$(INTDIR)\radix.obj" \ + "$(INTDIR)\random.obj" \ + "$(INTDIR)\ratelimiter.obj" \ + "$(INTDIR)\refcount.obj" \ + "$(INTDIR)\result.obj" \ + "$(INTDIR)\rwlock.obj" \ + "$(INTDIR)\safe.obj" \ + "$(INTDIR)\serial.obj" \ + "$(INTDIR)\sha1.obj" \ + "$(INTDIR)\sha2.obj" \ + "$(INTDIR)\sockaddr.obj" \ + "$(INTDIR)\stats.obj" \ + "$(INTDIR)\string.obj" \ + "$(INTDIR)\symtab.obj" \ + "$(INTDIR)\task.obj" \ + "$(INTDIR)\taskpool.obj" \ + "$(INTDIR)\timer.obj" \ + "$(INTDIR)\tm.obj" \ + "$(INTDIR)\parseint.obj" \ + "$(INTDIR)\pool.obj" \ + "$(INTDIR)\portset.obj" \ + "$(INTDIR)\regex.obj" \ + "$(INTDIR)\region.obj" + +"..\..\..\Build\Release\libisc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + $(_VC_MANIFEST_EMBED_DLL) + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +ALL : "..\..\..\Build\Debug\libisc.dll" "$(OUTDIR)\libisc.bsc" + + +CLEAN : +@IF AES + -@erase "$(INTDIR)\aes.obj" + -@erase "$(INTDIR)\aes.sbr" +@END AES + -@erase "$(INTDIR)\app.obj" + -@erase "$(INTDIR)\app.sbr" + -@erase "$(INTDIR)\assertions.obj" + -@erase "$(INTDIR)\assertions.sbr" + -@erase "$(INTDIR)\backtrace.obj" + -@erase "$(INTDIR)\backtrace-emptytbl.obj" + -@erase "$(INTDIR)\backtrace.sbr" + -@erase "$(INTDIR)\backtrace-emptytbl.sbr" + -@erase "$(INTDIR)\base32.obj" + -@erase "$(INTDIR)\base32.sbr" + -@erase "$(INTDIR)\base64.obj" + -@erase "$(INTDIR)\base64.sbr" + -@erase "$(INTDIR)\bind9.obj" + -@erase "$(INTDIR)\bind9.sbr" + -@erase "$(INTDIR)\buffer.obj" + -@erase "$(INTDIR)\buffer.sbr" + -@erase "$(INTDIR)\bufferlist.obj" + -@erase "$(INTDIR)\bufferlist.sbr" + -@erase "$(INTDIR)\commandline.obj" + -@erase "$(INTDIR)\commandline.sbr" + -@erase "$(INTDIR)\counter.obj" + -@erase "$(INTDIR)\counter.sbr" + -@erase "$(INTDIR)\condition.obj" + -@erase "$(INTDIR)\condition.sbr" + -@erase "$(INTDIR)\crc64.obj" + -@erase "$(INTDIR)\crc64.sbr" + -@erase "$(INTDIR)\dir.obj" + -@erase "$(INTDIR)\dir.sbr" + -@erase "$(INTDIR)\DLLMain.obj" + -@erase "$(INTDIR)\DLLMain.sbr" + -@erase "$(INTDIR)\entropy.obj" + -@erase "$(INTDIR)\entropy.sbr" + -@erase "$(INTDIR)\errno.obj" + -@erase "$(INTDIR)\errno.sbr" + -@erase "$(INTDIR)\errno2result.obj" + -@erase "$(INTDIR)\errno2result.sbr" + -@erase "$(INTDIR)\error.obj" + -@erase "$(INTDIR)\error.sbr" + -@erase "$(INTDIR)\event.obj" + -@erase "$(INTDIR)\event.sbr" + -@erase "$(INTDIR)\file.obj" + -@erase "$(INTDIR)\file.sbr" + -@erase "$(INTDIR)\fsaccess.obj" + -@erase "$(INTDIR)\fsaccess.sbr" + -@erase "$(INTDIR)\hash.obj" + -@erase "$(INTDIR)\hash.sbr" + -@erase "$(INTDIR)\heap.obj" + -@erase "$(INTDIR)\heap.sbr" + -@erase "$(INTDIR)\hex.obj" + -@erase "$(INTDIR)\hex.sbr" + -@erase "$(INTDIR)\hmacmd5.obj" + -@erase "$(INTDIR)\hmacmd5.sbr" + -@erase "$(INTDIR)\hmacsha.obj" + -@erase "$(INTDIR)\hmacsha.sbr" + -@erase "$(INTDIR)\ht.obj" + -@erase "$(INTDIR)\ht.sbr" + -@erase "$(INTDIR)\httpd.obj" + -@erase "$(INTDIR)\httpd.sbr" + -@erase "$(INTDIR)\inet_aton.obj" + -@erase "$(INTDIR)\inet_aton.sbr" + -@erase "$(INTDIR)\inet_ntop.obj" + -@erase "$(INTDIR)\inet_ntop.sbr" + -@erase "$(INTDIR)\inet_pton.obj" + -@erase "$(INTDIR)\inet_pton.sbr" + -@erase "$(INTDIR)\interfaceiter.obj" + -@erase "$(INTDIR)\interfaceiter.sbr" + -@erase "$(INTDIR)\ipv6.obj" + -@erase "$(INTDIR)\ipv6.sbr" + -@erase "$(INTDIR)\iterated_hash.obj" + -@erase "$(INTDIR)\iterated_hash.sbr" + -@erase "$(INTDIR)\keyboard.obj" + -@erase "$(INTDIR)\keyboard.sbr" + -@erase "$(INTDIR)\lex.obj" + -@erase "$(INTDIR)\lex.sbr" + -@erase "$(INTDIR)\lfsr.obj" + -@erase "$(INTDIR)\lfsr.sbr" + -@erase "$(INTDIR)\lib.obj" + -@erase "$(INTDIR)\lib.sbr" + -@erase "$(INTDIR)\log.obj" + -@erase "$(INTDIR)\log.sbr" + -@erase "$(INTDIR)\md5.obj" + -@erase "$(INTDIR)\md5.sbr" + -@erase "$(INTDIR)\mem.obj" + -@erase "$(INTDIR)\mem.sbr" + -@erase "$(INTDIR)\msgcat.obj" + -@erase "$(INTDIR)\msgcat.sbr" + -@erase "$(INTDIR)\mutexblock.obj" + -@erase "$(INTDIR)\mutexblock.sbr" + -@erase "$(INTDIR)\meminfo.obj" + -@erase "$(INTDIR)\meminfo.sbr" + -@erase "$(INTDIR)\net.obj" + -@erase "$(INTDIR)\net.sbr" + -@erase "$(INTDIR)\netaddr.obj" + -@erase "$(INTDIR)\netaddr.sbr" + -@erase "$(INTDIR)\netscope.obj" + -@erase "$(INTDIR)\netscope.sbr" + -@erase "$(INTDIR)\ntpaths.obj" + -@erase "$(INTDIR)\ntpaths.sbr" + -@erase "$(INTDIR)\once.obj" + -@erase "$(INTDIR)\once.sbr" + -@erase "$(INTDIR)\ondestroy.obj" + -@erase "$(INTDIR)\ondestroy.sbr" + -@erase "$(INTDIR)\os.obj" + -@erase "$(INTDIR)\os.sbr" + -@erase "$(INTDIR)\parseint.obj" + -@erase "$(INTDIR)\parseint.sbr" +@IF PKCS11 + -@erase "$(INTDIR)\pk11.obj" + -@erase "$(INTDIR)\pk11_api.obj" + -@erase "$(INTDIR)\pk11_result.obj" +@END PKCS11 + -@erase "$(INTDIR)\pool.obj" + -@erase "$(INTDIR)\pool.sbr" + -@erase "$(INTDIR)\portset.obj" + -@erase "$(INTDIR)\portset.sbr" + -@erase "$(INTDIR)\quota.obj" + -@erase "$(INTDIR)\quota.sbr" + -@erase "$(INTDIR)\radix.obj" + -@erase "$(INTDIR)\radix.sbr" + -@erase "$(INTDIR)\random.obj" + -@erase "$(INTDIR)\random.sbr" + -@erase "$(INTDIR)\ratelimiter.obj" + -@erase "$(INTDIR)\ratelimiter.sbr" + -@erase "$(INTDIR)\refcount.obj" + -@erase "$(INTDIR)\refcount.sbr" + -@erase "$(INTDIR)\regex.obj" + -@erase "$(INTDIR)\regex.sbr" + -@erase "$(INTDIR)\region.obj" + -@erase "$(INTDIR)\region.sbr" + -@erase "$(INTDIR)\resource.obj" + -@erase "$(INTDIR)\resource.sbr" + -@erase "$(INTDIR)\result.obj" + -@erase "$(INTDIR)\result.sbr" + -@erase "$(INTDIR)\rwlock.obj" + -@erase "$(INTDIR)\rwlock.sbr" + -@erase "$(INTDIR)\safe.obj" + -@erase "$(INTDIR)\safe.sbr" + -@erase "$(INTDIR)\serial.obj" + -@erase "$(INTDIR)\serial.sbr" + -@erase "$(INTDIR)\sha1.obj" + -@erase "$(INTDIR)\sha1.sbr" + -@erase "$(INTDIR)\sha2.obj" + -@erase "$(INTDIR)\sha2.sbr" + -@erase "$(INTDIR)\sockaddr.obj" + -@erase "$(INTDIR)\sockaddr.sbr" + -@erase "$(INTDIR)\socket.obj" + -@erase "$(INTDIR)\socket.sbr" + -@erase "$(INTDIR)\stats.obj" + -@erase "$(INTDIR)\stats.sbr" + -@erase "$(INTDIR)\stdio.obj" + -@erase "$(INTDIR)\stdio.sbr" + -@erase "$(INTDIR)\stdtime.obj" + -@erase "$(INTDIR)\stdtime.sbr" + -@erase "$(INTDIR)\strerror.obj" + -@erase "$(INTDIR)\strerror.sbr" + -@erase "$(INTDIR)\string.obj" + -@erase "$(INTDIR)\string.sbr" + -@erase "$(INTDIR)\symtab.obj" + -@erase "$(INTDIR)\symtab.sbr" + -@erase "$(INTDIR)\syslog.obj" + -@erase "$(INTDIR)\syslog.sbr" + -@erase "$(INTDIR)\task.obj" + -@erase "$(INTDIR)\task.sbr" + -@erase "$(INTDIR)\taskpool.obj" + -@erase "$(INTDIR)\taskpool.sbr" + -@erase "$(INTDIR)\thread.obj" + -@erase "$(INTDIR)\thread.sbr" + -@erase "$(INTDIR)\time.obj" + -@erase "$(INTDIR)\time.sbr" + -@erase "$(INTDIR)\timer.obj" + -@erase "$(INTDIR)\timer.sbr" + -@erase "$(INTDIR)\tm.obj" + -@erase "$(INTDIR)\tm.sbr" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(INTDIR)\version.obj" + -@erase "$(INTDIR)\version.sbr" + -@erase "$(INTDIR)\win32os.obj" + -@erase "$(INTDIR)\win32os.sbr" + -@erase "$(OUTDIR)\libisc.bsc" + -@erase "$(OUTDIR)\libisc.exp" + -@erase "$(OUTDIR)\libisc.lib" + -@erase "$(OUTDIR)\libisc.map" + -@erase "$(OUTDIR)\libisc.pdb" + -@erase "..\..\..\Build\Debug\libisc.dll" + -@erase "..\..\..\Build\Debug\libisc.ilk" + -@$(_VC_MANIFEST_CLEAN) + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +@IF PKCS11 +CPP_PROJ=/nologo /MDd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /I "./" /I "../../../" /I "include" /I "../include" /I "win32" /I "../../isccfg/include" /I "../../dns/win32/include" /I "../../dns/include" @LIBXML2_INC@ @OPENSSL_INC@ @ZLIB_INC@ /D "BIND9" @CRYPTO@ @PK11_LIB_LOCATION@ /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libisc.pch" @COPTY@ /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c +@ELSE PKCS11 +CPP_PROJ=/nologo /MDd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /I "./" /I "../../../" /I "include" /I "../include" /I "win32" /I "../../isccfg/include" @LIBXML2_INC@ @OPENSSL_INC@ @ZLIB_INC@ /D "BIND9" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "__STDC__" /D "_MBCS" /D "_USRDLL" /D "LIBISC_EXPORTS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\libisc.pch" @COPTY@ /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c +@END PKCS11 +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\libisc.bsc" +BSC32_SBRS= \ + "$(INTDIR)\app.sbr" \ + "$(INTDIR)\condition.sbr" \ + "$(INTDIR)\dir.sbr" \ + "$(INTDIR)\DLLMain.sbr" \ + "$(INTDIR)\entropy.sbr" \ + "$(INTDIR)\errno.sbr" \ + "$(INTDIR)\errno2result.sbr" \ + "$(INTDIR)\file.sbr" \ + "$(INTDIR)\fsaccess.sbr" \ + "$(INTDIR)\interfaceiter.sbr" \ + "$(INTDIR)\ipv6.sbr" \ + "$(INTDIR)\iterated_hash.sbr" \ + "$(INTDIR)\keyboard.sbr" \ + "$(INTDIR)\meminfo.sbr" \ + "$(INTDIR)\net.sbr" \ + "$(INTDIR)\ntpaths.sbr" \ + "$(INTDIR)\once.sbr" \ + "$(INTDIR)\os.sbr" \ +@IF PKCS11 + "$(INTDIR)\pk11_api.sbr" \ +@END PKCS11 + "$(INTDIR)\resource.sbr" \ + "$(INTDIR)\socket.sbr" \ + "$(INTDIR)\stdio.sbr" \ + "$(INTDIR)\stdtime.sbr" \ + "$(INTDIR)\strerror.sbr" \ + "$(INTDIR)\syslog.sbr" \ + "$(INTDIR)\thread.sbr" \ + "$(INTDIR)\time.sbr" \ + "$(INTDIR)\version.sbr" \ + "$(INTDIR)\win32os.sbr" \ +@IF AES + "$(INTDIR)\aes.sbr" \ +@END AES + "$(INTDIR)\assertions.sbr" \ + "$(INTDIR)\backtrace.sbr" \ + "$(INTDIR)\backtrace-emptytbl.sbr" \ + "$(INTDIR)\base32.sbr" \ + "$(INTDIR)\base64.sbr" \ + "$(INTDIR)\bind9.sbr" \ + "$(INTDIR)\buffer.sbr" \ + "$(INTDIR)\bufferlist.sbr" \ + "$(INTDIR)\commandline.sbr" \ + "$(INTDIR)\counter.sbr" \ + "$(INTDIR)\crc64.sbr" \ + "$(INTDIR)\error.sbr" \ + "$(INTDIR)\event.sbr" \ + "$(INTDIR)\hash.sbr" \ + "$(INTDIR)\heap.sbr" \ + "$(INTDIR)\hex.sbr" \ + "$(INTDIR)\hmacmd5.sbr" \ + "$(INTDIR)\hmacsha.sbr" \ + "$(INTDIR)\ht.sbr" \ + "$(INTDIR)\httpd.sbr" \ + "$(INTDIR)\inet_aton.sbr" \ + "$(INTDIR)\inet_ntop.sbr" \ + "$(INTDIR)\inet_pton.sbr" \ + "$(INTDIR)\lex.sbr" \ + "$(INTDIR)\lfsr.sbr" \ + "$(INTDIR)\lib.sbr" \ + "$(INTDIR)\log.sbr" \ + "$(INTDIR)\md5.sbr" \ + "$(INTDIR)\mem.sbr" \ + "$(INTDIR)\msgcat.sbr" \ + "$(INTDIR)\mutexblock.sbr" \ + "$(INTDIR)\netaddr.sbr" \ + "$(INTDIR)\netscope.sbr" \ + "$(INTDIR)\ondestroy.sbr" \ +@IF PKCS11 + "$(INTDIR)\pk11.sbr" \ + "$(INTDIR)\pk11_result.sbr" \ +@END PKCS11 + "$(INTDIR)\quota.sbr" \ + "$(INTDIR)\radix.sbr" \ + "$(INTDIR)\random.sbr" \ + "$(INTDIR)\ratelimiter.sbr" \ + "$(INTDIR)\refcount.sbr" \ + "$(INTDIR)\result.sbr" \ + "$(INTDIR)\rwlock.sbr" \ + "$(INTDIR)\safe.sbr" \ + "$(INTDIR)\serial.sbr" \ + "$(INTDIR)\sha1.sbr" \ + "$(INTDIR)\sha2.sbr" \ + "$(INTDIR)\sockaddr.sbr" \ + "$(INTDIR)\stats.sbr" \ + "$(INTDIR)\string.sbr" \ + "$(INTDIR)\symtab.sbr" \ + "$(INTDIR)\task.sbr" \ + "$(INTDIR)\taskpool.sbr" \ + "$(INTDIR)\timer.sbr" \ + "$(INTDIR)\tm.sbr" \ + "$(INTDIR)\parseint.sbr" \ + "$(INTDIR)\pool.sbr" \ + "$(INTDIR)\portset.sbr" \ + "$(INTDIR)\regex.sbr" \ + "$(INTDIR)\region.sbr" + +"$(OUTDIR)\libisc.bsc" : "$(OUTDIR)" $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +LINK32_FLAGS=user32.lib advapi32.lib ws2_32.lib $(LIBXML) @OPENSSL_LIB@ @ZLIB_LIB@ /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\libisc.pdb" /map:"$(INTDIR)\libisc.map" /debug @MACHINE@ /def:".\libisc.def" /out:"../../../Build/Debug/libisc.dll" /implib:"$(OUTDIR)\libisc.lib" /pdbtype:sept +DEF_FILE= \ + ".\libisc.def" +LINK32_OBJS= \ + "$(INTDIR)\app.obj" \ + "$(INTDIR)\condition.obj" \ + "$(INTDIR)\dir.obj" \ + "$(INTDIR)\DLLMain.obj" \ + "$(INTDIR)\entropy.obj" \ + "$(INTDIR)\errno.obj" \ + "$(INTDIR)\errno2result.obj" \ + "$(INTDIR)\file.obj" \ + "$(INTDIR)\fsaccess.obj" \ + "$(INTDIR)\interfaceiter.obj" \ + "$(INTDIR)\ipv6.obj" \ + "$(INTDIR)\iterated_hash.obj" \ + "$(INTDIR)\keyboard.obj" \ + "$(INTDIR)\meminfo.obj" \ + "$(INTDIR)\net.obj" \ + "$(INTDIR)\ntpaths.obj" \ + "$(INTDIR)\once.obj" \ + "$(INTDIR)\os.obj" \ +@IF PKCS11 + "$(INTDIR)\pk11_api.obj" \ +@END PKCS11 + "$(INTDIR)\resource.obj" \ + "$(INTDIR)\socket.obj" \ + "$(INTDIR)\stdio.obj" \ + "$(INTDIR)\stdtime.obj" \ + "$(INTDIR)\strerror.obj" \ + "$(INTDIR)\syslog.obj" \ + "$(INTDIR)\thread.obj" \ + "$(INTDIR)\time.obj" \ + "$(INTDIR)\version.obj" \ + "$(INTDIR)\win32os.obj" \ +@IF AES + "$(INTDIR)\aes.obj" \ +@END AES + "$(INTDIR)\assertions.obj" \ + "$(INTDIR)\backtrace.obj" \ + "$(INTDIR)\backtrace-emptytbl.obj" \ + "$(INTDIR)\base32.obj" \ + "$(INTDIR)\base64.obj" \ + "$(INTDIR)\bind9.obj" \ + "$(INTDIR)\buffer.obj" \ + "$(INTDIR)\bufferlist.obj" \ + "$(INTDIR)\commandline.obj" \ + "$(INTDIR)\counter.obj" \ + "$(INTDIR)\crc64.obj" \ + "$(INTDIR)\error.obj" \ + "$(INTDIR)\event.obj" \ + "$(INTDIR)\hash.obj" \ + "$(INTDIR)\heap.obj" \ + "$(INTDIR)\hex.obj" \ + "$(INTDIR)\hmacmd5.obj" \ + "$(INTDIR)\hmacsha.obj" \ + "$(INTDIR)\ht.obj" \ + "$(INTDIR)\httpd.obj" \ + "$(INTDIR)\inet_aton.obj" \ + "$(INTDIR)\inet_ntop.obj" \ + "$(INTDIR)\inet_pton.obj" \ + "$(INTDIR)\lex.obj" \ + "$(INTDIR)\lfsr.obj" \ + "$(INTDIR)\lib.obj" \ + "$(INTDIR)\log.obj" \ + "$(INTDIR)\md5.obj" \ + "$(INTDIR)\mem.obj" \ + "$(INTDIR)\msgcat.obj" \ + "$(INTDIR)\mutexblock.obj" \ + "$(INTDIR)\netaddr.obj" \ + "$(INTDIR)\netscope.obj" \ + "$(INTDIR)\ondestroy.obj" \ +@IF PKCS11 + "$(INTDIR)\pk11.obj" \ + "$(INTDIR)\pk11_result.obj" \ +@END PKCS11 + "$(INTDIR)\quota.obj" \ + "$(INTDIR)\radix.obj" \ + "$(INTDIR)\random.obj" \ + "$(INTDIR)\ratelimiter.obj" \ + "$(INTDIR)\refcount.obj" \ + "$(INTDIR)\result.obj" \ + "$(INTDIR)\rwlock.obj" \ + "$(INTDIR)\safe.obj" \ + "$(INTDIR)\serial.obj" \ + "$(INTDIR)\sha1.obj" \ + "$(INTDIR)\sha2.obj" \ + "$(INTDIR)\sockaddr.obj" \ + "$(INTDIR)\stats.obj" \ + "$(INTDIR)\string.obj" \ + "$(INTDIR)\symtab.obj" \ + "$(INTDIR)\task.obj" \ + "$(INTDIR)\taskpool.obj" \ + "$(INTDIR)\timer.obj" \ + "$(INTDIR)\tm.obj" \ + "$(INTDIR)\parseint.obj" \ + "$(INTDIR)\pool.obj" \ + "$(INTDIR)\portset.obj" \ + "$(INTDIR)\regex.obj" \ + "$(INTDIR)\region.obj" + +"..\..\..\Build\Debug\libisc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + $(_VC_MANIFEST_EMBED_DLL) + +!ENDIF + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("libisc.dep") +!INCLUDE "libisc.dep" +!ELSE +!MESSAGE Warning: cannot find "libisc.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" || "$(CFG)" == "libisc - @PLATFORM@ Debug" +SOURCE=.\app.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\app.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\app.obj" "$(INTDIR)\app.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\condition.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\condition.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\condition.obj" "$(INTDIR)\condition.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\dir.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\dir.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\dir.obj" "$(INTDIR)\dir.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\DLLMain.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\DLLMain.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\DLLMain.obj" "$(INTDIR)\DLLMain.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\entropy.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\entropy.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\entropy.obj" "$(INTDIR)\entropy.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\errno.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\errno.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\errno.obj" "$(INTDIR)\errno.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + + +SOURCE=.\errno2result.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\errno2result.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\errno2result.obj" "$(INTDIR)\errno2result.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\file.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\file.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\file.obj" "$(INTDIR)\file.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\fsaccess.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\fsaccess.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\fsaccess.obj" "$(INTDIR)\fsaccess.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\interfaceiter.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\interfaceiter.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\interfaceiter.obj" "$(INTDIR)\interfaceiter.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\ipv6.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\ipv6.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\ipv6.obj" "$(INTDIR)\ipv6.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + + +SOURCE=.\keyboard.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\keyboard.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\keyboard.obj" "$(INTDIR)\keyboard.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\meminfo.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\meminfo.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\meminfo.obj" "$(INTDIR)\meminfo.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\net.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\net.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\net.obj" "$(INTDIR)\net.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\ntpaths.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\ntpaths.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\ntpaths.obj" "$(INTDIR)\ntpaths.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\once.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\once.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\once.obj" "$(INTDIR)\once.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\os.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\os.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\os.obj" "$(INTDIR)\os.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\pk11_api.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\pk11_api.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\pk11_api.obj" "$(INTDIR)\pk11_api.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=.\resource.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\resource.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\resource.obj" "$(INTDIR)\resource.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\socket.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\socket.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\socket.obj" "$(INTDIR)\socket.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\stdio.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\stdio.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\stdio.obj" "$(INTDIR)\stdio.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\stdtime.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\stdtime.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\stdtime.obj" "$(INTDIR)\stdtime.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\strerror.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\strerror.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\strerror.obj" "$(INTDIR)\strerror.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\syslog.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\syslog.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\syslog.obj" "$(INTDIR)\syslog.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\thread.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\thread.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\thread.obj" "$(INTDIR)\thread.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\time.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\time.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\time.obj" "$(INTDIR)\time.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\version.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\version.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\version.obj" "$(INTDIR)\version.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +SOURCE=.\win32os.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\win32os.obj" : $(SOURCE) "$(INTDIR)" + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\win32os.obj" "$(INTDIR)\win32os.sbr" : $(SOURCE) "$(INTDIR)" + + +!ENDIF + +@IF AES +SOURCE=..\aes.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\aes.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\aes.obj" "$(INTDIR)\aes.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF +@END AES + +SOURCE=..\assertions.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\assertions.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\assertions.obj" "$(INTDIR)\assertions.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\backtrace.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\backtrace.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\backtrace.obj" "$(INTDIR)\backtrace.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\backtrace-emptytbl.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\backtrace-emptytbl.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\backtrace-emptytbl.obj" "$(INTDIR)\backtrace-emptytbl.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\base32.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\base32.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\base32.obj" "$(INTDIR)\base32.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\base64.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\base64.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\base64.obj" "$(INTDIR)\base64.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\bind9.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\bind9.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\bind9.obj" "$(INTDIR)\bind9.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\buffer.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\buffer.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\buffer.obj" "$(INTDIR)\buffer.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\bufferlist.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\bufferlist.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\bufferlist.obj" "$(INTDIR)\bufferlist.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\commandline.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\commandline.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\commandline.obj" "$(INTDIR)\commandline.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\counter.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\counter.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\counter.obj" "$(INTDIR)\counter.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\crc64.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\crc64.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\crc64.obj" "$(INTDIR)\crc64.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\error.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\error.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\error.obj" "$(INTDIR)\error.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\event.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\event.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\event.obj" "$(INTDIR)\event.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\hash.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\hash.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\hash.obj" "$(INTDIR)\hash.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\heap.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\heap.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\heap.obj" "$(INTDIR)\heap.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\hex.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\hex.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\hex.obj" "$(INTDIR)\hex.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\hmacmd5.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\hmacmd5.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\hmacmd5.obj" "$(INTDIR)\hmacmd5.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\hmacsha.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\hmacsha.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\hmacsha.obj" "$(INTDIR)\hmacsha.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\ht.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\ht.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\ht.obj" "$(INTDIR)\ht.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\httpd.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\httpd.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\httpd.obj" "$(INTDIR)\httpd.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\inet_aton.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\inet_aton.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\inet_aton.obj" "$(INTDIR)\inet_aton.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\inet_ntop.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\inet_ntop.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\inet_ntop.obj" "$(INTDIR)\inet_ntop.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\inet_pton.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\inet_pton.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\inet_pton.obj" "$(INTDIR)\inet_pton.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\iterated_hash.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\iterated_hash.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\iterated_hash.obj" "$(INTDIR)\iterated_hash.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\lex.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\lex.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\lex.obj" "$(INTDIR)\lex.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\lfsr.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\lfsr.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\lfsr.obj" "$(INTDIR)\lfsr.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\lib.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\lib.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\lib.obj" "$(INTDIR)\lib.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\log.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\log.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\log.obj" "$(INTDIR)\log.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\md5.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\md5.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\md5.obj" "$(INTDIR)\md5.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\mem.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\mem.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\mem.obj" "$(INTDIR)\mem.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\nls\msgcat.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\msgcat.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\msgcat.obj" "$(INTDIR)\msgcat.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\mutexblock.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\mutexblock.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\mutexblock.obj" "$(INTDIR)\mutexblock.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\netaddr.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\netaddr.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\netaddr.obj" "$(INTDIR)\netaddr.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\netscope.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\netscope.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\netscope.obj" "$(INTDIR)\netscope.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\ondestroy.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\ondestroy.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\ondestroy.obj" "$(INTDIR)\ondestroy.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\parseint.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\parseint.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\parseint.obj" "$(INTDIR)\parseint.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\pk11.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\pk11.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\pk11.obj" "$(INTDIR)\pk11.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\pk11_result.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\pk11_result.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\pk11_result.obj" "$(INTDIR)\pk11_result.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\pool.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\pool.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\pool.obj" "$(INTDIR)\pool.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\portset.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\portset.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\portset.obj" "$(INTDIR)\portset.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\quota.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\quota.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\quota.obj" "$(INTDIR)\quota.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\radix.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\radix.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\radix.obj" "$(INTDIR)\radix.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\random.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\random.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\random.obj" "$(INTDIR)\random.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\ratelimiter.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\ratelimiter.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\ratelimiter.obj" "$(INTDIR)\ratelimiter.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\refcount.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\refcount.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\refcount.obj" "$(INTDIR)\refcount.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\regex.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\regex.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\regex.obj" "$(INTDIR)\regex.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + + +SOURCE=..\region.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\region.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\region.obj" "$(INTDIR)\region.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\result.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\result.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\result.obj" "$(INTDIR)\result.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\rwlock.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\rwlock.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\rwlock.obj" "$(INTDIR)\rwlock.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\safe.c + +!IF "$(CFG)" == "libisc - Win32 Release" + + +"$(INTDIR)\safe.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - Win32 Debug" + + +"$(INTDIR)\safe.obj" "$(INTDIR)\safe.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\serial.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\serial.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\serial.obj" "$(INTDIR)\serial.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\sha1.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\sha1.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\sha1.obj" "$(INTDIR)\sha1.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\sha2.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\sha2.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\sha2.obj" "$(INTDIR)\sha2.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\sockaddr.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\sockaddr.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\sockaddr.obj" "$(INTDIR)\sockaddr.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\stats.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\stats.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\stats.obj" "$(INTDIR)\stats.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\string.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\string.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\string.obj" "$(INTDIR)\string.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\symtab.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\symtab.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\symtab.obj" "$(INTDIR)\symtab.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\task.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\task.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\task.obj" "$(INTDIR)\task.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\taskpool.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\taskpool.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\taskpool.obj" "$(INTDIR)\taskpool.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\timer.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\timer.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\timer.obj" "$(INTDIR)\timer.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + +SOURCE=..\tm.c + +!IF "$(CFG)" == "libisc - @PLATFORM@ Release" + + +"$(INTDIR)\tm.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ELSEIF "$(CFG)" == "libisc - @PLATFORM@ Debug" + + +"$(INTDIR)\tm.obj" "$(INTDIR)\tm.sbr" : $(SOURCE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +!ENDIF + + +!ENDIF + +#################################################### +# Commands to generate initial empty manifest file and the RC file +# that references it, and for generating the .res file: + +$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_MANIFEST_BASENAME).auto.rc + +$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_MANIFEST_BASENAME).auto.manifest + type <<$@ +#include <winuser.h> +1RT_MANIFEST"$(_VC_MANIFEST_BASENAME).auto.manifest" +<< KEEP + +$(_VC_MANIFEST_BASENAME).auto.manifest : + type <<$@ +<?xml version='1.0' encoding='UTF-8' standalone='yes'?> +<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> +</assembly> +<< KEEP diff --git a/lib/isc/win32/libisc.vcxproj.filters.in b/lib/isc/win32/libisc.vcxproj.filters.in new file mode 100644 index 0000000..d6a5234 --- /dev/null +++ b/lib/isc/win32/libisc.vcxproj.filters.in @@ -0,0 +1,675 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Library Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Win32 Source Files"> + <UniqueIdentifier>{289562c2-1bdd-4582-b6bd-3f598ee23cbd}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Win32 Header Files"> + <UniqueIdentifier>{d03c3e6a-e78e-4a01-bd77-64c839b1adfe}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Library Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <None Include="libisc.def" /> + </ItemGroup> + <ItemGroup> +@IF AES + <ClInclude Include="..\include\isc\aes.h"> + <Filter>Library Header Files</Filter> + </ClInclude> +@END AES + <ClInclude Include="..\include\isc\app.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\assertions.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\backtrace.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\base32.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\base64.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\bind9.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\boolean.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\buffer.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\bufferlist.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\commandline.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\counter.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\crc64.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\entropy.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\errno.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\error.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\event.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\eventclass.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\file.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\formatcheck.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\fsaccess.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\hash.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\heap.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\hex.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\hmacmd5.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\hmacsha.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\ht.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\httpd.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\int.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\interfaceiter.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\iterated_hash.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\json.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\lang.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\lex.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\lfsr.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\lib.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\list.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\log.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\magic.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\md5.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\mem.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\meminfo.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\msgcat.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\msgs.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\mutexblock.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\netaddr.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\netscope.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\ondestroy.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\os.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\parseint.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\pool.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\portset.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\print.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\queue.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\quota.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\radix.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\random.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\ratelimiter.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\refcount.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\regex.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\region.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\resource.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\result.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\resultclass.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\rwlock.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\safe.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\serial.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\sha1.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\sha2.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\sockaddr.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\socket.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\stats.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\stdio.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\stdlib.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\string.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\symtab.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\task.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\taskpool.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\timer.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\tm.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\types.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\util.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\version.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\isc\xml.h"> + <Filter>Library Header Files</Filter> + </ClInclude> +@IF PKCS11 + <ClInclude Include="..\include\pk11\constants.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\pk11\internal.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\pk11\pk11.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\pk11\result.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\pkcs11\pkcs11.h"> + <Filter>Pkcs11 Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\pkcs11\pkcs11f.h"> + <Filter>Pkcs11 Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\pkcs11\pkcs11t.h"> + <Filter>Pkcs11 Header Files</Filter> + </ClInclude> +@END PKCS11 + <ClInclude Include="include\isc\bind_registry.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\bindevt.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\condition.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\dir.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\ipv6.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\keyboard.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\mutex.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\net.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\netdb.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\ntgroups.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\ntpaths.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\offset.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\once.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\platform.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\stat.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\stdtime.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\strerror.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\syslog.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\thread.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\time.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="include\isc\win32os.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="errno2result.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="syslog.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> + <ClInclude Include="unistd.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> +@IF PKCS11 + <ClInclude Include="include\pkcs11\cryptoki.h"> + <Filter>Win32 Header Files</Filter> + </ClInclude> +@END PKCS11 +@IF ATOMIC + <ClInclude Include="include\isc\atomic.h"> +@ELSE ATOMIC + <ClInclude Include="..\noatomic\include\isc\atomic.h"> +@END ATOMIC + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\..\config.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\versions.h"> + <Filter>Library Header Files</Filter> + </ClInclude> + </ItemGroup> + <ItemGroup> + <ClCompile Include="app.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="condition.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="dir.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="DLLMain.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="entropy.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="errno.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="errno2result.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="file.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="fsaccess.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="interfaceiter.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="ipv6.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="keyboard.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="meminfo.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="net.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="ntpaths.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="once.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="os.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="resource.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="socket.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="stdio.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="stdtime.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="strerror.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="syslog.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="thread.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="time.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="version.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> + <ClCompile Include="win32os.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> +@IF PKCS11 + <ClCompile Include="pk11_api.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> +@END PKCS11 +@IF AES + <ClCompile Include="..\aes.c"> + <Filter>Win32 Source Files</Filter> + </ClCompile> +@END AES + <ClCompile Include="..\assertions.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\backtrace.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\backtrace-emptytbl.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\base32.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\base64.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\bind9.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\buffer.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\bufferlist.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\commandline.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\counter.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\crc64.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\error.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\event.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\hash.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\heap.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\hex.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\hmacmd5.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\hmacsha.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\ht.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\httpd.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\inet_aton.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\inet_ntop.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\inet_pton.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\iterated_hash.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\lex.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\lfsr.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\lib.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\log.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\md5.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\mem.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\nls\msgcat.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\mutexblock.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\netaddr.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\netscope.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\ondestroy.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\parseint.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\pool.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\portset.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\quota.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\radix.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\random.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\ratelimiter.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\refcount.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\regex.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\region.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\result.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\rwlock.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\safe.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\serial.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\sha1.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\sha2.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\sockaddr.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\stats.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\string.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\symtab.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\task.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\taskpool.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\timer.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\tm.c"> + <Filter>Library Source Files</Filter> + </ClCompile> +@IF PKCS11 + <ClCompile Include="..\pk11.c"> + <Filter>Library Source Files</Filter> + </ClCompile> + <ClCompile Include="..\pk11_result.c"> + <Filter>Library Source Files</Filter> + </ClCompile> +@END PKCS11 + </ItemGroup> +</Project> diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in new file mode 100644 index 0000000..fb9222a --- /dev/null +++ b/lib/isc/win32/libisc.vcxproj.in @@ -0,0 +1,530 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|@PLATFORM@"> + <Configuration>Debug</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|@PLATFORM@"> + <Configuration>Release</Configuration> + <Platform>@PLATFORM@</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{3840E563-D180-4761-AA9C-E6155F02EAFF}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>libisc</RootNamespace> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <LinkIncremental>true</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> +@IF PKCS11 + <PreprocessorDefinitions>BIND9;@CRYPTO@@PK11_LIB_LOCATION@WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> +@ELSE PKCS11 + <PreprocessorDefinitions>BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> +@END PKCS11 + <FunctionLevelLinking>true</FunctionLevelLinking> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <BrowseInformation>true</BrowseInformation> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <AdditionalDependencies>@LIBXML2_LIB@@OPENSSL_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <ModuleDefinitionFile>$(ProjectName).def</ModuleDefinitionFile> + <ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary> + </Link> + <PreBuildEvent> + <Command>cd ..\..\..\win32utils + +if NOT Exist ..\Build mkdir ..\Build +if NOT Exist ..\Build\Debug mkdir ..\Build\Debug + +echo Copying COPYRIGHT notice. + +copy ..\COPYRIGHT ..\Build\Debug + +@IF OPENSSL +echo Copying the OpenSSL DLL and LICENSE. + +copy @OPENSSL_DLL@ ..\Build\Debug\ +copy @OPENSSL_PATH@\LICENSE ..\Build\Debug\OpenSSL-LICENSE +@END OPENSSL + +@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 GEOIP +echo Copying the GeoIP DLL. + +copy @GEOIP_DLL@ ..\Build\Debug\ +@END GEOIP + +@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\ + +</Command> + </PreBuildEvent> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions> +@IF PKCS11 + <PreprocessorDefinitions>BIND9;@CRYPTO@@PK11_LIB_LOCATION@WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;win32;..\..\isccfg\include;..\..\dns\win32\include;..\..\dns\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> +@ELSE PKCS11 + <PreprocessorDefinitions>BIND9;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBISC_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <AdditionalIncludeDirectories>.\;..\..\..\;@LIBXML2_INC@@OPENSSL_INC@@ZLIB_INC@include;..\include;win32;..\..\isccfg\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> +@END PKCS11 + <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion> + <WholeProgramOptimization>false</WholeProgramOptimization> + <StringPooling>true</StringPooling> + <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile> + <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation> + <ObjectFileName>.\$(Configuration)\</ObjectFileName> + <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName> + <CompileAs>CompileAsC</CompileAs> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>false</GenerateDebugInformation> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + <AdditionalDependencies>@LIBXML2_LIB@@OPENSSL_LIB@@ZLIB_LIB@ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> + <ModuleDefinitionFile>$(ProjectName).def</ModuleDefinitionFile> + <ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + </Link> + <PreBuildEvent> + <Command>cd ..\..\..\win32utils + +if NOT Exist ..\Build mkdir ..\Build +if NOT Exist ..\Build\Release mkdir ..\Build\Release + +echo Copying the ARM and the Installation Notes. + +copy ..\COPYRIGHT ..\Build\Release +copy ..\README ..\Build\Release +copy ..\HISTORY ..\Build\Release +copy readme1st.txt ..\Build\Release +copy index.html ..\Build\Release +copy ..\doc\arm\*.html ..\Build\Release +copy ..\doc\arm\notes.pdf ..\Build\Release +copy ..\doc\arm\Bv9ARM.pdf ..\Build\Release +copy ..\CHANGES ..\Build\Release +if Exist ..\CHANGES.SE copy ..\CHANGES.SE ..\Build\Release +copy ..\FAQ ..\Build\Release + +echo Copying the standalone manual pages. + +copy ..\bin\named\named.html ..\Build\Release +copy ..\bin\named\named.conf.html ..\Build\Release +copy ..\bin\named\lwresd.html ..\Build\Release +copy ..\bin\rndc\*.html ..\Build\Release +copy ..\bin\confgen\*.html ..\Build\Release +copy ..\bin\dig\*.html ..\Build\Release +copy ..\bin\delv\*.html ..\Build\Release +copy ..\bin\nsupdate\*.html ..\Build\Release +copy ..\bin\check\*.html ..\Build\Release +copy ..\bin\dnssec\dnssec-keygen.html ..\Build\Release +copy ..\bin\dnssec\dnssec-signzone.html ..\Build\Release +copy ..\bin\dnssec\dnssec-dsfromkey.html ..\Build\Release +copy ..\bin\dnssec\dnssec-keyfromlabel.html ..\Build\Release +copy ..\bin\dnssec\dnssec-settime.html ..\Build\Release +copy ..\bin\dnssec\dnssec-revoke.html ..\Build\Release +copy ..\bin\dnssec\dnssec-verify.html ..\Build\Release +copy ..\bin\dnssec\dnssec-importkey.html ..\Build\Release +@IF PYTHON +copy ..\bin\python\dnssec-checkds.html ..\Build\Release +copy ..\bin\python\dnssec-coverage.html ..\Build\Release +copy ..\bin\python\dnssec-keymgr.html ..\Build\Release +@END PYTHON +@IF PKCS11 +copy ..\bin\pkcs11\pkcs11-keygen.html ..\Build\Release +copy ..\bin\pkcs11\pkcs11-list.html ..\Build\Release +copy ..\bin\pkcs11\pkcs11-destroy.html ..\Build\Release +copy ..\bin\pkcs11\pkcs11-tokens.html ..\Build\Release +@END PKCS11 +copy ..\bin\tools\arpaname.html ..\Build\Release +copy ..\bin\tools\genrandom.html ..\Build\Release +copy ..\bin\tools\isc-hmac-fixup.html ..\Build\Release +copy ..\bin\tools\named-journalprint.html ..\Build\Release +copy ..\bin\tools\named-rrchecker.html ..\Build\Release +copy ..\bin\tools\nsec3hash.html ..\Build\Release +copy ..\bin\tools\mdig.html ..\Build\Release + +echo Copying the migration notes. + +copy ..\doc\misc\migration ..\Build\Release +copy ..\doc\misc\migration-4to9 ..\Build\Release + +@IF OPENSSL +echo Copying the OpenSSL DLL and LICENSE. + +copy @OPENSSL_DLL@ ..\Build\Release\ +copy @OPENSSL_PATH@\LICENSE ..\Build\Release\OpenSSL-LICENSE +@END OPENSSL + +@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 GEOIP +echo Copying the GeoIP DLL. + +copy @GEOIP_DLL@ ..\Build\Release\ +@END GEOIP + +@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\ + +</Command> + </PreBuildEvent> + </ItemDefinitionGroup> + <ItemGroup> + <None Include="libisc.def" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\..\config.h" /> +@IF AES + <ClInclude Include="..\include\isc\aes.h" /> +@END AES + <ClInclude Include="..\include\isc\app.h" /> + <ClInclude Include="..\include\isc\assertions.h" /> + <ClInclude Include="..\include\isc\backtrace.h" /> + <ClInclude Include="..\include\isc\base32.h" /> + <ClInclude Include="..\include\isc\base64.h" /> + <ClInclude Include="..\include\isc\bind9.h" /> + <ClInclude Include="..\include\isc\boolean.h" /> + <ClInclude Include="..\include\isc\buffer.h" /> + <ClInclude Include="..\include\isc\bufferlist.h" /> + <ClInclude Include="..\include\isc\commandline.h" /> + <ClInclude Include="..\include\isc\counter.h" /> + <ClInclude Include="..\include\isc\crc64.h" /> + <ClInclude Include="..\include\isc\entropy.h" /> + <ClInclude Include="..\include\isc\errno.h" /> + <ClInclude Include="..\include\isc\error.h" /> + <ClInclude Include="..\include\isc\event.h" /> + <ClInclude Include="..\include\isc\eventclass.h" /> + <ClInclude Include="..\include\isc\file.h" /> + <ClInclude Include="..\include\isc\formatcheck.h" /> + <ClInclude Include="..\include\isc\fsaccess.h" /> + <ClInclude Include="..\include\isc\hash.h" /> + <ClInclude Include="..\include\isc\heap.h" /> + <ClInclude Include="..\include\isc\hex.h" /> + <ClInclude Include="..\include\isc\hmacmd5.h" /> + <ClInclude Include="..\include\isc\hmacsha.h" /> + <ClInclude Include="..\include\isc\ht.h" /> + <ClInclude Include="..\include\isc\httpd.h" /> + <ClInclude Include="..\include\isc\int.h" /> + <ClInclude Include="..\include\isc\interfaceiter.h" /> + <ClInclude Include="..\include\isc\iterated_hash.h" /> + <ClInclude Include="..\include\isc\json.h" /> + <ClInclude Include="..\include\isc\lang.h" /> + <ClInclude Include="..\include\isc\lex.h" /> + <ClInclude Include="..\include\isc\lfsr.h" /> + <ClInclude Include="..\include\isc\lib.h" /> + <ClInclude Include="..\include\isc\list.h" /> + <ClInclude Include="..\include\isc\log.h" /> + <ClInclude Include="..\include\isc\magic.h" /> + <ClInclude Include="..\include\isc\md5.h" /> + <ClInclude Include="..\include\isc\mem.h" /> + <ClInclude Include="..\include\isc\meminfo.h" /> + <ClInclude Include="..\include\isc\msgcat.h" /> + <ClInclude Include="..\include\isc\msgs.h" /> + <ClInclude Include="..\include\isc\mutexblock.h" /> + <ClInclude Include="..\include\isc\netaddr.h" /> + <ClInclude Include="..\include\isc\netscope.h" /> + <ClInclude Include="..\include\isc\ondestroy.h" /> + <ClInclude Include="..\include\isc\os.h" /> + <ClInclude Include="..\include\isc\parseint.h" /> + <ClInclude Include="..\include\isc\pool.h" /> + <ClInclude Include="..\include\isc\portset.h" /> + <ClInclude Include="..\include\isc\print.h" /> + <ClInclude Include="..\include\isc\queue.h" /> + <ClInclude Include="..\include\isc\quota.h" /> + <ClInclude Include="..\include\isc\radix.h" /> + <ClInclude Include="..\include\isc\random.h" /> + <ClInclude Include="..\include\isc\ratelimiter.h" /> + <ClInclude Include="..\include\isc\refcount.h" /> + <ClInclude Include="..\include\isc\regex.h" /> + <ClInclude Include="..\include\isc\region.h" /> + <ClInclude Include="..\include\isc\resource.h" /> + <ClInclude Include="..\include\isc\result.h" /> + <ClInclude Include="..\include\isc\resultclass.h" /> + <ClInclude Include="..\include\isc\rwlock.h" /> + <ClInclude Include="..\include\isc\safe.h" /> + <ClInclude Include="..\include\isc\serial.h" /> + <ClInclude Include="..\include\isc\sha1.h" /> + <ClInclude Include="..\include\isc\sha2.h" /> + <ClInclude Include="..\include\isc\sockaddr.h" /> + <ClInclude Include="..\include\isc\socket.h" /> + <ClInclude Include="..\include\isc\stats.h" /> + <ClInclude Include="..\include\isc\stdio.h" /> + <ClInclude Include="..\include\isc\stdlib.h" /> + <ClInclude Include="..\include\isc\string.h" /> + <ClInclude Include="..\include\isc\symtab.h" /> + <ClInclude Include="..\include\isc\task.h" /> + <ClInclude Include="..\include\isc\taskpool.h" /> + <ClInclude Include="..\include\isc\timer.h" /> + <ClInclude Include="..\include\isc\tm.h" /> + <ClInclude Include="..\include\isc\types.h" /> + <ClInclude Include="..\include\isc\util.h" /> + <ClInclude Include="..\include\isc\version.h" /> + <ClInclude Include="..\include\isc\xml.h" /> +@IF PKCS11 + <ClInclude Include="..\include\pk11\constants.h" /> + <ClInclude Include="..\include\pk11\internal.h" /> + <ClInclude Include="..\include\pk11\pk11.h" /> + <ClInclude Include="..\include\pk11\result.h" /> + <ClInclude Include="..\include\pkcs11\pkcs11.h" /> + <ClInclude Include="..\include\pkcs11\pkcs11f.h" /> + <ClInclude Include="..\include\pkcs11\pkcs11t.h" /> +@END PKCS11 +@IF ATOMIC + <ClInclude Include="include\isc\atomic.h" /> +@ELSE ATOMIC + <ClInclude Include="..\noatomic\include\isc\atomic.h" /> +@END ATOMIC + <ClInclude Include="errno2result.h" /> + <ClInclude Include="include\isc\bindevt.h" /> + <ClInclude Include="include\isc\bind_registry.h" /> + <ClInclude Include="include\isc\condition.h" /> + <ClInclude Include="include\isc\dir.h" /> + <ClInclude Include="include\isc\ipv6.h" /> + <ClInclude Include="include\isc\keyboard.h" /> + <ClInclude Include="include\isc\mutex.h" /> + <ClInclude Include="include\isc\net.h" /> + <ClInclude Include="include\isc\netdb.h" /> + <ClInclude Include="include\isc\ntgroups.h" /> + <ClInclude Include="include\isc\ntpaths.h" /> + <ClInclude Include="include\isc\offset.h" /> + <ClInclude Include="include\isc\once.h" /> + <ClInclude Include="include\isc\platform.h" /> + <ClInclude Include="include\isc\stat.h" /> + <ClInclude Include="include\isc\stdtime.h" /> + <ClInclude Include="include\isc\strerror.h" /> + <ClInclude Include="include\isc\syslog.h" /> + <ClInclude Include="include\isc\thread.h" /> + <ClInclude Include="include\isc\time.h" /> + <ClInclude Include="include\isc\win32os.h" /> + <ClInclude Include="syslog.h" /> + <ClInclude Include="unistd.h" /> +@IF PKCS11 + <ClInclude Include="include\pkcs11\cryptoki.h" /> +@END PKCS11 + <ClInclude Include="..\..\versions.h" /> + </ItemGroup> + <ItemGroup> +@IF AES + <ClCompile Include="..\aes.c" /> +@END AES + <ClCompile Include="..\assertions.c" /> + <ClCompile Include="..\backtrace-emptytbl.c" /> + <ClCompile Include="..\backtrace.c" /> + <ClCompile Include="..\base32.c" /> + <ClCompile Include="..\base64.c" /> + <ClCompile Include="..\bind9.c" /> + <ClCompile Include="..\buffer.c" /> + <ClCompile Include="..\bufferlist.c" /> + <ClCompile Include="..\commandline.c" /> + <ClCompile Include="..\counter.c" /> + <ClCompile Include="..\crc64.c" /> + <ClCompile Include="..\error.c" /> + <ClCompile Include="..\event.c" /> + <ClCompile Include="..\hash.c" /> + <ClCompile Include="..\heap.c" /> + <ClCompile Include="..\hex.c" /> + <ClCompile Include="..\hmacmd5.c" /> + <ClCompile Include="..\hmacsha.c" /> + <ClCompile Include="..\ht.c" /> + <ClCompile Include="..\httpd.c" /> + <ClCompile Include="..\inet_aton.c" /> + <ClCompile Include="..\inet_ntop.c" /> + <ClCompile Include="..\inet_pton.c" /> + <ClCompile Include="..\iterated_hash.c" /> + <ClCompile Include="..\lex.c" /> + <ClCompile Include="..\lfsr.c" /> + <ClCompile Include="..\lib.c" /> + <ClCompile Include="..\log.c" /> + <ClCompile Include="..\md5.c" /> + <ClCompile Include="..\mem.c" /> + <ClCompile Include="..\mutexblock.c" /> + <ClCompile Include="..\netaddr.c" /> + <ClCompile Include="..\netscope.c" /> + <ClCompile Include="..\nls\msgcat.c" /> + <ClCompile Include="..\ondestroy.c" /> + <ClCompile Include="..\parseint.c" /> + <ClCompile Include="..\pool.c" /> + <ClCompile Include="..\portset.c" /> + <ClCompile Include="..\quota.c" /> + <ClCompile Include="..\radix.c" /> + <ClCompile Include="..\random.c" /> + <ClCompile Include="..\ratelimiter.c" /> + <ClCompile Include="..\refcount.c" /> + <ClCompile Include="..\regex.c" /> + <ClCompile Include="..\region.c" /> + <ClCompile Include="..\result.c" /> + <ClCompile Include="..\rwlock.c" /> + <ClCompile Include="..\safe.c" /> + <ClCompile Include="..\serial.c" /> + <ClCompile Include="..\sha1.c" /> + <ClCompile Include="..\sha2.c" /> + <ClCompile Include="..\sockaddr.c" /> + <ClCompile Include="..\stats.c" /> + <ClCompile Include="..\string.c" /> + <ClCompile Include="..\symtab.c" /> + <ClCompile Include="..\task.c" /> + <ClCompile Include="..\taskpool.c" /> + <ClCompile Include="..\timer.c" /> + <ClCompile Include="..\tm.c" /> +@IF PKCS11 + <ClCompile Include="..\pk11.c" /> + <ClCompile Include="..\pk11_result.c" /> +@END PKCS11 + <ClCompile Include="app.c" /> + <ClCompile Include="condition.c" /> + <ClCompile Include="dir.c" /> + <ClCompile Include="DLLMain.c" /> + <ClCompile Include="entropy.c" /> + <ClCompile Include="errno.c" /> + <ClCompile Include="errno2result.c" /> + <ClCompile Include="file.c" /> + <ClCompile Include="fsaccess.c" /> + <ClCompile Include="interfaceiter.c" /> + <ClCompile Include="ipv6.c" /> + <ClCompile Include="keyboard.c" /> + <ClCompile Include="meminfo.c" /> + <ClCompile Include="net.c" /> + <ClCompile Include="ntpaths.c" /> + <ClCompile Include="once.c" /> + <ClCompile Include="os.c" /> + <ClCompile Include="resource.c" /> + <ClCompile Include="socket.c" /> + <ClCompile Include="stdio.c" /> + <ClCompile Include="stdtime.c" /> + <ClCompile Include="strerror.c" /> + <ClCompile Include="syslog.c" /> + <ClCompile Include="thread.c" /> + <ClCompile Include="time.c" /> + <ClCompile Include="version.c" /> + <ClCompile Include="win32os.c" /> +@IF PKCS11 + <ClCompile Include="pk11_api.c" /> +@END PKCS11 + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/lib/isc/win32/libisc.vcxproj.user b/lib/isc/win32/libisc.vcxproj.user new file mode 100644 index 0000000..695b5c7 --- /dev/null +++ b/lib/isc/win32/libisc.vcxproj.user @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project>
\ 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..51077f6 --- /dev/null +++ b/lib/isc/win32/meminfo.c @@ -0,0 +1,26 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <inttypes.h> +#include <windows.h> + +#include <isc/meminfo.h> + +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..c49bb6b --- /dev/null +++ b/lib/isc/win32/net.c @@ -0,0 +1,341 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <errno.h> +#include <unistd.h> + +#include <isc/log.h> +#include <isc/msgs.h> +#include <isc/net.h> +#include <isc/once.h> +#include <isc/strerror.h> +#include <isc/string.h> +#include <isc/util.h> + +/*% + * 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 1024 +#endif /* ISC_NET_PORTRANGELOW */ +#ifndef ISC_NET_PORTRANGEHIGH +#define ISC_NET_PORTRANGEHIGH 65535 +#endif /* ISC_NET_PORTRANGEHIGH */ + +#if defined(ISC_PLATFORM_HAVEIPV6) && defined(ISC_PLATFORM_NEEDIN6ADDRANY) +const struct in6_addr isc_net_in6addrany = IN6ADDR_ANY_INIT; +#endif + +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: + isc__strerror(errval, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + return (ISC_R_UNEXPECTED); + } + } + + closesocket(s); + + return (ISC_R_SUCCESS); +} + +static void +initialize_action(void) { + InitSockets(); + ipv4_result = try_proto(PF_INET); +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 +#ifdef ISC_PLATFORM_HAVEIN6PKTINFO + ipv6_result = try_proto(PF_INET6); +#endif +#endif +#endif +} + +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); +} + +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 +static void +try_ipv6only(void) { +#ifdef IPV6_V6ONLY + SOCKET s; + int on; + char strbuf[ISC_STRERRORSIZE]; +#endif + 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 + /* check for TCP sockets */ + s = socket(PF_INET6, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + 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) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + 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) { + isc__strerror(errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + ipv6pktinfo_result = ISC_R_UNEXPECTED; + return; + } + +#ifdef IPV6_RECVPKTINFO + optname = IPV6_RECVPKTINFO; +#else + optname = IPV6_PKTINFO; +#endif + 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__ */ +#endif /* WANT_IPV6 */ +#endif /* ISC_PLATFORM_HAVEIPV6 */ + +isc_result_t +isc_net_probe_ipv6only(void) { +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 + initialize_ipv6only(); +#else + ipv6only_result = ISC_R_NOTFOUND; +#endif +#endif + return (ipv6only_result); +} + +isc_result_t +isc_net_probe_ipv6pktinfo(void) { +#ifdef __notyet__ +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifdef WANT_IPV6 + initialize_ipv6pktinfo(); +#else + ipv6pktinfo_result = ISC_R_NOTFOUND; +#endif +#endif +#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..a9398c8 --- /dev/null +++ b/lib/isc/win32/netdb.h @@ -0,0 +1,182 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <stddef.h> +#include <winsock2.h> + +/* + * Define if <netdb.h> 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 + + +/* + * Undefine all \#defines we are interested in as <netdb.h> 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..e3a45ca --- /dev/null +++ b/lib/isc/win32/ntgroups.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + +#include <windows.h> +#include <assert.h> +#include <lm.h> + +#include <isc/ntgroups.h> +#include <isc/result.h> + +#define MAX_NAME_LENGTH 256 + +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]; + + retlen = mbstowcs(user, username, MAX_NAME_LENGTH); + + *totalGroups = 0; + /* + * 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) + return (ISC_R_NOMEMORY); + + retlen = wcstombs(GroupList[*totalGroups], + pTmpLBuf->lgrui0_name, retlen); + GroupList[*totalGroups][retlen] = '\0'; + if (strcmp(GroupList[*totalGroups], "None") == 0) + free(GroupList[*totalGroups]); + else + (*totalGroups)++; + pTmpLBuf++; + } + } + /* Free the allocated memory. */ + 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) + return (ISC_R_NOMEMORY); + + retlen = wcstombs(GroupList[*totalGroups], + pTmpBuf->grui0_name, retlen); + GroupList[*totalGroups][retlen] = '\0'; + if (strcmp(GroupList[*totalGroups], "None") == 0) + free(GroupList[*totalGroups]); + else + (*totalGroups)++; + pTmpBuf++; + } + } + /* + * Free the allocated memory. + */ + if (pgrpBuf != NULL) + NetApiBufferFree(pgrpBuf); + + return (ISC_R_SUCCESS); +} diff --git a/lib/isc/win32/ntpaths.c b/lib/isc/win32/ntpaths.c new file mode 100644 index 0000000..df71bfa --- /dev/null +++ b/lib/isc/win32/ntpaths.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <config.h> + +#include <isc/bind_registry.h> +#include <isc/ntpaths.h> +#include <isc/string.h> + +/* + * Module Variables + */ + +static char systemDir[MAX_PATH]; +static char namedBase[MAX_PATH]; +static char ns_confFile[MAX_PATH]; +static char lwresd_confFile[MAX_PATH]; +static char lwresd_resolvconfFile[MAX_PATH]; +static char rndc_confFile[MAX_PATH]; +static char ns_defaultpidfile[MAX_PATH]; +static char lwresd_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 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(lwresd_confFile, namedBase, sizeof(lwresd_confFile)) ; + strlcat(lwresd_confFile, "\\etc\\lwresd.conf", + sizeof(lwresd_confFile)); + + strlcpy(lwresd_defaultpidfile, namedBase, + sizeof(lwresd_defaultpidfile)); + strlcat(lwresd_defaultpidfile, "\\etc\\lwresd.pid", + sizeof(lwresd_defaultpidfile)); + + strlcpy(lwresd_resolvconfFile, systemDir, + sizeof(lwresd_resolvconfFile)); + strlcat(lwresd_resolvconfFile, "\\Drivers\\etc\\resolv.conf", + sizeof(lwresd_resolvconfFile)); + + 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)); + + Initialized = TRUE; +} + +char * +isc_ntpaths_get(int ind) { + if (!Initialized) + isc_ntpaths_init(); + + switch (ind) { + case NAMED_CONF_PATH: + return (ns_confFile); + break; + case LWRES_CONF_PATH: + return (lwresd_confFile); + break; + case RESOLV_CONF_PATH: + return (lwresd_resolvconfFile); + break; + case RNDC_CONF_PATH: + return (rndc_confFile); + break; + case NAMED_PID_PATH: + return (ns_defaultpidfile); + break; + case LWRESD_PID_PATH: + return (lwresd_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; + default: + return (NULL); + } +} diff --git a/lib/isc/win32/once.c b/lib/isc/win32/once.c new file mode 100644 index 0000000..baf178d --- /dev/null +++ b/lib/isc/win32/once.c @@ -0,0 +1,44 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <windows.h> + +#include <isc/once.h> +#include <isc/assertions.h> +#include <isc/util.h> + +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..0f4023f --- /dev/null +++ b/lib/isc/win32/os.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <windows.h> + +#include <isc/os.h> + +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..76e4abb --- /dev/null +++ b/lib/isc/win32/pk11_api.c @@ -0,0 +1,675 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <config.h> + +#include <string.h> +#include <windows.h> + +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/once.h> +#include <isc/print.h> +#include <isc/stdio.h> +#include <isc/thread.h> +#include <isc/util.h> + +#include <pk11/pk11.h> +#include <pk11/internal.h> + +#define HAVE_GETPASSPHRASE + +char * +getpassphrase(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 convertion 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_GetMechanismInfo"); + 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_DestroyObject"); + 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_GetAttributeValue"); + 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_SetAttributeValue"); + 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_FindObjectsInit"); + 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_FindObjectsFinal"); + 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_GenerateKeyPair"); + 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_GenerateRandom"); + 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..0fbc9a4 --- /dev/null +++ b/lib/isc/win32/resource.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <stdio.h> + +#include <isc/platform.h> +#include <isc/resource.h> +#include <isc/result.h> +#include <isc/util.h> + +#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..75b1c26 --- /dev/null +++ b/lib/isc/win32/socket.c @@ -0,0 +1,4277 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 + */ + +#include <config.h> + +#define MAKE_EXTERNAL 1 + +#include <sys/types.h> + +#ifndef _WINSOCKAPI_ +#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */ +#endif + +#include <errno.h> +#include <stdbool.h> +#include <stddef.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <io.h> +#include <fcntl.h> +#include <process.h> + +#include <isc/app.h> +#include <isc/buffer.h> +#include <isc/bufferlist.h> +#include <isc/condition.h> +#include <isc/list.h> +#include <isc/log.h> +#include <isc/mem.h> +#include <isc/msgs.h> +#include <isc/mutex.h> +#include <isc/net.h> +#include <isc/once.h> +#include <isc/os.h> +#include <isc/platform.h> +#include <isc/print.h> +#include <isc/region.h> +#include <isc/socket.h> +#include <isc/stats.h> +#include <isc/strerror.h> +#include <isc/string.h> +#include <isc/syslog.h> +#include <isc/task.h> +#include <isc/thread.h> +#include <isc/util.h> +#include <isc/win32os.h> + +#include <mswsock.h> + +#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 +#define CONSISTENT(sock) do {} while (0) +#endif +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 + +/* + * Some systems define the socket length argument as an int, some as size_t, + * some as socklen_t. This is here so it can be easily changed if needed. + */ +#ifndef ISC_SOCKADDR_LEN_T +#define ISC_SOCKADDR_LEN_T unsigned int +#endif + +/* + * 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. + */ +#ifdef ISC_PLATFORM_HAVEIPV6 +#ifndef USE_CMSG +#define USE_CMSG 1 +#endif +#endif + +/* + * 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; + unsigned int 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) + +/* + * Buffer structure + */ +typedef struct buflist buflist_t; + +struct buflist { + void *buf; + unsigned int buflen; + ISC_LINK(buflist_t) link; +}; + +/* + * 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; + ISC_LIST(buflist_t) bufferlist; /*%< list of buffers */ +} 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]; + DWORD dwIOCPThreadIds[MAX_IOCPTHREADS]; + + /* + * 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 + + printf("\n\t\tSock Dump\n"); + printf("\t\tfd: %Iu\n", sock->fd); + printf("\t\treferences: %u\n", 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, isc_sockaddr_t *address, + isc_logcategory_t *category, isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int message, + const char *fmt, ...) ISC_FORMAT_PRINTF(9, 10); + +/* 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(); + isc__strerror(errval, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_FAILED, + "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++) { + manager->hIOCPThreads[i] = CreateThread(NULL, 0, SocketIoThread, + manager, 0, + &manager->dwIOCPThreadIds[i]); + if (manager->hIOCPThreads[i] == NULL) { + errval = GetLastError(); + isc__strerror(errval, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_FAILED, + "Can't create IOCP thread: %s"), + strbuf); + } + } +} + +/* + * 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(); + isc__strerror(errval, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_FAILED, + "HeapCreate() failed during " + "initialization: %s"), + strbuf); + } + + manager->maxIOCPThreads = min(isc_os_ncpus() + 1, MAX_IOCPTHREADS); + + /* Now Create the Completion Port */ + manager->hIoCompletionPort = CreateIoCompletionPort( + INVALID_HANDLE_VALUE, NULL, + 0, manager->maxIOCPThreads); + if (manager->hIoCompletionPort == NULL) { + errval = GetLastError(); + isc__strerror(errval, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_FAILED, + "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(); + isc__strerror(errval, strbuf, sizeof(strbuf)); + isc_log_iwrite(isc_lctx, + ISC_LOGCATEGORY_GENERAL, + ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_TOOMANYHANDLES, + "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__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_FAILED, + "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]; + isc__strerror(err, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, "WSAStartup() %s: %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed"), + 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, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_DOIORECV, + "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, isc_sockaddr_t *address, + isc_logcategory_t *category, isc_logmodule_t *module, int level, + isc_msgcat_t *msgcat, int msgset, int message, + 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_iwrite(isc_lctx, category, module, level, + msgcat, msgset, message, + "socket %p line %d: %s", sock, lineno, msgbuf); + } else { + isc_sockaddr_format(address, peerbuf, sizeof(peerbuf)); + isc_log_iwrite(isc_lctx, category, module, level, + msgcat, msgset, message, + "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) { + isc__strerror(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) %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "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; + isc_buffer_t *buffer; + buflist_t *cpbuffer; + isc_region_t used; + size_t write_count; + size_t skip_count; + + memset(msg, 0, sizeof(*msg)); + + memmove(&msg->to_addr, &dev->address.type, dev->address.length); + msg->to_addr_len = dev->address.length; + + buffer = ISC_LIST_HEAD(dev->bufferlist); + write_count = 0; + iovcount = 0; + + /* + * Single buffer I/O? Skip what we've done so far in this region. + */ + if (buffer == NULL) { + write_count = dev->region.length - dev->n; + cpbuffer = HeapAlloc(hHeapHandle, HEAP_ZERO_MEMORY, sizeof(buflist_t)); + RUNTIME_CHECK(cpbuffer != NULL); + cpbuffer->buf = HeapAlloc(hHeapHandle, HEAP_ZERO_MEMORY, write_count); + RUNTIME_CHECK(cpbuffer->buf != NULL); + + socket_log(__LINE__, sock, NULL, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTLOCK, + "alloc_buffer %p %d %p %d", cpbuffer, sizeof(buflist_t), + cpbuffer->buf, write_count); + + memmove(cpbuffer->buf,(dev->region.base + dev->n), write_count); + cpbuffer->buflen = (unsigned int)write_count; + ISC_LINK_INIT(cpbuffer, link); + ISC_LIST_ENQUEUE(lpo->bufferlist, cpbuffer, link); + iov[0].buf = cpbuffer->buf; + iov[0].len = (u_long)write_count; + iovcount = 1; + + goto config; + } + + /* + * Multibuffer I/O. + * Skip the data in the buffer list that we have already written. + */ + skip_count = dev->n; + while (buffer != NULL) { + REQUIRE(ISC_BUFFER_VALID(buffer)); + if (skip_count < isc_buffer_usedlength(buffer)) + break; + skip_count -= isc_buffer_usedlength(buffer); + buffer = ISC_LIST_NEXT(buffer, link); + } + + while (buffer != NULL) { + INSIST(iovcount < MAXSCATTERGATHER_SEND); + + isc_buffer_usedregion(buffer, &used); + + if (used.length > 0) { + int uselen = (int)(used.length - skip_count); + cpbuffer = HeapAlloc(hHeapHandle, HEAP_ZERO_MEMORY, sizeof(buflist_t)); + RUNTIME_CHECK(cpbuffer != NULL); + cpbuffer->buf = HeapAlloc(hHeapHandle, HEAP_ZERO_MEMORY, uselen); + RUNTIME_CHECK(cpbuffer->buf != NULL); + + socket_log(__LINE__, sock, NULL, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTLOCK, + "alloc_buffer %p %d %p %d", cpbuffer, sizeof(buflist_t), + cpbuffer->buf, write_count); + + memmove(cpbuffer->buf,(used.base + skip_count), uselen); + cpbuffer->buflen = uselen; + iov[iovcount].buf = cpbuffer->buf; + iov[iovcount].len = (u_long)(used.length - skip_count); + write_count += uselen; + skip_count = 0; + iovcount++; + } + buffer = ISC_LIST_NEXT(buffer, link); + } + + INSIST(skip_count == 0); + + config: + msg->msg_iov = iov; + msg->msg_iovlen = iovcount; + msg->msg_totallen = (u_int)write_count; +} + +static void +set_dev_address(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; + + INSIST(ISC_LIST_EMPTY(ev->bufferlist)); + + (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)); + if (ev == NULL) + return (NULL); + + ev->result = ISC_R_IOERROR; // XXXMLG temporary change to detect failure to set + ISC_LINK_INIT(ev, ev_link); + ISC_LIST_INIT(ev->bufferlist); + 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 + +/* + * 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) { + isc__strerror(windows_errno, errorstring, bufsize); + } + return (doreturn); +} + +static void +fill_recv(isc_socket_t *sock, isc_socketevent_t *dev) { + isc_region_t r; + int copylen; + isc_buffer_t *buffer; + + 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, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_ZEROPORT, + "dropping source port zero packet"); + } + sock->recvbuf.remaining = 0; + return; + } + } else if (sock->type == isc_sockettype_tcp) { + dev->address = sock->address; + } + + /* + * Run through the list of buffers we were given, and find the + * first one with space. Once it is found, loop through, filling + * the buffers as much as possible. + */ + buffer = ISC_LIST_HEAD(dev->bufferlist); + if (buffer != NULL) { // Multi-buffer receive + while (buffer != NULL && sock->recvbuf.remaining > 0) { + REQUIRE(ISC_BUFFER_VALID(buffer)); + if (isc_buffer_availablelength(buffer) > 0) { + isc_buffer_availableregion(buffer, &r); + copylen = min(r.length, + sock->recvbuf.remaining); + memmove(r.base, sock->recvbuf.consume_position, + copylen); + sock->recvbuf.consume_position += copylen; + sock->recvbuf.remaining -= copylen; + isc_buffer_add(buffer, copylen); + dev->n += copylen; + } + buffer = ISC_LIST_NEXT(buffer, link); + } + } else { // Single-buffer receive + 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; + + 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)); + ISC_LIST_INIT(lpo->bufferlist); + + 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)) { + isc__strerror(*send_errno, strbuf, sizeof(strbuf)); + socket_log(__LINE__, sock, NULL, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_INTERNALSEND, + "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 + UNUSED(sock); +#endif +} + +static isc_result_t +allocate_socket(isc_socketmgr_t *manager, isc_sockettype_t type, + isc_socket_t **socketp) { + isc_socket_t *sock; + isc_result_t result; + + sock = isc_mem_get(manager->mctx, sizeof(*sock)); + + if (sock == NULL) + return (ISC_R_NOMEMORY); + + sock->magic = 0; + 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 + if (sock->recvbuf.base == NULL) { + result = ISC_R_NOMEMORY; + goto error; + } + + /* + * Initialize the lock. + */ + result = isc_mutex_init(&sock->lock); + if (result != ISC_R_SUCCESS) + goto error; + + socket_log(__LINE__, sock, NULL, EVENT, NULL, 0, 0, + "allocated"); + + sock->magic = SOCKET_MAGIC; + *socketp = sock; + + return (ISC_R_SUCCESS); + + error: + if (sock->recvbuf.base != NULL) + isc_mem_put(manager->mctx, sock->recvbuf.base, sock->recvbuf.len); + isc_mem_put(manager->mctx, sock, sizeof(*sock)); + + return (result); +} + +/* + * 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, isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_DESTROYING, "SOCKET INCONSISTENT: %s", + crash_reason); + sock_dump(sock); + INSIST(crash == false); + } +} + +/* + * Maybe free the socket. + * + * This function will verify tht 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 + || 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, isc_msgcat, + ISC_MSGSET_SOCKET, ISC_MSG_DESTROYING, + "freeing socket line %d fd %d lock %p semaphore %p", + lineno, sock->fd, &sock->lock, sock->lock.LockSemaphore); + + sock->magic = 0; + DESTROYLOCK(&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(SO_RCVBUF) + ISC_SOCKADDR_LEN_T optlen; + int size; +#endif + int socket_errno; + char strbuf[ISC_STRERRORSIZE]; + + REQUIRE(VALID_MANAGER(manager)); + REQUIRE(socketp != NULL && *socketp == NULL); + REQUIRE(type != isc_sockettype_fdwatch); + +#ifndef SOCK_RAW + if (type == isc_sockettype_raw) + return (ISC_R_NOTIMPLEMENTED); +#endif + + 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, NULL, 0, 0, + "closed %d %d %d " + "con_reset_fix_failed", + sock->pending_recv, + sock->pending_send, + 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 + break; +#endif + } + + 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: + isc__strerror(socket_errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "socket() %s: %s", + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + return (ISC_R_UNEXPECTED); + } + } + + result = make_nonblock(sock->fd); + if (result != ISC_R_SUCCESS) { + socket_log(__LINE__, sock, NULL, EVENT, NULL, 0, 0, + "closed %d %d %d make_nonblock_failed", + sock->pending_recv, sock->pending_send, + 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) +#if defined(ISC_PLATFORM_HAVEIPV6) +#ifdef IPV6_RECVPKTINFO + /* 2292bis */ + if ((pf == AF_INET6) + && (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, + (char *)&on, sizeof(on)) < 0)) { + isc__strerror(WSAGetLastError(), strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_RECVPKTINFO) " + "%s: %s", sock->fd, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } +#else + /* 2292 */ + if ((pf == AF_INET6) + && (setsockopt(sock->fd, IPPROTO_IPV6, IPV6_PKTINFO, + (char *)&on, sizeof(on)) < 0)) { + isc__strerror(WSAGetLastError(), strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "setsockopt(%d, IPV6_PKTINFO) %s: %s", + sock->fd, + isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, + "failed"), + strbuf); + } +#endif /* IPV6_RECVPKTINFO */ +#endif /* ISC_PLATFORM_HAVEIPV6 */ +#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 + + } +#endif /* defined(USE_CMSG) || defined(SO_RCVBUF) */ + + _set_state(sock, SOCK_OPEN); + sock->references = 1; + *socketp = sock; + + iocompletionport_update(sock); + + if (dup_socket) { +#ifndef ISC_ALLOW_MAPPED + isc__socket_ipv6only(sock, true); +#endif + + 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, isc_msgcat, + ISC_MSGSET_SOCKET, ISC_MSG_CREATED, + "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)); + REQUIRE(sock->type != isc_sockettype_fdwatch); + + 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); + sock->references++; + UNLOCK(&sock->lock); + + *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; + + REQUIRE(socketp != NULL); + sock = *socketp; + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(sock->type != isc_sockettype_fdwatch); + + LOCK(&sock->lock); + CONSISTENT(sock); + REQUIRE(sock->references > 0); + sock->references--; + + socket_log(__LINE__, sock, NULL, EVENT, NULL, 0, 0, + "detach_socket %d %d %d", + sock->pending_recv, sock->pending_send, + sock->references); + + if (sock->references == 0 && sock->fd != INVALID_SOCKET) { + closesocket(sock->fd); + sock->fd = INVALID_SOCKET; + _set_state(sock, SOCK_CLOSED); + } + + maybe_free_socket(&sock, __LINE__); + + *socketp = NULL; +} + +isc_result_t +isc_socket_close(isc_socket_t *sock) { + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(sock->type != isc_sockettype_fdwatch); + + 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) + == ISC_SOCKEVENTATTR_ATTACHED) + 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) + == ISC_SOCKEVENTATTR_ATTACHED) + 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, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTLOCK, + "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, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTLOCK, + "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, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTEDCXN, + "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; + isc__strerror(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, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTEDCXN, + "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, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALRECV, + "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) +{ + buflist_t *buffer; + + /* + * Find out what socket this is and lock it. + */ + INSIST(VALID_SOCKET(sock)); + + LOCK(&sock->lock); + CONSISTENT(sock); + + socket_log(__LINE__, sock, NULL, IOEVENT, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_INTERNALSEND, + "internal_send: task got socket event %p", dev); + + buffer = ISC_LIST_HEAD(lpo->bufferlist); + while (buffer != NULL) { + ISC_LIST_DEQUEUE(lpo->bufferlist, buffer, link); + + socket_log(__LINE__, sock, NULL, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_ACCEPTLOCK, + "free_buffer %p %p", buffer, buffer->buf); + + HeapFree(hHeapHandle, 0, buffer->buf); + HeapFree(hHeapHandle, 0, buffer); + buffer = ISC_LIST_HEAD(lpo->bufferlist); + } + + 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 consiers 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 lengh + 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; + BOOL bSuccess = FALSE; + 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(); + isc__strerror(errval, strbuf, sizeof(strbuf)); + FATAL_ERROR(__FILE__, __LINE__, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_FAILED, + "Can't set thread priority: %s"), + strbuf); + } + + /* + * Loop forever waiting on I/O Completions and then processing them + */ + while (TRUE) { + 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, NULL, 0, 0, + "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, NULL, 0, 0, + "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, NULL, 0, 0, + "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; + lpo->adev->newsocket->references--; + free_socket(&lpo->adev->newsocket, __LINE__); + lpo->adev->result = isc_result; + socket_log(__LINE__, sock, NULL, EVENT, NULL, 0, 0, + "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, NULL, 0, 0, + "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, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_EXITING, "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)); +} + +isc_result_t +isc__socketmgr_create2(isc_mem_t *mctx, isc_socketmgr_t **managerp, + unsigned int maxsocks) +{ + isc_socketmgr_t *manager; + isc_result_t result; + + REQUIRE(managerp != NULL && *managerp == NULL); + + if (maxsocks != 0) + return (ISC_R_NOTIMPLEMENTED); + + manager = isc_mem_get(mctx, sizeof(*manager)); + if (manager == NULL) + return (ISC_R_NOMEMORY); + + InitSockets(); + + manager->magic = SOCKET_MANAGER_MAGIC; + manager->mctx = NULL; + manager->stats = NULL; + ISC_LIST_INIT(manager->socklist); + result = isc_mutex_init(&manager->lock); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, manager, sizeof(*manager)); + return (result); + } + if (isc_condition_init(&manager->shutdown_ok) != ISC_R_SUCCESS) { + DESTROYLOCK(&manager->lock); + isc_mem_put(mctx, manager, sizeof(*manager)); + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + return (ISC_R_UNEXPECTED); + } + + isc_mem_attach(mctx, &manager->mctx); + + iocompletionport_init(manager); /* Create the Completion Ports */ + + manager->bShutdown = false; + manager->totalSockets = 0; + manager->iocp_total = 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; + int i; + isc_mem_t *mctx; + + /* + * Destroy a socket manager. + */ + + REQUIRE(managerp != NULL); + manager = *managerp; + REQUIRE(VALID_MANAGER(manager)); + + LOCK(&manager->lock); + + /* + * Wait for all sockets to be destroyed. + */ + while (!ISC_LIST_EMPTY(manager->socklist)) { + manager_log(manager, CREATION, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_SOCKETSREMAIN, + "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 (i = 0; i < manager->maxIOCPThreads; i++) { + if (isc_thread_join((isc_thread_t) manager->hIOCPThreads[i], + NULL) != ISC_R_SUCCESS) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_thread_join() for Completion Port %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + } + /* + * Clean up. + */ + + CloseHandle(manager->hIoCompletionPort); + + (void)isc_condition_destroy(&manager->shutdown_ok); + + DESTROYLOCK(&manager->lock); + if (manager->stats != NULL) + isc_stats_detach(&manager->stats); + manager->magic = 0; + mctx= manager->mctx; + isc_mem_put(mctx, manager, sizeof(*manager)); + + isc_mem_detach(&mctx); + + *managerp = NULL; +} + +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, NULL, 0, 0, + "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_recvv(isc_socket_t *sock, isc_bufferlist_t *buflist, + unsigned int minimum, isc_task_t *task, + isc_taskaction_t action, void *arg) +{ + isc_socketevent_t *dev; + isc_socketmgr_t *manager; + unsigned int iocount; + isc_buffer_t *buffer; + isc_result_t ret; + + REQUIRE(VALID_SOCKET(sock)); + LOCK(&sock->lock); + CONSISTENT(sock); + + /* + * Make sure that the socket is not closed. XXXMLG change error here? + */ + if (sock->fd == INVALID_SOCKET) { + UNLOCK(&sock->lock); + return (ISC_R_CONNREFUSED); + } + + REQUIRE(buflist != NULL); + REQUIRE(!ISC_LIST_EMPTY(*buflist)); + REQUIRE(task != NULL); + REQUIRE(action != NULL); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + iocount = isc_bufferlist_availablecount(buflist); + REQUIRE(iocount > 0); + + INSIST(sock->bound); + + dev = allocate_socketevent(manager->mctx, sock, + ISC_SOCKEVENT_RECVDONE, action, arg); + if (dev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + + /* + * UDP sockets are always partial read + */ + if (sock->type == isc_sockettype_udp) + dev->minimum = 1; + else { + if (minimum == 0) + dev->minimum = iocount; + else + dev->minimum = minimum; + } + + /* + * Move each buffer from the passed in list to our internal one. + */ + buffer = ISC_LIST_HEAD(*buflist); + while (buffer != NULL) { + ISC_LIST_DEQUEUE(*buflist, buffer, link); + ISC_LIST_ENQUEUE(dev->bufferlist, buffer, link); + buffer = ISC_LIST_HEAD(*buflist); + } + + ret = socket_recv(sock, dev, task, 0); + + UNLOCK(&sock->lock); + return (ret); +} + +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); + } + + ISC_LIST_INIT(event->bufferlist); + 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, + 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, isc_msgcat, ISC_MSGSET_SOCKET, + ISC_MSG_PKTINFOPROVIDED, + "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, NULL, 0, 0, + "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, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo) +{ + isc_socketevent_t *dev; + isc_socketmgr_t *manager; + isc_result_t ret; + + REQUIRE(VALID_SOCKET(sock)); + REQUIRE(sock->type != isc_sockettype_fdwatch); + + 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_sendv(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg) +{ + return (isc_socket_sendtov2(sock, buflist, task, action, arg, NULL, + NULL, 0)); +} + +isc_result_t +isc__socket_sendtov(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo) +{ + return (isc_socket_sendtov2(sock, buflist, task, action, arg, address, + pktinfo, 0)); +} + +isc_result_t +isc__socket_sendtov2(isc_socket_t *sock, isc_bufferlist_t *buflist, + isc_task_t *task, isc_taskaction_t action, void *arg, + isc_sockaddr_t *address, struct in6_pktinfo *pktinfo, + unsigned int flags) +{ + isc_socketevent_t *dev; + isc_socketmgr_t *manager; + unsigned int iocount; + isc_buffer_t *buffer; + 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(buflist != NULL); + REQUIRE(!ISC_LIST_EMPTY(*buflist)); + REQUIRE(task != NULL); + REQUIRE(action != NULL); + + manager = sock->manager; + REQUIRE(VALID_MANAGER(manager)); + + iocount = isc_bufferlist_usedcount(buflist); + REQUIRE(iocount > 0); + + dev = allocate_socketevent(manager->mctx, sock, + ISC_SOCKEVENT_SENDDONE, action, arg); + if (dev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + + /* + * Move each buffer from the passed in list to our internal one. + */ + buffer = ISC_LIST_HEAD(*buflist); + while (buffer != NULL) { + ISC_LIST_DEQUEUE(*buflist, buffer, link); + ISC_LIST_ENQUEUE(dev->bufferlist, buffer, link); + buffer = ISC_LIST_HEAD(*buflist); + } + + ret = socket_send(sock, dev, task, address, pktinfo, flags); + UNLOCK(&sock->lock); + return (ret); +} + +isc_result_t +isc__socket_sendto2(isc_socket_t *sock, isc_region_t *region, + isc_task_t *task, + 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); + } + ISC_LIST_INIT(event->bufferlist); + 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, isc_sockaddr_t *sockaddr, + unsigned int 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) %s", sock->fd, + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_FAILED, "failed")); + /* 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: + isc__strerror(bind_errno, strbuf, sizeof(strbuf)); + UNEXPECTED_ERROR(__FILE__, __LINE__, "bind: %s", + strbuf); + return (ISC_R_UNEXPECTED); + } + } + + socket_log(__LINE__, sock, sockaddr, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_BOUND, "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(ISC_PLATFORM_HAVETFO) && defined(TCP_FASTOPEN) + char on = 1; +#endif + + 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); + isc__strerror(WSAGetLastError(), strbuf, sizeof(strbuf)); + + UNEXPECTED_ERROR(__FILE__, __LINE__, "listen: %s", strbuf); + + return (ISC_R_UNEXPECTED); + } + +#if defined(ISC_PLATFORM_HAVETFO) && defined(TCP_FASTOPEN) + if (setsockopt(sock->fd, IPPROTO_TCP, TCP_FASTOPEN, + &on, sizeof(on)) < 0) { + isc__strerror(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 + + socket_log(__LINE__, sock, NULL, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_BOUND, "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)); + if (adev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + 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); + } + 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 lengh + 16 */ + (LPDWORD)&lpo->received_bytes, /* Bytes Recved */ + (LPOVERLAPPED)lpo /* Overlapped structure */ + ); + iocompletionport_update(nsock); + + socket_log(__LINE__, sock, NULL, TRACE, + isc_msgcat, ISC_MSGSET_SOCKET, ISC_MSG_BOUND, + "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, 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: + isc__strerror(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)); + if (cdev == NULL) { + UNLOCK(&sock->lock); + return (ISC_R_NOMEMORY); + } + 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) { + ISC_SOCKADDR_LEN_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) { + isc__strerror(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) == ISC_SOCKCANCEL_RECV) { + 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) == ISC_SOCKCANCEL_SEND) { + 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) == ISC_SOCKCANCEL_ACCEPT) + && !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)) { + + 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) == ISC_SOCKCANCEL_CONNECT) + && !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; + + 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); +} + +bool +isc__socket_isbound(isc_socket_t *sock) { + bool val; + + 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 (false); + } + + val = ((sock->bound) ? true : false); + UNLOCK(&sock->lock); + + return (val); +} + +void +isc__socket_ipv6only(isc_socket_t *sock, bool yes) { +#if defined(IPV6_V6ONLY) + int onoff = yes ? 1 : 0; +#else + UNUSED(yes); +#endif + + 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 +} + +void +isc__socket_dscp(isc_socket_t *sock, isc_dscp_t dscp) { +#if !defined(IP_TOS) && !defined(IPV6_TCLASS) + UNUSED(dscp); +#else + if (dscp < 0) + return; + + dscp <<= 2; + dscp &= 0xff; +#endif + + 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 IPV6_TCLASS + if (sock->pf == AF_INET6) { + (void)setsockopt(sock->fd, IPPROTO_IPV6, IPV6_TCLASS, + (char *)&dscp, sizeof(dscp)); + } +#endif +} + +void +isc__socket_cleanunix(isc_sockaddr_t *addr, bool active) { + UNUSED(addr); + UNUSED(active); +} + +isc_result_t +isc__socket_permunix(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); +} + +void +isc___socketmgr_maxudp(isc_socketmgr_t *manager, int maxudp) { + + UNUSED(manager); + UNUSED(maxudp); +} + +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)); +} + +#ifdef HAVE_LIBXML2 + +static const char * +_socktype(isc_sockettype_t type) { + if (type == isc_sockettype_udp) + return ("udp"); + else if (type == isc_sockettype_tcp) + return ("tcp"); + else if (type == isc_sockettype_unix) + return ("unix"); + else if (type == isc_sockettype_fdwatch) + return ("fdwatch"); + else + return ("not-initialized"); +} + +#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0) +int +isc_socketmgr_renderxml(isc_socketmgr_t *mgr, xmlTextWriterPtr writer) +{ + isc_socket_t *sock = NULL; + char peerbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t addr; + ISC_SOCKADDR_LEN_T len; + int xmlrc; + + LOCK(&mgr->lock); + +#ifndef ISC_PLATFORM_USETHREADS + TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "references")); + TRY0(xmlTextWriterWriteFormatString(writer, "%d", mgr->refs)); + TRY0(xmlTextWriterEndElement(writer)); +#endif + + 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, "%d", + 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 +#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, json_object *stats) { + isc_result_t result = ISC_R_SUCCESS; + isc_socket_t *sock = NULL; + char peerbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_t addr; + ISC_SOCKADDR_LEN_T len; + json_object *obj, *array = json_object_new_array(); + + 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(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 */ + +/* + * Replace ../socket_api.c + */ + +isc_result_t +isc__socket_register(void) { + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_socketmgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx, + isc_socketmgr_t **managerp) +{ + isc_result_t result; + + result = isc_socketmgr_create(mctx, managerp); + + if (result == ISC_R_SUCCESS) + isc_appctx_setsocketmgr(actx, *managerp); + + return (result); +} diff --git a/lib/isc/win32/stdio.c b/lib/isc/win32/stdio.c new file mode 100644 index 0000000..a4c8134 --- /dev/null +++ b/lib/isc/win32/stdio.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <io.h> +#include <errno.h> + +#include <isc/stdio.h> +#include <isc/util.h> + +#include <sys/stat.h> +#include <sys/types.h> + +#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 + r = _fseeki64(f, offset, whence); +#endif + 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 + __int64 r; +#endif + + REQUIRE(offsetp != NULL); + +#ifndef _WIN64 + r = ftell(f); +#else + r = _ftelli64(f); +#endif + 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..bff6fd1 --- /dev/null +++ b/lib/isc/win32/stdtime.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <time.h> + +#include <isc/assertions.h> +#include <isc/stdtime.h> +#include <isc/util.h> + +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); +} diff --git a/lib/isc/win32/strerror.c b/lib/isc/win32/strerror.c new file mode 100644 index 0000000..36c9b79 --- /dev/null +++ b/lib/isc/win32/strerror.c @@ -0,0 +1,452 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <stdio.h> +#include <string.h> +#include <winsock2.h> + +#include <isc/mutex.h> +#include <isc/once.h> +#include <isc/print.h> +#include <isc/strerror.h> +#include <isc/util.h> + +/* + * Forward declarations + */ + +char * +FormatError(int error); + +char * +GetWSAErrorMessage(int errval); + +char * +NTstrerror(int err, BOOL *bfreebuf); + +/* + * We need to do this this way for profiled locks. + */ + +static isc_mutex_t isc_strerror_lock; +static void init_lock(void) { + RUNTIME_CHECK(isc_mutex_init(&isc_strerror_lock) == ISC_R_SUCCESS); +} + +/* + * This routine needs to free up any buffer allocated by FormatMessage + * if that routine gets used. + */ + +void +isc__strerror(int num, char *buf, size_t size) { + char *msg; + BOOL freebuf; + unsigned int unum = num; + static isc_once_t once = ISC_ONCE_INIT; + + REQUIRE(buf != NULL); + + RUNTIME_CHECK(isc_once_do(&once, init_lock) == ISC_R_SUCCESS); + + LOCK(&isc_strerror_lock); + freebuf = FALSE; + msg = NTstrerror(num, &freebuf); + if (msg != NULL) + snprintf(buf, size, "%s", msg); + else + snprintf(buf, size, "Unknown error: %u", unum); + if(freebuf && msg != NULL) { + LocalFree(msg); + } + UNLOCK(&isc_strerror_lock); +} + +/* + * Note this will cause a memory leak unless the memory allocated here + * is freed by calling LocalFree. isc__strerror does this before unlocking. + * This only gets called if there is a system type of error and will likely + * be an unusual event. + */ +char * +FormatError(int error) { + LPVOID lpMsgBuf = NULL; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + /* Default language */ + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &lpMsgBuf, + 0, + NULL); + + return (lpMsgBuf); +} + +/* + * This routine checks the error value and calls the WSA Windows Sockets + * Error message function GetWSAErrorMessage below if it's within that range + * since those messages are not available in the system error messages. + */ +char * +NTstrerror(int err, BOOL *bfreebuf) { + char *retmsg = NULL; + + /* Copy the error value first in case of other errors */ + DWORD errval = err; + + *bfreebuf = FALSE; + + /* Get the Winsock2 error messages */ + if (errval >= WSABASEERR && errval <= (WSABASEERR + 1015)) { + retmsg = GetWSAErrorMessage(errval); + if (retmsg != NULL) + return (retmsg); + } + /* + * If it's not one of the standard Unix error codes, + * try a system error message + */ + if (errval > (DWORD) _sys_nerr) { + *bfreebuf = TRUE; + return (FormatError(errval)); + } else { + return (strerror(errval)); + } +} + +/* + * This is a replacement for perror + */ +void __cdecl +NTperror(char *errmsg) { + /* Copy the error value first in case of other errors */ + int errval = errno; + BOOL bfreebuf = FALSE; + char *msg; + + msg = NTstrerror(errval, &bfreebuf); + fprintf(stderr, "%s: %s\n", errmsg, msg); + if(bfreebuf == TRUE) { + LocalFree(msg); + } + +} + +/* + * Return the error string related to Winsock2 errors. + * This function is necessary since FormatMessage knows nothing about them + * and there is no function to get them. + */ +char * +GetWSAErrorMessage(int errval) { + char *msg; + + switch (errval) { + + case WSAEINTR: + msg = "Interrupted system call"; + break; + + case WSAEBADF: + msg = "Bad file number"; + break; + + case WSAEACCES: + msg = "Permission denied"; + break; + + case WSAEFAULT: + msg = "Bad address"; + break; + + case WSAEINVAL: + msg = "Invalid argument"; + break; + + case WSAEMFILE: + msg = "Too many open sockets"; + break; + + case WSAEWOULDBLOCK: + msg = "Operation would block"; + break; + + case WSAEINPROGRESS: + msg = "Operation now in progress"; + break; + + case WSAEALREADY: + msg = "Operation already in progress"; + break; + + case WSAENOTSOCK: + msg = "Socket operation on non-socket"; + break; + + case WSAEDESTADDRREQ: + msg = "Destination address required"; + break; + + case WSAEMSGSIZE: + msg = "Message too long"; + break; + + case WSAEPROTOTYPE: + msg = "Protocol wrong type for socket"; + break; + + case WSAENOPROTOOPT: + msg = "Bad protocol option"; + break; + + case WSAEPROTONOSUPPORT: + msg = "Protocol not supported"; + break; + + case WSAESOCKTNOSUPPORT: + msg = "Socket type not supported"; + break; + + case WSAEOPNOTSUPP: + msg = "Operation not supported on socket"; + break; + + case WSAEPFNOSUPPORT: + msg = "Protocol family not supported"; + break; + + case WSAEAFNOSUPPORT: + msg = "Address family not supported"; + break; + + case WSAEADDRINUSE: + msg = "Address already in use"; + break; + + case WSAEADDRNOTAVAIL: + msg = "Can't assign requested address"; + break; + + case WSAENETDOWN: + msg = "Network is down"; + break; + + case WSAENETUNREACH: + msg = "Network is unreachable"; + break; + + case WSAENETRESET: + msg = "Net connection reset"; + break; + + case WSAECONNABORTED: + msg = "Software caused connection abort"; + break; + + case WSAECONNRESET: + msg = "Connection reset by peer"; + break; + + case WSAENOBUFS: + msg = "No buffer space available"; + break; + + case WSAEISCONN: + msg = "Socket is already connected"; + break; + + case WSAENOTCONN: + msg = "Socket is not connected"; + break; + + case WSAESHUTDOWN: + msg = "Can't send after socket shutdown"; + break; + + case WSAETOOMANYREFS: + msg = "Too many references: can't splice"; + break; + + case WSAETIMEDOUT: + msg = "Connection timed out"; + break; + + case WSAECONNREFUSED: + msg = "Connection refused"; + break; + + case WSAELOOP: + msg = "Too many levels of symbolic links"; + break; + + case WSAENAMETOOLONG: + msg = "File name too long"; + break; + + case WSAEHOSTDOWN: + msg = "Host is down"; + break; + + case WSAEHOSTUNREACH: + msg = "No route to host"; + break; + + case WSAENOTEMPTY: + msg = "Directory not empty"; + break; + + case WSAEPROCLIM: + msg = "Too many processes"; + break; + + case WSAEUSERS: + msg = "Too many users"; + break; + + case WSAEDQUOT: + msg = "Disc quota exceeded"; + break; + + case WSAESTALE: + msg = "Stale NFS file handle"; + break; + + case WSAEREMOTE: + msg = "Too many levels of remote in path"; + break; + + case WSASYSNOTREADY: + msg = "Network system is unavailable"; + break; + + case WSAVERNOTSUPPORTED: + msg = "Winsock version out of range"; + break; + + case WSANOTINITIALISED: + msg = "WSAStartup not yet called"; + break; + + case WSAEDISCON: + msg = "Graceful shutdown in progress"; + break; +/* + case WSAHOST_NOT_FOUND: + msg = "Host not found"; + break; + + case WSANO_DATA: + msg = "No host data of that type was found"; + break; +*/ + default: + msg = NULL; + break; + } + return (msg); +} + +/* + * These error messages are more informative about CryptAPI Errors than the + * standard error messages + */ + +char * +GetCryptErrorMessage(int errval) { + char *msg; + + switch (errval) { + + case NTE_BAD_FLAGS: + msg = "The dwFlags parameter has an illegal value."; + break; + case NTE_BAD_KEYSET: + msg = "The Registry entry for the key container " + "could not be opened and may not exist."; + break; + case NTE_BAD_KEYSET_PARAM: + msg = "The pszContainer or pszProvider parameter " + "is set to an illegal value."; + break; + case NTE_BAD_PROV_TYPE: + msg = "The value of the dwProvType parameter is out " + "of range. All provider types must be from " + "1 to 999, inclusive."; + break; + case NTE_BAD_SIGNATURE: + msg = "The provider DLL signature did not verify " + "correctly. Either the DLL or the digital " + "signature has been tampered with."; + break; + case NTE_EXISTS: + msg = "The dwFlags parameter is CRYPT_NEWKEYSET, but the key" + " container already exists."; + break; + case NTE_KEYSET_ENTRY_BAD: + msg = "The Registry entry for the pszContainer key container " + "was found (in the HKEY_CURRENT_USER window), but is " + "corrupt. See the section System Administration for " + " etails about CryptoAPI's Registry usage."; + break; + case NTE_KEYSET_NOT_DEF: + msg = "No Registry entry exists in the HKEY_CURRENT_USER " + "window for the key container specified by " + "pszContainer."; + break; + case NTE_NO_MEMORY: + msg = "The CSP ran out of memory during the operation."; + break; + case NTE_PROV_DLL_NOT_FOUND: + msg = "The provider DLL file does not exist or is not on the " + "current path."; + break; + case NTE_PROV_TYPE_ENTRY_BAD: + msg = "The Registry entry for the provider type specified by " + "dwProvType is corrupt. This error may relate to " + "either the user default CSP list or the machine " + "default CSP list. See the section System " + "Administration for details about CryptoAPI's " + "Registry usage."; + break; + case NTE_PROV_TYPE_NO_MATCH: + msg = "The provider type specified by dwProvType does not " + "match the provider type found in the Registry. Note " + "that this error can only occur when pszProvider " + "specifies an actual CSP name."; + break; + case NTE_PROV_TYPE_NOT_DEF: + msg = "No Registry entry exists for the provider type " + "specified by dwProvType."; + break; + case NTE_PROVIDER_DLL_FAIL: + msg = "The provider DLL file could not be loaded, and " + "may not exist. If it exists, then the file is " + "not a valid DLL."; + break; + case NTE_SIGNATURE_FILE_BAD: + msg = "An error occurred while loading the DLL file image, " + "prior to verifying its signature."; + break; + + default: + msg = NULL; + break; + } + return msg; +} + diff --git a/lib/isc/win32/syslog.c b/lib/isc/win32/syslog.c new file mode 100644 index 0000000..071e187 --- /dev/null +++ b/lib/isc/win32/syslog.c @@ -0,0 +1,174 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <stdio.h> +#include <windows.h> +#include <string.h> +#include <stdlib.h> +#include <syslog.h> + +#include <isc/bindevt.h> +#include <isc/result.h> +#include <isc/syslog.h> +#include <isc/util.h> + +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_UUCP + { LOG_UUCP, "uucp" }, +#endif +#ifdef LOG_CRON + { LOG_CRON, "cron" }, +#endif +#ifdef LOG_AUTHPRIV + { LOG_AUTHPRIV, "authpriv" }, +#endif +#ifdef LOG_FTP + { LOG_FTP, "ftp" }, +#endif + { 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..0197165 --- /dev/null +++ b/lib/isc/win32/syslog.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <stdio.h> + +/* 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 <isc/bindevt.h> + +#endif diff --git a/lib/isc/win32/thread.c b/lib/isc/win32/thread.c new file mode 100644 index 0000000..ebc2f29 --- /dev/null +++ b/lib/isc/win32/thread.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <config.h> + +#include <process.h> + +#include <isc/thread.h> +#include <isc/util.h> + +isc_result_t +isc_thread_create(isc_threadfunc_t start, isc_threadarg_t arg, + isc_thread_t *threadp) +{ + isc_thread_t thread; + unsigned int id; + + thread = (isc_thread_t)_beginthreadex(NULL, 0, start, arg, 0, &id); + if (thread == NULL) { + /* XXX */ + return (ISC_R_UNEXPECTED); + } + + *threadp = thread; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_thread_join(isc_thread_t thread, isc_threadresult_t *rp) { + DWORD result; + + result = WaitForSingleObject(thread, INFINITE); + if (result != WAIT_OBJECT_0) { + /* XXX */ + return (ISC_R_UNEXPECTED); + } + if (rp != NULL && !GetExitCodeThread(thread, rp)) { + /* XXX */ + return (ISC_R_UNEXPECTED); + } + (void)CloseHandle(thread); + + return (ISC_R_SUCCESS); +} + +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); +} + +void * +isc_thread_key_getspecific(isc_thread_key_t key) { + return(TlsGetValue(key)); +} + +int +isc_thread_key_setspecific(isc_thread_key_t key, void *value) { + return (TlsSetValue(key, value) ? 0 : GetLastError()); +} + +int +isc_thread_key_create(isc_thread_key_t *key, void (*func)(void *)) { + *key = TlsAlloc(); + + return ((*key != -1) ? 0 : GetLastError()); +} + +int +isc_thread_key_delete(isc_thread_key_t key) { + return (TlsFree(key) ? 0 : GetLastError()); +} diff --git a/lib/isc/win32/time.c b/lib/isc/win32/time.c new file mode 100644 index 0000000..a1c8a84 --- /dev/null +++ b/lib/isc/win32/time.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <config.h> + +#include <errno.h> +#include <limits.h> +#include <stdbool.h> +#include <stddef.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include <windows.h> + +#include <isc/assertions.h> +#include <isc/string.h> +#include <isc/time.h> +#include <isc/tm.h> +#include <isc/util.h> + +/* + * 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; + + i1.QuadPart += (unsigned __int64)nanoseconds/100; + 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_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); + + 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); + + 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); + + 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); + + i1.LowPart = t1->absolute.dwLowDateTime; + i1.HighPart = t1->absolute.dwHighDateTime; + i2.LowPart = t2->absolute.dwLowDateTime; + 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); + + i1.LowPart = t->absolute.dwLowDateTime; + i1.HighPart = t->absolute.dwHighDateTime; + i2.LowPart = temp.dwLowDateTime; + 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_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; + } +} diff --git a/lib/isc/win32/unistd.h b/lib/isc/win32/unistd.h new file mode 100644 index 0000000..170577e --- /dev/null +++ b/lib/isc/win32/unistd.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://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 <io.h> +#ifndef F_OK +#define F_OK 0 +#endif + +#ifndef X_OK +#define X_OK 1 +#endif + +#ifndef W_OK +#define W_OK 2 +#endif + +#ifndef R_OK +#define R_OK 4 +#endif + +#define access _access + +#include <process.h> diff --git a/lib/isc/win32/version.c b/lib/isc/win32/version.c new file mode 100644 index 0000000..d86c781 --- /dev/null +++ b/lib/isc/win32/version.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#include <versions.h> + +#include <isc/version.h> + +LIBISC_EXTERNAL_DATA const char isc_version[] = VERSION; + +LIBISC_EXTERNAL_DATA const unsigned int isc_libinterface = LIBINTERFACE; +LIBISC_EXTERNAL_DATA const unsigned int isc_librevision = LIBREVISION; +LIBISC_EXTERNAL_DATA const unsigned int isc_libage = LIBAGE; diff --git a/lib/isc/win32/win32os.c b/lib/isc/win32/win32os.c new file mode 100644 index 0000000..9af95c8 --- /dev/null +++ b/lib/isc/win32/win32os.c @@ -0,0 +1,117 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <windows.h> + +#ifndef TESTVERSION +#include <isc/win32os.h> +#else +#include <stdio.h> +#include <isc/util.h> +#endif +#include <isc/print.h> + +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 diff --git a/lib/isc/x86_32/Makefile.in b/lib/isc/x86_32/Makefile.in new file mode 100644 index 0000000..419cf9f --- /dev/null +++ b/lib/isc/x86_32/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = include +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/x86_32/include/Makefile.in b/lib/isc/x86_32/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/x86_32/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/x86_32/include/isc/Makefile.in b/lib/isc/x86_32/include/isc/Makefile.in new file mode 100644 index 0000000..97b6b41 --- /dev/null +++ b/lib/isc/x86_32/include/isc/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = atomic.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/x86_32/include/isc/atomic.h b/lib/isc/x86_32/include/isc/atomic.h new file mode 100644 index 0000000..8bcc8b0 --- /dev/null +++ b/lib/isc/x86_32/include/isc/atomic.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_ATOMIC_H +#define ISC_ATOMIC_H 1 + +#include <inttypes.h> + +#include <isc/platform.h> +#include <isc/types.h> + +#ifdef ISC_PLATFORM_USEGCCASM +/* + * This routine atomically increments the value stored in 'p' by 'val', and + * returns the previous value. + */ +static __inline__ int32_t +isc_atomic_xadd(int32_t *p, int32_t val) { + int32_t prev = val; + + __asm__ volatile( +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + "xadd %0, %1" + :"=q"(prev) + :"m"(*p), "0"(prev) + :"memory", "cc"); + + return (prev); +} + +#ifdef ISC_PLATFORM_HAVEXADDQ +static __inline__ int64_t +isc_atomic_xaddq(int64_t *p, int64_t val) { + int64_t prev = val; + + __asm__ volatile( +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + "xaddq %0, %1" + :"=q"(prev) + :"m"(*p), "0"(prev) + :"memory", "cc"); + + return (prev); +} +#endif /* ISC_PLATFORM_HAVEXADDQ */ + +/* + * This routine atomically stores the value 'val' in 'p' (32-bit version). + */ +static __inline__ void +isc_atomic_store(int32_t *p, int32_t val) { + __asm__ volatile( +#ifdef ISC_PLATFORM_USETHREADS + /* + * xchg should automatically lock memory, but we add it + * explicitly just in case (it at least doesn't harm) + */ + "lock;" +#endif + + "xchgl %1, %0" + : + : "r"(val), "m"(*p) + : "memory"); +} + +#ifdef ISC_PLATFORM_HAVEATOMICSTOREQ +/* + * This routine atomically stores the value 'val' in 'p' (64-bit version). + */ +static __inline__ void +isc_atomic_storeq(int64_t *p, int64_t val) { + __asm__ volatile( +#ifdef ISC_PLATFORM_USETHREADS + /* + * xchg should automatically lock memory, but we add it + * explicitly just in case (it at least doesn't harm) + */ + "lock;" +#endif + + "xchgq %1, %0" + : + : "r"(val), "m"(*p) + : "memory"); +} +#endif /* ISC_PLATFORM_HAVEATOMICSTOREQ */ + +/* + * This routine atomically replaces the value in 'p' with 'val', if the + * original value is equal to 'cmpval'. The original value is returned in any + * case. + */ +static __inline__ int32_t +isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val) { + __asm__ volatile( +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + "cmpxchgl %1, %2" + : "=a"(cmpval) + : "r"(val), "m"(*p), "a"(cmpval) + : "memory"); + + return (cmpval); +} + +#elif defined(ISC_PLATFORM_USESTDASM) +/* + * The followings are "generic" assembly code which implements the same + * functionality in case the gcc extension cannot be used. It should be + * better to avoid inlining below, since we directly refer to specific + * positions of the stack frame, which would not actually point to the + * intended address in the embedded mnemonic. + */ +static int32_t +isc_atomic_xadd(int32_t *p, int32_t val) { + (void)(p); + (void)(val); + + __asm ( + "movl 8(%ebp), %ecx\n" + "movl 12(%ebp), %edx\n" +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + "xadd %edx, (%ecx)\n" + + /* + * set the return value directly in the register so that we + * can avoid guessing the correct position in the stack for a + * local variable. + */ + "movl %edx, %eax" + ); +} + +static void +isc_atomic_store(int32_t *p, int32_t val) { + (void)(p); + (void)(val); + + __asm ( + "movl 8(%ebp), %ecx\n" + "movl 12(%ebp), %edx\n" +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + "xchgl (%ecx), %edx\n" + ); +} + +static int32_t +isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val) { + (void)(p); + (void)(cmpval); + (void)(val); + + __asm ( + "movl 8(%ebp), %ecx\n" + "movl 12(%ebp), %eax\n" /* must be %eax for cmpxchgl */ + "movl 16(%ebp), %edx\n" +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + + /* + * If (%ecx) == %eax then (%ecx) := %edx. + % %eax is set to old (%ecx), which will be the return value. + */ + "cmpxchgl %edx, (%ecx)" + ); +} +#else /* !ISC_PLATFORM_USEGCCASM && !ISC_PLATFORM_USESTDASM */ + +#error "unsupported compiler. disable atomic ops by --disable-atomic" + +#endif +#endif /* ISC_ATOMIC_H */ diff --git a/lib/isc/x86_64/Makefile.in b/lib/isc/x86_64/Makefile.in new file mode 100644 index 0000000..419cf9f --- /dev/null +++ b/lib/isc/x86_64/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = include +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/isc/x86_64/include/Makefile.in b/lib/isc/x86_64/include/Makefile.in new file mode 100644 index 0000000..d33c0fc --- /dev/null +++ b/lib/isc/x86_64/include/Makefile.in @@ -0,0 +1,17 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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/x86_64/include/isc/Makefile.in b/lib/isc/x86_64/include/isc/Makefile.in new file mode 100644 index 0000000..97b6b41 --- /dev/null +++ b/lib/isc/x86_64/include/isc/Makefile.in @@ -0,0 +1,34 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# 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 http://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 = atomic.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 ${DESTDIR}${includedir}/isc || exit 1; \ + done + +uninstall:: + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/isc/$$i || exit 1; \ + done diff --git a/lib/isc/x86_64/include/isc/atomic.h b/lib/isc/x86_64/include/isc/atomic.h new file mode 100644 index 0000000..b2d7880 --- /dev/null +++ b/lib/isc/x86_64/include/isc/atomic.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * 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 http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + + +#ifndef ISC_ATOMIC_H +#define ISC_ATOMIC_H 1 + +#include <inttypes.h> + +#include <isc/platform.h> +#include <isc/types.h> + +#ifdef ISC_PLATFORM_USEGCCASM + +/* We share the gcc-version with x86_32 */ +#error "impossible case. check build configuration" + +#elif defined(ISC_PLATFORM_USESTDASM) +/* + * The followings are "generic" assembly code which implements the same + * functionality in case the gcc extension cannot be used. It should be + * better to avoid inlining below, since we directly refer to specific + * registers for arguments, which would not actually correspond to the + * intended address or value in the embedded mnemonic. + */ + +static int32_t +isc_atomic_xadd(int32_t *p, int32_t val) { + (void)(p); + (void)(val); + + __asm ( + "movq %rdi, %rdx\n" + "movl %esi, %eax\n" +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + "xadd %eax, (%rdx)\n" + /* + * XXX: assume %eax will be used as the return value. + */ + ); +} + +#ifdef ISC_PLATFORM_HAVEXADDQ +static int64_t +isc_atomic_xaddq(int64_t *p, int64_t val) { + (void)(p); + (void)(val); + + __asm ( + "movq %rdi, %rdx\n" + "movq %rsi, %rax\n" +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + "xaddq %rax, (%rdx)\n" + /* + * XXX: assume %rax will be used as the return value. + */ + ); +} +#endif + +static void +isc_atomic_store(int32_t *p, int32_t val) { + (void)(p); + (void)(val); + + __asm ( + "movq %rdi, %rax\n" + "movl %esi, %edx\n" +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + "xchgl (%rax), %edx\n" + ); +} + +#ifdef ISC_PLATFORM_HAVEATOMICSTOREQ +static void +isc_atomic_storeq(int64_t *p, int64_t val) { + (void)(p); + (void)(val); + + __asm ( + "movq %rdi, %rax\n" + "movq %rsi, %rdx\n" +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + "xchgq (%rax), %rdx\n" + ); +} +#endif + +static int32_t +isc_atomic_cmpxchg(int32_t *p, int32_t cmpval, int32_t val) { + (void)(p); + (void)(cmpval); + (void)(val); + + __asm ( + /* + * p is %rdi, cmpval is %esi, val is %edx. + */ + "movl %edx, %ecx\n" + "movl %esi, %eax\n" + "movq %rdi, %rdx\n" + +#ifdef ISC_PLATFORM_USETHREADS + "lock;" +#endif + /* + * If [%rdi] == %eax then [%rdi] := %ecx (equal to %edx + * from above), and %eax is untouched (equal to %esi) + * from above. + * + * Else if [%rdi] != %eax then [%rdi] := [%rdi] + * (rewritten in write cycle) and %eax := [%rdi]. + */ + "cmpxchgl %ecx, (%rdx)" + ); +} + +#else /* !ISC_PLATFORM_USEGCCASM && !ISC_PLATFORM_USESTDASM */ + +#error "unsupported compiler. disable atomic ops by --disable-atomic" + +#endif +#endif /* ISC_ATOMIC_H */ |