summaryrefslogtreecommitdiffstats
path: root/lib/irs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/irs')
-rw-r--r--lib/irs/Kyuafile15
-rw-r--r--lib/irs/Makefile.in90
-rw-r--r--lib/irs/context.c334
-rw-r--r--lib/irs/dnsconf.c291
-rw-r--r--lib/irs/gai_strerror.c96
-rw-r--r--lib/irs/getaddrinfo.c1365
-rw-r--r--lib/irs/getnameinfo.c438
l---------lib/irs/include/.clang-format1
-rw-r--r--lib/irs/include/Makefile.in19
-rw-r--r--lib/irs/include/irs/Makefile.in46
-rw-r--r--lib/irs/include/irs/context.h155
-rw-r--r--lib/irs/include/irs/dnsconf.h92
-rw-r--r--lib/irs/include/irs/netdb.h.in193
-rw-r--r--lib/irs/include/irs/platform.h.in32
-rw-r--r--lib/irs/include/irs/resconf.h118
-rw-r--r--lib/irs/include/irs/types.h26
-rw-r--r--lib/irs/include/irs/version.h18
-rw-r--r--lib/irs/resconf.c689
-rw-r--r--lib/irs/tests/Kyuafile15
-rw-r--r--lib/irs/tests/Makefile.in51
-rw-r--r--lib/irs/tests/resconf_test.c142
-rw-r--r--lib/irs/tests/testdata/domain.conf12
-rw-r--r--lib/irs/tests/testdata/nameserver-v4.conf12
-rw-r--r--lib/irs/tests/testdata/nameserver-v6-scoped.conf12
-rw-r--r--lib/irs/tests/testdata/nameserver-v6.conf12
-rw-r--r--lib/irs/tests/testdata/options-bad-ndots.conf13
-rw-r--r--lib/irs/tests/testdata/options-debug.conf12
-rw-r--r--lib/irs/tests/testdata/options-empty.conf13
-rw-r--r--lib/irs/tests/testdata/options-ndots.conf12
-rw-r--r--lib/irs/tests/testdata/options-timeout.conf12
-rw-r--r--lib/irs/tests/testdata/options-unknown.conf12
-rw-r--r--lib/irs/tests/testdata/options.conf12
-rw-r--r--lib/irs/tests/testdata/port.conf12
-rw-r--r--lib/irs/tests/testdata/resolv.conf19
-rw-r--r--lib/irs/tests/testdata/search.conf12
-rw-r--r--lib/irs/tests/testdata/sortlist-v4.conf12
-rw-r--r--lib/irs/tests/testdata/timeout.conf12
-rw-r--r--lib/irs/tests/testdata/unknown.conf12
-rw-r--r--lib/irs/version.c18
-rw-r--r--lib/irs/win32/DLLMain.c49
-rw-r--r--lib/irs/win32/Makefile.in19
l---------lib/irs/win32/include/.clang-format1
-rw-r--r--lib/irs/win32/include/Makefile.in19
-rw-r--r--lib/irs/win32/include/irs/Makefile.in28
-rw-r--r--lib/irs/win32/include/irs/netdb.h207
-rw-r--r--lib/irs/win32/include/irs/platform.h34
-rw-r--r--lib/irs/win32/libirs.def27
-rw-r--r--lib/irs/win32/libirs.vcxproj.filters.in69
-rw-r--r--lib/irs/win32/libirs.vcxproj.in142
-rw-r--r--lib/irs/win32/libirs.vcxproj.user3
-rw-r--r--lib/irs/win32/resconf.c130
-rw-r--r--lib/irs/win32/version.c18
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;