diff options
Diffstat (limited to 'lib/irs')
52 files changed, 5203 insertions, 0 deletions
diff --git a/lib/irs/Kyuafile b/lib/irs/Kyuafile new file mode 100644 index 0000000..c796010 --- /dev/null +++ b/lib/irs/Kyuafile @@ -0,0 +1,15 @@ +-- Copyright (C) Internet Systems Consortium, Inc. ("ISC") +-- +-- SPDX-License-Identifier: MPL-2.0 +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, you can obtain one at https://mozilla.org/MPL/2.0/. +-- +-- See the COPYRIGHT file distributed with this work for additional +-- information regarding copyright ownership. + +syntax(2) +test_suite('bind9') + +include('tests/Kyuafile') diff --git a/lib/irs/Makefile.in b/lib/irs/Makefile.in new file mode 100644 index 0000000..6152e9a --- /dev/null +++ b/lib/irs/Makefile.in @@ -0,0 +1,90 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +VERSION=@BIND9_VERSION@ + +@BIND9_MAKE_INCLUDES@ + +CINCLUDES = -I. -I./include -I${srcdir}/include \ + ${DNS_INCLUDES} ${ISC_INCLUDES} \ + ${ISCCFG_INCLUDES} \ + ${OPENSSL_CFLAGS} + +CDEFINES = +CWARNINGS = + +ISCLIBS = ../../lib/isc/libisc.@A@ @NO_LIBTOOL_ISCLIBS@ + +ISCDEPLIBS = ../../lib/isc/libisc.@A@ + +DNSLIBS = ../../lib/dns/libdns.@A@ @NO_LIBTOOL_DNSLIBS@ + +DNSDEPLIBS = ../../lib/dns/libdns.@A@ + +ISCCFGLIBS = ../../lib/isccfg/libisccfg.@A@ + +ISCCFGDEPLIBS = ../../lib/isccfg/libisccfg.@A@ + +LIBS = @LIBS@ + +# Alphabetically +OBJS = context.@O@ \ + dnsconf.@O@ \ + gai_strerror.@O@ getaddrinfo.@O@ getnameinfo.@O@ \ + resconf.@O@ + +# Alphabetically +SRCS = context.c \ + dnsconf.c \ + gai_strerror.c getaddrinfo.c getnameinfo.c \ + resconf.c + +SUBDIRS = include +TESTDIRS = @UNITTESTS@ +TARGETS = timestamp + +@BIND9_MAKE_RULES@ + +version.@O@: version.c + ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \ + -DVERSION=\"${VERSION}\" \ + -c ${srcdir}/version.c + +libirs.@SA@: ${OBJS} version.@O@ + ${AR} ${ARFLAGS} $@ ${OBJS} version.@O@ + ${RANLIB} $@ + +libirs.la: ${OBJS} version.@O@ + ${LIBTOOL_MODE_LINK} \ + ${CC} ${ALL_CFLAGS} ${LDFLAGS} -o libirs.la -rpath ${libdir} \ + -release "${VERSION}" \ + ${OBJS} version.@O@ ${ISCLIBS} ${DNSLIBS} ${ISCCFGLIBS} ${LIBS} + +timestamp: libirs.@A@ + touch timestamp + +testdirs: libirs.@A@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${libdir} + +install:: timestamp installdirs + ${LIBTOOL_MODE_INSTALL} ${INSTALL_LIBRARY} libirs.@A@ ${DESTDIR}${libdir} + +uninstall:: + ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${libdir}/libirs.@A@ + +clean distclean:: + rm -f libirs.@A@ libirs.la timestamp diff --git a/lib/irs/context.c b/lib/irs/context.c new file mode 100644 index 0000000..e783358 --- /dev/null +++ b/lib/irs/context.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <stdbool.h> + +#include <isc/app.h> +#include <isc/lib.h> +#include <isc/magic.h> +#include <isc/managers.h> +#include <isc/mem.h> +#include <isc/netmgr.h> +#include <isc/once.h> +#include <isc/socket.h> +#include <isc/task.h> +#include <isc/thread.h> +#include <isc/timer.h> +#include <isc/util.h> + +#include <dns/client.h> +#include <dns/lib.h> + +#include <irs/context.h> +#include <irs/dnsconf.h> +#include <irs/resconf.h> + +#define IRS_CONTEXT_MAGIC ISC_MAGIC('I', 'R', 'S', 'c') +#define IRS_CONTEXT_VALID(c) ISC_MAGIC_VALID(c, IRS_CONTEXT_MAGIC) + +#ifndef RESOLV_CONF +/*% location of resolve.conf */ +#define RESOLV_CONF "/etc/resolv.conf" +#endif /* ifndef RESOLV_CONF */ + +#ifndef DNS_CONF +/*% location of dns.conf */ +#define DNS_CONF "/etc/dns.conf" +#endif /* ifndef DNS_CONF */ + +ISC_THREAD_LOCAL irs_context_t *irs_context = NULL; + +struct irs_context { + /* + * An IRS context is a thread-specific object, and does not need to + * be locked. + */ + unsigned int magic; + isc_mem_t *mctx; + isc_appctx_t *actx; + isc_nm_t *netmgr; + isc_taskmgr_t *taskmgr; + isc_task_t *task; + isc_socketmgr_t *socketmgr; + isc_timermgr_t *timermgr; + dns_client_t *dnsclient; + irs_resconf_t *resconf; + irs_dnsconf_t *dnsconf; +}; + +static void +ctxs_destroy(isc_mem_t **mctxp, isc_appctx_t **actxp, isc_nm_t **netmgrp, + isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, + isc_timermgr_t **timermgrp) { + isc_managers_destroy(netmgrp == NULL ? NULL : netmgrp, + taskmgrp == NULL ? NULL : taskmgrp); + + if (timermgrp != NULL) { + isc_timermgr_destroy(timermgrp); + } + + if (socketmgrp != NULL) { + isc_socketmgr_destroy(socketmgrp); + } + + if (actxp != NULL) { + isc_appctx_destroy(actxp); + } + + if (mctxp != NULL) { + isc_mem_destroy(mctxp); + } +} + +static isc_result_t +ctxs_init(isc_mem_t **mctxp, isc_appctx_t **actxp, isc_nm_t **netmgrp, + isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp, + isc_timermgr_t **timermgrp) { + isc_result_t result; + + isc_mem_create(mctxp); + + result = isc_appctx_create(*mctxp, actxp); + if (result != ISC_R_SUCCESS) { + goto fail; + } + + result = isc_managers_create(*mctxp, 1, 0, netmgrp, taskmgrp); + if (result != ISC_R_SUCCESS) { + goto fail; + } + + result = isc_socketmgr_create(*mctxp, socketmgrp); + if (result != ISC_R_SUCCESS) { + goto fail; + } + + result = isc_timermgr_create(*mctxp, timermgrp); + if (result != ISC_R_SUCCESS) { + goto fail; + } + + return (ISC_R_SUCCESS); + +fail: + ctxs_destroy(mctxp, actxp, netmgrp, taskmgrp, socketmgrp, timermgrp); + + return (result); +} + +isc_result_t +irs_context_get(irs_context_t **contextp) { + isc_result_t result; + + REQUIRE(contextp != NULL && *contextp == NULL); + + if (irs_context == NULL) { + result = irs_context_create(&irs_context); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + *contextp = irs_context; + + return (ISC_R_SUCCESS); +} + +isc_result_t +irs_context_create(irs_context_t **contextp) { + isc_result_t result; + irs_context_t *context; + isc_appctx_t *actx = NULL; + isc_mem_t *mctx = NULL; + isc_nm_t *netmgr = NULL; + isc_taskmgr_t *taskmgr = NULL; + isc_socketmgr_t *socketmgr = NULL; + isc_timermgr_t *timermgr = NULL; + dns_client_t *client = NULL; + isc_sockaddrlist_t *nameservers; + irs_dnsconf_dnskeylist_t *trustedkeys; + irs_dnsconf_dnskey_t *trustedkey; + + isc_lib_register(); + result = dns_lib_init(); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = ctxs_init(&mctx, &actx, &netmgr, &taskmgr, &socketmgr, + &timermgr); + if (result != ISC_R_SUCCESS) { + return (result); + } + + result = isc_app_ctxstart(actx); + if (result != ISC_R_SUCCESS) { + ctxs_destroy(&mctx, &actx, &netmgr, &taskmgr, &socketmgr, + &timermgr); + return (result); + } + + context = isc_mem_get(mctx, sizeof(*context)); + + context->mctx = mctx; + context->actx = actx; + context->taskmgr = taskmgr; + context->socketmgr = socketmgr; + context->timermgr = timermgr; + context->resconf = NULL; + context->dnsconf = NULL; + context->task = NULL; + result = isc_task_create(taskmgr, 0, &context->task); + if (result != ISC_R_SUCCESS) { + goto fail; + } + + /* Create a DNS client object */ + result = dns_client_create(mctx, actx, taskmgr, socketmgr, timermgr, 0, + &client, NULL, NULL); + if (result != ISC_R_SUCCESS) { + goto fail; + } + context->dnsclient = client; + + /* Read resolver configuration file */ + result = irs_resconf_load(mctx, RESOLV_CONF, &context->resconf); + if (result != ISC_R_SUCCESS) { + goto fail; + } + /* Set nameservers */ + nameservers = irs_resconf_getnameservers(context->resconf); + result = dns_client_setservers(client, dns_rdataclass_in, NULL, + nameservers); + if (result != ISC_R_SUCCESS) { + goto fail; + } + + /* Read advanced DNS configuration (if any) */ + result = irs_dnsconf_load(mctx, DNS_CONF, &context->dnsconf); + if (result != ISC_R_SUCCESS) { + goto fail; + } + trustedkeys = irs_dnsconf_gettrustedkeys(context->dnsconf); + for (trustedkey = ISC_LIST_HEAD(*trustedkeys); trustedkey != NULL; + trustedkey = ISC_LIST_NEXT(trustedkey, link)) + { + result = dns_client_addtrustedkey( + client, dns_rdataclass_in, dns_rdatatype_dnskey, + trustedkey->keyname, trustedkey->keydatabuf); + if (result != ISC_R_SUCCESS) { + goto fail; + } + } + + context->magic = IRS_CONTEXT_MAGIC; + *contextp = context; + + return (ISC_R_SUCCESS); + +fail: + if (context->task != NULL) { + isc_task_detach(&context->task); + } + if (context->resconf != NULL) { + irs_resconf_destroy(&context->resconf); + } + if (context->dnsconf != NULL) { + irs_dnsconf_destroy(&context->dnsconf); + } + if (client != NULL) { + dns_client_destroy(&client); + } + ctxs_destroy(NULL, &actx, &netmgr, &taskmgr, &socketmgr, &timermgr); + isc_mem_putanddetach(&mctx, context, sizeof(*context)); + + return (result); +} + +void +irs_context_destroy(irs_context_t **contextp) { + irs_context_t *context; + + REQUIRE(contextp != NULL); + context = *contextp; + REQUIRE(IRS_CONTEXT_VALID(context)); + *contextp = irs_context = NULL; + + isc_task_detach(&context->task); + irs_dnsconf_destroy(&context->dnsconf); + irs_resconf_destroy(&context->resconf); + dns_client_destroy(&context->dnsclient); + + ctxs_destroy(NULL, &context->actx, &context->netmgr, &context->taskmgr, + &context->socketmgr, &context->timermgr); + + context->magic = 0; + + isc_mem_putanddetach(&context->mctx, context, sizeof(*context)); +} + +isc_mem_t * +irs_context_getmctx(irs_context_t *context) { + REQUIRE(IRS_CONTEXT_VALID(context)); + + return (context->mctx); +} + +isc_appctx_t * +irs_context_getappctx(irs_context_t *context) { + REQUIRE(IRS_CONTEXT_VALID(context)); + + return (context->actx); +} + +isc_taskmgr_t * +irs_context_gettaskmgr(irs_context_t *context) { + REQUIRE(IRS_CONTEXT_VALID(context)); + + return (context->taskmgr); +} + +isc_timermgr_t * +irs_context_gettimermgr(irs_context_t *context) { + REQUIRE(IRS_CONTEXT_VALID(context)); + + return (context->timermgr); +} + +isc_task_t * +irs_context_gettask(irs_context_t *context) { + REQUIRE(IRS_CONTEXT_VALID(context)); + + return (context->task); +} + +dns_client_t * +irs_context_getdnsclient(irs_context_t *context) { + REQUIRE(IRS_CONTEXT_VALID(context)); + + return (context->dnsclient); +} + +irs_resconf_t * +irs_context_getresconf(irs_context_t *context) { + REQUIRE(IRS_CONTEXT_VALID(context)); + + return (context->resconf); +} + +irs_dnsconf_t * +irs_context_getdnsconf(irs_context_t *context) { + REQUIRE(IRS_CONTEXT_VALID(context)); + + return (context->dnsconf); +} diff --git a/lib/irs/dnsconf.c b/lib/irs/dnsconf.c new file mode 100644 index 0000000..a1421d2 --- /dev/null +++ b/lib/irs/dnsconf.c @@ -0,0 +1,291 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <inttypes.h> +#include <string.h> + +#include <isc/base64.h> +#include <isc/buffer.h> +#include <isc/file.h> +#include <isc/mem.h> +#include <isc/util.h> + +#include <dns/fixedname.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdatastruct.h> + +#include <isccfg/dnsconf.h> + +#include <irs/dnsconf.h> + +#define IRS_DNSCONF_MAGIC ISC_MAGIC('D', 'c', 'f', 'g') +#define IRS_DNSCONF_VALID(c) ISC_MAGIC_VALID(c, IRS_DNSCONF_MAGIC) + +/*! + * configuration data structure + */ + +struct irs_dnsconf { + unsigned int magic; + isc_mem_t *mctx; + irs_dnsconf_dnskeylist_t trusted_keylist; +}; + +static isc_result_t +configure_key(isc_mem_t *mctx, const cfg_obj_t *key, irs_dnsconf_t *conf, + dns_rdataclass_t rdclass) { + isc_result_t result; + uint32_t flags, proto, alg; + dns_fixedname_t fkeyname; + dns_name_t *keyname_base = NULL, *keyname = NULL; + const char *keystr = NULL, *keynamestr = NULL; + unsigned char keydata[4096]; + isc_buffer_t keydatabuf_base, *keydatabuf = NULL; + dns_rdata_dnskey_t keystruct; + unsigned char rrdata[4096]; + isc_buffer_t rrdatabuf; + isc_region_t r; + isc_buffer_t namebuf; + irs_dnsconf_dnskey_t *keyent = NULL; + + flags = cfg_obj_asuint32(cfg_tuple_get(key, "flags")); + proto = cfg_obj_asuint32(cfg_tuple_get(key, "protocol")); + alg = cfg_obj_asuint32(cfg_tuple_get(key, "algorithm")); + keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name")); + + keystruct.common.rdclass = rdclass; + keystruct.common.rdtype = dns_rdatatype_dnskey; + keystruct.mctx = NULL; + ISC_LINK_INIT(&keystruct.common, link); + + if (flags > 0xffff) { + return (ISC_R_RANGE); + } + if (proto > 0xff) { + return (ISC_R_RANGE); + } + if (alg > 0xff) { + return (ISC_R_RANGE); + } + keystruct.flags = (uint16_t)flags; + keystruct.protocol = (uint8_t)proto; + keystruct.algorithm = (uint8_t)alg; + + isc_buffer_init(&keydatabuf_base, keydata, sizeof(keydata)); + isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata)); + + /* Configure key value */ + keystr = cfg_obj_asstring(cfg_tuple_get(key, "key")); + result = isc_base64_decodestring(keystr, &keydatabuf_base); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_usedregion(&keydatabuf_base, &r); + keystruct.datalen = r.length; + keystruct.data = r.base; + + result = dns_rdata_fromstruct(NULL, keystruct.common.rdclass, + keystruct.common.rdtype, &keystruct, + &rrdatabuf); + if (result != ISC_R_SUCCESS) { + return (result); + } + isc_buffer_usedregion(&rrdatabuf, &r); + isc_buffer_allocate(mctx, &keydatabuf, r.length); + result = isc_buffer_copyregion(keydatabuf, &r); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + /* Configure key name */ + keyname_base = dns_fixedname_initname(&fkeyname); + isc_buffer_constinit(&namebuf, keynamestr, strlen(keynamestr)); + isc_buffer_add(&namebuf, strlen(keynamestr)); + result = dns_name_fromtext(keyname_base, &namebuf, dns_rootname, 0, + NULL); + if (result != ISC_R_SUCCESS) { + return (result); + } + keyname = isc_mem_get(mctx, sizeof(*keyname)); + dns_name_init(keyname, NULL); + dns_name_dup(keyname_base, mctx, keyname); + + /* Add the key data to the list */ + keyent = isc_mem_get(mctx, sizeof(*keyent)); + keyent->keyname = keyname; + keyent->keydatabuf = keydatabuf; + + ISC_LIST_APPEND(conf->trusted_keylist, keyent, link); + +cleanup: + if (keydatabuf != NULL) { + isc_buffer_free(&keydatabuf); + } + if (keyname != NULL) { + isc_mem_put(mctx, keyname, sizeof(*keyname)); + } + + return (result); +} + +static isc_result_t +configure_keygroup(irs_dnsconf_t *conf, const cfg_obj_t *keys, + dns_rdataclass_t rdclass) { + isc_result_t result; + const cfg_obj_t *key, *keylist; + const cfg_listelt_t *element, *element2; + isc_mem_t *mctx = conf->mctx; + + for (element = cfg_list_first(keys); element != NULL; + element = cfg_list_next(element)) + { + keylist = cfg_listelt_value(element); + for (element2 = cfg_list_first(keylist); element2 != NULL; + element2 = cfg_list_next(element2)) + { + key = cfg_listelt_value(element2); + result = configure_key(mctx, key, conf, rdclass); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +configure_dnsseckeys(irs_dnsconf_t *conf, cfg_obj_t *cfgobj, + dns_rdataclass_t rdclass) { + isc_result_t result; + const cfg_obj_t *keys = NULL; + + cfg_map_get(cfgobj, "trusted-keys", &keys); + if (keys == NULL) { + return (ISC_R_SUCCESS); + } + + result = configure_keygroup(conf, keys, rdclass); + if (result != ISC_R_SUCCESS) { + return (result); + } + + keys = NULL; + cfg_map_get(cfgobj, "trust-anchors", &keys); + if (keys == NULL) { + return (ISC_R_SUCCESS); + } + + result = configure_keygroup(conf, keys, rdclass); + if (result != ISC_R_SUCCESS) { + return (result); + } + + keys = NULL; + cfg_map_get(cfgobj, "managed-keys", &keys); + if (keys == NULL) { + return (ISC_R_SUCCESS); + } + + result = configure_keygroup(conf, keys, rdclass); + if (result != ISC_R_SUCCESS) { + return (result); + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +irs_dnsconf_load(isc_mem_t *mctx, const char *filename, irs_dnsconf_t **confp) { + irs_dnsconf_t *conf; + cfg_parser_t *parser = NULL; + cfg_obj_t *cfgobj = NULL; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(confp != NULL && *confp == NULL); + + conf = isc_mem_get(mctx, sizeof(*conf)); + + conf->mctx = mctx; + ISC_LIST_INIT(conf->trusted_keylist); + + /* + * If the specified file does not exist, we'll simply with an empty + * configuration. + */ + if (!isc_file_exists(filename)) { + goto cleanup; + } + + result = cfg_parser_create(mctx, NULL, &parser); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = cfg_parse_file(parser, filename, &cfg_type_dnsconf, &cfgobj); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = configure_dnsseckeys(conf, cfgobj, dns_rdataclass_in); + +cleanup: + if (parser != NULL) { + if (cfgobj != NULL) { + cfg_obj_destroy(parser, &cfgobj); + } + cfg_parser_destroy(&parser); + } + + conf->magic = IRS_DNSCONF_MAGIC; + + if (result == ISC_R_SUCCESS) { + *confp = conf; + } else { + irs_dnsconf_destroy(&conf); + } + + return (result); +} + +void +irs_dnsconf_destroy(irs_dnsconf_t **confp) { + irs_dnsconf_t *conf; + irs_dnsconf_dnskey_t *keyent; + + REQUIRE(confp != NULL); + conf = *confp; + *confp = NULL; + REQUIRE(IRS_DNSCONF_VALID(conf)); + + while ((keyent = ISC_LIST_HEAD(conf->trusted_keylist)) != NULL) { + ISC_LIST_UNLINK(conf->trusted_keylist, keyent, link); + + isc_buffer_free(&keyent->keydatabuf); + dns_name_free(keyent->keyname, conf->mctx); + isc_mem_put(conf->mctx, keyent->keyname, sizeof(dns_name_t)); + isc_mem_put(conf->mctx, keyent, sizeof(*keyent)); + } + + isc_mem_put(conf->mctx, conf, sizeof(*conf)); +} + +irs_dnsconf_dnskeylist_t * +irs_dnsconf_gettrustedkeys(irs_dnsconf_t *conf) { + REQUIRE(IRS_DNSCONF_VALID(conf)); + + return (&conf->trusted_keylist); +} diff --git a/lib/irs/gai_strerror.c b/lib/irs/gai_strerror.c new file mode 100644 index 0000000..a2b989f --- /dev/null +++ b/lib/irs/gai_strerror.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file gai_strerror.c + * gai_strerror() returns an error message corresponding to an + * error code returned by getaddrinfo() and getnameinfo(). The following error + * codes and their meaning are defined in + * \link netdb.h include/irs/netdb.h.\endlink + * This implementation is almost an exact copy of lwres/gai_sterror.c except + * that it catches up the latest API standard, RFC3493. + * + * \li #EAI_ADDRFAMILY address family for hostname not supported + * \li #EAI_AGAIN temporary failure in name resolution + * \li #EAI_BADFLAGS invalid value for ai_flags + * \li #EAI_FAIL non-recoverable failure in name resolution + * \li #EAI_FAMILY ai_family not supported + * \li #EAI_MEMORY memory allocation failure + * \li #EAI_NODATA no address associated with hostname (obsoleted in RFC3493) + * \li #EAI_NONAME hostname nor servname provided, or not known + * \li #EAI_SERVICE servname not supported for ai_socktype + * \li #EAI_SOCKTYPE ai_socktype not supported + * \li #EAI_SYSTEM system error returned in errno + * \li #EAI_BADHINTS Invalid value for hints (non-standard) + * \li #EAI_PROTOCOL Resolved protocol is unknown (non-standard) + * \li #EAI_OVERFLOW Argument buffer overflow + * \li #EAI_INSECUREDATA Insecure Data (experimental) + * + * The message invalid error code is returned if ecode is out of range. + * + * ai_flags, ai_family and ai_socktype are elements of the struct + * addrinfo used by lwres_getaddrinfo(). + * + * \section gai_strerror_see See Also + * + * strerror(), getaddrinfo(), getnameinfo(), RFC3493. + */ + +#include <isc/net.h> + +#include <irs/netdb.h> + +/*% Text of error messages. */ +static const char *gai_messages[] = { "no error", + "address family for hostname not " + "supported", + "temporary failure in name resolution", + "invalid value for ai_flags", + "non-recoverable failure in name " + "resolution", + "ai_family not supported", + "memory allocation failure", + "no address associated with hostname", + "hostname nor servname provided, or not " + "known", + "servname not supported for ai_socktype", + "ai_socktype not supported", + "system error returned in errno", + "bad hints", + "bad protocol", + "argument buffer overflow", + "insecure data provided" }; + +/*% + * Returns an error message corresponding to an error code returned by + * getaddrinfo() and getnameinfo() + */ +#if defined _WIN32 +char * +#else /* if defined _WIN32 */ +const char * +#endif /* if defined _WIN32 */ +gai_strerror(int ecode) { + union { + const char *const_ptr; + char *deconst_ptr; + } ptr; + + if ((ecode < 0) || + (ecode >= (int)(sizeof(gai_messages) / sizeof(*gai_messages)))) + { + ptr.const_ptr = "invalid error code"; + } else { + ptr.const_ptr = gai_messages[ecode]; + } + return (ptr.deconst_ptr); +} diff --git a/lib/irs/getaddrinfo.c b/lib/irs/getaddrinfo.c new file mode 100644 index 0000000..f450a55 --- /dev/null +++ b/lib/irs/getaddrinfo.c @@ -0,0 +1,1365 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +/** + * getaddrinfo() is used to get a list of IP addresses and port + * numbers for host hostname and service servname as defined in RFC3493. + * hostname and servname are pointers to null-terminated strings + * or NULL. hostname is either a host name or a numeric host address + * string: a dotted decimal IPv4 address or an IPv6 address. servname is + * either a decimal port number or a service name as listed in + * /etc/services. + * + * If the operating system does not provide a struct addrinfo, the + * following structure is used: + * + * \code + * 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 + * }; + * \endcode + * + * + * hints is an optional pointer to a struct addrinfo. This structure can + * be used to provide hints concerning the type of socket that the caller + * supports or wishes to use. The caller can supply the following + * structure elements in *hints: + * + * <ul> + * <li>ai_family: + * The protocol family that should be used. When ai_family is set + * to PF_UNSPEC, it means the caller will accept any protocol + * family supported by the operating system.</li> + * + * <li>ai_socktype: + * denotes the type of socket -- SOCK_STREAM, SOCK_DGRAM or + * SOCK_RAW -- that is wanted. When ai_socktype is zero the caller + * will accept any socket type.</li> + * + * <li>ai_protocol: + * indicates which transport protocol is wanted: IPPROTO_UDP or + * IPPROTO_TCP. If ai_protocol is zero the caller will accept any + * protocol.</li> + * + * <li>ai_flags: + * Flag bits. If the AI_CANONNAME bit is set, a successful call to + * getaddrinfo() will return a null-terminated string + * containing the canonical name of the specified hostname in + * ai_canonname of the first addrinfo structure returned. Setting + * the AI_PASSIVE bit indicates that the returned socket address + * structure is intended for used in a call to bind(2). In this + * case, if the hostname argument is a NULL pointer, then the IP + * address portion of the socket address structure will be set to + * INADDR_ANY for an IPv4 address or IN6ADDR_ANY_INIT for an IPv6 + * address.<br /><br /> + * + * When ai_flags does not set the AI_PASSIVE bit, the returned + * socket address structure will be ready for use in a call to + * connect(2) for a connection-oriented protocol or connect(2), + * sendto(2), or sendmsg(2) if a connectionless protocol was + * chosen. The IP address portion of the socket address structure + * will be set to the loopback address if hostname is a NULL + * pointer and AI_PASSIVE is not set in ai_flags.<br /><br /> + * + * If ai_flags is set to AI_NUMERICHOST it indicates that hostname + * should be treated as a numeric string defining an IPv4 or IPv6 + * address and no name resolution should be attempted. + * </li></ul> + * + * All other elements of the struct addrinfo passed via hints must be + * zero. + * + * A hints of NULL is treated as if the caller provided a struct addrinfo + * initialized to zero with ai_familyset to PF_UNSPEC. + * + * After a successful call to getaddrinfo(), *res is a pointer to a + * linked list of one or more addrinfo structures. Each struct addrinfo + * in this list cn be processed by following the ai_next pointer, until a + * NULL pointer is encountered. The three members ai_family, ai_socktype, + * and ai_protocol in each returned addrinfo structure contain the + * corresponding arguments for a call to socket(2). For each addrinfo + * structure in the list, the ai_addr member points to a filled-in socket + * address structure of length ai_addrlen. + * + * All of the information returned by getaddrinfo() is dynamically + * allocated: the addrinfo structures, and the socket address structures + * and canonical host name strings pointed to by the addrinfostructures. + * Memory allocated for the dynamically allocated structures created by a + * successful call to getaddrinfo() is released by freeaddrinfo(). + * ai is a pointer to a struct addrinfo created by a call to getaddrinfo(). + * + * \section irsreturn RETURN VALUES + * + * getaddrinfo() returns zero on success or one of the error codes + * listed in gai_strerror() if an error occurs. If both hostname and + * servname are NULL getaddrinfo() returns #EAI_NONAME. + * + * \section irssee SEE ALSO + * + * getaddrinfo(), freeaddrinfo(), + * gai_strerror(), RFC3493, getservbyname(3), connect(2), + * sendto(2), sendmsg(2), socket(2). + */ + +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _WIN32 +#include <windows.h> +#include <winsock2.h> +#include <ws2tcpip.h> +#endif /* ifdef _WIN32 */ + +#include <isc/app.h> +#include <isc/buffer.h> +#include <isc/lib.h> +#include <isc/mem.h> +#include <isc/mutex.h> +#include <isc/print.h> +#include <isc/sockaddr.h> +#include <isc/string.h> +#include <isc/util.h> + +#include <dns/client.h> +#include <dns/fixedname.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataset.h> +#include <dns/rdatastruct.h> +#include <dns/rdatatype.h> +#include <dns/result.h> + +#include <irs/context.h> +#include <irs/netdb.h> +#include <irs/resconf.h> + +#define SA(addr) ((struct sockaddr *)(addr)) +#define SIN(addr) ((struct sockaddr_in *)(addr)) +#define SIN6(addr) ((struct sockaddr_in6 *)(addr)) +#define SLOCAL(addr) ((struct sockaddr_un *)(addr)) + +/*! \struct addrinfo + */ +static struct addrinfo * +ai_concat(struct addrinfo *ai1, struct addrinfo *ai2), + *ai_reverse(struct addrinfo *oai), + *ai_clone(struct addrinfo *oai, int family), + *ai_alloc(int family, int addrlen); +#ifdef AF_LOCAL +static int +get_local(const char *name, int socktype, struct addrinfo **res); +#endif /* ifdef AF_LOCAL */ + +static int +resolve_name(int family, const char *hostname, int flags, struct addrinfo **aip, + int socktype, int port); + +static int +add_ipv4(const char *hostname, int flags, struct addrinfo **aip, int socktype, + int port); +static int +add_ipv6(const char *hostname, int flags, struct addrinfo **aip, int socktype, + int port); +static void +set_order(int, int (**)(const char *, int, struct addrinfo **, int, int)); +static void +_freeaddrinfo(struct addrinfo *ai); + +#define FOUND_IPV4 0x1 +#define FOUND_IPV6 0x2 +#define FOUND_MAX 2 + +/*% + * Try converting the scope identifier in 'src' to a network interface index. + * Upon success, return true and store the resulting index in 'dst'. Upon + * failure, return false. + */ +static bool +parse_scopeid(const char *src, uint32_t *dst) { + uint32_t scopeid = 0; + + REQUIRE(src != NULL); + REQUIRE(dst != NULL); + +#ifdef HAVE_IF_NAMETOINDEX + /* + * Try using if_nametoindex() first if it is available. As it does not + * handle numeric scopes, we do not simply return if it fails. + */ + scopeid = (uint32_t)if_nametoindex(src); +#endif /* ifdef HAVE_IF_NAMETOINDEX */ + + /* + * Fall back to numeric scope processing if if_nametoindex() either + * fails or is unavailable. + */ + if (scopeid == 0) { + char *endptr = NULL; + scopeid = (uint32_t)strtoul(src, &endptr, 10); + /* + * The scope identifier must not be empty and no trailing + * characters are allowed after it. + */ + if (src == endptr || endptr == NULL || *endptr != '\0') { + return (false); + } + } + + *dst = scopeid; + + return (true); +} + +#define ISC_AI_MASK (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST) +/*% + * Get a list of IP addresses and port numbers for host hostname and + * service servname. + */ +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) { + struct servent *sp; + const char *proto; + int family, socktype, flags, protocol; + struct addrinfo *ai, *ai_list; + int err = 0; + int port, i; + int (*net_order[FOUND_MAX + 1])(const char *, int, struct addrinfo **, + int, int); + + if (hostname == NULL && servname == NULL) { + return (EAI_NONAME); + } + + proto = NULL; + if (hints != NULL) { + if ((hints->ai_flags & ~(ISC_AI_MASK)) != 0) { + return (EAI_BADFLAGS); + } + if (hints->ai_addrlen || hints->ai_canonname || + hints->ai_addr || hints->ai_next) + { + errno = EINVAL; + return (EAI_SYSTEM); + } + family = hints->ai_family; + socktype = hints->ai_socktype; + protocol = hints->ai_protocol; + flags = hints->ai_flags; + switch (family) { + case AF_UNSPEC: + switch (hints->ai_socktype) { + case SOCK_STREAM: + proto = "tcp"; + break; + case SOCK_DGRAM: + proto = "udp"; + break; + } + break; + case AF_INET: + case AF_INET6: + switch (hints->ai_socktype) { + case 0: + break; + case SOCK_STREAM: + proto = "tcp"; + break; + case SOCK_DGRAM: + proto = "udp"; + break; + case SOCK_RAW: + break; + default: + return (EAI_SOCKTYPE); + } + break; +#ifdef AF_LOCAL + case AF_LOCAL: + switch (hints->ai_socktype) { + case 0: + break; + case SOCK_STREAM: + break; + case SOCK_DGRAM: + break; + default: + return (EAI_SOCKTYPE); + } + break; +#endif /* ifdef AF_LOCAL */ + default: + return (EAI_FAMILY); + } + } else { + protocol = 0; + family = 0; + socktype = 0; + flags = 0; + } + +#ifdef AF_LOCAL + /*! + * First, deal with AF_LOCAL. If the family was not set, + * then assume AF_LOCAL if the first character of the + * hostname/servname is '/'. + */ + + if (hostname != NULL && + (family == AF_LOCAL || (family == 0 && *hostname == '/'))) + { + return (get_local(hostname, socktype, res)); + } + + if (servname != NULL && + (family == AF_LOCAL || (family == 0 && *servname == '/'))) + { + return (get_local(servname, socktype, res)); + } +#endif /* ifdef AF_LOCAL */ + + /* + * Ok, only AF_INET and AF_INET6 left. + */ + ai_list = NULL; + + /* + * First, look up the service name (port) if it was + * requested. If the socket type wasn't specified, then + * try and figure it out. + */ + if (servname != NULL) { + char *e; + + port = strtol(servname, &e, 10); + if (*e == '\0') { + if (socktype == 0) { + return (EAI_SOCKTYPE); + } + if (port < 0 || port > 65535) { + return (EAI_SERVICE); + } + port = htons((unsigned short)port); + } else { +#ifdef _WIN32 + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 0); + + err = WSAStartup(wVersionRequested, &wsaData); + if (err != 0) { + return (EAI_FAIL); + } +#endif /* ifdef _WIN32 */ + sp = getservbyname(servname, proto); + if (sp != NULL) { + port = sp->s_port; + } +#ifdef _WIN32 + WSACleanup(); +#endif /* ifdef _WIN32 */ + if (sp == NULL) { + return (EAI_SERVICE); + } + if (socktype == 0) { + if (strcmp(sp->s_proto, "tcp") == 0) { + socktype = SOCK_STREAM; + } else if (strcmp(sp->s_proto, "udp") == 0) { + socktype = SOCK_DGRAM; + } + } + } + } else { + port = 0; + } + + /* + * Next, deal with just a service name, and no hostname. + * (we verified that one of them was non-null up above). + */ + if (hostname == NULL && (flags & AI_PASSIVE) != 0) { + if (family == AF_INET || family == 0) { + ai = ai_alloc(AF_INET, sizeof(struct sockaddr_in)); + if (ai == NULL) { + return (EAI_MEMORY); + } + ai->ai_socktype = socktype; + ai->ai_protocol = protocol; + SIN(ai->ai_addr)->sin_port = port; + ai->ai_next = ai_list; + ai_list = ai; + } + + if (family == AF_INET6 || family == 0) { + ai = ai_alloc(AF_INET6, sizeof(struct sockaddr_in6)); + if (ai == NULL) { + _freeaddrinfo(ai_list); + return (EAI_MEMORY); + } + ai->ai_socktype = socktype; + ai->ai_protocol = protocol; + SIN6(ai->ai_addr)->sin6_port = port; + ai->ai_next = ai_list; + ai_list = ai; + } + + *res = ai_list; + return (0); + } + + /* + * If the family isn't specified or AI_NUMERICHOST specified, check + * first to see if it is a numeric address. + * Though the gethostbyname2() routine will recognize numeric addresses, + * it will only recognize the format that it is being called for. Thus, + * a numeric AF_INET address will be treated by the AF_INET6 call as + * a domain name, and vice versa. Checking for both numerics here + * avoids that. + */ + if (hostname != NULL && (family == 0 || (flags & AI_NUMERICHOST) != 0)) + { + char abuf[sizeof(struct in6_addr)]; + char nbuf[NI_MAXHOST]; + int addrsize, addroff; + char ntmp[NI_MAXHOST]; + uint32_t scopeid = 0; + + /* + * Scope identifier portion. + */ + ntmp[0] = '\0'; + if (strchr(hostname, '%') != NULL) { + char *p; + strlcpy(ntmp, hostname, sizeof(ntmp)); + p = strchr(ntmp, '%'); + + if (p != NULL && parse_scopeid(p + 1, &scopeid)) { + *p = '\0'; + } else { + ntmp[0] = '\0'; + } + } + + if (inet_pton(AF_INET, hostname, (struct in_addr *)abuf) == 1) { + if (family == AF_INET6) { + /* + * Convert to a V4 mapped address. + */ + struct in6_addr *a6 = (struct in6_addr *)abuf; + memmove(&a6->s6_addr[12], &a6->s6_addr[0], 4); + memset(&a6->s6_addr[10], 0xff, 2); + memset(&a6->s6_addr[0], 0, 10); + goto inet6_addr; + } + addrsize = sizeof(struct in_addr); + addroff = offsetof(struct sockaddr_in, sin_addr); + family = AF_INET; + goto common; + } else if (ntmp[0] != '\0' && + inet_pton(AF_INET6, ntmp, abuf) == 1) + { + if (family && family != AF_INET6) { + return (EAI_NONAME); + } + addrsize = sizeof(struct in6_addr); + addroff = offsetof(struct sockaddr_in6, sin6_addr); + family = AF_INET6; + goto common; + } else if (inet_pton(AF_INET6, hostname, abuf) == 1) { + if (family != 0 && family != AF_INET6) { + return (EAI_NONAME); + } + inet6_addr: + addrsize = sizeof(struct in6_addr); + addroff = offsetof(struct sockaddr_in6, sin6_addr); + family = AF_INET6; + + common: + ai = ai_alloc(family, + ((family == AF_INET6) + ? sizeof(struct sockaddr_in6) + : sizeof(struct sockaddr_in))); + if (ai == NULL) { + return (EAI_MEMORY); + } + ai_list = ai; + ai->ai_socktype = socktype; + SIN(ai->ai_addr)->sin_port = port; + memmove((char *)ai->ai_addr + addroff, abuf, addrsize); + if (ai->ai_family == AF_INET6) { + SIN6(ai->ai_addr)->sin6_scope_id = scopeid; + } + if ((flags & AI_CANONNAME) != 0) { + if (getnameinfo(ai->ai_addr, + (socklen_t)ai->ai_addrlen, nbuf, + sizeof(nbuf), NULL, 0, + NI_NUMERICHOST) == 0) + { + ai->ai_canonname = strdup(nbuf); + if (ai->ai_canonname == NULL) { + _freeaddrinfo(ai); + return (EAI_MEMORY); + } + } else { + /* XXX raise error? */ + ai->ai_canonname = NULL; + } + } + goto done; + } else if ((flags & AI_NUMERICHOST) != 0) { + return (EAI_NONAME); + } + } + + if (hostname == NULL && (flags & AI_PASSIVE) == 0) { + set_order(family, net_order); + for (i = 0; i < FOUND_MAX; i++) { + if (net_order[i] == NULL) { + break; + } + err = (net_order[i])(hostname, flags, &ai_list, + socktype, port); + if (err != 0) { + if (ai_list != NULL) { + _freeaddrinfo(ai_list); + ai_list = NULL; + } + break; + } + } + } else { + err = resolve_name(family, hostname, flags, &ai_list, socktype, + port); + } + + if (ai_list == NULL) { + if (err == 0) { + err = EAI_NONAME; + } + return (err); + } + +done: + ai_list = ai_reverse(ai_list); + + *res = ai_list; + return (0); +} + +typedef struct gai_restrans { + dns_clientrestrans_t *xid; + bool is_inprogress; + int error; + struct addrinfo ai_sentinel; + struct gai_resstate *resstate; +} gai_restrans_t; + +typedef struct gai_resstate { + isc_mem_t *mctx; + struct gai_statehead *head; + dns_fixedname_t fixedname; + dns_name_t *qname; + gai_restrans_t *trans4; + gai_restrans_t *trans6; + ISC_LINK(struct gai_resstate) link; +} gai_resstate_t; + +typedef struct gai_statehead { + int ai_family; + int ai_flags; + int ai_socktype; + int ai_port; + isc_appctx_t *actx; + dns_client_t *dnsclient; + isc_mutex_t list_lock; + ISC_LIST(struct gai_resstate) resstates; + unsigned int activestates; +} gai_statehead_t; + +static isc_result_t +make_resstate(isc_mem_t *mctx, gai_statehead_t *head, const char *hostname, + const char *domain, gai_resstate_t **statep) { + isc_result_t result; + gai_resstate_t *state; + dns_fixedname_t fixeddomain; + dns_name_t *qdomain; + unsigned int namelen; + isc_buffer_t b; + bool need_v4 = false; + bool need_v6 = false; + + state = isc_mem_get(mctx, sizeof(*state)); + + /* Construct base domain name */ + namelen = strlen(domain); + isc_buffer_constinit(&b, domain, namelen); + isc_buffer_add(&b, namelen); + qdomain = dns_fixedname_initname(&fixeddomain); + result = dns_name_fromtext(qdomain, &b, dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, state, sizeof(*state)); + return (result); + } + + /* Construct query name */ + namelen = strlen(hostname); + isc_buffer_constinit(&b, hostname, namelen); + isc_buffer_add(&b, namelen); + state->qname = dns_fixedname_initname(&state->fixedname); + result = dns_name_fromtext(state->qname, &b, qdomain, 0, NULL); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, state, sizeof(*state)); + return (result); + } + + if (head->ai_family == AF_UNSPEC || head->ai_family == AF_INET) { + need_v4 = true; + } + if (head->ai_family == AF_UNSPEC || head->ai_family == AF_INET6) { + need_v6 = true; + } + + state->trans6 = NULL; + state->trans4 = NULL; + if (need_v4) { + state->trans4 = isc_mem_get(mctx, sizeof(gai_restrans_t)); + state->trans4->error = 0; + state->trans4->xid = NULL; + state->trans4->resstate = state; + state->trans4->is_inprogress = true; + state->trans4->ai_sentinel.ai_next = NULL; + } + if (need_v6) { + state->trans6 = isc_mem_get(mctx, sizeof(gai_restrans_t)); + state->trans6->error = 0; + state->trans6->xid = NULL; + state->trans6->resstate = state; + state->trans6->is_inprogress = true; + state->trans6->ai_sentinel.ai_next = NULL; + } + + state->mctx = mctx; + state->head = head; + ISC_LINK_INIT(state, link); + + *statep = state; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +make_resstates(isc_mem_t *mctx, const char *hostname, gai_statehead_t *head, + irs_resconf_t *resconf) { + isc_result_t result; + irs_resconf_searchlist_t *searchlist; + irs_resconf_search_t *searchent; + gai_resstate_t *resstate, *resstate0; + + resstate0 = NULL; + result = make_resstate(mctx, head, hostname, ".", &resstate0); + if (result != ISC_R_SUCCESS) { + return (result); + } + + searchlist = irs_resconf_getsearchlist(resconf); + for (searchent = ISC_LIST_HEAD(*searchlist); searchent != NULL; + searchent = ISC_LIST_NEXT(searchent, link)) + { + resstate = NULL; + result = make_resstate(mctx, head, hostname, + (const char *)searchent->domain, + &resstate); + if (result != ISC_R_SUCCESS) { + break; + } + + ISC_LIST_APPEND(head->resstates, resstate, link); + head->activestates++; + } + + /* + * Insert the original hostname either at the head or the tail of the + * state list, depending on the number of labels contained in the + * original name and the 'ndots' configuration parameter. + */ + if (dns_name_countlabels(resstate0->qname) > + irs_resconf_getndots(resconf) + 1) + { + ISC_LIST_PREPEND(head->resstates, resstate0, link); + } else { + ISC_LIST_APPEND(head->resstates, resstate0, link); + } + head->activestates++; + + if (result != ISC_R_SUCCESS) { + while ((resstate = ISC_LIST_HEAD(head->resstates)) != NULL) { + ISC_LIST_UNLINK(head->resstates, resstate, link); + if (resstate->trans4 != NULL) { + isc_mem_put(mctx, resstate->trans4, + sizeof(*resstate->trans4)); + } + if (resstate->trans6 != NULL) { + isc_mem_put(mctx, resstate->trans6, + sizeof(*resstate->trans6)); + } + + isc_mem_put(mctx, resstate, sizeof(*resstate)); + } + } + + return (result); +} + +static void +process_answer(isc_task_t *task, isc_event_t *event) { + int error = 0, family; + gai_restrans_t *trans = event->ev_arg; + gai_resstate_t *resstate; + dns_clientresevent_t *rev = (dns_clientresevent_t *)event; + dns_rdatatype_t qtype; + dns_name_t *name; + bool wantcname; + + REQUIRE(trans != NULL); + resstate = trans->resstate; + REQUIRE(resstate != NULL); + REQUIRE(task != NULL); + + if (trans == resstate->trans4) { + family = AF_INET; + qtype = dns_rdatatype_a; + } else { + INSIST(trans == resstate->trans6); + family = AF_INET6; + qtype = dns_rdatatype_aaaa; + } + + INSIST(trans->is_inprogress); + trans->is_inprogress = false; + + switch (rev->result) { + case ISC_R_SUCCESS: + case DNS_R_NCACHENXDOMAIN: /* treat this as a fatal error? */ + case DNS_R_NCACHENXRRSET: + break; + default: + switch (rev->vresult) { + case DNS_R_SIGINVALID: + case DNS_R_SIGEXPIRED: + case DNS_R_SIGFUTURE: + case DNS_R_KEYUNAUTHORIZED: + case DNS_R_MUSTBESECURE: + case DNS_R_COVERINGNSEC: + case DNS_R_NOTAUTHORITATIVE: + case DNS_R_NOVALIDKEY: + case DNS_R_NOVALIDDS: + case DNS_R_NOVALIDSIG: + error = EAI_INSECUREDATA; + break; + default: + error = EAI_FAIL; + } + goto done; + } + + wantcname = ((resstate->head->ai_flags & AI_CANONNAME) != 0); + + /* Parse the response and construct the addrinfo chain */ + for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL; + name = ISC_LIST_NEXT(name, link)) + { + isc_result_t result; + dns_rdataset_t *rdataset; + char cname[1024]; + + if (wantcname) { + isc_buffer_t b; + + isc_buffer_init(&b, cname, sizeof(cname)); + result = dns_name_totext(name, true, &b); + if (result != ISC_R_SUCCESS) { + error = EAI_FAIL; + goto done; + } + isc_buffer_putuint8(&b, '\0'); + } + + for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (!dns_rdataset_isassociated(rdataset)) { + continue; + } + if (rdataset->type != qtype) { + continue; + } + + for (result = dns_rdataset_first(rdataset); + result == ISC_R_SUCCESS; + result = dns_rdataset_next(rdataset)) + { + struct addrinfo *ai; + dns_rdata_t rdata; + dns_rdata_in_a_t rdata_a; + dns_rdata_in_aaaa_t rdata_aaaa; + + ai = ai_alloc( + family, + ((family == AF_INET6) + ? sizeof(struct sockaddr_in6) + : sizeof(struct sockaddr_in))); + if (ai == NULL) { + error = EAI_MEMORY; + goto done; + } + ai->ai_socktype = resstate->head->ai_socktype; + ai->ai_next = trans->ai_sentinel.ai_next; + trans->ai_sentinel.ai_next = ai; + + /* + * Set AF-specific parameters + * (IPv4/v6 address/port) + */ + dns_rdata_init(&rdata); + switch (family) { + case AF_INET: + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct( + &rdata, &rdata_a, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + SIN(ai->ai_addr)->sin_port = + resstate->head->ai_port; + memmove(&SIN(ai->ai_addr)->sin_addr, + &rdata_a.in_addr, 4); + dns_rdata_freestruct(&rdata_a); + break; + case AF_INET6: + dns_rdataset_current(rdataset, &rdata); + result = dns_rdata_tostruct( + &rdata, &rdata_aaaa, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + SIN6(ai->ai_addr)->sin6_port = + resstate->head->ai_port; + memmove(&SIN6(ai->ai_addr)->sin6_addr, + &rdata_aaaa.in6_addr, 16); + dns_rdata_freestruct(&rdata_aaaa); + break; + } + + if (wantcname) { + ai->ai_canonname = strdup(cname); + if (ai->ai_canonname == NULL) { + error = EAI_MEMORY; + goto done; + } + } + } + } + } + +done: + dns_client_freeresanswer(resstate->head->dnsclient, &rev->answerlist); + dns_client_destroyrestrans(&trans->xid); + + isc_event_free(&event); + + /* Make sure that error == 0 iff we have a non-empty list */ + if (error == 0) { + if (trans->ai_sentinel.ai_next == NULL) { + error = EAI_NONAME; + } + } else { + if (trans->ai_sentinel.ai_next != NULL) { + _freeaddrinfo(trans->ai_sentinel.ai_next); + trans->ai_sentinel.ai_next = NULL; + } + } + trans->error = error; + + /* Check whether we are done */ + if ((resstate->trans4 == NULL || !resstate->trans4->is_inprogress) && + (resstate->trans6 == NULL || !resstate->trans6->is_inprogress)) + { + /* + * We're done for this state. If there is no other outstanding + * state, we can exit. + */ + resstate->head->activestates--; + if (resstate->head->activestates == 0) { + isc_app_ctxsuspend(resstate->head->actx); + return; + } + + /* + * There are outstanding states, but if we are at the head + * of the state list (i.e., at the highest search priority) + * and have any answer, we can stop now by canceling the + * others. + */ + LOCK(&resstate->head->list_lock); + if (resstate == ISC_LIST_HEAD(resstate->head->resstates)) { + if ((resstate->trans4 != NULL && + resstate->trans4->ai_sentinel.ai_next != NULL) || + (resstate->trans6 != NULL && + resstate->trans6->ai_sentinel.ai_next != NULL)) + { + gai_resstate_t *rest; + + for (rest = ISC_LIST_NEXT(resstate, link); + rest != NULL; + rest = ISC_LIST_NEXT(rest, link)) + { + if (rest->trans4 != NULL && + rest->trans4->xid != NULL) + { + dns_client_cancelresolve( + rest->trans4->xid); + } + if (rest->trans6 != NULL && + rest->trans6->xid != NULL) + { + dns_client_cancelresolve( + rest->trans6->xid); + } + } + } else { + /* + * This search fails, so we move to the tail + * of the list so that the next entry will + * have the highest priority. + */ + ISC_LIST_UNLINK(resstate->head->resstates, + resstate, link); + ISC_LIST_APPEND(resstate->head->resstates, + resstate, link); + } + } + UNLOCK(&resstate->head->list_lock); + } +} + +static int +resolve_name(int family, const char *hostname, int flags, struct addrinfo **aip, + int socktype, int port) { + isc_result_t result; + irs_context_t *irsctx; + irs_resconf_t *conf; + isc_mem_t *mctx; + isc_appctx_t *actx; + isc_task_t *task; + int terror = 0; + int error = 0; + dns_client_t *client; + gai_resstate_t *resstate; + gai_statehead_t head; + bool all_fail = true; + + /* get IRS context and the associated parameters */ + irsctx = NULL; + result = irs_context_get(&irsctx); + if (result != ISC_R_SUCCESS) { + return (EAI_FAIL); + } + actx = irs_context_getappctx(irsctx); + + mctx = irs_context_getmctx(irsctx); + task = irs_context_gettask(irsctx); + conf = irs_context_getresconf(irsctx); + client = irs_context_getdnsclient(irsctx); + + /* construct resolution states */ + head.activestates = 0; + head.ai_family = family; + head.ai_socktype = socktype; + head.ai_flags = flags; + head.ai_port = port; + head.actx = actx; + head.dnsclient = client; + isc_mutex_init(&head.list_lock); + + ISC_LIST_INIT(head.resstates); + result = make_resstates(mctx, hostname, &head, conf); + if (result != ISC_R_SUCCESS) { + isc_mutex_destroy(&head.list_lock); + return (EAI_FAIL); + } + + LOCK(&head.list_lock); + for (resstate = ISC_LIST_HEAD(head.resstates); resstate != NULL; + resstate = ISC_LIST_NEXT(resstate, link)) + { + if (resstate->trans4 != NULL) { + result = dns_client_startresolve( + client, resstate->qname, dns_rdataclass_in, + dns_rdatatype_a, 0, task, process_answer, + resstate->trans4, &resstate->trans4->xid); + if (result == ISC_R_SUCCESS) { + resstate->trans4->is_inprogress = true; + all_fail = false; + } else { + resstate->trans4->is_inprogress = false; + } + } + if (resstate->trans6 != NULL) { + result = dns_client_startresolve( + client, resstate->qname, dns_rdataclass_in, + dns_rdatatype_aaaa, 0, task, process_answer, + resstate->trans6, &resstate->trans6->xid); + if (result == ISC_R_SUCCESS) { + resstate->trans6->is_inprogress = true; + all_fail = false; + } else { + resstate->trans6->is_inprogress = false; + } + } + } + UNLOCK(&head.list_lock); + + if (!all_fail) { + /* Start all the events */ + isc_app_ctxrun(actx); + } else { + error = EAI_FAIL; + } + + /* Cleanup */ + while ((resstate = ISC_LIST_HEAD(head.resstates)) != NULL) { + int terror4 = 0, terror6 = 0; + + ISC_LIST_UNLINK(head.resstates, resstate, link); + + if (*aip == NULL) { + struct addrinfo *sentinel4 = NULL; + struct addrinfo *sentinel6 = NULL; + + if (resstate->trans4 != NULL) { + sentinel4 = + resstate->trans4->ai_sentinel.ai_next; + resstate->trans4->ai_sentinel.ai_next = NULL; + } + if (resstate->trans6 != NULL) { + sentinel6 = + resstate->trans6->ai_sentinel.ai_next; + resstate->trans6->ai_sentinel.ai_next = NULL; + } + *aip = ai_concat(sentinel4, sentinel6); + } + + if (resstate->trans4 != NULL) { + INSIST(resstate->trans4->xid == NULL); + terror4 = resstate->trans4->error; + isc_mem_put(mctx, resstate->trans4, + sizeof(*resstate->trans4)); + } + if (resstate->trans6 != NULL) { + INSIST(resstate->trans6->xid == NULL); + terror6 = resstate->trans6->error; + isc_mem_put(mctx, resstate->trans6, + sizeof(*resstate->trans6)); + } + + /* + * If the entire lookup fails, we need to choose an appropriate + * error code from individual codes. We'll try to provide as + * specific a code as possible. In general, we are going to + * find an error code other than EAI_NONAME (which is too + * generic and may actually not be problematic in some cases). + * EAI_NONAME will be set below if no better code is found. + */ + if (terror == 0 || terror == EAI_NONAME) { + if (terror4 != 0 && terror4 != EAI_NONAME) { + terror = terror4; + } else if (terror6 != 0 && terror6 != EAI_NONAME) { + terror = terror6; + } + } + + isc_mem_put(mctx, resstate, sizeof(*resstate)); + } + + if (*aip == NULL) { + error = terror; + if (error == 0) { + error = EAI_NONAME; + } + } + +#if 1 /* XXX: enabled for finding leaks. should be cleaned up later. */ + isc_app_ctxfinish(actx); + irs_context_destroy(&irsctx); +#endif /* if 1 */ + + isc_mutex_destroy(&head.list_lock); + return (error); +} + +static void +set_order(int family, + int (**net_order)(const char *, int, struct addrinfo **, int, int)) { + char *order, *tok, *last; + int found; + + if (family) { + switch (family) { + case AF_INET: + *net_order++ = add_ipv4; + break; + case AF_INET6: + *net_order++ = add_ipv6; + break; + } + } else { + order = getenv("NET_ORDER"); + found = 0; + if (order != NULL) { + last = NULL; + for (tok = strtok_r(order, ":", &last); tok; + tok = strtok_r(NULL, ":", &last)) + { + if (strcasecmp(tok, "inet6") == 0) { + if ((found & FOUND_IPV6) == 0) { + *net_order++ = add_ipv6; + } + found |= FOUND_IPV6; + } else if (strcasecmp(tok, "inet") == 0 || + strcasecmp(tok, "inet4") == 0) + { + if ((found & FOUND_IPV4) == 0) { + *net_order++ = add_ipv4; + } + found |= FOUND_IPV4; + } + } + } + + /* + * Add in anything that we didn't find. + */ + if ((found & FOUND_IPV4) == 0) { + *net_order++ = add_ipv4; + } + if ((found & FOUND_IPV6) == 0) { + *net_order++ = add_ipv6; + } + } + *net_order = NULL; + return; +} + +static char v4_loop[4] = { 127, 0, 0, 1 }; + +static int +add_ipv4(const char *hostname, int flags, struct addrinfo **aip, int socktype, + int port) { + struct addrinfo *ai; + + UNUSED(hostname); + UNUSED(flags); + + ai = ai_clone(*aip, AF_INET); /* don't use ai_clone() */ + if (ai == NULL) { + return (EAI_MEMORY); + } + + *aip = ai; + ai->ai_socktype = socktype; + SIN(ai->ai_addr)->sin_port = port; + memmove(&SIN(ai->ai_addr)->sin_addr, v4_loop, 4); + + return (0); +} + +static char v6_loop[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; + +static int +add_ipv6(const char *hostname, int flags, struct addrinfo **aip, int socktype, + int port) { + struct addrinfo *ai; + + UNUSED(hostname); + UNUSED(flags); + + ai = ai_clone(*aip, AF_INET6); /* don't use ai_clone() */ + if (ai == NULL) { + return (EAI_MEMORY); + } + + *aip = ai; + ai->ai_socktype = socktype; + SIN6(ai->ai_addr)->sin6_port = port; + memmove(&SIN6(ai->ai_addr)->sin6_addr, v6_loop, 16); + + return (0); +} + +/*% Free address info. */ +void +freeaddrinfo(struct addrinfo *ai) { + _freeaddrinfo(ai); +} + +static void +_freeaddrinfo(struct addrinfo *ai) { + struct addrinfo *ai_next; + + while (ai != NULL) { + ai_next = ai->ai_next; + if (ai->ai_addr != NULL) { + free(ai->ai_addr); + } + if (ai->ai_canonname) { + free(ai->ai_canonname); + } + free(ai); + ai = ai_next; + } +} + +#ifdef AF_LOCAL +static int +get_local(const char *name, int socktype, struct addrinfo **res) { + struct addrinfo *ai; + struct sockaddr_un *slocal; + + if (socktype == 0) { + return (EAI_SOCKTYPE); + } + + ai = ai_alloc(AF_LOCAL, sizeof(*slocal)); + if (ai == NULL) { + return (EAI_MEMORY); + } + + slocal = SLOCAL(ai->ai_addr); + strlcpy(slocal->sun_path, name, sizeof(slocal->sun_path)); + + ai->ai_socktype = socktype; + /* + * ai->ai_flags, ai->ai_protocol, ai->ai_canonname, + * and ai->ai_next were initialized to zero. + */ + + *res = ai; + return (0); +} +#endif /* ifdef AF_LOCAL */ + +/*! + * Allocate an addrinfo structure, and a sockaddr structure + * of the specified length. We initialize: + * ai_addrlen + * ai_family + * ai_addr + * ai_addr->sa_family + * ai_addr->sa_len (IRS_PLATFORM_HAVESALEN) + * and everything else is initialized to zero. + */ +static struct addrinfo * +ai_alloc(int family, int addrlen) { + struct addrinfo *ai; + + ai = (struct addrinfo *)calloc(1, sizeof(*ai)); + if (ai == NULL) { + return (NULL); + } + + ai->ai_addr = SA(calloc(1, addrlen)); + if (ai->ai_addr == NULL) { + free(ai); + return (NULL); + } + ai->ai_addrlen = addrlen; + ai->ai_family = family; + ai->ai_addr->sa_family = family; +#ifdef IRS_PLATFORM_HAVESALEN + ai->ai_addr->sa_len = addrlen; +#endif /* ifdef IRS_PLATFORM_HAVESALEN */ + return (ai); +} + +static struct addrinfo * +ai_clone(struct addrinfo *oai, int family) { + struct addrinfo *ai; + + ai = ai_alloc(family, + ((family == AF_INET6) ? sizeof(struct sockaddr_in6) + : sizeof(struct sockaddr_in))); + + if (ai == NULL) { + return (NULL); + } + if (oai == NULL) { + return (ai); + } + + ai->ai_flags = oai->ai_flags; + ai->ai_socktype = oai->ai_socktype; + ai->ai_protocol = oai->ai_protocol; + ai->ai_canonname = NULL; + ai->ai_next = oai; + return (ai); +} + +static struct addrinfo * +ai_reverse(struct addrinfo *oai) { + struct addrinfo *nai, *tai; + + nai = NULL; + + while (oai != NULL) { + /* + * Grab one off the old list. + */ + tai = oai; + oai = oai->ai_next; + /* + * Put it on the front of the new list. + */ + tai->ai_next = nai; + nai = tai; + } + return (nai); +} + +static struct addrinfo * +ai_concat(struct addrinfo *ai1, struct addrinfo *ai2) { + struct addrinfo *ai_tmp; + + if (ai1 == NULL) { + return (ai2); + } else if (ai2 == NULL) { + return (ai1); + } + + for (ai_tmp = ai1; ai_tmp != NULL && ai_tmp->ai_next != NULL; + ai_tmp = ai_tmp->ai_next) + { + } + + ai_tmp->ai_next = ai2; + + return (ai1); +} diff --git a/lib/irs/getnameinfo.c b/lib/irs/getnameinfo.c new file mode 100644 index 0000000..62c7cea --- /dev/null +++ b/lib/irs/getnameinfo.c @@ -0,0 +1,438 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +/* + * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. + * 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 project 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 PROJECT 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 PROJECT 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. + */ + +/** + * getnameinfo() returns the hostname for the struct sockaddr sa which is + * salen bytes long. The hostname is of length hostlen and is returned via + * *host. The maximum length of the hostname is 1025 bytes: #NI_MAXHOST. + * + * The name of the service associated with the port number in sa is + * returned in *serv. It is servlen bytes long. The maximum length of the + * service name is #NI_MAXSERV - 32 bytes. + * + * The flags argument sets the following bits: + * + * \li #NI_NOFQDN: + * A fully qualified domain name is not required for local hosts. + * The local part of the fully qualified domain name is returned + * instead. + * + * \li #NI_NUMERICHOST + * Return the address in numeric form, as if calling inet_ntop(), + * instead of a host name. + * + * \li #NI_NAMEREQD + * A name is required. If the hostname cannot be found in the DNS + * and this flag is set, a non-zero error code is returned. If the + * hostname is not found and the flag is not set, the address is + * returned in numeric form. + * + * \li #NI_NUMERICSERV + * The service name is returned as a digit string representing the + * port number. + * + * \li #NI_DGRAM + * Specifies that the service being looked up is a datagram + * service, and causes getservbyport() to be called with a second + * argument of "udp" instead of its default of "tcp". This is + * required for the few ports (512-514) that have different + * services for UDP and TCP. + * + * \section getnameinfo_return Return Values + * + * getnameinfo() returns 0 on success or a non-zero error code if + * an error occurs. + * + * \section getname_see See Also + * + * RFC3493, getservbyport(), + * getnamebyaddr(). inet_ntop(). + */ + +#include <stdbool.h> +#include <stdio.h> +#include <string.h> + +#include <isc/netaddr.h> +#include <isc/print.h> +#include <isc/sockaddr.h> +#include <isc/string.h> +#include <isc/util.h> + +#include <dns/byaddr.h> +#include <dns/client.h> +#include <dns/fixedname.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataset.h> +#include <dns/rdatastruct.h> +#include <dns/result.h> + +#include <irs/context.h> +#include <irs/netdb.h> + +#define SUCCESS 0 + +/*% afd structure definition */ +static struct afd { + int a_af; + size_t a_addrlen; + size_t a_socklen; +} afdl[] = { + /*! + * First entry is linked last... + */ + { AF_INET, sizeof(struct in_addr), sizeof(struct sockaddr_in) }, + { AF_INET6, sizeof(struct in6_addr), sizeof(struct sockaddr_in6) }, + { 0, 0, 0 }, +}; + +/*! + * The test against 0 is there to keep the Solaris compiler + * from complaining about "end-of-loop code not reached". + */ +#define ERR(code) \ + do { \ + result = (code); \ + if (result != 0) \ + goto cleanup; \ + } while (0) + +#ifdef _WIN32 +int +getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, + DWORD hostlen, char *serv, DWORD servlen, int flags) { +#else +int +getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, + socklen_t hostlen, char *serv, socklen_t servlen, int flags) { +#endif + struct afd *afd = NULL; + struct servent *sp; + unsigned short port = 0; +#ifdef IRS_PLATFORM_HAVESALEN + size_t len; +#endif /* ifdef IRS_PLATFORM_HAVESALEN */ + int family, i; + const void *addr = NULL; + char *p; +#if 0 + unsigned long v4a; + unsigned char pfx; +#endif /* if 0 */ + char numserv[sizeof("65000")]; + char numaddr[sizeof("abcd:abcd:abcd:abcd:abcd:abcd:255.255.255.255") + + 1 + sizeof("4294967295")]; + const char *proto; + int result = SUCCESS; + + if (sa == NULL) { + ERR(EAI_FAIL); + } + +#ifdef IRS_PLATFORM_HAVESALEN + len = sa->sa_len; + if (len != salen) { + ERR(EAI_FAIL); + } +#endif /* ifdef IRS_PLATFORM_HAVESALEN */ + + family = sa->sa_family; + for (i = 0; afdl[i].a_af; i++) { + if (afdl[i].a_af == family) { + { + afd = &afdl[i]; + goto found; + } + } + } + ERR(EAI_FAMILY); + +found: + if (salen != afd->a_socklen) { + ERR(EAI_FAIL); + } + + switch (family) { + case AF_INET: + port = ((const struct sockaddr_in *)sa)->sin_port; + addr = &((const struct sockaddr_in *)sa)->sin_addr.s_addr; + break; + + case AF_INET6: + port = ((const struct sockaddr_in6 *)sa)->sin6_port; + addr = ((const struct sockaddr_in6 *)sa)->sin6_addr.s6_addr; + break; + + default: + UNREACHABLE(); + } + proto = ((flags & NI_DGRAM) != 0) ? "udp" : "tcp"; + + if (serv == NULL || servlen == 0U) { + /* + * Caller does not want service. + */ + } else if ((flags & NI_NUMERICSERV) != 0 || + (sp = getservbyport(port, proto)) == NULL) + { + snprintf(numserv, sizeof(numserv), "%d", ntohs(port)); + if ((strlen(numserv) + 1) > servlen) { + ERR(EAI_OVERFLOW); + } + strlcpy(serv, numserv, servlen); + } else { + if ((strlen(sp->s_name) + 1) > servlen) { + ERR(EAI_OVERFLOW); + } + strlcpy(serv, sp->s_name, servlen); + } + +#if 0 + switch (sa->sa_family) { + case AF_INET: + v4a = ((struct sockaddr_in *)sa)->sin_addr.s_addr; + if (IN_MULTICAST(v4a) || IN_EXPERIMENTAL(v4a)) { + flags |= NI_NUMERICHOST; + } + v4a >>= IN_CLASSA_NSHIFT; + if (v4a == 0 || v4a == IN_LOOPBACKNET) { + flags |= NI_NUMERICHOST; + } + break; + + case AF_INET6: + pfx = ((struct sockaddr_in6 *)sa)->sin6_addr.s6_addr[0]; + if (pfx == 0 || pfx == 0xfe || pfx == 0xff) { + flags |= NI_NUMERICHOST; + } + break; + } +#endif /* if 0 */ + + if (host == NULL || hostlen == 0U) { + /* + * do nothing in this case. + * in case you are wondering if "&&" is more correct than + * "||" here: RFC3493 says that host == NULL or hostlen == 0 + * means that the caller does not want the result. + */ + } else if ((flags & NI_NUMERICHOST) != 0) { + if (inet_ntop(afd->a_af, addr, numaddr, sizeof(numaddr)) == + NULL) + { + ERR(EAI_SYSTEM); + } +#if defined(IRS_HAVE_SIN6_SCOPE_ID) + if (afd->a_af == AF_INET6 && + ((const struct sockaddr_in6 *)sa)->sin6_scope_id) + { + char *p = numaddr + strlen(numaddr); + const char *stringscope = NULL; +#ifdef VENDOR_SPECIFIC + /* + * Vendors may want to add support for + * non-numeric scope identifier. + */ + stringscope = foo; +#endif /* ifdef VENDOR_SPECIFIC */ + if (stringscope == NULL) { + snprintf(p, sizeof(numaddr) - (p - numaddr), + "%%%u", + ((const struct sockaddr_in6 *)sa) + ->sin6_scope_id); + } else { + snprintf(p, sizeof(numaddr) - (p - numaddr), + "%%%s", stringscope); + } + } +#endif /* if defined(IRS_HAVE_SIN6_SCOPE_ID) */ + if (strlen(numaddr) + 1 > hostlen) { + ERR(EAI_OVERFLOW); + } + strlcpy(host, numaddr, hostlen); + } else { + isc_netaddr_t netaddr; + dns_fixedname_t ptrfname; + dns_name_t *ptrname; + irs_context_t *irsctx = NULL; + dns_client_t *client; + bool found = false; + dns_namelist_t answerlist; + dns_rdataset_t *rdataset; + isc_region_t hostregion; + char hoststr[1024]; /* is this enough? */ + isc_result_t iresult; + + /* Get IRS context and the associated DNS client object */ + iresult = irs_context_get(&irsctx); + if (iresult != ISC_R_SUCCESS) { + ERR(EAI_FAIL); + } + client = irs_context_getdnsclient(irsctx); + + /* Make query name */ + isc_netaddr_fromsockaddr(&netaddr, (const isc_sockaddr_t *)sa); + ptrname = dns_fixedname_initname(&ptrfname); + iresult = dns_byaddr_createptrname(&netaddr, 0, ptrname); + if (iresult != ISC_R_SUCCESS) { + ERR(EAI_FAIL); + } + + /* Get the PTR RRset */ + ISC_LIST_INIT(answerlist); + iresult = dns_client_resolve(client, ptrname, dns_rdataclass_in, + dns_rdatatype_ptr, 0, &answerlist); + switch (iresult) { + case ISC_R_SUCCESS: + /* + * a 'non-existent' error is not necessarily fatal for + * getnameinfo(). + */ + case DNS_R_NCACHENXDOMAIN: + case DNS_R_NCACHENXRRSET: + break; + case DNS_R_SIGINVALID: + case DNS_R_SIGEXPIRED: + case DNS_R_SIGFUTURE: + case DNS_R_KEYUNAUTHORIZED: + case DNS_R_MUSTBESECURE: + case DNS_R_COVERINGNSEC: + case DNS_R_NOTAUTHORITATIVE: + case DNS_R_NOVALIDKEY: + case DNS_R_NOVALIDDS: + case DNS_R_NOVALIDSIG: + /* + * Don't use ERR as GCC 7 wants to raise a + * warning with ERR about possible falling + * through which is impossible. + */ + result = EAI_INSECUREDATA; + goto cleanup; + default: + ERR(EAI_FAIL); + } + + /* Parse the answer for the hostname */ + for (ptrname = ISC_LIST_HEAD(answerlist); ptrname != NULL; + ptrname = ISC_LIST_NEXT(ptrname, link)) + { + for (rdataset = ISC_LIST_HEAD(ptrname->list); + rdataset != NULL; + rdataset = ISC_LIST_NEXT(rdataset, link)) + { + if (!dns_rdataset_isassociated(rdataset)) { + continue; + } + if (rdataset->type != dns_rdatatype_ptr) { + continue; + } + + for (iresult = dns_rdataset_first(rdataset); + iresult == ISC_R_SUCCESS; + iresult = dns_rdataset_next(rdataset)) + { + dns_rdata_t rdata; + dns_rdata_ptr_t rdata_ptr; + isc_buffer_t b; + + dns_rdata_init(&rdata); + dns_rdataset_current(rdataset, &rdata); + dns_rdata_tostruct(&rdata, &rdata_ptr, + NULL); + + isc_buffer_init(&b, hoststr, + sizeof(hoststr)); + iresult = dns_name_totext( + &rdata_ptr.ptr, true, &b); + dns_rdata_freestruct(&rdata_ptr); + if (iresult == ISC_R_SUCCESS) { + /* + * We ignore the rest of the + * answer. After all, + * getnameinfo() can return + * at most one hostname. + */ + found = true; + isc_buffer_usedregion( + &b, &hostregion); + goto ptrfound; + } + } + } + } + ptrfound: + dns_client_freeresanswer(client, &answerlist); + if (found) { + if ((flags & NI_NOFQDN) != 0) { + p = strchr(hoststr, '.'); + if (p) { + *p = '\0'; + } + } + if (hostregion.length + 1 > hostlen) { + ERR(EAI_OVERFLOW); + } + snprintf(host, hostlen, "%.*s", (int)hostregion.length, + (char *)hostregion.base); + } else { + if ((flags & NI_NAMEREQD) != 0) { + ERR(EAI_NONAME); + } + if (inet_ntop(afd->a_af, addr, numaddr, + sizeof(numaddr)) == NULL) + { + ERR(EAI_SYSTEM); + } + if ((strlen(numaddr) + 1) > hostlen) { + ERR(EAI_OVERFLOW); + } + strlcpy(host, numaddr, hostlen); + } + } + result = SUCCESS; + +cleanup: + return (result); +} diff --git a/lib/irs/include/.clang-format b/lib/irs/include/.clang-format new file mode 120000 index 0000000..0e62f72 --- /dev/null +++ b/lib/irs/include/.clang-format @@ -0,0 +1 @@ +../../../.clang-format.headers
\ No newline at end of file diff --git a/lib/irs/include/Makefile.in b/lib/irs/include/Makefile.in new file mode 100644 index 0000000..53a5348 --- /dev/null +++ b/lib/irs/include/Makefile.in @@ -0,0 +1,19 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +SUBDIRS = irs +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/irs/include/irs/Makefile.in b/lib/irs/include/irs/Makefile.in new file mode 100644 index 0000000..fb37979 --- /dev/null +++ b/lib/irs/include/irs/Makefile.in @@ -0,0 +1,46 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +# +# Only list headers that are to be installed and are not +# machine generated. The latter are handled specially in the +# install target below. +# +HEADERS = context.h dnsconf.h resconf.h types.h version.h + +SUBDIRS = +TARGETS = + +@BIND9_MAKE_RULES@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/irs + +install:: installdirs + for i in ${HEADERS}; do \ + ${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/irs || exit 1; \ + done + ${INSTALL_DATA} netdb.h ${DESTDIR}${includedir}/irs + ${INSTALL_DATA} platform.h ${DESTDIR}${includedir}/irs + +uninstall:: + rm -f ${DESTDIR}${includedir}/irs/platform.h + rm -f ${DESTDIR}${includedir}/irs/netdb.h + for i in ${HEADERS}; do \ + rm -f ${DESTDIR}${includedir}/irs/$$i || exit 1; \ + done + +distclean:: + rm -f netdb.h platform.h diff --git a/lib/irs/include/irs/context.h b/lib/irs/include/irs/context.h new file mode 100644 index 0000000..c2d800a --- /dev/null +++ b/lib/irs/include/irs/context.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef IRS_CONTEXT_H +#define IRS_CONTEXT_H 1 + +/*! \file + * + * \brief + * The IRS context module provides an abstract interface to the DNS library + * with an application. An IRS context object initializes and holds various + * resources used in the DNS library. + */ + +#include <dns/types.h> + +#include <irs/types.h> + +ISC_LANG_BEGINDECLS + +isc_result_t +irs_context_create(irs_context_t **contextp); +/*%< + * Create an IRS context. It internally initializes the ISC and DNS libraries + * (if not yet), creates a DNS client object and initializes the client using + * the configuration files parsed via the 'resconf' and 'dnsconf' IRS modules. + * Some of the internally initialized objects can be used by the application + * via irs_context_getxxx() functions (see below). + * + * Requires: + * + *\li contextp != NULL && *contextp == NULL. + */ + +isc_result_t +irs_context_get(irs_context_t **contextp); +/*%< + * Return an IRS context for the calling thread. If no IRS context is + * associated to the thread, this function creates a new one by calling + * irs_context_create(), and associates it with the thread as a thread specific + * data value. This function is provided for standard libraries that are + * expected to be thread-safe but do not accept an appropriate IRS context + * as a library parameter, e.g., getaddrinfo(). + * + * Requires: + * + *\li contextp != NULL && *contextp == NULL. + */ + +void +irs_context_destroy(irs_context_t **contextp); +/*%< + * Destroy an IRS context. + * + * Requires: + * + *\li '*contextp' is a valid IRS context. + * + * Ensures: + *\li '*contextp' == NULL. + */ + +isc_mem_t * +irs_context_getmctx(irs_context_t *context); +/*%< + * Return the memory context held in the context. + * + * Requires: + * + *\li 'context' is a valid IRS context. + */ + +isc_appctx_t * +irs_context_getappctx(irs_context_t *context); +/*%< + * Return the application context held in the context. + * + * Requires: + * + *\li 'context' is a valid IRS context. + */ + +isc_taskmgr_t * +irs_context_gettaskmgr(irs_context_t *context); +/*%< + * Return the task manager held in the context. + * + * Requires: + * + *\li 'context' is a valid IRS context. + */ + +isc_timermgr_t * +irs_context_gettimermgr(irs_context_t *context); +/*%< + * Return the timer manager held in the context. + * + * Requires: + * + *\li 'context' is a valid IRS context. + */ + +isc_task_t * +irs_context_gettask(irs_context_t *context); +/*%< + * Return the task object held in the context. + * + * Requires: + * + *\li 'context' is a valid IRS context. + */ + +dns_client_t * +irs_context_getdnsclient(irs_context_t *context); +/*%< + * Return the DNS client object held in the context. + * + * Requires: + * + *\li 'context' is a valid IRS context. + */ + +irs_resconf_t * +irs_context_getresconf(irs_context_t *context); +/*%< + * Return the resolver configuration object held in the context. + * + * Requires: + * + *\li 'context' is a valid IRS context. + */ + +irs_dnsconf_t * +irs_context_getdnsconf(irs_context_t *context); +/*%< + * Return the advanced DNS configuration object held in the context. + * + * Requires: + * + *\li 'context' is a valid IRS context. + */ + +ISC_LANG_ENDDECLS + +#endif /* IRS_CONTEXT_H */ diff --git a/lib/irs/include/irs/dnsconf.h b/lib/irs/include/irs/dnsconf.h new file mode 100644 index 0000000..7456b89 --- /dev/null +++ b/lib/irs/include/irs/dnsconf.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef IRS_DNSCONF_H +#define IRS_DNSCONF_H 1 + +/*! \file + * + * \brief + * The IRS dnsconf module parses an "advanced" configuration file related to + * the DNS library, such as trust anchors for DNSSEC validation, and creates + * the corresponding configuration objects for the DNS library modules. + * + * Notes: + * This module is very experimental and the configuration syntax or library + * interfaces may change in future versions. Currently, only static + * key configuration is supported; "trusted-keys" and "trust-anchors"/ + * "managed-keys" statements will be parsed exactly as they are in + * named.conf, except that "trust-anchors" and "managed-keys" entries will + * be treated as if they were configured with "static-key", even if they + * were actually configured with "initial-key". + */ + +#include <irs/types.h> + +/*% + * A compound structure storing DNS key information mainly for DNSSEC + * validation. A dns_key_t object will be created using the 'keyname' and + * 'keydatabuf' members with the dst_key_fromdns() function. + */ +typedef struct irs_dnsconf_dnskey { + dns_name_t *keyname; + isc_buffer_t *keydatabuf; + ISC_LINK(struct irs_dnsconf_dnskey) link; +} irs_dnsconf_dnskey_t; + +typedef ISC_LIST(irs_dnsconf_dnskey_t) irs_dnsconf_dnskeylist_t; + +ISC_LANG_BEGINDECLS + +isc_result_t +irs_dnsconf_load(isc_mem_t *mctx, const char *filename, irs_dnsconf_t **confp); +/*%< + * Load the "advanced" DNS configuration file 'filename' in the "dns.conf" + * format, and create a new irs_dnsconf_t object from the configuration. + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li 'filename' != NULL + * + *\li 'confp' != NULL && '*confp' == NULL + */ + +void +irs_dnsconf_destroy(irs_dnsconf_t **confp); +/*%< + * Destroy the dnsconf object. + * + * Requires: + * + *\li '*confp' is a valid dnsconf object. + * + * Ensures: + * + *\li *confp == NULL + */ + +irs_dnsconf_dnskeylist_t * +irs_dnsconf_gettrustedkeys(irs_dnsconf_t *conf); +/*%< + * Return a list of key information stored in 'conf'. + * + * Requires: + * + *\li 'conf' is a valid dnsconf object. + */ + +ISC_LANG_ENDDECLS + +#endif /* IRS_DNSCONF_H */ diff --git a/lib/irs/include/irs/netdb.h.in b/lib/irs/include/irs/netdb.h.in new file mode 100644 index 0000000..c3b4dae --- /dev/null +++ b/lib/irs/include/irs/netdb.h.in @@ -0,0 +1,193 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#ifndef IRS_NETDB_H +#define IRS_NETDB_H 1 + +#include <stddef.h> /* Required on FreeBSD (and others?) for size_t. */ +#include <netdb.h> /* Contractual provision. */ + +/* + * 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-Authoritive 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(). EAI_INSECUREDATA is our own extension + * and it's very unlikely to be already defined, but undef it just in case; it + * at least doesn't do any harm. + */ + +#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_OVERFLOW +#undef EAI_INSECUREDATA +#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_OVERFLOW 14 +#define EAI_INSECUREDATA 15 +#define EAI_MAX 16 + +/* + * 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 to map into irs_ namespace. + */ + +#define IRS_NAMESPACE + +#ifdef IRS_NAMESPACE + +/* + * Use our versions not the ones from the C library. + */ + +#ifdef getnameinfo +#undef getnameinfo +#endif +#define getnameinfo irs_getnameinfo + +#ifdef getaddrinfo +#undef getaddrinfo +#endif +#define getaddrinfo irs_getaddrinfo + +#ifdef freeaddrinfo +#undef freeaddrinfo +#endif +#define freeaddrinfo irs_freeaddrinfo + +#ifdef gai_strerror +#undef gai_strerror +#endif +#define gai_strerror irs_gai_strerror + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res); + +int +getnameinfo(const struct sockaddr *sa, socklen_t salen, + char *host, socklen_t hostlen, + char *serv, socklen_t servlen, + int flags); + +void freeaddrinfo (struct addrinfo *ai); + +const char * +gai_strerror(int ecode); + +#endif /* IRS_NAMESPACE */ + +/* + * Tell Emacs to use C mode on this file. + * Local variables: + * mode: c + * End: + */ + +#endif /* IRS_NETDB_H */ diff --git a/lib/irs/include/irs/platform.h.in b/lib/irs/include/irs/platform.h.in new file mode 100644 index 0000000..54bae37 --- /dev/null +++ b/lib/irs/include/irs/platform.h.in @@ -0,0 +1,32 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#ifndef IRS_PLATFORM_H +#define IRS_PLATFORM_H 1 + +/***** + ***** Platform-dependent defines. + *****/ + +#define LIBIRS_EXTERNAL_DATA + +/* + * Tell Emacs to use C mode on this file. + * Local Variables: + * mode: c + * End: + */ + +#endif /* IRS_PLATFORM_H */ diff --git a/lib/irs/include/irs/resconf.h b/lib/irs/include/irs/resconf.h new file mode 100644 index 0000000..424b795 --- /dev/null +++ b/lib/irs/include/irs/resconf.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef IRS_RESCONF_H +#define IRS_RESCONF_H 1 + +/*! \file + * + * \brief + * The IRS resconf module parses the legacy "/etc/resolv.conf" file and + * creates the corresponding configuration objects for the DNS library + * modules. + */ + +#include <irs/types.h> + +/*% + * A DNS search list specified in the 'domain' or 'search' statements + * in the "resolv.conf" file. + */ +typedef struct irs_resconf_search { + char *domain; + ISC_LINK(struct irs_resconf_search) link; +} irs_resconf_search_t; + +typedef ISC_LIST(irs_resconf_search_t) irs_resconf_searchlist_t; + +ISC_LANG_BEGINDECLS + +isc_result_t +irs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp); +/*%< + * Load the resolver configuration file 'filename' in the "resolv.conf" format, + * and create a new irs_resconf_t object from the configuration. If the file + * is not found ISC_R_FILENOTFOUND is returned with the structure initialized + * as if file contained only: + * + * nameserver ::1 + * nameserver 127.0.0.1 + * + * Notes: + * + *\li Currently, only the following options are supported: + * nameserver, domain, search, sortlist, ndots, and options. + * In addition, 'sortlist' is not actually effective; it's parsed, but + * the application cannot use the configuration. + * + * Returns: + * \li ISC_R_SUCCESS on success + * \li ISC_R_FILENOTFOUND if the file was not found. *confp will be valid. + * \li other on error. + * + * Requires: + * + *\li 'mctx' is a valid memory context. + * + *\li 'filename' != NULL + * + *\li 'confp' != NULL && '*confp' == NULL + */ + +void +irs_resconf_destroy(irs_resconf_t **confp); +/*%< + * Destroy the resconf object. + * + * Requires: + * + *\li '*confp' is a valid resconf object. + * + * Ensures: + * + *\li *confp == NULL + */ + +isc_sockaddrlist_t * +irs_resconf_getnameservers(irs_resconf_t *conf); +/*%< + * Return a list of name server addresses stored in 'conf'. + * + * Requires: + * + *\li 'conf' is a valid resconf object. + */ + +irs_resconf_searchlist_t * +irs_resconf_getsearchlist(irs_resconf_t *conf); +/*%< + * Return the search list stored in 'conf'. + * + * Requires: + * + *\li 'conf' is a valid resconf object. + */ + +unsigned int +irs_resconf_getndots(irs_resconf_t *conf); +/*%< + * Return the 'ndots' value stored in 'conf'. + * + * Requires: + * + *\li 'conf' is a valid resconf object. + */ + +ISC_LANG_ENDDECLS + +#endif /* IRS_RESCONF_H */ diff --git a/lib/irs/include/irs/types.h b/lib/irs/include/irs/types.h new file mode 100644 index 0000000..54153f8 --- /dev/null +++ b/lib/irs/include/irs/types.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef IRS_TYPES_H +#define IRS_TYPES_H 1 + +/* Core Types. Alphabetized by defined type. */ + +/*%< per-thread IRS context */ +typedef struct irs_context irs_context_t; +/*%< resolv.conf configuration information */ +typedef struct irs_resconf irs_resconf_t; +/*%< advanced DNS-related configuration information */ +typedef struct irs_dnsconf irs_dnsconf_t; + +#endif /* IRS_TYPES_H */ diff --git a/lib/irs/include/irs/version.h b/lib/irs/include/irs/version.h new file mode 100644 index 0000000..75ba926 --- /dev/null +++ b/lib/irs/include/irs/version.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <irs/platform.h> + +LIBIRS_EXTERNAL_DATA extern const char irs_version[]; diff --git a/lib/irs/resconf.c b/lib/irs/resconf.c new file mode 100644 index 0000000..f3b7be5 --- /dev/null +++ b/lib/irs/resconf.c @@ -0,0 +1,689 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file resconf.c */ + +/** + * Module for parsing resolv.conf files (largely derived from lwconfig.c). + * + * irs_resconf_load() opens the file filename and parses it to initialize + * the configuration structure. + * + * \section lwconfig_return Return Values + * + * irs_resconf_load() returns #IRS_R_SUCCESS if it successfully read and + * parsed filename. It returns a non-0 error code if filename could not be + * opened or contained incorrect resolver statements. + * + * \section lwconfig_see See Also + * + * stdio(3), \link resolver resolver \endlink + * + * \section files Files + * + * /etc/resolv.conf + */ + +#ifndef WIN32 +#include <netdb.h> +#include <sys/socket.h> +#include <sys/types.h> +#endif /* ifndef WIN32 */ + +#include <ctype.h> +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/netaddr.h> +#include <isc/sockaddr.h> +#include <isc/util.h> + +#include <irs/netdb.h> +#include <irs/resconf.h> + +#define IRS_RESCONF_MAGIC ISC_MAGIC('R', 'E', 'S', 'c') +#define IRS_RESCONF_VALID(c) ISC_MAGIC_VALID(c, IRS_RESCONF_MAGIC) + +/*! + * protocol constants + */ + +#if !defined(NS_INADDRSZ) +#define NS_INADDRSZ 4 +#endif /* if !defined(NS_INADDRSZ) */ + +#if !defined(NS_IN6ADDRSZ) +#define NS_IN6ADDRSZ 16 +#endif /* if !defined(NS_IN6ADDRSZ) */ + +/*! + * resolv.conf parameters + */ + +#define RESCONFMAXNAMESERVERS 3U /*%< max 3 "nameserver" entries */ +#define RESCONFMAXSEARCH 8U /*%< max 8 domains in "search" entry */ +#define RESCONFMAXLINELEN 256U /*%< max size of a line */ +#define RESCONFMAXSORTLIST 10U /*%< max 10 */ + +/*! + * configuration data structure + */ + +struct irs_resconf { + /* + * The configuration data is a thread-specific object, and does not + * need to be locked. + */ + unsigned int magic; + isc_mem_t *mctx; + + isc_sockaddrlist_t nameservers; + unsigned int numns; /*%< number of configured servers + * */ + + char *domainname; + char *search[RESCONFMAXSEARCH]; + uint8_t searchnxt; /*%< index for next free slot + * */ + + irs_resconf_searchlist_t searchlist; + + struct { + isc_netaddr_t addr; + /*% mask has a non-zero 'family' if set */ + isc_netaddr_t mask; + } sortlist[RESCONFMAXSORTLIST]; + uint8_t sortlistnxt; + + /*%< non-zero if 'options debug' set */ + uint8_t resdebug; + /*%< set to n in 'options ndots:n' */ + uint8_t ndots; +}; + +static isc_result_t +resconf_parsenameserver(irs_resconf_t *conf, FILE *fp); +static isc_result_t +resconf_parsedomain(irs_resconf_t *conf, FILE *fp); +static isc_result_t +resconf_parsesearch(irs_resconf_t *conf, FILE *fp); +static isc_result_t +resconf_parsesortlist(irs_resconf_t *conf, FILE *fp); +static isc_result_t +resconf_parseoption(irs_resconf_t *ctx, FILE *fp); + +#if HAVE_GET_WIN32_NAMESERVERS +static isc_result_t +get_win32_nameservers(irs_resconf_t *conf); +#endif /* if HAVE_GET_WIN32_NAMESERVERS */ + +/*! + * Eat characters from FP until EOL or EOF. Returns EOF or '\n' + */ +static int +eatline(FILE *fp) { + int ch; + + ch = fgetc(fp); + while (ch != '\n' && ch != EOF) { + ch = fgetc(fp); + } + + return (ch); +} + +/*! + * Eats white space up to next newline or non-whitespace character (of + * EOF). Returns the last character read. Comments are considered white + * space. + */ +static int +eatwhite(FILE *fp) { + int ch; + + ch = fgetc(fp); + while (ch != '\n' && ch != EOF && isspace((unsigned char)ch)) { + ch = fgetc(fp); + } + + if (ch == ';' || ch == '#') { + ch = eatline(fp); + } + + return (ch); +} + +/*! + * Skip over any leading whitespace and then read in the next sequence of + * non-whitespace characters. In this context newline is not considered + * whitespace. Returns EOF on end-of-file, or the character + * that caused the reading to stop. + */ +static int +getword(FILE *fp, char *buffer, size_t size) { + int ch; + char *p; + + REQUIRE(buffer != NULL); + REQUIRE(size > 0U); + + p = buffer; + *p = '\0'; + + ch = eatwhite(fp); + + if (ch == EOF) { + return (EOF); + } + + do { + *p = '\0'; + + if (ch == EOF || isspace((unsigned char)ch)) { + break; + } else if ((size_t)(p - buffer) == size - 1) { + return (EOF); /* Not enough space. */ + } + + *p++ = (char)ch; + ch = fgetc(fp); + } while (1); + + return (ch); +} + +static isc_result_t +add_server(isc_mem_t *mctx, const char *address_str, + isc_sockaddrlist_t *nameservers) { + int error; + isc_sockaddr_t *address = NULL; + struct addrinfo hints, *res; + isc_result_t result = ISC_R_SUCCESS; + + res = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_protocol = IPPROTO_UDP; + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(address_str, "53", &hints, &res); + if (error != 0) { + return (ISC_R_BADADDRESSFORM); + } + + /* XXX: special case: treat all-0 IPv4 address as loopback */ + if (res->ai_family == AF_INET) { + struct in_addr *v4; + unsigned char zeroaddress[] = { 0, 0, 0, 0 }; + unsigned char loopaddress[] = { 127, 0, 0, 1 }; + + v4 = &((struct sockaddr_in *)res->ai_addr)->sin_addr; + if (memcmp(v4, zeroaddress, 4) == 0) { + memmove(v4, loopaddress, 4); + } + } + + address = isc_mem_get(mctx, sizeof(*address)); + if (res->ai_addrlen > sizeof(address->type)) { + isc_mem_put(mctx, address, sizeof(*address)); + result = ISC_R_RANGE; + goto cleanup; + } + address->length = (unsigned int)res->ai_addrlen; + memmove(&address->type.ss, res->ai_addr, res->ai_addrlen); + ISC_LINK_INIT(address, link); + ISC_LIST_APPEND(*nameservers, address, link); + +cleanup: + freeaddrinfo(res); + + return (result); +} + +static isc_result_t +create_addr(const char *buffer, isc_netaddr_t *addr, int convert_zero) { + struct in_addr v4; + struct in6_addr v6; + + if (inet_pton(AF_INET, buffer, &v4) == 1) { + if (convert_zero) { + unsigned char zeroaddress[] = { 0, 0, 0, 0 }; + unsigned char loopaddress[] = { 127, 0, 0, 1 }; + if (memcmp(&v4, zeroaddress, 4) == 0) { + memmove(&v4, loopaddress, 4); + } + } + addr->family = AF_INET; + memmove(&addr->type.in, &v4, NS_INADDRSZ); + addr->zone = 0; + } else if (inet_pton(AF_INET6, buffer, &v6) == 1) { + addr->family = AF_INET6; + memmove(&addr->type.in6, &v6, NS_IN6ADDRSZ); + addr->zone = 0; + } else { + return (ISC_R_BADADDRESSFORM); /* Unrecognised format. */ + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +resconf_parsenameserver(irs_resconf_t *conf, FILE *fp) { + char word[RESCONFMAXLINELEN]; + int cp; + isc_result_t result; + + cp = getword(fp, word, sizeof(word)); + if (strlen(word) == 0U) { + return (ISC_R_UNEXPECTEDEND); /* Nothing on line. */ + } else if (cp == ' ' || cp == '\t') { + cp = eatwhite(fp); + } + + if (cp != EOF && cp != '\n') { + return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */ + } + + if (conf->numns == RESCONFMAXNAMESERVERS) { + return (ISC_R_SUCCESS); + } + + result = add_server(conf->mctx, word, &conf->nameservers); + if (result != ISC_R_SUCCESS) { + return (result); + } + conf->numns++; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +resconf_parsedomain(irs_resconf_t *conf, FILE *fp) { + char word[RESCONFMAXLINELEN]; + int res; + unsigned int i; + + res = getword(fp, word, sizeof(word)); + if (strlen(word) == 0U) { + return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */ + } else if (res == ' ' || res == '\t') { + res = eatwhite(fp); + } + + if (res != EOF && res != '\n') { + return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */ + } + + if (conf->domainname != NULL) { + isc_mem_free(conf->mctx, conf->domainname); + } + + /* + * Search and domain are mutually exclusive. + */ + for (i = 0; i < RESCONFMAXSEARCH; i++) { + if (conf->search[i] != NULL) { + isc_mem_free(conf->mctx, conf->search[i]); + conf->search[i] = NULL; + } + } + conf->searchnxt = 0; + + conf->domainname = isc_mem_strdup(conf->mctx, word); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +resconf_parsesearch(irs_resconf_t *conf, FILE *fp) { + int delim; + unsigned int idx; + char word[RESCONFMAXLINELEN]; + + if (conf->domainname != NULL) { + /* + * Search and domain are mutually exclusive. + */ + isc_mem_free(conf->mctx, conf->domainname); + conf->domainname = NULL; + } + + /* + * Remove any previous search definitions. + */ + for (idx = 0; idx < RESCONFMAXSEARCH; idx++) { + if (conf->search[idx] != NULL) { + isc_mem_free(conf->mctx, conf->search[idx]); + conf->search[idx] = NULL; + } + } + conf->searchnxt = 0; + + delim = getword(fp, word, sizeof(word)); + if (strlen(word) == 0U) { + return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */ + } + + idx = 0; + while (strlen(word) > 0U) { + if (conf->searchnxt == RESCONFMAXSEARCH) { + goto ignore; /* Too many domains. */ + } + + INSIST(idx < sizeof(conf->search) / sizeof(conf->search[0])); + conf->search[idx] = isc_mem_strdup(conf->mctx, word); + idx++; + conf->searchnxt++; + + ignore: + if (delim == EOF || delim == '\n') { + break; + } else { + delim = getword(fp, word, sizeof(word)); + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +resconf_parsesortlist(irs_resconf_t *conf, FILE *fp) { + int delim, res; + unsigned int idx; + char word[RESCONFMAXLINELEN]; + char *p; + + delim = getword(fp, word, sizeof(word)); + if (strlen(word) == 0U) { + return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */ + } + + while (strlen(word) > 0U) { + if (conf->sortlistnxt == RESCONFMAXSORTLIST) { + return (ISC_R_QUOTA); /* Too many values. */ + } + + p = strchr(word, '/'); + if (p != NULL) { + *p++ = '\0'; + } + + idx = conf->sortlistnxt; + INSIST(idx < + sizeof(conf->sortlist) / sizeof(conf->sortlist[0])); + res = create_addr(word, &conf->sortlist[idx].addr, 1); + if (res != ISC_R_SUCCESS) { + return (res); + } + + if (p != NULL) { + res = create_addr(p, &conf->sortlist[idx].mask, 0); + if (res != ISC_R_SUCCESS) { + return (res); + } + } else { + /* + * Make up a mask. (XXX: is this correct?) + */ + conf->sortlist[idx].mask = conf->sortlist[idx].addr; + memset(&conf->sortlist[idx].mask.type, 0xff, + sizeof(conf->sortlist[idx].mask.type)); + } + + conf->sortlistnxt++; + + if (delim == EOF || delim == '\n') { + break; + } else { + delim = getword(fp, word, sizeof(word)); + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +resconf_parseoption(irs_resconf_t *conf, FILE *fp) { + int delim; + long ndots; + char *p; + char word[RESCONFMAXLINELEN]; + + delim = getword(fp, word, sizeof(word)); + if (strlen(word) == 0U) { + return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */ + } + + while (strlen(word) > 0U) { + if (strcmp("debug", word) == 0) { + conf->resdebug = 1; + } else if (strncmp("ndots:", word, 6) == 0) { + ndots = strtol(word + 6, &p, 10); + if (*p != '\0') { /* Bad string. */ + return (ISC_R_UNEXPECTEDTOKEN); + } + if (ndots < 0 || ndots > 0xff) { /* Out of range. */ + return (ISC_R_RANGE); + } + conf->ndots = (uint8_t)ndots; + } + + if (delim == EOF || delim == '\n') { + break; + } else { + delim = getword(fp, word, sizeof(word)); + } + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +add_search(irs_resconf_t *conf, char *domain) { + irs_resconf_search_t *entry; + + entry = isc_mem_get(conf->mctx, sizeof(*entry)); + + entry->domain = domain; + ISC_LINK_INIT(entry, link); + ISC_LIST_APPEND(conf->searchlist, entry, link); + + return (ISC_R_SUCCESS); +} + +/*% parses a file and fills in the data structure. */ +isc_result_t +irs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp) { + FILE *fp = NULL; + char word[256]; + isc_result_t rval, ret = ISC_R_SUCCESS; + irs_resconf_t *conf; + unsigned int i; + int stopchar; + + REQUIRE(mctx != NULL); + REQUIRE(filename != NULL); + REQUIRE(strlen(filename) > 0U); + REQUIRE(confp != NULL && *confp == NULL); + + conf = isc_mem_get(mctx, sizeof(*conf)); + + conf->mctx = mctx; + ISC_LIST_INIT(conf->nameservers); + ISC_LIST_INIT(conf->searchlist); + conf->numns = 0; + conf->domainname = NULL; + conf->searchnxt = 0; + conf->sortlistnxt = 0; + conf->resdebug = 0; + conf->ndots = 1; + for (i = 0; i < RESCONFMAXSEARCH; i++) { + conf->search[i] = NULL; + } + + errno = 0; + if ((fp = fopen(filename, "r")) != NULL) { + do { + stopchar = getword(fp, word, sizeof(word)); + if (stopchar == EOF) { + rval = ISC_R_SUCCESS; + POST(rval); + break; + } + + if (strlen(word) == 0U) { + rval = ISC_R_SUCCESS; + } else if (strcmp(word, "nameserver") == 0) { + rval = resconf_parsenameserver(conf, fp); + } else if (strcmp(word, "domain") == 0) { + rval = resconf_parsedomain(conf, fp); + } else if (strcmp(word, "search") == 0) { + rval = resconf_parsesearch(conf, fp); + } else if (strcmp(word, "sortlist") == 0) { + rval = resconf_parsesortlist(conf, fp); + } else if (strcmp(word, "options") == 0) { + rval = resconf_parseoption(conf, fp); + } else { + /* unrecognised word. Ignore entire line */ + rval = ISC_R_SUCCESS; + stopchar = eatline(fp); + if (stopchar == EOF) { + break; + } + } + if (ret == ISC_R_SUCCESS && rval != ISC_R_SUCCESS) { + ret = rval; + } + } while (1); + + fclose(fp); + } else { + switch (errno) { + case ENOENT: + break; + default: + isc_mem_put(mctx, conf, sizeof(*conf)); + return (ISC_R_INVALIDFILE); + } + } + + if (ret != ISC_R_SUCCESS) { + goto error; + } + + /* + * Construct unified search list from domain or configured + * search list + */ + if (conf->domainname != NULL) { + ret = add_search(conf, conf->domainname); + } else if (conf->searchnxt > 0) { + for (i = 0; i < conf->searchnxt; i++) { + ret = add_search(conf, conf->search[i]); + if (ret != ISC_R_SUCCESS) { + break; + } + } + } + +#if HAVE_GET_WIN32_NAMESERVERS + ret = get_win32_nameservers(conf); + if (ret != ISC_R_SUCCESS) { + goto error; + } +#endif /* if HAVE_GET_WIN32_NAMESERVERS */ + + /* If we don't find a nameserver fall back to localhost */ + if (conf->numns == 0U) { + INSIST(ISC_LIST_EMPTY(conf->nameservers)); + + /* XXX: should we catch errors? */ + (void)add_server(conf->mctx, "::1", &conf->nameservers); + (void)add_server(conf->mctx, "127.0.0.1", &conf->nameservers); + } + +error: + conf->magic = IRS_RESCONF_MAGIC; + + if (ret != ISC_R_SUCCESS) { + irs_resconf_destroy(&conf); + } else { + if (fp == NULL) { + ret = ISC_R_FILENOTFOUND; + } + *confp = conf; + } + + return (ret); +} + +void +irs_resconf_destroy(irs_resconf_t **confp) { + irs_resconf_t *conf; + isc_sockaddr_t *address; + irs_resconf_search_t *searchentry; + unsigned int i; + + REQUIRE(confp != NULL); + conf = *confp; + *confp = NULL; + REQUIRE(IRS_RESCONF_VALID(conf)); + + while ((searchentry = ISC_LIST_HEAD(conf->searchlist)) != NULL) { + ISC_LIST_UNLINK(conf->searchlist, searchentry, link); + isc_mem_put(conf->mctx, searchentry, sizeof(*searchentry)); + } + + while ((address = ISC_LIST_HEAD(conf->nameservers)) != NULL) { + ISC_LIST_UNLINK(conf->nameservers, address, link); + isc_mem_put(conf->mctx, address, sizeof(*address)); + } + + if (conf->domainname != NULL) { + isc_mem_free(conf->mctx, conf->domainname); + } + + for (i = 0; i < RESCONFMAXSEARCH; i++) { + if (conf->search[i] != NULL) { + isc_mem_free(conf->mctx, conf->search[i]); + } + } + + isc_mem_put(conf->mctx, conf, sizeof(*conf)); +} + +isc_sockaddrlist_t * +irs_resconf_getnameservers(irs_resconf_t *conf) { + REQUIRE(IRS_RESCONF_VALID(conf)); + + return (&conf->nameservers); +} + +irs_resconf_searchlist_t * +irs_resconf_getsearchlist(irs_resconf_t *conf) { + REQUIRE(IRS_RESCONF_VALID(conf)); + + return (&conf->searchlist); +} + +unsigned int +irs_resconf_getndots(irs_resconf_t *conf) { + REQUIRE(IRS_RESCONF_VALID(conf)); + + return ((unsigned int)conf->ndots); +} diff --git a/lib/irs/tests/Kyuafile b/lib/irs/tests/Kyuafile new file mode 100644 index 0000000..a203172 --- /dev/null +++ b/lib/irs/tests/Kyuafile @@ -0,0 +1,15 @@ +-- Copyright (C) Internet Systems Consortium, Inc. ("ISC") +-- +-- SPDX-License-Identifier: MPL-2.0 +-- +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, you can obtain one at https://mozilla.org/MPL/2.0/. +-- +-- See the COPYRIGHT file distributed with this work for additional +-- information regarding copyright ownership. + +syntax(2) +test_suite('bind9') + +tap_test_program{name='resconf_test'} diff --git a/lib/irs/tests/Makefile.in b/lib/irs/tests/Makefile.in new file mode 100644 index 0000000..327d31d --- /dev/null +++ b/lib/irs/tests/Makefile.in @@ -0,0 +1,51 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +VERSION=@BIND9_VERSION@ + +@BIND9_MAKE_INCLUDES@ + +CINCLUDES = -I. -Iinclude -I../include ${ISC_INCLUDES} ${IRS_INCLUDES} @CMOCKA_CFLAGS@ +CDEFINES = -DTESTS="\"${top_builddir}/lib/irs/tests/\"" + +CFGLIBS = ../../isccfg/libisccfg.@A@ +CFGDEPLIBS = ../../isccfg/libisccfg.@A@ +DNSLIBS = ../../dns/libdns.@A@ @NO_LIBTOOL_DNSLIBS@ +DNSDEPLIBS = ../../dns/libdns.@A@ +ISCLIBS = ../../isc/libisc.@A@ @NO_LIBTOOL_ISCLIBS@ +ISCDEPLIBS = ../../isc/libisc.@A@ +IRSLIBS = ../libirs.@A@ +IRSDEPLIBS = ../libirs.@A@ + +LIBS = ${IRSLIBS} ${CFGLIBS} ${DNSLIBS} ${ISCLIBS} @LIBS@ @CMOCKA_LIBS@ + +OBJS = +SRCS = resconf_test.c + +SUBDIRS = +TARGETS = resconf_test@EXEEXT@ + +@BIND9_MAKE_RULES@ + +resconf_test@EXEEXT@: resconf_test.@O@ ${CFGDEPLIBS} ${DNSDEPLIBS} ${IRSDEPLIBS} ${ISCDEPLIBS} + ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} \ + ${LDFLAGS} -o $@ resconf_test.@O@ ${LIBS} + +unit:: + sh ${top_builddir}/unit/unittest.sh + +clean distclean:: + rm -f ${TARGETS} + rm -f atf.out diff --git a/lib/irs/tests/resconf_test.c b/lib/irs/tests/resconf_test.c new file mode 100644 index 0000000..6951758 --- /dev/null +++ b/lib/irs/tests/resconf_test.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#if HAVE_CMOCKA + +#include <sched.h> /* IWYU pragma: keep */ +#include <setjmp.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define UNIT_TESTING +#include <cmocka.h> + +#include <isc/mem.h> +#include <isc/util.h> + +#include <irs/resconf.h> +#include <irs/types.h> + +static isc_mem_t *mctx = NULL; + +static void +setup_test() { + isc_mem_create(&mctx); + + /* + * the caller might run from another directory, but tests + * that access test data files must first chdir to the proper + * location. + */ + assert_return_code(chdir(TESTS), 0); +} + +/* test irs_resconf_load() */ +static void +irs_resconf_load_test(void **state) { + isc_result_t result; + irs_resconf_t *resconf = NULL; + unsigned int i; + struct { + const char *file; + isc_result_t loadres; + isc_result_t (*check)(irs_resconf_t *resconf); + isc_result_t checkres; + } tests[] = { + { "testdata/domain.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, + { "testdata/nameserver-v4.conf", ISC_R_SUCCESS, NULL, + ISC_R_SUCCESS }, + { "testdata/nameserver-v6.conf", ISC_R_SUCCESS, NULL, + ISC_R_SUCCESS }, + { "testdata/nameserver-v6-scoped.conf", ISC_R_SUCCESS, NULL, + ISC_R_SUCCESS }, + { "testdata/options-debug.conf", ISC_R_SUCCESS, NULL, + ISC_R_SUCCESS }, + { "testdata/options-ndots.conf", ISC_R_SUCCESS, NULL, + ISC_R_SUCCESS }, + { "testdata/options-timeout.conf", ISC_R_SUCCESS, NULL, + ISC_R_SUCCESS }, + { "testdata/options-unknown.conf", ISC_R_SUCCESS, NULL, + ISC_R_SUCCESS }, + { "testdata/options.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, + { "testdata/options-bad-ndots.conf", ISC_R_RANGE, NULL, + ISC_R_SUCCESS }, + { "testdata/options-empty.conf", ISC_R_UNEXPECTEDEND, NULL, + ISC_R_SUCCESS }, + { "testdata/port.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, + { "testdata/resolv.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, + { "testdata/search.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, + { "testdata/sortlist-v4.conf", ISC_R_SUCCESS, NULL, + ISC_R_SUCCESS }, + { "testdata/timeout.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, + { "testdata/unknown.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS } + }; + + UNUSED(state); + + setup_test(); + + for (i = 0; i < sizeof(tests) / sizeof(tests[1]); i++) { + result = irs_resconf_load(mctx, tests[i].file, &resconf); + if (result != tests[i].loadres) { + fail_msg("# unexpected result %s loading %s", + isc_result_totext(result), tests[i].file); + } + + if (result == ISC_R_SUCCESS && resconf == NULL) { + fail_msg("# NULL on success loading %s", tests[i].file); + } else if (result != ISC_R_SUCCESS && resconf != NULL) { + fail_msg("# non-NULL on failure loading %s", + tests[i].file); + } + + if (resconf != NULL && tests[i].check != NULL) { + result = (tests[i].check)(resconf); + if (result != tests[i].checkres) { + fail_msg("# unexpected result %s loading %s", + isc_result_totext(result), + tests[i].file); + } + } + if (resconf != NULL) { + irs_resconf_destroy(&resconf); + } + } + + isc_mem_detach(&mctx); +} + +int +main(void) { + const struct CMUnitTest tests[] = { + cmocka_unit_test(irs_resconf_load_test), + }; + + return (cmocka_run_group_tests(tests, NULL, NULL)); +} + +#else /* HAVE_CMOCKA */ + +#include <stdio.h> + +int +main(void) { + printf("1..0 # Skipped: cmocka not available\n"); + return (SKIPPED_TEST_EXIT_CODE); +} + +#endif /* if HAVE_CMOCKA */ diff --git a/lib/irs/tests/testdata/domain.conf b/lib/irs/tests/testdata/domain.conf new file mode 100644 index 0000000..ee43f5c --- /dev/null +++ b/lib/irs/tests/testdata/domain.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +domain example.com diff --git a/lib/irs/tests/testdata/nameserver-v4.conf b/lib/irs/tests/testdata/nameserver-v4.conf new file mode 100644 index 0000000..5054de0 --- /dev/null +++ b/lib/irs/tests/testdata/nameserver-v4.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +nameserver 10.0.0.1 diff --git a/lib/irs/tests/testdata/nameserver-v6-scoped.conf b/lib/irs/tests/testdata/nameserver-v6-scoped.conf new file mode 100644 index 0000000..d5bd97f --- /dev/null +++ b/lib/irs/tests/testdata/nameserver-v6-scoped.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +nameserver fe80::1%1 diff --git a/lib/irs/tests/testdata/nameserver-v6.conf b/lib/irs/tests/testdata/nameserver-v6.conf new file mode 100644 index 0000000..b9ed093 --- /dev/null +++ b/lib/irs/tests/testdata/nameserver-v6.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +nameserver 2001:DB8::1 diff --git a/lib/irs/tests/testdata/options-bad-ndots.conf b/lib/irs/tests/testdata/options-bad-ndots.conf new file mode 100644 index 0000000..18d3f8b --- /dev/null +++ b/lib/irs/tests/testdata/options-bad-ndots.conf @@ -0,0 +1,13 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +search example.com example.net +options ndots:256 diff --git a/lib/irs/tests/testdata/options-debug.conf b/lib/irs/tests/testdata/options-debug.conf new file mode 100644 index 0000000..04bcf2e --- /dev/null +++ b/lib/irs/tests/testdata/options-debug.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +options debug diff --git a/lib/irs/tests/testdata/options-empty.conf b/lib/irs/tests/testdata/options-empty.conf new file mode 100644 index 0000000..0b1dc9e --- /dev/null +++ b/lib/irs/tests/testdata/options-empty.conf @@ -0,0 +1,13 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +domain example.com +options diff --git a/lib/irs/tests/testdata/options-ndots.conf b/lib/irs/tests/testdata/options-ndots.conf new file mode 100644 index 0000000..5d18d26 --- /dev/null +++ b/lib/irs/tests/testdata/options-ndots.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +option ndots:2 diff --git a/lib/irs/tests/testdata/options-timeout.conf b/lib/irs/tests/testdata/options-timeout.conf new file mode 100644 index 0000000..96787c4 --- /dev/null +++ b/lib/irs/tests/testdata/options-timeout.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +options timeout:1 diff --git a/lib/irs/tests/testdata/options-unknown.conf b/lib/irs/tests/testdata/options-unknown.conf new file mode 100644 index 0000000..fdf82b4 --- /dev/null +++ b/lib/irs/tests/testdata/options-unknown.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +options unknown diff --git a/lib/irs/tests/testdata/options.conf b/lib/irs/tests/testdata/options.conf new file mode 100644 index 0000000..7a8d5f3 --- /dev/null +++ b/lib/irs/tests/testdata/options.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +options unknown debug timeout:1 ndots:2 diff --git a/lib/irs/tests/testdata/port.conf b/lib/irs/tests/testdata/port.conf new file mode 100644 index 0000000..54e2094 --- /dev/null +++ b/lib/irs/tests/testdata/port.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +port 5300 diff --git a/lib/irs/tests/testdata/resolv.conf b/lib/irs/tests/testdata/resolv.conf new file mode 100644 index 0000000..3c14408 --- /dev/null +++ b/lib/irs/tests/testdata/resolv.conf @@ -0,0 +1,19 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +port 5300 +nameserver 10.0.0.1 +nameserver 2001:DB8::1 +search example.com example.net +sortlist 130.155.160.0/255.255.240.0 130.155.0.0 +timeout 10 +unknown directive +options unknown debug timeout:1 ndots:2 diff --git a/lib/irs/tests/testdata/search.conf b/lib/irs/tests/testdata/search.conf new file mode 100644 index 0000000..f019ab9 --- /dev/null +++ b/lib/irs/tests/testdata/search.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +search example.com example.net diff --git a/lib/irs/tests/testdata/sortlist-v4.conf b/lib/irs/tests/testdata/sortlist-v4.conf new file mode 100644 index 0000000..3f4d54a --- /dev/null +++ b/lib/irs/tests/testdata/sortlist-v4.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +sortlist 130.155.160.0/255.255.240.0 130.155.0.0 diff --git a/lib/irs/tests/testdata/timeout.conf b/lib/irs/tests/testdata/timeout.conf new file mode 100644 index 0000000..2ccb92d --- /dev/null +++ b/lib/irs/tests/testdata/timeout.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +timeout 10 diff --git a/lib/irs/tests/testdata/unknown.conf b/lib/irs/tests/testdata/unknown.conf new file mode 100644 index 0000000..0eafb76 --- /dev/null +++ b/lib/irs/tests/testdata/unknown.conf @@ -0,0 +1,12 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +unknown directive diff --git a/lib/irs/version.c b/lib/irs/version.c new file mode 100644 index 0000000..b0b4fd9 --- /dev/null +++ b/lib/irs/version.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#include <irs/version.h> + +const char irs_version[] = VERSION; diff --git a/lib/irs/win32/DLLMain.c b/lib/irs/win32/DLLMain.c new file mode 100644 index 0000000..62c6e53 --- /dev/null +++ b/lib/irs/win32/DLLMain.c @@ -0,0 +1,49 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <signal.h> +#include <windows.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/irs/win32/Makefile.in b/lib/irs/win32/Makefile.in new file mode 100644 index 0000000..5641e1a --- /dev/null +++ b/lib/irs/win32/Makefile.in @@ -0,0 +1,19 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +SUBDIRS = include +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/irs/win32/include/.clang-format b/lib/irs/win32/include/.clang-format new file mode 120000 index 0000000..e919bba --- /dev/null +++ b/lib/irs/win32/include/.clang-format @@ -0,0 +1 @@ +../../../../.clang-format.headers
\ No newline at end of file diff --git a/lib/irs/win32/include/Makefile.in b/lib/irs/win32/include/Makefile.in new file mode 100644 index 0000000..53a5348 --- /dev/null +++ b/lib/irs/win32/include/Makefile.in @@ -0,0 +1,19 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +SUBDIRS = irs +TARGETS = + +@BIND9_MAKE_RULES@ diff --git a/lib/irs/win32/include/irs/Makefile.in b/lib/irs/win32/include/irs/Makefile.in new file mode 100644 index 0000000..6d87432 --- /dev/null +++ b/lib/irs/win32/include/irs/Makefile.in @@ -0,0 +1,28 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# SPDX-License-Identifier: MPL-2.0 +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +HEADERS = +SUBDIRS = +TARGETS = + +@BIND9_MAKE_RULES@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/irs + +install:: installdirs + for i in ${HEADERS}; do \ + ${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/irs || exit 1; \ + done diff --git a/lib/irs/win32/include/irs/netdb.h b/lib/irs/win32/include/irs/netdb.h new file mode 100644 index 0000000..3e3e5d3 --- /dev/null +++ b/lib/irs/win32/include/irs/netdb.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#ifndef IRS_NETDB_H +#define IRS_NETDB_H 1 + +#include <stddef.h> /* Required on FreeBSD (and others?) for size_t. */ + +/* + * Define if <netdb.h> does not declare struct addrinfo. + */ +#undef ISC_IRS_NEEDADDRINFO + +#ifdef ISC_IRS_NEEDADDRINFO +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 /* ifdef ISC_IRS_NEEDADDRINFO */ + +/* + * 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-Authoritive 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(). EAI_INSECUREDATA is our own extension + * and it's very unlikely to be already defined, but undef it just in case; it + * at least doesn't do any harm. + */ + +#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_OVERFLOW +#undef EAI_INSECUREDATA +#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_OVERFLOW 14 +#define EAI_INSECUREDATA 15 +#define EAI_MAX 16 + +/* + * 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 to map into irs_ namespace. + */ + +#define IRS_NAMESPACE + +#ifdef IRS_NAMESPACE + +/* + * Use our versions not the ones from the C library. + */ + +#ifdef getnameinfo +#undef getnameinfo +#endif /* ifdef getnameinfo */ +#define getnameinfo irs_getnameinfo + +#ifdef getaddrinfo +#undef getaddrinfo +#endif /* ifdef getaddrinfo */ +#define getaddrinfo irs_getaddrinfo + +#ifdef freeaddrinfo +#undef freeaddrinfo +#endif /* ifdef freeaddrinfo */ +#define freeaddrinfo irs_freeaddrinfo + +#ifdef gai_strerror +#undef gai_strerror +#endif /* ifdef gai_strerror */ +#define gai_strerror irs_gai_strerror + +#endif /* ifdef IRS_NAMESPACE */ + +int +getaddrinfo(const char *, const char *, const struct addrinfo *, + struct addrinfo **); +int +getnameinfo(const struct sockaddr *, socklen_t, char *, DWORD, char *, DWORD, + int); +void +freeaddrinfo(struct addrinfo *); +char * +gai_strerror(int); + +/* + * Tell Emacs to use C mode on this file. + * Local variables: + * mode: c + * End: + */ + +#endif /* IRS_NETDB_H */ diff --git a/lib/irs/win32/include/irs/platform.h b/lib/irs/win32/include/irs/platform.h new file mode 100644 index 0000000..2f0cced --- /dev/null +++ b/lib/irs/win32/include/irs/platform.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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/*! \file */ + +#ifndef IRS_PLATFORM_H +#define IRS_PLATFORM_H 1 + +/***** +***** Platform-dependent defines. +*****/ + +#ifdef LIBIRS_EXPORTS +#define LIBIRS_EXTERNAL_DATA __declspec(dllexport) +#else /* ifdef LIBIRS_EXPORTS */ +#define LIBIRS_EXTERNAL_DATA __declspec(dllimport) +#endif /* ifdef LIBIRS_EXPORTS */ + +/* + * Tell Emacs to use C mode on this file. + * Local Variables: + * mode: c + * End: + */ + +#endif /* IRS_PLATFORM_H */ diff --git a/lib/irs/win32/libirs.def b/lib/irs/win32/libirs.def new file mode 100644 index 0000000..d4e9f6f --- /dev/null +++ b/lib/irs/win32/libirs.def @@ -0,0 +1,27 @@ +LIBRARY libirs + +; Exported Functions +EXPORTS +irs_context_create +irs_context_destroy +irs_context_get +irs_context_getappctx +irs_context_getdnsclient +irs_context_getdnsconf +irs_context_getmctx +irs_context_getresconf +irs_context_gettask +irs_context_gettaskmgr +irs_context_gettimermgr +irs_dnsconf_destroy +irs_dnsconf_gettrustedkeys +irs_dnsconf_load +irs_freeaddrinfo +irs_gai_strerror +irs_getaddrinfo +irs_getnameinfo +irs_resconf_destroy +irs_resconf_getnameservers +irs_resconf_getndots +irs_resconf_getsearchlist +irs_resconf_load diff --git a/lib/irs/win32/libirs.vcxproj.filters.in b/lib/irs/win32/libirs.vcxproj.filters.in new file mode 100644 index 0000000..0ffbc07 --- /dev/null +++ b/lib/irs/win32/libirs.vcxproj.filters.in @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="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="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hpp;hxx;hm;inl;inc;xsd</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="libirs.def" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="DLLMain.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="version.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\context.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\dnsconf.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\gai_strerror.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\getaddrinfo.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\getnameinfo.c"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="resconf.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\include\irs\context.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\irs\dnsconf.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\irs\netdb.h.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\irs\platform.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\irs\resconf.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\irs\types.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\include\irs\version.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project> diff --git a/lib/irs/win32/libirs.vcxproj.in b/lib/irs/win32/libirs.vcxproj.in new file mode 100644 index 0000000..078ec92 --- /dev/null +++ b/lib/irs/win32/libirs.vcxproj.in @@ -0,0 +1,142 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="@TOOLS_VERSION@" 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>{A4F29CEB-7644-4A7F-BE9E-02B6A90E4919}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>libirs</RootNamespace> + @WINDOWS_TARGET_PLATFORM_VERSION@ + </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> + @PLATFORM_TOOLSET@ + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>MultiByte</CharacterSet> + @PLATFORM_TOOLSET@ + </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> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <LinkIncremental>false</LinkIncremental> + <OutDir>..\..\..\Build\$(Configuration)\</OutDir> + <IntDir>.\$(Configuration)\</IntDir> + <IntDirSharingDetected>None</IntDirSharingDetected> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level4</WarningLevel> + <TreatWarningAsError>false</TreatWarningAsError> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBIRS_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\..\..\;include;..\include;..\..\isc\win32;..\..\isc\win32\include;..\..\isc\include;..\..\isccfg\include;..\..\dns\include;@LIBXML2_INC@@OPENSSL_INC@@GEOIP_INC@%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <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> + <AdditionalLibraryDirectories>..\..\isc\win32\$(Configuration);..\..\dns\win32\$(Configuration);..\..\isccfg\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@libisc.lib;libdns.lib;libisccfg.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <ModuleDefinitionFile>.\libirs.def</ModuleDefinitionFile> + <ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'"> + <ClCompile> + <WarningLevel>Level1</WarningLevel> + <TreatWarningAsError>true</TreatWarningAsError> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBIRS_EXPORTS;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions> + <ForcedIncludeFiles>..\..\..\config.h</ForcedIncludeFiles> + <AdditionalIncludeDirectories>.\;..\..\..\;include;..\include;..\..\isc\win32;..\..\isc\win32\include;..\..\isc\include;..\..\isccfg\include;..\..\dns\include;@LIBXML2_INC@@OPENSSL_INC@@GEOIP_INC@%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <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> + <AdditionalLibraryDirectories>..\..\isc\win32\$(Configuration);..\..\dns\win32\$(Configuration);..\..\isccfg\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> + <AdditionalDependencies>@OPENSSL_LIBCRYPTO@@OPENSSL_LIBSSL@libisc.lib;libdns.lib;libisccfg.lib;ws2_32.lib;iphlpapi.lib;%(AdditionalDependencies)</AdditionalDependencies> + <ModuleDefinitionFile>.\libirs.def</ModuleDefinitionFile> + <ImportLibrary>.\$(Configuration)\$(ProjectName).lib</ImportLibrary> + <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration> + <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <None Include="libirs.def" /> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\context.c" /> + <ClCompile Include="..\dnsconf.c" /> + <ClCompile Include="..\gai_strerror.c" /> + <ClCompile Include="..\getaddrinfo.c" /> + <ClCompile Include="..\getnameinfo.c" /> + <ClCompile Include="DLLMain.c" /> + <ClCompile Include="resconf.c" /> + <ClCompile Include="version.c" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\include\irs\context.h" /> + <ClInclude Include="..\include\irs\dnsconf.h" /> + <ClInclude Include="..\include\irs\netdb.h" /> + <ClInclude Include="..\include\irs\platform.h" /> + <ClInclude Include="..\include\irs\resconf.h" /> + <ClInclude Include="..\include\irs\types.h" /> + <ClInclude Include="..\include\irs\version.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project> diff --git a/lib/irs/win32/libirs.vcxproj.user b/lib/irs/win32/libirs.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/lib/irs/win32/libirs.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/irs/win32/resconf.c b/lib/irs/win32/resconf.c new file mode 100644 index 0000000..3b46b8e --- /dev/null +++ b/lib/irs/win32/resconf.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Note that on Win32 there is normally no resolv.conf since all information + * is stored in the registry. Therefore there is no ordering like the + * contents of resolv.conf. Since the "search" or "domain" keyword, on + * Win32 if a search list is found it is used, otherwise the domain name + * is used since they are mutually exclusive. The search list can be entered + * in the DNS tab of the "Advanced TCP/IP settings" window under the same place + * that you add your nameserver list. + */ + +#define HAVE_GET_WIN32_NAMESERVERS 1 + +#include "../resconf.c" +#include <iphlpapi.h> + +#define TCPIP_SUBKEY "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters" + +isc_result_t +get_win32_searchlist(irs_resconf_t *conf) { + isc_result_t result = ISC_R_SUCCESS; + HKEY hKey; + char searchlist[MAX_PATH]; + DWORD searchlen = MAX_PATH; + LSTATUS status; + char *cp; + + REQUIRE(conf != NULL); + + memset(searchlist, 0, MAX_PATH); + status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TCPIP_SUBKEY, 0, KEY_READ, + &hKey); + if (status != ERROR_SUCCESS) { + return (ISC_R_SUCCESS); + } + + status = RegQueryValueEx(hKey, "SearchList", NULL, NULL, + (LPBYTE)searchlist, &searchlen); + RegCloseKey(hKey); + if (status != ERROR_SUCCESS) { + return (ISC_R_SUCCESS); + } + + cp = strtok((char *)searchlist, ", \0"); + while (cp != NULL) { + result = add_search(conf, cp); + if (result != ISC_R_SUCCESS) { + break; + } + cp = strtok(NULL, ", \0"); + } + return (result); +} + +isc_result_t +get_win32_nameservers(irs_resconf_t *conf) { + isc_result_t result; + FIXED_INFO *FixedInfo; + ULONG BufLen = sizeof(FIXED_INFO); + DWORD dwRetVal; + IP_ADDR_STRING *pIPAddr; + + REQUIRE(conf != NULL); + + FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, BufLen); + if (FixedInfo == NULL) { + return (ISC_R_NOMEMORY); + } + dwRetVal = GetNetworkParams(FixedInfo, &BufLen); + if (dwRetVal == ERROR_BUFFER_OVERFLOW) { + GlobalFree(FixedInfo); + FixedInfo = GlobalAlloc(GPTR, BufLen); + if (FixedInfo == NULL) { + return (ISC_R_NOMEMORY); + } + dwRetVal = GetNetworkParams(FixedInfo, &BufLen); + } + if (dwRetVal != ERROR_SUCCESS) { + GlobalFree(FixedInfo); + return (ISC_R_FAILURE); + } + + result = get_win32_searchlist(conf); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + if (ISC_LIST_EMPTY(conf->searchlist) && + strlen(FixedInfo->DomainName) > 0) + { + result = add_search(conf, FixedInfo->DomainName); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + + /* Get the list of nameservers */ + pIPAddr = &FixedInfo->DnsServerList; + while (pIPAddr) { + if (conf->numns >= RESCONFMAXNAMESERVERS) { + break; + } + + result = add_server(conf->mctx, pIPAddr->IpAddress.String, + &conf->nameservers); + if (result != ISC_R_SUCCESS) { + break; + } + conf->numns++; + pIPAddr = pIPAddr->Next; + } + +cleanup: + if (FixedInfo != NULL) { + GlobalFree(FixedInfo); + } + return (result); +} diff --git a/lib/irs/win32/version.c b/lib/irs/win32/version.c new file mode 100644 index 0000000..3c635ad --- /dev/null +++ b/lib/irs/win32/version.c @@ -0,0 +1,18 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <versions.h> + +#include <irs/version.h> + +LIBIRS_EXTERNAL_DATA const char irs_version[] = VERSION; |