summaryrefslogtreecommitdiffstats
path: root/bin/named
diff options
context:
space:
mode:
authorMartin Nagy <mnagy@redhat.com>2009-02-11 20:37:59 +0100
committerMartin Nagy <mnagy@redhat.com>2009-02-11 20:37:59 +0100
commitf50ae72ec3417cae55dd4e085991c01af9fdc5f1 (patch)
tree0e36c9a3320f6d068df93d3ff6d84b821d23db40 /bin/named
downloadbind_dynamic-f50ae72ec3417cae55dd4e085991c01af9fdc5f1.tar.gz
bind_dynamic-f50ae72ec3417cae55dd4e085991c01af9fdc5f1.tar.xz
bind_dynamic-f50ae72ec3417cae55dd4e085991c01af9fdc5f1.zip
Initial commitstart
Diffstat (limited to 'bin/named')
-rw-r--r--bin/named/Makefile.in154
-rw-r--r--bin/named/bind9.xsl481
-rw-r--r--bin/named/bind9.xsl.h486
-rw-r--r--bin/named/builtin.c307
-rw-r--r--bin/named/client.c2798
-rw-r--r--bin/named/config.c807
-rw-r--r--bin/named/control.c197
-rw-r--r--bin/named/controlconf.c1459
-rwxr-xr-xbin/named/convertxsl.pl57
-rw-r--r--bin/named/include/named/builtin.h31
-rw-r--r--bin/named/include/named/client.h375
-rw-r--r--bin/named/include/named/config.h79
-rw-r--r--bin/named/include/named/control.h95
-rw-r--r--bin/named/include/named/globals.h139
-rw-r--r--bin/named/include/named/interfacemgr.h176
-rw-r--r--bin/named/include/named/listenlist.h105
-rw-r--r--bin/named/include/named/log.h98
-rw-r--r--bin/named/include/named/logconf.h34
-rw-r--r--bin/named/include/named/lwaddr.h36
-rw-r--r--bin/named/include/named/lwdclient.h234
-rw-r--r--bin/named/include/named/lwresd.h121
-rw-r--r--bin/named/include/named/lwsearch.h112
-rw-r--r--bin/named/include/named/main.h34
-rw-r--r--bin/named/include/named/notify.h55
-rw-r--r--bin/named/include/named/ns_smf_globals.h44
-rw-r--r--bin/named/include/named/query.h87
-rw-r--r--bin/named/include/named/server.h298
-rw-r--r--bin/named/include/named/sortlist.h87
-rw-r--r--bin/named/include/named/statschannel.h61
-rw-r--r--bin/named/include/named/tkeyconf.h53
-rw-r--r--bin/named/include/named/tsigconf.h49
-rw-r--r--bin/named/include/named/types.h46
-rw-r--r--bin/named/include/named/update.h50
-rw-r--r--bin/named/include/named/xfrout.h39
-rw-r--r--bin/named/include/named/zoneconf.h63
-rw-r--r--bin/named/interfacemgr.c974
-rw-r--r--bin/named/listenlist.c138
-rw-r--r--bin/named/log.c235
-rw-r--r--bin/named/logconf.c299
-rw-r--r--bin/named/lwaddr.c94
-rw-r--r--bin/named/lwdclient.c468
-rw-r--r--bin/named/lwderror.c80
-rw-r--r--bin/named/lwdgabn.c657
-rw-r--r--bin/named/lwdgnba.c270
-rw-r--r--bin/named/lwdgrbn.c513
-rw-r--r--bin/named/lwdnoop.c87
-rw-r--r--bin/named/lwresd.8223
-rw-r--r--bin/named/lwresd.c870
-rw-r--r--bin/named/lwresd.docbook373
-rw-r--r--bin/named/lwresd.html225
-rw-r--r--bin/named/lwsearch.c206
-rw-r--r--bin/named/main.c991
-rw-r--r--bin/named/named.8256
-rw-r--r--bin/named/named.conf.5548
-rw-r--r--bin/named/named.conf.docbook629
-rw-r--r--bin/named/named.conf.html585
-rw-r--r--bin/named/named.docbook445
-rw-r--r--bin/named/named.html284
-rw-r--r--bin/named/notify.c174
-rw-r--r--bin/named/query.c5096
-rw-r--r--bin/named/server.c5462
-rw-r--r--bin/named/sortlist.c170
-rw-r--r--bin/named/statschannel.c1257
-rw-r--r--bin/named/tkeyconf.c128
-rw-r--r--bin/named/tsigconf.c181
-rw-r--r--bin/named/unix/Makefile.in36
-rw-r--r--bin/named/unix/include/named/os.h71
-rw-r--r--bin/named/unix/os.c816
-rw-r--r--bin/named/update.c4296
-rw-r--r--bin/named/win32/include/named/ntservice.h35
-rw-r--r--bin/named/win32/include/named/os.h70
-rw-r--r--bin/named/win32/named.dsp327
-rw-r--r--bin/named/win32/named.dsw29
-rw-r--r--bin/named/win32/named.mak1174
-rw-r--r--bin/named/win32/ntservice.c239
-rw-r--r--bin/named/win32/os.c304
-rw-r--r--bin/named/xfrout.c1842
-rw-r--r--bin/named/zoneconf.c1016
78 files changed, 41520 insertions, 0 deletions
diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in
new file mode 100644
index 0000000..4d800a6
--- /dev/null
+++ b/bin/named/Makefile.in
@@ -0,0 +1,154 @@
+# Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 1998-2002 Internet Software Consortium.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: Makefile.in,v 1.101 2008/09/23 17:25:47 jinmei Exp $
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+@BIND9_VERSION@
+
+@BIND9_CONFIGARGS@
+
+@BIND9_MAKE_INCLUDES@
+
+#
+# Add database drivers here.
+#
+DBDRIVER_OBJS =
+DBDRIVER_SRCS =
+DBDRIVER_INCLUDES =
+DBDRIVER_LIBS =
+
+DLZ_DRIVER_DIR = ${top_srcdir}/contrib/dlz/drivers
+
+DLZDRIVER_OBJS = @DLZ_DRIVER_OBJS@
+DLZDRIVER_SRCS = @DLZ_DRIVER_SRCS@
+DLZDRIVER_INCLUDES = @DLZ_DRIVER_INCLUDES@
+DLZDRIVER_LIBS = @DLZ_DRIVER_LIBS@
+
+CINCLUDES = -I${srcdir}/include -I${srcdir}/unix/include -I. \
+ ${LWRES_INCLUDES} ${DNS_INCLUDES} ${BIND9_INCLUDES} \
+ ${ISCCFG_INCLUDES} ${ISCCC_INCLUDES} ${ISC_INCLUDES} \
+ ${DLZDRIVER_INCLUDES} ${DBDRIVER_INCLUDES}
+
+CDEFINES = @USE_DLZ@
+
+CWARNINGS =
+
+DNSLIBS = ../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@
+ISCCFGLIBS = ../../lib/isccfg/libisccfg.@A@
+ISCCCLIBS = ../../lib/isccc/libisccc.@A@
+ISCLIBS = ../../lib/isc/libisc.@A@
+LWRESLIBS = ../../lib/lwres/liblwres.@A@
+BIND9LIBS = ../../lib/bind9/libbind9.@A@
+
+DNSDEPLIBS = ../../lib/dns/libdns.@A@
+ISCCFGDEPLIBS = ../../lib/isccfg/libisccfg.@A@
+ISCCCDEPLIBS = ../../lib/isccc/libisccc.@A@
+ISCDEPLIBS = ../../lib/isc/libisc.@A@
+LWRESDEPLIBS = ../../lib/lwres/liblwres.@A@
+BIND9DEPLIBS = ../../lib/bind9/libbind9.@A@
+
+DEPLIBS = ${LWRESDEPLIBS} ${DNSDEPLIBS} ${BIND9DEPLIBS} \
+ ${ISCCFGDEPLIBS} ${ISCCCDEPLIBS} ${ISCDEPLIBS}
+
+LIBS = ${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} \
+ ${ISCCFGLIBS} ${ISCCCLIBS} ${ISCLIBS} \
+ ${DLZDRIVER_LIBS} ${DBDRIVER_LIBS} @LIBS@
+
+SUBDIRS = unix
+
+TARGETS = named@EXEEXT@ lwresd@EXEEXT@
+
+OBJS = builtin.@O@ client.@O@ config.@O@ control.@O@ \
+ controlconf.@O@ interfacemgr.@O@ \
+ listenlist.@O@ log.@O@ logconf.@O@ main.@O@ notify.@O@ \
+ query.@O@ server.@O@ sortlist.@O@ statschannel.@O@ \
+ tkeyconf.@O@ tsigconf.@O@ update.@O@ xfrout.@O@ \
+ zoneconf.@O@ \
+ lwaddr.@O@ lwresd.@O@ lwdclient.@O@ lwderror.@O@ lwdgabn.@O@ \
+ lwdgnba.@O@ lwdgrbn.@O@ lwdnoop.@O@ lwsearch.@O@ \
+ ${DLZDRIVER_OBJS} ${DBDRIVER_OBJS}
+
+UOBJS = unix/os.@O@
+
+SRCS = builtin.c client.c config.c control.c \
+ controlconf.c interfacemgr.c \
+ listenlist.c log.c logconf.c main.c notify.c \
+ query.c server.c sortlist.c statschannel.c \
+ tkeyconf.c tsigconf.c update.c xfrout.c \
+ zoneconf.c \
+ lwaddr.c lwresd.c lwdclient.c lwderror.c lwdgabn.c \
+ lwdgnba.c lwdgrbn.c lwdnoop.c lwsearch.c \
+ ${DLZDRIVER_SRCS} ${DBDRIVER_SRCS}
+
+MANPAGES = named.8 lwresd.8 named.conf.5
+
+HTMLPAGES = named.html lwresd.html named.conf.html
+
+MANOBJS = ${MANPAGES} ${HTMLPAGES}
+
+@BIND9_MAKE_RULES@
+
+main.@O@: main.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \
+ -DVERSION=\"${VERSION}\" \
+ -DCONFIGARGS="\"${CONFIGARGS}\"" \
+ -DNS_LOCALSTATEDIR=\"${localstatedir}\" \
+ -DNS_SYSCONFDIR=\"${sysconfdir}\" -c ${srcdir}/main.c
+
+config.@O@: config.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \
+ -DVERSION=\"${VERSION}\" \
+ -DNS_LOCALSTATEDIR=\"${localstatedir}\" \
+ -c ${srcdir}/config.c
+
+named@EXEEXT@: ${OBJS} ${UOBJS} ${DEPLIBS}
+ ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ \
+ ${OBJS} ${UOBJS} ${LIBS}
+
+lwresd@EXEEXT@: named@EXEEXT@
+ rm -f lwresd@EXEEXT@
+ @LN@ named@EXEEXT@ lwresd@EXEEXT@
+
+doc man:: ${MANOBJS}
+
+docclean manclean maintainer-clean::
+ rm -f ${MANOBJS}
+
+clean distclean maintainer-clean::
+ rm -f ${TARGETS} ${OBJS}
+
+bind9.xsl.h: bind9.xsl convertxsl.pl
+ ${PERL} ${srcdir}/convertxsl.pl < ${srcdir}/bind9.xsl > bind9.xsl.h
+
+depend: bind9.xsl.h
+statschannel.@O@: bind9.xsl.h
+
+installdirs:
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${sbindir}
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man5
+ $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man8
+
+install:: named@EXEEXT@ lwresd@EXEEXT@ installdirs
+ ${LIBTOOL_MODE_INSTALL} ${INSTALL_PROGRAM} named@EXEEXT@ ${DESTDIR}${sbindir}
+ (cd ${DESTDIR}${sbindir}; rm -f lwresd@EXEEXT@; @LN@ named@EXEEXT@ lwresd@EXEEXT@)
+ ${INSTALL_DATA} ${srcdir}/named.8 ${DESTDIR}${mandir}/man8
+ ${INSTALL_DATA} ${srcdir}/lwresd.8 ${DESTDIR}${mandir}/man8
+ ${INSTALL_DATA} ${srcdir}/named.conf.5 ${DESTDIR}${mandir}/man5
+
+@DLZ_DRIVER_RULES@
diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl
new file mode 100644
index 0000000..b6562b8
--- /dev/null
+++ b/bin/named/bind9.xsl
@@ -0,0 +1,481 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright (C) 2006-2008 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id: bind9.xsl,v 1.19 2008/07/17 23:43:26 jinmei Exp $ -->
+
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns="http://www.w3.org/1999/xhtml">
+ <xsl:template match="isc/bind/statistics">
+ <html>
+ <head>
+ <style type="text/css">
+body {
+ font-family: sans-serif;
+ background-color: #ffffff;
+ color: #000000;
+}
+
+table {
+ border-collapse: collapse;
+}
+
+tr.rowh {
+ text-align: center;
+ border: 1px solid #000000;
+ background-color: #8080ff;
+ color: #ffffff;
+}
+
+tr.row {
+ text-align: right;
+ border: 1px solid #000000;
+ background-color: teal;
+ color: #ffffff;
+}
+
+tr.lrow {
+ text-align: left;
+ border: 1px solid #000000;
+ background-color: teal;
+ color: #ffffff;
+}
+
+td, th {
+ padding-right: 5px;
+ padding-left: 5px;
+}
+
+.header h1 {
+ background-color: teal;
+ color: #ffffff;
+ padding: 4px;
+}
+
+.content {
+ background-color: #ffffff;
+ color: #000000;
+ padding: 4px;
+}
+
+.item {
+ padding: 4px;
+ align: right;
+}
+
+.value {
+ padding: 4px;
+ font-weight: bold;
+}
+
+div.statcounter h2 {
+ text-align: center;
+ font-size: large;
+ border: 1px solid #000000;
+ background-color: #8080ff;
+ color: #ffffff;
+}
+
+div.statcounter dl {
+ float: left;
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-left: 0;
+ margin-right: 0;
+}
+
+div.statcounter dt {
+ width: 200px;
+ text-align: center;
+ font-weight: bold;
+ border: 0.5px solid #000000;
+ background-color: #8080ff;
+ color: #ffffff;
+}
+
+div.statcounter dd {
+ width: 200px;
+ text-align: right;
+ border: 0.5px solid #000000;
+ background-color: teal;
+ color: #ffffff;
+ margin-left: 0;
+ margin-right: 0;
+}
+
+div.statcounter br {
+ clear: left;
+}
+ </style>
+ <title>BIND 9 Statistics</title>
+ </head>
+ <body>
+ <div class="header">
+ <h1>Bind 9 Configuration and Statistics</h1>
+ </div>
+
+ <br/>
+
+ <table>
+ <tr class="rowh"><th colspan="2">Times</th></tr>
+ <tr class="lrow">
+ <td>boot-time</td>
+ <td><xsl:value-of select="server/boot-time"/></td>
+ </tr>
+ <tr class="lrow">
+ <td>current-time</td>
+ <td><xsl:value-of select="server/current-time"/></td>
+ </tr>
+ </table>
+
+ <br/>
+
+ <table>
+ <tr class="rowh"><th colspan="2">Incoming Requests</th></tr>
+ <xsl:for-each select="server/requests/opcode">
+ <tr class="lrow">
+ <td><xsl:value-of select="name"/></td>
+ <td><xsl:value-of select="counter"/></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+
+ <br/>
+
+ <table>
+ <tr class="rowh"><th colspan="2">Incoming Queries</th></tr>
+ <xsl:for-each select="server/queries-in/rdtype">
+ <tr class="lrow">
+ <td><xsl:value-of select="name"/></td>
+ <td><xsl:value-of select="counter"/></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+
+ <br/>
+
+ <xsl:for-each select="views/view">
+ <table>
+ <tr class="rowh">
+ <th colspan="2">Outgoing Queries from View <xsl:value-of select="name"/></th>
+ </tr>
+ <xsl:for-each select="rdtype">
+ <tr class="lrow">
+ <td><xsl:value-of select="name"/></td>
+ <td><xsl:value-of select="counter"/></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:for-each>
+
+ <br/>
+
+ <div class="statcounter">
+ <h2>Server Statistics</h2>
+ <xsl:for-each select="server/nsstat">
+ <dl>
+ <dt><xsl:value-of select="name"/></dt>
+ <dd><xsl:value-of select="counter"/></dd>
+ </dl>
+ </xsl:for-each>
+ <br/>
+ </div>
+
+ <div class="statcounter">
+ <h2>Zone Maintenance Statistics</h2>
+ <xsl:for-each select="server/zonestat">
+ <dl>
+ <dt><xsl:value-of select="name"/></dt>
+ <dd><xsl:value-of select="counter"/></dd>
+ </dl>
+ </xsl:for-each>
+ <br />
+ </div>
+
+ <div class="statcounter">
+ <h2>Resolver Statistics (Common)</h2>
+ <xsl:for-each select="server/resstat">
+ <dl>
+ <dt><xsl:value-of select="name"/></dt>
+ <dd><xsl:value-of select="counter"/></dd>
+ </dl>
+ </xsl:for-each>
+ <br />
+ </div>
+
+ <xsl:for-each select="views/view">
+ <div class="statcounter">
+ <h2>Resolver Statistics for View <xsl:value-of select="name"/></h2>
+ <xsl:for-each select="resstat">
+ <dl>
+ <dt><xsl:value-of select="name"/></dt>
+ <dd><xsl:value-of select="counter"/></dd>
+ </dl>
+ </xsl:for-each>
+ <br />
+ </div>
+ </xsl:for-each>
+
+ <br />
+
+ <xsl:for-each select="views/view">
+ <table>
+ <tr class="rowh">
+ <th colspan="2">Cache DB RRsets for View <xsl:value-of select="name"/></th>
+ </tr>
+ <xsl:for-each select="cache/rrset">
+ <tr class="lrow">
+ <td><xsl:value-of select="name"/></td>
+ <td><xsl:value-of select="counter"/></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:for-each>
+
+ <br/>
+
+ <xsl:for-each select="views/view">
+ <table>
+ <tr class="rowh">
+ <th colspan="10">Zones for View <xsl:value-of select="name"/></th>
+ </tr>
+ <tr class="rowh">
+ <th>Name</th>
+ <th>Class</th>
+ <th>Serial</th>
+ <th>Success</th>
+ <th>Referral</th>
+ <th>NXRRSET</th>
+ <th>NXDOMAIN</th>
+ <th>Failure</th>
+ <th>XfrReqDone</th>
+ <th>XfrRej</th>
+ </tr>
+ <xsl:for-each select="zones/zone">
+ <tr class="lrow">
+ <td>
+ <xsl:value-of select="name"/>
+ </td>
+ <td>
+ <xsl:value-of select="rdataclass"/>
+ </td>
+ <td>
+ <xsl:value-of select="serial"/>
+ </td>
+ <td>
+ <xsl:value-of select="counters/QrySuccess"/>
+ </td>
+ <td>
+ <xsl:value-of select="counters/QryReferral"/>
+ </td>
+ <td>
+ <xsl:value-of select="counters/QryNxrrset"/>
+ </td>
+ <td>
+ <xsl:value-of select="counters/QryNXDOMAIN"/>
+ </td>
+ <td>
+ <xsl:value-of select="counters/QryFailure"/>
+ </td>
+ <td>
+ <xsl:value-of select="counters/XfrReqDone"/>
+ </td>
+ <td>
+ <xsl:value-of select="counters/XfrRej"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:for-each>
+
+ <br/>
+
+ <table>
+ <tr class="rowh">
+ <th colspan="7">Network Status</th>
+ </tr>
+ <tr class="rowh">
+ <th>ID</th>
+ <th>Name</th>
+ <th>Type</th>
+ <th>References</th>
+ <th>LocalAddress</th>
+ <th>PeerAddress</th>
+ <th>State</th>
+ </tr>
+ <xsl:for-each select="socketmgr/sockets/socket">
+ <tr class="lrow">
+ <td>
+ <xsl:value-of select="id"/>
+ </td>
+ <td>
+ <xsl:value-of select="name"/>
+ </td>
+ <td>
+ <xsl:value-of select="type"/>
+ </td>
+ <td>
+ <xsl:value-of select="references"/>
+ </td>
+ <td>
+ <xsl:value-of select="local-address"/>
+ </td>
+ <td>
+ <xsl:value-of select="peer-address"/>
+ </td>
+ <td>
+ <xsl:for-each select="states">
+ <xsl:value-of select="."/>
+ </xsl:for-each>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ <table>
+ <tr class="rowh">
+ <th colspan="2">Task Manager Configuration</th>
+ </tr>
+ <tr class="lrow">
+ <td>Thread-Model</td>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/type"/>
+ </td>
+ </tr>
+ <tr class="lrow">
+ <td>Worker Threads</td>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/worker-threads"/>
+ </td>
+ </tr>
+ <tr class="lrow">
+ <td>Default Quantum</td>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/default-quantum"/>
+ </td>
+ </tr>
+ <tr class="lrow">
+ <td>Tasks Running</td>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/tasks-running"/>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ <table>
+ <tr class="rowh">
+ <th colspan="5">Tasks</th>
+ </tr>
+ <tr class="rowh">
+ <th>ID</th>
+ <th>Name</th>
+ <th>References</th>
+ <th>State</th>
+ <th>Quantum</th>
+ </tr>
+ <xsl:for-each select="taskmgr/tasks/task">
+ <tr class="lrow">
+ <td>
+ <xsl:value-of select="id"/>
+ </td>
+ <td>
+ <xsl:value-of select="name"/>
+ </td>
+ <td>
+ <xsl:value-of select="references"/>
+ </td>
+ <td>
+ <xsl:value-of select="state"/>
+ </td>
+ <td>
+ <xsl:value-of select="quantum"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br />
+ <table>
+ <tr class="rowh">
+ <th colspan="4">Memory Usage Summary</th>
+ </tr>
+ <xsl:for-each select="memory/summary/*">
+ <tr class="lrow">
+ <td><xsl:value-of select="name()"/></td>
+ <td><xsl:value-of select="."/></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br />
+ <table>
+ <tr class="rowh">
+ <th colspan="10">Memory Contexts</th>
+ </tr>
+ <tr class="rowh">
+ <th>ID</th>
+ <th>Name</th>
+ <th>References</th>
+ <th>TotalUse</th>
+ <th>InUse</th>
+ <th>MaxUse</th>
+ <th>BlockSize</th>
+ <th>Pools</th>
+ <th>HiWater</th>
+ <th>LoWater</th>
+ </tr>
+ <xsl:for-each select="memory/contexts/context">
+ <tr class="lrow">
+ <td>
+ <xsl:value-of select="id"/>
+ </td>
+ <td>
+ <xsl:value-of select="name"/>
+ </td>
+ <td>
+ <xsl:value-of select="references"/>
+ </td>
+ <td>
+ <xsl:value-of select="total"/>
+ </td>
+ <td>
+ <xsl:value-of select="inuse"/>
+ </td>
+ <td>
+ <xsl:value-of select="maxinuse"/>
+ </td>
+ <td>
+ <xsl:value-of select="blocksize"/>
+ </td>
+ <td>
+ <xsl:value-of select="pools"/>
+ </td>
+ <td>
+ <xsl:value-of select="hiwater"/>
+ </td>
+ <td>
+ <xsl:value-of select="lowater"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+
+ </body>
+ </html>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/bin/named/bind9.xsl.h b/bin/named/bind9.xsl.h
new file mode 100644
index 0000000..d9dc624
--- /dev/null
+++ b/bin/named/bind9.xsl.h
@@ -0,0 +1,486 @@
+/*
+ * Generated by convertxsl.pl 1.14 2008/07/17 23:43:26 jinmei Exp
+ * From bind9.xsl 1.19 2008/07/17 23:43:26 jinmei Exp
+ */
+static char xslmsg[] =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!--\n"
+ " - Copyright (C) 2006-2008 Internet Systems Consortium, Inc. (\"ISC\")\n"
+ " -\n"
+ " - Permission to use, copy, modify, and/or distribute this software for any\n"
+ " - purpose with or without fee is hereby granted, provided that the above\n"
+ " - copyright notice and this permission notice appear in all copies.\n"
+ " -\n"
+ " - THE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES WITH\n"
+ " - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n"
+ " - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,\n"
+ " - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n"
+ " - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE\n"
+ " - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n"
+ " - PERFORMANCE OF THIS SOFTWARE.\n"
+ "-->\n"
+ "\n"
+ "<!-- \045Id: bind9.xsl,v 1.19 2008/07/17 23:43:26 jinmei Exp \045 -->\n"
+ "\n"
+ "<xsl:stylesheet version=\"1.0\"\n"
+ " xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n"
+ " xmlns=\"http://www.w3.org/1999/xhtml\">\n"
+ " <xsl:template match=\"isc/bind/statistics\">\n"
+ " <html>\n"
+ " <head>\n"
+ " <style type=\"text/css\">\n"
+ "body {\n"
+ " font-family: sans-serif;\n"
+ " background-color: #ffffff;\n"
+ " color: #000000;\n"
+ "}\n"
+ "\n"
+ "table {\n"
+ " border-collapse: collapse;\n"
+ "}\n"
+ "\n"
+ "tr.rowh {\n"
+ " text-align: center;\n"
+ " border: 1px solid #000000;\n"
+ " background-color: #8080ff;\n"
+ " color: #ffffff;\n"
+ "}\n"
+ "\n"
+ "tr.row {\n"
+ " text-align: right;\n"
+ " border: 1px solid #000000;\n"
+ " background-color: teal;\n"
+ " color: #ffffff;\n"
+ "}\n"
+ "\n"
+ "tr.lrow {\n"
+ " text-align: left;\n"
+ " border: 1px solid #000000;\n"
+ " background-color: teal;\n"
+ " color: #ffffff;\n"
+ "}\n"
+ "\n"
+ "td, th {\n"
+ " padding-right: 5px;\n"
+ " padding-left: 5px;\n"
+ "}\n"
+ "\n"
+ ".header h1 {\n"
+ " background-color: teal;\n"
+ " color: #ffffff;\n"
+ " padding: 4px;\n"
+ "}\n"
+ "\n"
+ ".content {\n"
+ " background-color: #ffffff;\n"
+ " color: #000000;\n"
+ " padding: 4px;\n"
+ "}\n"
+ "\n"
+ ".item {\n"
+ " padding: 4px;\n"
+ " align: right;\n"
+ "}\n"
+ "\n"
+ ".value {\n"
+ " padding: 4px;\n"
+ " font-weight: bold;\n"
+ "}\n"
+ "\n"
+ "div.statcounter h2 {\n"
+ " text-align: center;\n"
+ " font-size: large;\n"
+ " border: 1px solid #000000;\n"
+ " background-color: #8080ff;\n"
+ " color: #ffffff;\n"
+ "}\n"
+ "\n"
+ "div.statcounter dl {\n"
+ " float: left;\n"
+ " margin-top: 0;\n"
+ " margin-bottom: 0;\n"
+ " margin-left: 0;\n"
+ " margin-right: 0;\n"
+ "}\n"
+ "\n"
+ "div.statcounter dt {\n"
+ " width: 200px;\n"
+ " text-align: center;\n"
+ " font-weight: bold;\n"
+ " border: 0.5px solid #000000;\n"
+ " background-color: #8080ff;\n"
+ " color: #ffffff;\n"
+ "}\n"
+ "\n"
+ "div.statcounter dd {\n"
+ " width: 200px;\n"
+ " text-align: right;\n"
+ " border: 0.5px solid #000000;\n"
+ " background-color: teal;\n"
+ " color: #ffffff;\n"
+ " margin-left: 0;\n"
+ " margin-right: 0;\n"
+ "}\n"
+ "\n"
+ "div.statcounter br {\n"
+ " clear: left;\n"
+ "}\n"
+ " </style>\n"
+ " <title>BIND 9 Statistics</title>\n"
+ " </head>\n"
+ " <body>\n"
+ " <div class=\"header\">\n"
+ " <h1>Bind 9 Configuration and Statistics</h1>\n"
+ " </div>\n"
+ "\n"
+ " <br/>\n"
+ "\n"
+ " <table>\n"
+ " <tr class=\"rowh\"><th colspan=\"2\">Times</th></tr>\n"
+ " <tr class=\"lrow\">\n"
+ " <td>boot-time</td>\n"
+ " <td><xsl:value-of select=\"server/boot-time\"/></td>\n"
+ " </tr>\n"
+ " <tr class=\"lrow\">\n"
+ " <td>current-time</td>\n"
+ " <td><xsl:value-of select=\"server/current-time\"/></td>\n"
+ " </tr>\n"
+ " </table>\n"
+ "\n"
+ " <br/>\n"
+ "\n"
+ " <table>\n"
+ " <tr class=\"rowh\"><th colspan=\"2\">Incoming Requests</th></tr>\n"
+ " <xsl:for-each select=\"server/requests/opcode\">\n"
+ " <tr class=\"lrow\">\n"
+ " <td><xsl:value-of select=\"name\"/></td>\n"
+ " <td><xsl:value-of select=\"counter\"/></td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ "\n"
+ " <br/>\n"
+ "\n"
+ " <table>\n"
+ " <tr class=\"rowh\"><th colspan=\"2\">Incoming Queries</th></tr>\n"
+ " <xsl:for-each select=\"server/queries-in/rdtype\">\n"
+ " <tr class=\"lrow\">\n"
+ " <td><xsl:value-of select=\"name\"/></td>\n"
+ " <td><xsl:value-of select=\"counter\"/></td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ "\n"
+ " <br/>\n"
+ "\n"
+ " <xsl:for-each select=\"views/view\">\n"
+ " <table>\n"
+ " <tr class=\"rowh\">\n"
+ " <th colspan=\"2\">Outgoing Queries from View <xsl:value-of select=\"name\"/></th>\n"
+ " </tr>\n"
+ " <xsl:for-each select=\"rdtype\">\n"
+ " <tr class=\"lrow\">\n"
+ " <td><xsl:value-of select=\"name\"/></td>\n"
+ " <td><xsl:value-of select=\"counter\"/></td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:for-each>\n"
+ "\n"
+ " <br/>\n"
+ "\n"
+ " <div class=\"statcounter\">\n"
+ " <h2>Server Statistics</h2>\n"
+ " <xsl:for-each select=\"server/nsstat\">\n"
+ " <dl>\n"
+ " <dt><xsl:value-of select=\"name\"/></dt>\n"
+ " <dd><xsl:value-of select=\"counter\"/></dd>\n"
+ " </dl>\n"
+ " </xsl:for-each>\n"
+ " <br/>\n"
+ " </div>\n"
+ "\n"
+ " <div class=\"statcounter\">\n"
+ " <h2>Zone Maintenance Statistics</h2>\n"
+ " <xsl:for-each select=\"server/zonestat\">\n"
+ " <dl>\n"
+ " <dt><xsl:value-of select=\"name\"/></dt>\n"
+ " <dd><xsl:value-of select=\"counter\"/></dd>\n"
+ " </dl>\n"
+ " </xsl:for-each>\n"
+ " <br />\n"
+ " </div>\n"
+ "\n"
+ " <div class=\"statcounter\">\n"
+ " <h2>Resolver Statistics (Common)</h2>\n"
+ " <xsl:for-each select=\"server/resstat\">\n"
+ " <dl>\n"
+ " <dt><xsl:value-of select=\"name\"/></dt>\n"
+ " <dd><xsl:value-of select=\"counter\"/></dd>\n"
+ " </dl>\n"
+ " </xsl:for-each>\n"
+ " <br />\n"
+ " </div>\n"
+ "\n"
+ " <xsl:for-each select=\"views/view\">\n"
+ " <div class=\"statcounter\">\n"
+ " <h2>Resolver Statistics for View <xsl:value-of select=\"name\"/></h2>\n"
+ " <xsl:for-each select=\"resstat\">\n"
+ " <dl>\n"
+ " <dt><xsl:value-of select=\"name\"/></dt>\n"
+ " <dd><xsl:value-of select=\"counter\"/></dd>\n"
+ " </dl>\n"
+ " </xsl:for-each>\n"
+ " <br />\n"
+ " </div>\n"
+ " </xsl:for-each>\n"
+ "\n"
+ " <br />\n"
+ "\n"
+ " <xsl:for-each select=\"views/view\">\n"
+ " <table>\n"
+ " <tr class=\"rowh\">\n"
+ " <th colspan=\"2\">Cache DB RRsets for View <xsl:value-of select=\"name\"/></th>\n"
+ " </tr>\n"
+ " <xsl:for-each select=\"cache/rrset\">\n"
+ " <tr class=\"lrow\">\n"
+ " <td><xsl:value-of select=\"name\"/></td>\n"
+ " <td><xsl:value-of select=\"counter\"/></td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:for-each>\n"
+ "\n"
+ " <br/>\n"
+ "\n"
+ " <xsl:for-each select=\"views/view\">\n"
+ " <table>\n"
+ " <tr class=\"rowh\">\n"
+ " <th colspan=\"10\">Zones for View <xsl:value-of select=\"name\"/></th>\n"
+ " </tr>\n"
+ " <tr class=\"rowh\">\n"
+ " <th>Name</th>\n"
+ " <th>Class</th>\n"
+ " <th>Serial</th>\n"
+ " <th>Success</th>\n"
+ " <th>Referral</th>\n"
+ " <th>NXRRSET</th>\n"
+ " <th>NXDOMAIN</th>\n"
+ " <th>Failure</th>\n"
+ " <th>XfrReqDone</th>\n"
+ " <th>XfrRej</th>\n"
+ " </tr>\n"
+ " <xsl:for-each select=\"zones/zone\">\n"
+ " <tr class=\"lrow\">\n"
+ " <td>\n"
+ " <xsl:value-of select=\"name\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"rdataclass\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"serial\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"counters/QrySuccess\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"counters/QryReferral\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"counters/QryNxrrset\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"counters/QryNXDOMAIN\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"counters/QryFailure\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"counters/XfrReqDone\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"counters/XfrRej\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:for-each>\n"
+ "\n"
+ " <br/>\n"
+ "\n"
+ " <table>\n"
+ " <tr class=\"rowh\">\n"
+ " <th colspan=\"7\">Network Status</th>\n"
+ " </tr>\n"
+ " <tr class=\"rowh\">\n"
+ " <th>ID</th>\n"
+ " <th>Name</th>\n"
+ " <th>Type</th>\n"
+ " <th>References</th>\n"
+ " <th>LocalAddress</th>\n"
+ " <th>PeerAddress</th>\n"
+ " <th>State</th>\n"
+ " </tr>\n"
+ " <xsl:for-each select=\"socketmgr/sockets/socket\">\n"
+ " <tr class=\"lrow\">\n"
+ " <td>\n"
+ " <xsl:value-of select=\"id\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"name\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"type\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"references\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"local-address\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"peer-address\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:for-each select=\"states\">\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </xsl:for-each>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " <table>\n"
+ " <tr class=\"rowh\">\n"
+ " <th colspan=\"2\">Task Manager Configuration</th>\n"
+ " </tr>\n"
+ " <tr class=\"lrow\">\n"
+ " <td>Thread-Model</td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"taskmgr/thread-model/type\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr class=\"lrow\">\n"
+ " <td>Worker Threads</td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"taskmgr/thread-model/worker-threads\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr class=\"lrow\">\n"
+ " <td>Default Quantum</td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"taskmgr/thread-model/default-quantum\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr class=\"lrow\">\n"
+ " <td>Tasks Running</td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"taskmgr/thread-model/tasks-running\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </table>\n"
+ " <br/>\n"
+ " <table>\n"
+ " <tr class=\"rowh\">\n"
+ " <th colspan=\"5\">Tasks</th>\n"
+ " </tr>\n"
+ " <tr class=\"rowh\">\n"
+ " <th>ID</th>\n"
+ " <th>Name</th>\n"
+ " <th>References</th>\n"
+ " <th>State</th>\n"
+ " <th>Quantum</th>\n"
+ " </tr>\n"
+ " <xsl:for-each select=\"taskmgr/tasks/task\">\n"
+ " <tr class=\"lrow\">\n"
+ " <td>\n"
+ " <xsl:value-of select=\"id\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"name\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"references\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"state\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"quantum\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br />\n"
+ " <table>\n"
+ " <tr class=\"rowh\">\n"
+ " <th colspan=\"4\">Memory Usage Summary</th>\n"
+ " </tr>\n"
+ " <xsl:for-each select=\"memory/summary/*\">\n"
+ " <tr class=\"lrow\">\n"
+ " <td><xsl:value-of select=\"name()\"/></td>\n"
+ " <td><xsl:value-of select=\".\"/></td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br />\n"
+ " <table>\n"
+ " <tr class=\"rowh\">\n"
+ " <th colspan=\"10\">Memory Contexts</th>\n"
+ " </tr>\n"
+ " <tr class=\"rowh\">\n"
+ " <th>ID</th>\n"
+ " <th>Name</th>\n"
+ " <th>References</th>\n"
+ " <th>TotalUse</th>\n"
+ " <th>InUse</th>\n"
+ " <th>MaxUse</th>\n"
+ " <th>BlockSize</th>\n"
+ " <th>Pools</th>\n"
+ " <th>HiWater</th>\n"
+ " <th>LoWater</th>\n"
+ " </tr>\n"
+ " <xsl:for-each select=\"memory/contexts/context\">\n"
+ " <tr class=\"lrow\">\n"
+ " <td>\n"
+ " <xsl:value-of select=\"id\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"name\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"references\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"total\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"inuse\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"maxinuse\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"blocksize\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"pools\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"hiwater\"/>\n"
+ " </td>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"lowater\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ "\n"
+ " </body>\n"
+ " </html>\n"
+ " </xsl:template>\n"
+ "</xsl:stylesheet>\n";
diff --git a/bin/named/builtin.c b/bin/named/builtin.c
new file mode 100644
index 0000000..7927737
--- /dev/null
+++ b/bin/named/builtin.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: builtin.c,v 1.12 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file
+ * \brief
+ * The built-in "version", "hostname", "id", "authors" and "empty" databases.
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <stdio.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+#include <dns/sdb.h>
+
+#include <named/builtin.h>
+#include <named/globals.h>
+#include <named/server.h>
+#include <named/os.h>
+
+typedef struct builtin builtin_t;
+
+static isc_result_t do_version_lookup(dns_sdblookup_t *lookup);
+static isc_result_t do_hostname_lookup(dns_sdblookup_t *lookup);
+static isc_result_t do_authors_lookup(dns_sdblookup_t *lookup);
+static isc_result_t do_id_lookup(dns_sdblookup_t *lookup);
+static isc_result_t do_empty_lookup(dns_sdblookup_t *lookup);
+
+/*
+ * We can't use function pointers as the db_data directly
+ * because ANSI C does not guarantee that function pointers
+ * can safely be cast to void pointers and back.
+ */
+
+struct builtin {
+ isc_result_t (*do_lookup)(dns_sdblookup_t *lookup);
+ char *server;
+ char *contact;
+};
+
+static builtin_t version_builtin = { do_version_lookup, NULL, NULL };
+static builtin_t hostname_builtin = { do_hostname_lookup, NULL, NULL };
+static builtin_t authors_builtin = { do_authors_lookup, NULL, NULL };
+static builtin_t id_builtin = { do_id_lookup, NULL, NULL };
+static builtin_t empty_builtin = { do_empty_lookup, NULL, NULL };
+
+static dns_sdbimplementation_t *builtin_impl;
+
+static isc_result_t
+builtin_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup)
+{
+ builtin_t *b = (builtin_t *) dbdata;
+
+ UNUSED(zone);
+
+ if (strcmp(name, "@") == 0)
+ return (b->do_lookup(lookup));
+ else
+ return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+put_txt(dns_sdblookup_t *lookup, const char *text) {
+ unsigned char buf[256];
+ unsigned int len = strlen(text);
+ if (len > 255)
+ len = 255; /* Silently truncate */
+ buf[0] = len;
+ memcpy(&buf[1], text, len);
+ return (dns_sdb_putrdata(lookup, dns_rdatatype_txt, 0, buf, len + 1));
+}
+
+static isc_result_t
+do_version_lookup(dns_sdblookup_t *lookup) {
+ if (ns_g_server->version_set) {
+ if (ns_g_server->version == NULL)
+ return (ISC_R_SUCCESS);
+ else
+ return (put_txt(lookup, ns_g_server->version));
+ } else {
+ return (put_txt(lookup, ns_g_version));
+ }
+}
+
+static isc_result_t
+do_hostname_lookup(dns_sdblookup_t *lookup) {
+ if (ns_g_server->hostname_set) {
+ if (ns_g_server->hostname == NULL)
+ return (ISC_R_SUCCESS);
+ else
+ return (put_txt(lookup, ns_g_server->hostname));
+ } else {
+ char buf[256];
+ isc_result_t result = ns_os_gethostname(buf, sizeof(buf));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (put_txt(lookup, buf));
+ }
+}
+
+static isc_result_t
+do_authors_lookup(dns_sdblookup_t *lookup) {
+ isc_result_t result;
+ const char **p;
+ static const char *authors[] = {
+ "Mark Andrews",
+ "James Brister",
+ "Ben Cottrell",
+ "Michael Graff",
+ "Andreas Gustafsson",
+ "Bob Halley",
+ "David Lawrence",
+ "Danny Mayer",
+ "Damien Neil",
+ "Matt Nelson",
+ "Michael Sawyer",
+ "Brian Wellington",
+ NULL
+ };
+
+ /*
+ * If a version string is specified, disable the authors.bind zone.
+ */
+ if (ns_g_server->version_set)
+ return (ISC_R_SUCCESS);
+
+ for (p = authors; *p != NULL; p++) {
+ result = put_txt(lookup, *p);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+do_id_lookup(dns_sdblookup_t *lookup) {
+
+ if (ns_g_server->server_usehostname) {
+ char buf[256];
+ isc_result_t result = ns_os_gethostname(buf, sizeof(buf));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (put_txt(lookup, buf));
+ }
+
+ if (ns_g_server->server_id == NULL)
+ return (ISC_R_SUCCESS);
+ else
+ return (put_txt(lookup, ns_g_server->server_id));
+}
+
+static isc_result_t
+do_empty_lookup(dns_sdblookup_t *lookup) {
+
+ UNUSED(lookup);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+builtin_authority(const char *zone, void *dbdata, dns_sdblookup_t *lookup) {
+ isc_result_t result;
+ const char *contact = "hostmaster";
+ const char *server = "@";
+ builtin_t *b = (builtin_t *) dbdata;
+
+ UNUSED(zone);
+ UNUSED(dbdata);
+
+ if (b == &empty_builtin) {
+ server = ".";
+ contact = ".";
+ } else {
+ if (b->server != NULL)
+ server = b->server;
+ if (b->contact != NULL)
+ contact = b->contact;
+ }
+
+ result = dns_sdb_putsoa(lookup, server, contact, 0);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+
+ result = dns_sdb_putrr(lookup, "ns", 0, server);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_FAILURE);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+builtin_create(const char *zone, int argc, char **argv,
+ void *driverdata, void **dbdata)
+{
+ REQUIRE(argc >= 1);
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ if (strcmp(argv[0], "empty") == 0) {
+ if (argc != 3)
+ return (DNS_R_SYNTAX);
+ } else if (argc != 1)
+ return (DNS_R_SYNTAX);
+
+ if (strcmp(argv[0], "version") == 0)
+ *dbdata = &version_builtin;
+ else if (strcmp(argv[0], "hostname") == 0)
+ *dbdata = &hostname_builtin;
+ else if (strcmp(argv[0], "authors") == 0)
+ *dbdata = &authors_builtin;
+ else if (strcmp(argv[0], "id") == 0)
+ *dbdata = &id_builtin;
+ else if (strcmp(argv[0], "empty") == 0) {
+ builtin_t *empty;
+ char *server;
+ char *contact;
+ /*
+ * We don't want built-in zones to fail. Fallback to
+ * the static configuration if memory allocation fails.
+ */
+ empty = isc_mem_get(ns_g_mctx, sizeof(*empty));
+ server = isc_mem_strdup(ns_g_mctx, argv[1]);
+ contact = isc_mem_strdup(ns_g_mctx, argv[2]);
+ if (empty == NULL || server == NULL || contact == NULL) {
+ *dbdata = &empty_builtin;
+ if (server != NULL)
+ isc_mem_free(ns_g_mctx, server);
+ if (contact != NULL)
+ isc_mem_free(ns_g_mctx, contact);
+ if (empty != NULL)
+ isc_mem_put(ns_g_mctx, empty, sizeof (*empty));
+ } else {
+ memcpy(empty, &empty_builtin, sizeof (empty_builtin));
+ empty->server = server;
+ empty->contact = contact;
+ *dbdata = empty;
+ }
+ } else
+ return (ISC_R_NOTIMPLEMENTED);
+ return (ISC_R_SUCCESS);
+}
+
+static void
+builtin_destroy(const char *zone, void *driverdata, void **dbdata) {
+ builtin_t *b = (builtin_t *) *dbdata;
+
+ UNUSED(zone);
+ UNUSED(driverdata);
+
+ /*
+ * Don't free the static versions.
+ */
+ if (*dbdata == &version_builtin || *dbdata == &hostname_builtin ||
+ *dbdata == &authors_builtin || *dbdata == &id_builtin ||
+ *dbdata == &empty_builtin)
+ return;
+
+ isc_mem_free(ns_g_mctx, b->server);
+ isc_mem_free(ns_g_mctx, b->contact);
+ isc_mem_put(ns_g_mctx, b, sizeof (*b));
+}
+
+static dns_sdbmethods_t builtin_methods = {
+ builtin_lookup,
+ builtin_authority,
+ NULL, /* allnodes */
+ builtin_create,
+ builtin_destroy
+};
+
+isc_result_t
+ns_builtin_init(void) {
+ RUNTIME_CHECK(dns_sdb_register("_builtin", &builtin_methods, NULL,
+ DNS_SDBFLAG_RELATIVEOWNER |
+ DNS_SDBFLAG_RELATIVERDATA,
+ ns_g_mctx, &builtin_impl)
+ == ISC_R_SUCCESS);
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_builtin_deinit(void) {
+ dns_sdb_unregister(&builtin_impl);
+}
diff --git a/bin/named/client.c b/bin/named/client.c
new file mode 100644
index 0000000..164af7c
--- /dev/null
+++ b/bin/named/client.c
@@ -0,0 +1,2798 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: client.c,v 1.259 2008/11/16 20:57:54 marka Exp $ */
+
+#include <config.h>
+
+#include <isc/formatcheck.h>
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#include <dns/events.h>
+#include <dns/message.h>
+#include <dns/peer.h>
+#include <dns/rcode.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/resolver.h>
+#include <dns/stats.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include <named/interfacemgr.h>
+#include <named/log.h>
+#include <named/notify.h>
+#include <named/os.h>
+#include <named/server.h>
+#include <named/update.h>
+
+/***
+ *** Client
+ ***/
+
+/*! \file
+ * Client Routines
+ *
+ * Important note!
+ *
+ * All client state changes, other than that from idle to listening, occur
+ * as a result of events. This guarantees serialization and avoids the
+ * need for locking.
+ *
+ * If a routine is ever created that allows someone other than the client's
+ * task to change the client, then the client will have to be locked.
+ */
+
+#define NS_CLIENT_TRACE
+#ifdef NS_CLIENT_TRACE
+#define CTRACE(m) ns_client_log(client, \
+ NS_LOGCATEGORY_CLIENT, \
+ NS_LOGMODULE_CLIENT, \
+ ISC_LOG_DEBUG(3), \
+ "%s", (m))
+#define MTRACE(m) isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_GENERAL, \
+ NS_LOGMODULE_CLIENT, \
+ ISC_LOG_DEBUG(3), \
+ "clientmgr @%p: %s", manager, (m))
+#else
+#define CTRACE(m) ((void)(m))
+#define MTRACE(m) ((void)(m))
+#endif
+
+#define TCP_CLIENT(c) (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
+
+#define TCP_BUFFER_SIZE (65535 + 2)
+#define SEND_BUFFER_SIZE 4096
+#define RECV_BUFFER_SIZE 4096
+
+#ifdef ISC_PLATFORM_USETHREADS
+#define NMCTXS 100
+/*%<
+ * Number of 'mctx pools' for clients. (Should this be configurable?)
+ * When enabling threads, we use a pool of memory contexts shared by
+ * client objects, since concurrent access to a shared context would cause
+ * heavy contentions. The above constant is expected to be enough for
+ * completely avoiding contentions among threads for an authoritative-only
+ * server.
+ */
+#else
+#define NMCTXS 0
+/*%<
+ * If named with built without thread, simply share manager's context. Using
+ * a separate context in this case would simply waste memory.
+ */
+#endif
+
+/*% nameserver client manager structure */
+struct ns_clientmgr {
+ /* Unlocked. */
+ unsigned int magic;
+ isc_mem_t * mctx;
+ isc_taskmgr_t * taskmgr;
+ isc_timermgr_t * timermgr;
+ isc_mutex_t lock;
+ /* Locked by lock. */
+ isc_boolean_t exiting;
+ client_list_t active; /*%< Active clients */
+ client_list_t recursing; /*%< Recursing clients */
+ client_list_t inactive; /*%< To be recycled */
+#if NMCTXS > 0
+ /*%< mctx pool for clients. */
+ unsigned int nextmctx;
+ isc_mem_t * mctxpool[NMCTXS];
+#endif
+};
+
+#define MANAGER_MAGIC ISC_MAGIC('N', 'S', 'C', 'm')
+#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, MANAGER_MAGIC)
+
+/*!
+ * Client object states. Ordering is significant: higher-numbered
+ * states are generally "more active", meaning that the client can
+ * have more dynamically allocated data, outstanding events, etc.
+ * In the list below, any such properties listed for state N
+ * also apply to any state > N.
+ *
+ * To force the client into a less active state, set client->newstate
+ * to that state and call exit_check(). This will cause any
+ * activities defined for higher-numbered states to be aborted.
+ */
+
+#define NS_CLIENTSTATE_FREED 0
+/*%<
+ * The client object no longer exists.
+ */
+
+#define NS_CLIENTSTATE_INACTIVE 1
+/*%<
+ * The client object exists and has a task and timer.
+ * Its "query" struct and sendbuf are initialized.
+ * It is on the client manager's list of inactive clients.
+ * It has a message and OPT, both in the reset state.
+ */
+
+#define NS_CLIENTSTATE_READY 2
+/*%<
+ * The client object is either a TCP or a UDP one, and
+ * it is associated with a network interface. It is on the
+ * client manager's list of active clients.
+ *
+ * If it is a TCP client object, it has a TCP listener socket
+ * and an outstanding TCP listen request.
+ *
+ * If it is a UDP client object, it has a UDP listener socket
+ * and an outstanding UDP receive request.
+ */
+
+#define NS_CLIENTSTATE_READING 3
+/*%<
+ * The client object is a TCP client object that has received
+ * a connection. It has a tcpsocket, tcpmsg, TCP quota, and an
+ * outstanding TCP read request. This state is not used for
+ * UDP client objects.
+ */
+
+#define NS_CLIENTSTATE_WORKING 4
+/*%<
+ * The client object has received a request and is working
+ * on it. It has a view, and it may have any of a non-reset OPT,
+ * recursion quota, and an outstanding write request.
+ */
+
+#define NS_CLIENTSTATE_MAX 9
+/*%<
+ * Sentinel value used to indicate "no state". When client->newstate
+ * has this value, we are not attempting to exit the current state.
+ * Must be greater than any valid state.
+ */
+
+/*
+ * Enable ns_client_dropport() by default.
+ */
+#ifndef NS_CLIENT_DROPPORT
+#define NS_CLIENT_DROPPORT 1
+#endif
+
+unsigned int ns_client_requests;
+
+static void client_read(ns_client_t *client);
+static void client_accept(ns_client_t *client);
+static void client_udprecv(ns_client_t *client);
+static void clientmgr_destroy(ns_clientmgr_t *manager);
+static isc_boolean_t exit_check(ns_client_t *client);
+static void ns_client_endrequest(ns_client_t *client);
+static void ns_client_checkactive(ns_client_t *client);
+static void client_start(isc_task_t *task, isc_event_t *event);
+static void client_request(isc_task_t *task, isc_event_t *event);
+static void ns_client_dumpmessage(ns_client_t *client, const char *reason);
+
+void
+ns_client_recursing(ns_client_t *client) {
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ LOCK(&client->manager->lock);
+ ISC_LIST_UNLINK(*client->list, client, link);
+ ISC_LIST_APPEND(client->manager->recursing, client, link);
+ client->list = &client->manager->recursing;
+ UNLOCK(&client->manager->lock);
+}
+
+void
+ns_client_killoldestquery(ns_client_t *client) {
+ ns_client_t *oldest;
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ LOCK(&client->manager->lock);
+ oldest = ISC_LIST_HEAD(client->manager->recursing);
+ if (oldest != NULL) {
+ ns_query_cancel(oldest);
+ ISC_LIST_UNLINK(*oldest->list, oldest, link);
+ ISC_LIST_APPEND(client->manager->active, oldest, link);
+ oldest->list = &client->manager->active;
+ }
+ UNLOCK(&client->manager->lock);
+}
+
+void
+ns_client_settimeout(ns_client_t *client, unsigned int seconds) {
+ isc_result_t result;
+ isc_interval_t interval;
+
+ isc_interval_set(&interval, seconds, 0);
+ result = isc_timer_reset(client->timer, isc_timertype_once, NULL,
+ &interval, ISC_FALSE);
+ client->timerset = ISC_TRUE;
+ if (result != ISC_R_SUCCESS) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
+ "setting timeout: %s",
+ isc_result_totext(result));
+ /* Continue anyway. */
+ }
+}
+
+/*%
+ * Check for a deactivation or shutdown request and take appropriate
+ * action. Returns ISC_TRUE if either is in progress; in this case
+ * the caller must no longer use the client object as it may have been
+ * freed.
+ */
+static isc_boolean_t
+exit_check(ns_client_t *client) {
+ ns_clientmgr_t *locked_manager = NULL;
+ ns_clientmgr_t *destroy_manager = NULL;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ if (client->state <= client->newstate)
+ return (ISC_FALSE); /* Business as usual. */
+
+ INSIST(client->newstate < NS_CLIENTSTATE_WORKING);
+
+ /*
+ * We need to detach from the view early when shutting down
+ * the server to break the following vicious circle:
+ *
+ * - The resolver will not shut down until the view refcount is zero
+ * - The view refcount does not go to zero until all clients detach
+ * - The client does not detach from the view until references is zero
+ * - references does not go to zero until the resolver has shut down
+ *
+ * Keep the view attached until any outstanding updates complete.
+ */
+ if (client->nupdates == 0 &&
+ client->newstate == NS_CLIENTSTATE_FREED && client->view != NULL)
+ dns_view_detach(&client->view);
+
+ if (client->state == NS_CLIENTSTATE_WORKING) {
+ INSIST(client->newstate <= NS_CLIENTSTATE_READING);
+ /*
+ * Let the update processing complete.
+ */
+ if (client->nupdates > 0)
+ return (ISC_TRUE);
+ /*
+ * We are trying to abort request processing.
+ */
+ if (client->nsends > 0) {
+ isc_socket_t *socket;
+ if (TCP_CLIENT(client))
+ socket = client->tcpsocket;
+ else
+ socket = client->udpsocket;
+ isc_socket_cancel(socket, client->task,
+ ISC_SOCKCANCEL_SEND);
+ }
+
+ if (! (client->nsends == 0 && client->nrecvs == 0 &&
+ client->references == 0))
+ {
+ /*
+ * Still waiting for I/O cancel completion.
+ * or lingering references.
+ */
+ return (ISC_TRUE);
+ }
+ /*
+ * I/O cancel is complete. Burn down all state
+ * related to the current request. Ensure that
+ * the client is on the active list and not the
+ * recursing list.
+ */
+ LOCK(&client->manager->lock);
+ if (client->list == &client->manager->recursing) {
+ ISC_LIST_UNLINK(*client->list, client, link);
+ ISC_LIST_APPEND(client->manager->active, client, link);
+ client->list = &client->manager->active;
+ }
+ UNLOCK(&client->manager->lock);
+ ns_client_endrequest(client);
+
+ client->state = NS_CLIENTSTATE_READING;
+ INSIST(client->recursionquota == NULL);
+ if (NS_CLIENTSTATE_READING == client->newstate) {
+ client_read(client);
+ client->newstate = NS_CLIENTSTATE_MAX;
+ return (ISC_TRUE); /* We're done. */
+ }
+ }
+
+ if (client->state == NS_CLIENTSTATE_READING) {
+ /*
+ * We are trying to abort the current TCP connection,
+ * if any.
+ */
+ INSIST(client->recursionquota == NULL);
+ INSIST(client->newstate <= NS_CLIENTSTATE_READY);
+ if (client->nreads > 0)
+ dns_tcpmsg_cancelread(&client->tcpmsg);
+ if (! client->nreads == 0) {
+ /* Still waiting for read cancel completion. */
+ return (ISC_TRUE);
+ }
+
+ if (client->tcpmsg_valid) {
+ dns_tcpmsg_invalidate(&client->tcpmsg);
+ client->tcpmsg_valid = ISC_FALSE;
+ }
+ if (client->tcpsocket != NULL) {
+ CTRACE("closetcp");
+ isc_socket_detach(&client->tcpsocket);
+ }
+
+ if (client->tcpquota != NULL)
+ isc_quota_detach(&client->tcpquota);
+
+ if (client->timerset) {
+ (void)isc_timer_reset(client->timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE);
+ client->timerset = ISC_FALSE;
+ }
+
+ client->peeraddr_valid = ISC_FALSE;
+
+ client->state = NS_CLIENTSTATE_READY;
+ INSIST(client->recursionquota == NULL);
+
+ /*
+ * Now the client is ready to accept a new TCP connection
+ * or UDP request, but we may have enough clients doing
+ * that already. Check whether this client needs to remain
+ * active and force it to go inactive if not.
+ */
+ ns_client_checkactive(client);
+
+ if (NS_CLIENTSTATE_READY == client->newstate) {
+ if (TCP_CLIENT(client)) {
+ client_accept(client);
+ } else
+ client_udprecv(client);
+ client->newstate = NS_CLIENTSTATE_MAX;
+ return (ISC_TRUE);
+ }
+ }
+
+ if (client->state == NS_CLIENTSTATE_READY) {
+ INSIST(client->newstate <= NS_CLIENTSTATE_INACTIVE);
+ /*
+ * We are trying to enter the inactive state.
+ */
+ if (client->naccepts > 0)
+ isc_socket_cancel(client->tcplistener, client->task,
+ ISC_SOCKCANCEL_ACCEPT);
+
+ if (! (client->naccepts == 0)) {
+ /* Still waiting for accept cancel completion. */
+ return (ISC_TRUE);
+ }
+ /* Accept cancel is complete. */
+
+ if (client->nrecvs > 0)
+ isc_socket_cancel(client->udpsocket, client->task,
+ ISC_SOCKCANCEL_RECV);
+ if (! (client->nrecvs == 0)) {
+ /* Still waiting for recv cancel completion. */
+ return (ISC_TRUE);
+ }
+ /* Recv cancel is complete. */
+
+ if (client->nctls > 0) {
+ /* Still waiting for control event to be delivered */
+ return (ISC_TRUE);
+ }
+
+ /* Deactivate the client. */
+ if (client->interface)
+ ns_interface_detach(&client->interface);
+
+ INSIST(client->naccepts == 0);
+ INSIST(client->recursionquota == NULL);
+ if (client->tcplistener != NULL)
+ isc_socket_detach(&client->tcplistener);
+
+ if (client->udpsocket != NULL)
+ isc_socket_detach(&client->udpsocket);
+
+ if (client->dispatch != NULL)
+ dns_dispatch_detach(&client->dispatch);
+
+ client->attributes = 0;
+ client->mortal = ISC_FALSE;
+
+ LOCK(&client->manager->lock);
+ /*
+ * Put the client on the inactive list. If we are aiming for
+ * the "freed" state, it will be removed from the inactive
+ * list shortly, and we need to keep the manager locked until
+ * that has been done, lest the manager decide to reactivate
+ * the dying client inbetween.
+ */
+ locked_manager = client->manager;
+ ISC_LIST_UNLINK(*client->list, client, link);
+ ISC_LIST_APPEND(client->manager->inactive, client, link);
+ client->list = &client->manager->inactive;
+ client->state = NS_CLIENTSTATE_INACTIVE;
+ INSIST(client->recursionquota == NULL);
+
+ if (client->state == client->newstate) {
+ client->newstate = NS_CLIENTSTATE_MAX;
+ if (client->needshutdown)
+ isc_task_shutdown(client->task);
+ goto unlock;
+ }
+ }
+
+ if (client->state == NS_CLIENTSTATE_INACTIVE) {
+ INSIST(client->newstate == NS_CLIENTSTATE_FREED);
+ /*
+ * We are trying to free the client.
+ *
+ * When "shuttingdown" is true, either the task has received
+ * its shutdown event or no shutdown event has ever been
+ * set up. Thus, we have no outstanding shutdown
+ * event at this point.
+ */
+ REQUIRE(client->state == NS_CLIENTSTATE_INACTIVE);
+
+ INSIST(client->recursionquota == NULL);
+
+ ns_query_free(client);
+ isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
+ isc_event_free((isc_event_t **)&client->sendevent);
+ isc_event_free((isc_event_t **)&client->recvevent);
+ isc_timer_detach(&client->timer);
+
+ if (client->tcpbuf != NULL)
+ isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ if (client->opt != NULL) {
+ INSIST(dns_rdataset_isassociated(client->opt));
+ dns_rdataset_disassociate(client->opt);
+ dns_message_puttemprdataset(client->message, &client->opt);
+ }
+ dns_message_destroy(&client->message);
+ if (client->manager != NULL) {
+ ns_clientmgr_t *manager = client->manager;
+ if (locked_manager == NULL) {
+ LOCK(&manager->lock);
+ locked_manager = manager;
+ }
+ ISC_LIST_UNLINK(*client->list, client, link);
+ client->list = NULL;
+ if (manager->exiting &&
+ ISC_LIST_EMPTY(manager->active) &&
+ ISC_LIST_EMPTY(manager->inactive) &&
+ ISC_LIST_EMPTY(manager->recursing))
+ destroy_manager = manager;
+ }
+ /*
+ * Detaching the task must be done after unlinking from
+ * the manager's lists because the manager accesses
+ * client->task.
+ */
+ if (client->task != NULL)
+ isc_task_detach(&client->task);
+
+ CTRACE("free");
+ client->magic = 0;
+ /*
+ * Check that there are no other external references to
+ * the memory context.
+ */
+ if (ns_g_clienttest && isc_mem_references(client->mctx) != 1) {
+ isc_mem_stats(client->mctx, stderr);
+ INSIST(0);
+ }
+ isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
+
+ goto unlock;
+ }
+
+ unlock:
+ if (locked_manager != NULL) {
+ UNLOCK(&locked_manager->lock);
+ locked_manager = NULL;
+ }
+
+ /*
+ * Only now is it safe to destroy the client manager (if needed),
+ * because we have accessed its lock for the last time.
+ */
+ if (destroy_manager != NULL)
+ clientmgr_destroy(destroy_manager);
+
+ return (ISC_TRUE);
+}
+
+/*%
+ * The client's task has received the client's control event
+ * as part of the startup process.
+ */
+static void
+client_start(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client = (ns_client_t *) event->ev_arg;
+
+ INSIST(task == client->task);
+
+ UNUSED(task);
+
+ INSIST(client->nctls == 1);
+ client->nctls--;
+
+ if (exit_check(client))
+ return;
+
+ if (TCP_CLIENT(client)) {
+ client_accept(client);
+ } else {
+ client_udprecv(client);
+ }
+}
+
+
+/*%
+ * The client's task has received a shutdown event.
+ */
+static void
+client_shutdown(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client;
+
+ REQUIRE(event != NULL);
+ REQUIRE(event->ev_type == ISC_TASKEVENT_SHUTDOWN);
+ client = event->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+
+ UNUSED(task);
+
+ CTRACE("shutdown");
+
+ isc_event_free(&event);
+
+ if (client->shutdown != NULL) {
+ (client->shutdown)(client->shutdown_arg, ISC_R_SHUTTINGDOWN);
+ client->shutdown = NULL;
+ client->shutdown_arg = NULL;
+ }
+
+ client->newstate = NS_CLIENTSTATE_FREED;
+ client->needshutdown = ISC_FALSE;
+ (void)exit_check(client);
+}
+
+static void
+ns_client_endrequest(ns_client_t *client) {
+ INSIST(client->naccepts == 0);
+ INSIST(client->nreads == 0);
+ INSIST(client->nsends == 0);
+ INSIST(client->nrecvs == 0);
+ INSIST(client->nupdates == 0);
+ INSIST(client->state == NS_CLIENTSTATE_WORKING);
+
+ CTRACE("endrequest");
+
+ if (client->next != NULL) {
+ (client->next)(client);
+ client->next = NULL;
+ }
+
+ if (client->view != NULL)
+ dns_view_detach(&client->view);
+ if (client->opt != NULL) {
+ INSIST(dns_rdataset_isassociated(client->opt));
+ dns_rdataset_disassociate(client->opt);
+ dns_message_puttemprdataset(client->message, &client->opt);
+ }
+
+ client->udpsize = 512;
+ client->extflags = 0;
+ client->ednsversion = -1;
+ dns_message_reset(client->message, DNS_MESSAGE_INTENTPARSE);
+
+ if (client->recursionquota != NULL)
+ isc_quota_detach(&client->recursionquota);
+
+ /*
+ * Clear all client attributes that are specific to
+ * the request; that's all except the TCP flag.
+ */
+ client->attributes &= NS_CLIENTATTR_TCP;
+}
+
+static void
+ns_client_checkactive(ns_client_t *client) {
+ if (client->mortal) {
+ /*
+ * This client object should normally go inactive
+ * at this point, but if we have fewer active client
+ * objects than desired due to earlier quota exhaustion,
+ * keep it active to make up for the shortage.
+ */
+ isc_boolean_t need_another_client = ISC_FALSE;
+ if (TCP_CLIENT(client) && !ns_g_clienttest) {
+ LOCK(&client->interface->lock);
+ if (client->interface->ntcpcurrent <
+ client->interface->ntcptarget)
+ need_another_client = ISC_TRUE;
+ UNLOCK(&client->interface->lock);
+ } else {
+ /*
+ * The UDP client quota is enforced by making
+ * requests fail rather than by not listening
+ * for new ones. Therefore, there is always a
+ * full set of UDP clients listening.
+ */
+ }
+ if (! need_another_client) {
+ /*
+ * We don't need this client object. Recycle it.
+ */
+ if (client->newstate >= NS_CLIENTSTATE_INACTIVE)
+ client->newstate = NS_CLIENTSTATE_INACTIVE;
+ }
+ }
+}
+
+void
+ns_client_next(ns_client_t *client, isc_result_t result) {
+ int newstate;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(client->state == NS_CLIENTSTATE_WORKING ||
+ client->state == NS_CLIENTSTATE_READING);
+
+ CTRACE("next");
+
+ if (result != ISC_R_SUCCESS)
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "request failed: %s", isc_result_totext(result));
+
+ /*
+ * An error processing a TCP request may have left
+ * the connection out of sync. To be safe, we always
+ * sever the connection when result != ISC_R_SUCCESS.
+ */
+ if (result == ISC_R_SUCCESS && TCP_CLIENT(client))
+ newstate = NS_CLIENTSTATE_READING;
+ else
+ newstate = NS_CLIENTSTATE_READY;
+
+ if (client->newstate > newstate)
+ client->newstate = newstate;
+ (void)exit_check(client);
+}
+
+
+static void
+client_senddone(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client;
+ isc_socketevent_t *sevent = (isc_socketevent_t *) event;
+
+ REQUIRE(sevent != NULL);
+ REQUIRE(sevent->ev_type == ISC_SOCKEVENT_SENDDONE);
+ client = sevent->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+ REQUIRE(sevent == client->sendevent);
+
+ UNUSED(task);
+
+ CTRACE("senddone");
+
+ if (sevent->result != ISC_R_SUCCESS)
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
+ "error sending response: %s",
+ isc_result_totext(sevent->result));
+
+ INSIST(client->nsends > 0);
+ client->nsends--;
+
+ if (client->tcpbuf != NULL) {
+ INSIST(TCP_CLIENT(client));
+ isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ client->tcpbuf = NULL;
+ }
+
+ if (exit_check(client))
+ return;
+
+ ns_client_next(client, ISC_R_SUCCESS);
+}
+
+/*%
+ * We only want to fail with ISC_R_NOSPACE when called from
+ * ns_client_sendraw() and not when called from ns_client_send(),
+ * tcpbuffer is NULL when called from ns_client_sendraw() and
+ * length != 0. tcpbuffer != NULL when called from ns_client_send()
+ * and length == 0.
+ */
+
+static isc_result_t
+client_allocsendbuf(ns_client_t *client, isc_buffer_t *buffer,
+ isc_buffer_t *tcpbuffer, isc_uint32_t length,
+ unsigned char *sendbuf, unsigned char **datap)
+{
+ unsigned char *data;
+ isc_uint32_t bufsize;
+ isc_result_t result;
+
+ INSIST(datap != NULL);
+ INSIST((tcpbuffer == NULL && length != 0) ||
+ (tcpbuffer != NULL && length == 0));
+
+ if (TCP_CLIENT(client)) {
+ INSIST(client->tcpbuf == NULL);
+ if (length + 2 > TCP_BUFFER_SIZE) {
+ result = ISC_R_NOSPACE;
+ goto done;
+ }
+ client->tcpbuf = isc_mem_get(client->mctx, TCP_BUFFER_SIZE);
+ if (client->tcpbuf == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto done;
+ }
+ data = client->tcpbuf;
+ if (tcpbuffer != NULL) {
+ isc_buffer_init(tcpbuffer, data, TCP_BUFFER_SIZE);
+ isc_buffer_init(buffer, data + 2, TCP_BUFFER_SIZE - 2);
+ } else {
+ isc_buffer_init(buffer, data, TCP_BUFFER_SIZE);
+ INSIST(length <= 0xffff);
+ isc_buffer_putuint16(buffer, (isc_uint16_t)length);
+ }
+ } else {
+ data = sendbuf;
+ if (client->udpsize < SEND_BUFFER_SIZE)
+ bufsize = client->udpsize;
+ else
+ bufsize = SEND_BUFFER_SIZE;
+ if (length > bufsize) {
+ result = ISC_R_NOSPACE;
+ goto done;
+ }
+ isc_buffer_init(buffer, data, bufsize);
+ }
+ *datap = data;
+ result = ISC_R_SUCCESS;
+
+ done:
+ return (result);
+}
+
+static isc_result_t
+client_sendpkg(ns_client_t *client, isc_buffer_t *buffer) {
+ struct in6_pktinfo *pktinfo;
+ isc_result_t result;
+ isc_region_t r;
+ isc_sockaddr_t *address;
+ isc_socket_t *socket;
+ isc_netaddr_t netaddr;
+ int match;
+ unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
+
+ if (TCP_CLIENT(client)) {
+ socket = client->tcpsocket;
+ address = NULL;
+ } else {
+ socket = client->udpsocket;
+ address = &client->peeraddr;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ if (ns_g_server->blackholeacl != NULL &&
+ dns_acl_match(&netaddr, NULL,
+ ns_g_server->blackholeacl,
+ &ns_g_server->aclenv,
+ &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ return (DNS_R_BLACKHOLED);
+ sockflags |= ISC_SOCKFLAG_NORETRY;
+ }
+
+ if ((client->attributes & NS_CLIENTATTR_PKTINFO) != 0 &&
+ (client->attributes & NS_CLIENTATTR_MULTICAST) == 0)
+ pktinfo = &client->pktinfo;
+ else
+ pktinfo = NULL;
+
+ isc_buffer_usedregion(buffer, &r);
+
+ CTRACE("sendto");
+
+ result = isc_socket_sendto2(socket, &r, client->task,
+ address, pktinfo,
+ client->sendevent, sockflags);
+ if (result == ISC_R_SUCCESS || result == ISC_R_INPROGRESS) {
+ client->nsends++;
+ if (result == ISC_R_SUCCESS)
+ client_senddone(client->task,
+ (isc_event_t *)client->sendevent);
+ result = ISC_R_SUCCESS;
+ }
+ return (result);
+}
+
+void
+ns_client_sendraw(ns_client_t *client, dns_message_t *message) {
+ isc_result_t result;
+ unsigned char *data;
+ isc_buffer_t buffer;
+ isc_region_t r;
+ isc_region_t *mr;
+ unsigned char sendbuf[SEND_BUFFER_SIZE];
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ CTRACE("sendraw");
+
+ mr = dns_message_getrawmessage(message);
+ if (mr == NULL) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto done;
+ }
+
+ result = client_allocsendbuf(client, &buffer, NULL, mr->length,
+ sendbuf, &data);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ /*
+ * Copy message to buffer and fixup id.
+ */
+ isc_buffer_availableregion(&buffer, &r);
+ result = isc_buffer_copyregion(&buffer, mr);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ r.base[0] = (client->message->id >> 8) & 0xff;
+ r.base[1] = client->message->id & 0xff;
+
+ result = client_sendpkg(client, &buffer);
+ if (result == ISC_R_SUCCESS)
+ return;
+
+ done:
+ if (client->tcpbuf != NULL) {
+ isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ client->tcpbuf = NULL;
+ }
+ ns_client_next(client, result);
+}
+
+void
+ns_client_send(ns_client_t *client) {
+ isc_result_t result;
+ unsigned char *data;
+ isc_buffer_t buffer;
+ isc_buffer_t tcpbuffer;
+ isc_region_t r;
+ dns_compress_t cctx;
+ isc_boolean_t cleanup_cctx = ISC_FALSE;
+ unsigned char sendbuf[SEND_BUFFER_SIZE];
+ unsigned int dnssec_opts;
+ unsigned int preferred_glue;
+ isc_boolean_t opt_included = ISC_FALSE;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ CTRACE("send");
+
+ if ((client->attributes & NS_CLIENTATTR_RA) != 0)
+ client->message->flags |= DNS_MESSAGEFLAG_RA;
+
+ if ((client->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
+ dnssec_opts = 0;
+ else
+ dnssec_opts = DNS_MESSAGERENDER_OMITDNSSEC;
+
+ preferred_glue = 0;
+ if (client->view != NULL) {
+ if (client->view->preferred_glue == dns_rdatatype_a)
+ preferred_glue = DNS_MESSAGERENDER_PREFER_A;
+ else if (client->view->preferred_glue == dns_rdatatype_aaaa)
+ preferred_glue = DNS_MESSAGERENDER_PREFER_AAAA;
+ }
+
+ /*
+ * XXXRTH The following doesn't deal with TCP buffer resizing.
+ */
+ result = client_allocsendbuf(client, &buffer, &tcpbuffer, 0,
+ sendbuf, &data);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ result = dns_compress_init(&cctx, -1, client->mctx);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ cleanup_cctx = ISC_TRUE;
+
+ result = dns_message_renderbegin(client->message, &cctx, &buffer);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ if (client->opt != NULL) {
+ result = dns_message_setopt(client->message, client->opt);
+ opt_included = ISC_TRUE;
+ client->opt = NULL;
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ }
+ result = dns_message_rendersection(client->message,
+ DNS_SECTION_QUESTION, 0);
+ if (result == ISC_R_NOSPACE) {
+ client->message->flags |= DNS_MESSAGEFLAG_TC;
+ goto renderend;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ result = dns_message_rendersection(client->message,
+ DNS_SECTION_ANSWER,
+ DNS_MESSAGERENDER_PARTIAL |
+ dnssec_opts);
+ if (result == ISC_R_NOSPACE) {
+ client->message->flags |= DNS_MESSAGEFLAG_TC;
+ goto renderend;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ result = dns_message_rendersection(client->message,
+ DNS_SECTION_AUTHORITY,
+ DNS_MESSAGERENDER_PARTIAL |
+ dnssec_opts);
+ if (result == ISC_R_NOSPACE) {
+ client->message->flags |= DNS_MESSAGEFLAG_TC;
+ goto renderend;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ result = dns_message_rendersection(client->message,
+ DNS_SECTION_ADDITIONAL,
+ preferred_glue | dnssec_opts);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOSPACE)
+ goto done;
+ renderend:
+ result = dns_message_renderend(client->message);
+
+ if (result != ISC_R_SUCCESS)
+ goto done;
+
+ if (cleanup_cctx) {
+ dns_compress_invalidate(&cctx);
+ cleanup_cctx = ISC_FALSE;
+ }
+
+ if (TCP_CLIENT(client)) {
+ isc_buffer_usedregion(&buffer, &r);
+ isc_buffer_putuint16(&tcpbuffer, (isc_uint16_t) r.length);
+ isc_buffer_add(&tcpbuffer, r.length);
+ result = client_sendpkg(client, &tcpbuffer);
+ } else
+ result = client_sendpkg(client, &buffer);
+
+ /* update statistics (XXXJT: is it okay to access message->xxxkey?) */
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_response);
+ if (opt_included) {
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_edns0out);
+ }
+ if (client->message->tsigkey != NULL) {
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_tsigout);
+ }
+ if (client->message->sig0key != NULL) {
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_sig0out);
+ }
+ if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_truncatedresp);
+
+ if (result == ISC_R_SUCCESS)
+ return;
+
+ done:
+ if (client->tcpbuf != NULL) {
+ isc_mem_put(client->mctx, client->tcpbuf, TCP_BUFFER_SIZE);
+ client->tcpbuf = NULL;
+ }
+
+ if (cleanup_cctx)
+ dns_compress_invalidate(&cctx);
+
+ ns_client_next(client, result);
+}
+
+#if NS_CLIENT_DROPPORT
+#define DROPPORT_NO 0
+#define DROPPORT_REQUEST 1
+#define DROPPORT_RESPONSE 2
+/*%
+ * ns_client_dropport determines if certain requests / responses
+ * should be dropped based on the port number.
+ *
+ * Returns:
+ * \li 0: Don't drop.
+ * \li 1: Drop request.
+ * \li 2: Drop (error) response.
+ */
+static int
+ns_client_dropport(in_port_t port) {
+ switch (port) {
+ case 7: /* echo */
+ case 13: /* daytime */
+ case 19: /* chargen */
+ case 37: /* time */
+ return (DROPPORT_REQUEST);
+ case 464: /* kpasswd */
+ return (DROPPORT_RESPONSE);
+ }
+ return (DROPPORT_NO);
+}
+#endif
+
+void
+ns_client_error(ns_client_t *client, isc_result_t result) {
+ dns_rcode_t rcode;
+ dns_message_t *message;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ CTRACE("error");
+
+ message = client->message;
+ rcode = dns_result_torcode(result);
+
+#if NS_CLIENT_DROPPORT
+ /*
+ * Don't send FORMERR to ports on the drop port list.
+ */
+ if (rcode == dns_rcode_formerr &&
+ ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) !=
+ DROPPORT_NO) {
+ char buf[64];
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, buf, sizeof(buf) - 1);
+ if (dns_rcode_totext(rcode, &b) != ISC_R_SUCCESS)
+ isc_buffer_putstr(&b, "UNKNOWN RCODE");
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "dropped error (%.*s) response: suspicious port",
+ (int)isc_buffer_usedlength(&b), buf);
+ ns_client_next(client, ISC_R_SUCCESS);
+ return;
+ }
+#endif
+
+ /*
+ * Message may be an in-progress reply that we had trouble
+ * with, in which case QR will be set. We need to clear QR before
+ * calling dns_message_reply() to avoid triggering an assertion.
+ */
+ message->flags &= ~DNS_MESSAGEFLAG_QR;
+ /*
+ * AA and AD shouldn't be set.
+ */
+ message->flags &= ~(DNS_MESSAGEFLAG_AA | DNS_MESSAGEFLAG_AD);
+ result = dns_message_reply(message, ISC_TRUE);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * It could be that we've got a query with a good header,
+ * but a bad question section, so we try again with
+ * want_question_section set to ISC_FALSE.
+ */
+ result = dns_message_reply(message, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_next(client, result);
+ return;
+ }
+ }
+ message->rcode = rcode;
+
+ /*
+ * FORMERR loop avoidance: If we sent a FORMERR message
+ * with the same ID to the same client less than two
+ * seconds ago, assume that we are in an infinite error
+ * packet dialog with a server for some protocol whose
+ * error responses look enough like DNS queries to
+ * elicit a FORMERR response. Drop a packet to break
+ * the loop.
+ */
+ if (rcode == dns_rcode_formerr) {
+ if (isc_sockaddr_equal(&client->peeraddr,
+ &client->formerrcache.addr) &&
+ message->id == client->formerrcache.id &&
+ client->requesttime - client->formerrcache.time < 2) {
+ /* Drop packet. */
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+ "possible error packet loop, "
+ "FORMERR dropped");
+ ns_client_next(client, result);
+ return;
+ }
+ client->formerrcache.addr = client->peeraddr;
+ client->formerrcache.time = client->requesttime;
+ client->formerrcache.id = message->id;
+ }
+ ns_client_send(client);
+}
+
+static inline isc_result_t
+client_addopt(ns_client_t *client) {
+ dns_rdataset_t *rdataset;
+ dns_rdatalist_t *rdatalist;
+ dns_rdata_t *rdata;
+ isc_result_t result;
+ dns_view_t *view;
+ dns_resolver_t *resolver;
+ isc_uint16_t udpsize;
+
+ REQUIRE(client->opt == NULL); /* XXXRTH free old. */
+
+ rdatalist = NULL;
+ result = dns_message_gettemprdatalist(client->message, &rdatalist);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdata = NULL;
+ result = dns_message_gettemprdata(client->message, &rdata);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdataset = NULL;
+ result = dns_message_gettemprdataset(client->message, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_init(rdataset);
+
+ rdatalist->type = dns_rdatatype_opt;
+ rdatalist->covers = 0;
+
+ /*
+ * Set the maximum UDP buffer size.
+ */
+ view = client->view;
+ resolver = (view != NULL) ? view->resolver : NULL;
+ if (resolver != NULL)
+ udpsize = dns_resolver_getudpsize(resolver);
+ else
+ udpsize = ns_g_udpsize;
+ rdatalist->rdclass = udpsize;
+
+ /*
+ * Set EXTENDED-RCODE, VERSION and Z to 0.
+ */
+ rdatalist->ttl = (client->extflags & DNS_MESSAGEEXTFLAG_REPLYPRESERVE);
+
+ /* Set EDNS options if applicable */
+ if (client->attributes & NS_CLIENTATTR_WANTNSID &&
+ (ns_g_server->server_id != NULL ||
+ ns_g_server->server_usehostname)) {
+ /*
+ * Space required for NSID data:
+ * 2 bytes for opt code
+ * + 2 bytes for NSID length
+ * + NSID itself
+ */
+ char nsid[BUFSIZ], *nsidp;
+ isc_buffer_t *buffer = NULL;
+
+ if (ns_g_server->server_usehostname) {
+ isc_result_t result;
+ result = ns_os_gethostname(nsid, sizeof(nsid));
+ if (result != ISC_R_SUCCESS) {
+ goto no_nsid;
+ }
+ nsidp = nsid;
+ } else
+ nsidp = ns_g_server->server_id;
+
+ rdata->length = strlen(nsidp) + 4;
+ result = isc_buffer_allocate(client->mctx, &buffer,
+ rdata->length);
+ if (result != ISC_R_SUCCESS)
+ goto no_nsid;
+
+ isc_buffer_putuint16(buffer, DNS_OPT_NSID);
+ isc_buffer_putuint16(buffer, strlen(nsidp));
+ isc_buffer_putstr(buffer, nsidp);
+ rdata->data = buffer->base;
+ dns_message_takebuffer(client->message, &buffer);
+ } else {
+no_nsid:
+ rdata->data = NULL;
+ rdata->length = 0;
+ }
+
+ rdata->rdclass = rdatalist->rdclass;
+ rdata->type = rdatalist->type;
+ rdata->flags = 0;
+
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset)
+ == ISC_R_SUCCESS);
+
+ client->opt = rdataset;
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_boolean_t
+allowed(isc_netaddr_t *addr, dns_name_t *signer, dns_acl_t *acl) {
+ int match;
+ isc_result_t result;
+
+ if (acl == NULL)
+ return (ISC_TRUE);
+ result = dns_acl_match(addr, signer, acl, &ns_g_server->aclenv,
+ &match, NULL);
+ if (result == ISC_R_SUCCESS && match > 0)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+/*
+ * Callback to see if a non-recursive query coming from 'srcaddr' to
+ * 'destaddr', with optional key 'mykey' for class 'rdclass' would be
+ * delivered to 'myview'.
+ *
+ * We run this unlocked as both the view list and the interface list
+ * are updated when the approprite task has exclusivity.
+ */
+isc_boolean_t
+ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *dstaddr,
+ dns_rdataclass_t rdclass, void *arg)
+{
+ dns_view_t *view;
+ dns_tsigkey_t *key = NULL;
+ dns_name_t *tsig = NULL;
+ isc_netaddr_t netsrc;
+ isc_netaddr_t netdst;
+
+ UNUSED(arg);
+
+ if (!ns_interfacemgr_listeningon(ns_g_server->interfacemgr, dstaddr))
+ return (ISC_FALSE);
+
+ isc_netaddr_fromsockaddr(&netsrc, srcaddr);
+ isc_netaddr_fromsockaddr(&netdst, dstaddr);
+
+ for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+
+ if (view->matchrecursiveonly)
+ continue;
+
+ if (rdclass != view->rdclass)
+ continue;
+
+ if (mykey != NULL) {
+ isc_boolean_t match;
+ isc_result_t result;
+
+ result = dns_view_gettsig(view, &mykey->name, &key);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ match = dst_key_compare(mykey->key, key->key);
+ dns_tsigkey_detach(&key);
+ if (!match)
+ continue;
+ tsig = dns_tsigkey_identity(mykey);
+ }
+
+ if (allowed(&netsrc, tsig, view->matchclients) &&
+ allowed(&netdst, tsig, view->matchdestinations))
+ break;
+ }
+ return (ISC_TF(view == myview));
+}
+
+/*
+ * Handle an incoming request event from the socket (UDP case)
+ * or tcpmsg (TCP case).
+ */
+static void
+client_request(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client;
+ isc_socketevent_t *sevent;
+ isc_result_t result;
+ isc_result_t sigresult = ISC_R_SUCCESS;
+ isc_buffer_t *buffer;
+ isc_buffer_t tbuffer;
+ dns_view_t *view;
+ dns_rdataset_t *opt;
+ dns_name_t *signame;
+ isc_boolean_t ra; /* Recursion available. */
+ isc_netaddr_t netaddr;
+ isc_netaddr_t destaddr;
+ int match;
+ dns_messageid_t id;
+ unsigned int flags;
+ isc_boolean_t notimp;
+ dns_rdata_t rdata;
+ isc_uint16_t optcode;
+
+ REQUIRE(event != NULL);
+ client = event->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+
+ INSIST(client->recursionquota == NULL);
+
+ INSIST(client->state ==
+ TCP_CLIENT(client) ?
+ NS_CLIENTSTATE_READING :
+ NS_CLIENTSTATE_READY);
+
+ ns_client_requests++;
+
+ if (event->ev_type == ISC_SOCKEVENT_RECVDONE) {
+ INSIST(!TCP_CLIENT(client));
+ sevent = (isc_socketevent_t *)event;
+ REQUIRE(sevent == client->recvevent);
+ isc_buffer_init(&tbuffer, sevent->region.base, sevent->n);
+ isc_buffer_add(&tbuffer, sevent->n);
+ buffer = &tbuffer;
+ result = sevent->result;
+ if (result == ISC_R_SUCCESS) {
+ client->peeraddr = sevent->address;
+ client->peeraddr_valid = ISC_TRUE;
+ }
+ if ((sevent->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
+ client->attributes |= NS_CLIENTATTR_PKTINFO;
+ client->pktinfo = sevent->pktinfo;
+ }
+ if ((sevent->attributes & ISC_SOCKEVENTATTR_MULTICAST) != 0)
+ client->attributes |= NS_CLIENTATTR_MULTICAST;
+ client->nrecvs--;
+ } else {
+ INSIST(TCP_CLIENT(client));
+ REQUIRE(event->ev_type == DNS_EVENT_TCPMSG);
+ REQUIRE(event->ev_sender == &client->tcpmsg);
+ buffer = &client->tcpmsg.buffer;
+ result = client->tcpmsg.result;
+ INSIST(client->nreads == 1);
+ /*
+ * client->peeraddr was set when the connection was accepted.
+ */
+ client->nreads--;
+ }
+
+ if (exit_check(client))
+ goto cleanup;
+ client->state = client->newstate = NS_CLIENTSTATE_WORKING;
+
+ isc_task_getcurrenttime(task, &client->requesttime);
+ client->now = client->requesttime;
+
+ if (result != ISC_R_SUCCESS) {
+ if (TCP_CLIENT(client)) {
+ ns_client_next(client, result);
+ } else {
+ if (result != ISC_R_CANCELED)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT,
+ ISC_LOG_ERROR,
+ "UDP client handler shutting "
+ "down due to fatal receive "
+ "error: %s",
+ isc_result_totext(result));
+ isc_task_shutdown(client->task);
+ }
+ goto cleanup;
+ }
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+#if NS_CLIENT_DROPPORT
+ if (ns_client_dropport(isc_sockaddr_getport(&client->peeraddr)) ==
+ DROPPORT_REQUEST) {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "dropped request: suspicious port");
+ ns_client_next(client, ISC_R_SUCCESS);
+ goto cleanup;
+ }
+#endif
+
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "%s request",
+ TCP_CLIENT(client) ? "TCP" : "UDP");
+
+ /*
+ * Check the blackhole ACL for UDP only, since TCP is done in
+ * client_newconn.
+ */
+ if (!TCP_CLIENT(client)) {
+
+ if (ns_g_server->blackholeacl != NULL &&
+ dns_acl_match(&netaddr, NULL, ns_g_server->blackholeacl,
+ &ns_g_server->aclenv,
+ &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "blackholed UDP datagram");
+ ns_client_next(client, ISC_R_SUCCESS);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Silently drop multicast requests for the present.
+ * XXXMPA look at when/if mDNS spec stabilizes.
+ */
+ if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+ "dropping multicast request");
+ ns_client_next(client, DNS_R_REFUSED);
+ goto cleanup;
+ }
+
+ result = dns_message_peekheader(buffer, &id, &flags);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * There isn't enough header to determine whether
+ * this was a request or a response. Drop it.
+ */
+ ns_client_next(client, result);
+ goto cleanup;
+ }
+
+ /*
+ * The client object handles requests, not responses.
+ * If this is a UDP response, forward it to the dispatcher.
+ * If it's a TCP response, discard it here.
+ */
+ if ((flags & DNS_MESSAGEFLAG_QR) != 0) {
+ if (TCP_CLIENT(client)) {
+ CTRACE("unexpected response");
+ ns_client_next(client, DNS_R_FORMERR);
+ goto cleanup;
+ } else {
+ dns_dispatch_importrecv(client->dispatch, event);
+ ns_client_next(client, ISC_R_SUCCESS);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Update some statistics counters. Don't count responses.
+ */
+ if (isc_sockaddr_pf(&client->peeraddr) == PF_INET) {
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_requestv4);
+ } else {
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_requestv6);
+ }
+ if (TCP_CLIENT(client))
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_tcp);
+
+ /*
+ * It's a request. Parse it.
+ */
+ result = dns_message_parse(client->message, buffer, 0);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Parsing the request failed. Send a response
+ * (typically FORMERR or SERVFAIL).
+ */
+ ns_client_error(client, result);
+ goto cleanup;
+ }
+
+ dns_opcodestats_increment(ns_g_server->opcodestats,
+ client->message->opcode);
+ switch (client->message->opcode) {
+ case dns_opcode_query:
+ case dns_opcode_update:
+ case dns_opcode_notify:
+ notimp = ISC_FALSE;
+ break;
+ case dns_opcode_iquery:
+ default:
+ notimp = ISC_TRUE;
+ break;
+ }
+
+ client->message->rcode = dns_rcode_noerror;
+
+ /* RFC1123 section 6.1.3.2 */
+ if ((client->attributes & NS_CLIENTATTR_MULTICAST) != 0)
+ client->message->flags &= ~DNS_MESSAGEFLAG_RD;
+
+ /*
+ * Deal with EDNS.
+ */
+ opt = dns_message_getopt(client->message);
+ if (opt != NULL) {
+ /*
+ * Set the client's UDP buffer size.
+ */
+ client->udpsize = opt->rdclass;
+
+ /*
+ * If the requested UDP buffer size is less than 512,
+ * ignore it and use 512.
+ */
+ if (client->udpsize < 512)
+ client->udpsize = 512;
+
+ /*
+ * Get the flags out of the OPT record.
+ */
+ client->extflags = (isc_uint16_t)(opt->ttl & 0xFFFF);
+
+ /*
+ * Do we understand this version of EDNS?
+ *
+ * XXXRTH need library support for this!
+ */
+ client->ednsversion = (opt->ttl & 0x00FF0000) >> 16;
+ if (client->ednsversion > 0) {
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_badednsver);
+ result = client_addopt(client);
+ if (result == ISC_R_SUCCESS)
+ result = DNS_R_BADVERS;
+ ns_client_error(client, result);
+ goto cleanup;
+ }
+
+ /* Check for NSID request */
+ result = dns_rdataset_first(opt);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdata_init(&rdata);
+ dns_rdataset_current(opt, &rdata);
+ if (rdata.length >= 2) {
+ isc_buffer_t nsidbuf;
+ isc_buffer_init(&nsidbuf,
+ rdata.data, rdata.length);
+ isc_buffer_add(&nsidbuf, rdata.length);
+ optcode = isc_buffer_getuint16(&nsidbuf);
+ if (optcode == DNS_OPT_NSID)
+ client->attributes |=
+ NS_CLIENTATTR_WANTNSID;
+ }
+ }
+
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_edns0in);
+
+ /*
+ * Create an OPT for our reply.
+ */
+ result = client_addopt(client);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_error(client, result);
+ goto cleanup;
+ }
+ }
+
+ if (client->message->rdclass == 0) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+ "message class could not be determined");
+ ns_client_dumpmessage(client,
+ "message class could not be determined");
+ ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_FORMERR);
+ goto cleanup;
+ }
+
+ /*
+ * Determine the destination address. If the receiving interface is
+ * bound to a specific address, we simply use it regardless of the
+ * address family. All IPv4 queries should fall into this case.
+ * Otherwise, if this is a TCP query, get the address from the
+ * receiving socket (this needs a system call and can be heavy).
+ * For IPv6 UDP queries, we get this from the pktinfo structure (if
+ * supported).
+ * If all the attempts fail (this can happen due to memory shortage,
+ * etc), we regard this as an error for safety.
+ */
+ if ((client->interface->flags & NS_INTERFACEFLAG_ANYADDR) == 0)
+ isc_netaddr_fromsockaddr(&destaddr, &client->interface->addr);
+ else {
+ result = ISC_R_FAILURE;
+
+ if (TCP_CLIENT(client)) {
+ isc_sockaddr_t destsockaddr;
+
+ result = isc_socket_getsockname(client->tcpsocket,
+ &destsockaddr);
+ if (result == ISC_R_SUCCESS)
+ isc_netaddr_fromsockaddr(&destaddr,
+ &destsockaddr);
+ }
+ if (result != ISC_R_SUCCESS &&
+ client->interface->addr.type.sa.sa_family == AF_INET6 &&
+ (client->attributes & NS_CLIENTATTR_PKTINFO) != 0) {
+ isc_uint32_t zone = 0;
+
+ /*
+ * XXXJT technically, we should convert the receiving
+ * interface ID to a proper scope zone ID. However,
+ * due to the fact there is no standard API for this,
+ * we only handle link-local addresses and use the
+ * interface index as link ID. Despite the assumption,
+ * it should cover most typical cases.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&client->pktinfo.ipi6_addr))
+ zone = (isc_uint32_t)client->pktinfo.ipi6_ifindex;
+
+ isc_netaddr_fromin6(&destaddr,
+ &client->pktinfo.ipi6_addr);
+ isc_netaddr_setzone(&destaddr, zone);
+ result = ISC_R_SUCCESS;
+ }
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "failed to get request's "
+ "destination: %s",
+ isc_result_totext(result));
+ ns_client_next(client, ISC_R_SUCCESS);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Find a view that matches the client's source address.
+ */
+ for (view = ISC_LIST_HEAD(ns_g_server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (client->message->rdclass == view->rdclass ||
+ client->message->rdclass == dns_rdataclass_any)
+ {
+ dns_name_t *tsig = NULL;
+
+ sigresult = dns_message_rechecksig(client->message,
+ view);
+ if (sigresult == ISC_R_SUCCESS)
+ tsig = dns_tsigkey_identity(client->message->tsigkey);
+
+ if (allowed(&netaddr, tsig, view->matchclients) &&
+ allowed(&destaddr, tsig, view->matchdestinations) &&
+ !((client->message->flags & DNS_MESSAGEFLAG_RD)
+ == 0 && view->matchrecursiveonly))
+ {
+ dns_view_attach(view, &client->view);
+ break;
+ }
+ }
+ }
+
+ if (view == NULL) {
+ char classname[DNS_RDATACLASS_FORMATSIZE];
+
+ /*
+ * Do a dummy TSIG verification attempt so that the
+ * response will have a TSIG if the query did, as
+ * required by RFC2845.
+ */
+ isc_buffer_t b;
+ isc_region_t *r;
+
+ dns_message_resetsig(client->message);
+
+ r = dns_message_getrawmessage(client->message);
+ isc_buffer_init(&b, r->base, r->length);
+ isc_buffer_add(&b, r->length);
+ (void)dns_tsig_verify(&b, client->message, NULL, NULL);
+
+ dns_rdataclass_format(client->message->rdclass, classname,
+ sizeof(classname));
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+ "no matching view in class '%s'", classname);
+ ns_client_dumpmessage(client, "no matching view in class");
+ ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
+ goto cleanup;
+ }
+
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
+ "using view '%s'", view->name);
+
+ /*
+ * Check for a signature. We log bad signatures regardless of
+ * whether they ultimately cause the request to be rejected or
+ * not. We do not log the lack of a signature unless we are
+ * debugging.
+ */
+ client->signer = NULL;
+ dns_name_init(&client->signername, NULL);
+ result = dns_message_signer(client->message, &client->signername);
+ if (result != ISC_R_NOTFOUND) {
+ signame = NULL;
+ if (dns_message_gettsig(client->message, &signame) != NULL) {
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_tsigin);
+ } else {
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_sig0in);
+ }
+
+ }
+ if (result == ISC_R_SUCCESS) {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "request has valid signature");
+ client->signer = &client->signername;
+ } else if (result == ISC_R_NOTFOUND) {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "request is not signed");
+ } else if (result == DNS_R_NOIDENTITY) {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "request is signed by a nonauthoritative key");
+ } else {
+ char tsigrcode[64];
+ isc_buffer_t b;
+ dns_rcode_t status;
+ isc_result_t tresult;
+
+ /* There is a signature, but it is bad. */
+ dns_generalstats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_invalidsig);
+ signame = NULL;
+ if (dns_message_gettsig(client->message, &signame) != NULL) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char cnamebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(signame, namebuf, sizeof(namebuf));
+ status = client->message->tsigstatus;
+ isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
+ tresult = dns_tsigrcode_totext(status, &b);
+ INSIST(tresult == ISC_R_SUCCESS);
+ tsigrcode[isc_buffer_usedlength(&b)] = '\0';
+ if (client->message->tsigkey->generated) {
+ dns_name_format(client->message->tsigkey->creator,
+ cnamebuf, sizeof(cnamebuf));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT,
+ ISC_LOG_ERROR,
+ "request has invalid signature: "
+ "TSIG %s (%s): %s (%s)", namebuf,
+ cnamebuf,
+ isc_result_totext(result),
+ tsigrcode);
+ } else {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT,
+ ISC_LOG_ERROR,
+ "request has invalid signature: "
+ "TSIG %s: %s (%s)", namebuf,
+ isc_result_totext(result),
+ tsigrcode);
+ }
+ } else {
+ status = client->message->sig0status;
+ isc_buffer_init(&b, tsigrcode, sizeof(tsigrcode) - 1);
+ tresult = dns_tsigrcode_totext(status, &b);
+ INSIST(tresult == ISC_R_SUCCESS);
+ tsigrcode[isc_buffer_usedlength(&b)] = '\0';
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_ERROR,
+ "request has invalid signature: %s (%s)",
+ isc_result_totext(result), tsigrcode);
+ }
+ /*
+ * Accept update messages signed by unknown keys so that
+ * update forwarding works transparently through slaves
+ * that don't have all the same keys as the master.
+ */
+ if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
+ client->message->opcode == dns_opcode_update)) {
+ ns_client_error(client, sigresult);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Decide whether recursive service is available to this client.
+ * We do this here rather than in the query code so that we can
+ * set the RA bit correctly on all kinds of responses, not just
+ * responses to ordinary queries. Note if you can't query the
+ * cache there is no point in setting RA.
+ */
+ ra = ISC_FALSE;
+ if (client->view->resolver != NULL &&
+ client->view->recursion == ISC_TRUE &&
+ ns_client_checkaclsilent(client, NULL,
+ client->view->recursionacl,
+ ISC_TRUE) == ISC_R_SUCCESS &&
+ ns_client_checkaclsilent(client, NULL,
+ client->view->queryacl,
+ ISC_TRUE) == ISC_R_SUCCESS &&
+ ns_client_checkaclsilent(client, &client->interface->addr,
+ client->view->recursiononacl,
+ ISC_TRUE) == ISC_R_SUCCESS &&
+ ns_client_checkaclsilent(client, &client->interface->addr,
+ client->view->queryonacl,
+ ISC_TRUE) == ISC_R_SUCCESS)
+ ra = ISC_TRUE;
+
+ if (ra == ISC_TRUE)
+ client->attributes |= NS_CLIENTATTR_RA;
+
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY, NS_LOGMODULE_CLIENT,
+ ISC_LOG_DEBUG(3), ra ? "recursion available" :
+ "recursion not available");
+
+ /*
+ * Adjust maximum UDP response size for this client.
+ */
+ if (client->udpsize > 512) {
+ dns_peer_t *peer = NULL;
+ isc_uint16_t udpsize = view->maxudp;
+ (void) dns_peerlist_peerbyaddr(view->peers, &netaddr, &peer);
+ if (peer != NULL)
+ dns_peer_getmaxudp(peer, &udpsize);
+ if (client->udpsize > udpsize)
+ client->udpsize = udpsize;
+ }
+
+ /*
+ * Dispatch the request.
+ */
+ switch (client->message->opcode) {
+ case dns_opcode_query:
+ CTRACE("query");
+ ns_query_start(client);
+ break;
+ case dns_opcode_update:
+ CTRACE("update");
+ ns_client_settimeout(client, 60);
+ ns_update_start(client, sigresult);
+ break;
+ case dns_opcode_notify:
+ CTRACE("notify");
+ ns_client_settimeout(client, 60);
+ ns_notify_start(client);
+ break;
+ case dns_opcode_iquery:
+ CTRACE("iquery");
+ ns_client_error(client, DNS_R_NOTIMP);
+ break;
+ default:
+ CTRACE("unknown opcode");
+ ns_client_error(client, DNS_R_NOTIMP);
+ }
+
+ cleanup:
+ return;
+}
+
+static void
+client_timeout(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client;
+
+ REQUIRE(event != NULL);
+ REQUIRE(event->ev_type == ISC_TIMEREVENT_LIFE ||
+ event->ev_type == ISC_TIMEREVENT_IDLE);
+ client = event->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+ REQUIRE(client->timer != NULL);
+
+ UNUSED(task);
+
+ CTRACE("timeout");
+
+ isc_event_free(&event);
+
+ if (client->shutdown != NULL) {
+ (client->shutdown)(client->shutdown_arg, ISC_R_TIMEDOUT);
+ client->shutdown = NULL;
+ client->shutdown_arg = NULL;
+ }
+
+ if (client->newstate > NS_CLIENTSTATE_READY)
+ client->newstate = NS_CLIENTSTATE_READY;
+ (void)exit_check(client);
+}
+
+static isc_result_t
+get_clientmctx(ns_clientmgr_t *manager, isc_mem_t **mctxp) {
+ isc_mem_t *clientmctx;
+ isc_result_t result;
+
+ /*
+ * Caller must be holding the manager lock.
+ */
+ if (ns_g_clienttest) {
+ result = isc_mem_create(0, 0, mctxp);
+ if (result == ISC_R_SUCCESS)
+ isc_mem_setname(*mctxp, "client", NULL);
+ return (result);
+ }
+#if NMCTXS > 0
+ INSIST(manager->nextmctx < NMCTXS);
+ clientmctx = manager->mctxpool[manager->nextmctx];
+ if (clientmctx == NULL) {
+ result = isc_mem_create(0, 0, &clientmctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_mem_setname(clientmctx, "client", NULL);
+
+ manager->mctxpool[manager->nextmctx] = clientmctx;
+ }
+ manager->nextmctx++;
+ if (manager->nextmctx == NMCTXS)
+ manager->nextmctx = 0;
+#else
+ clientmctx = manager->mctx;
+#endif
+
+ isc_mem_attach(clientmctx, mctxp);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+client_create(ns_clientmgr_t *manager, ns_client_t **clientp) {
+ ns_client_t *client;
+ isc_result_t result;
+ isc_mem_t *mctx = NULL;
+
+ /*
+ * Caller must be holding the manager lock.
+ *
+ * Note: creating a client does not add the client to the
+ * manager's client list or set the client's manager pointer.
+ * The caller is responsible for that.
+ */
+
+ REQUIRE(clientp != NULL && *clientp == NULL);
+
+ result = get_clientmctx(manager, &mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ client = isc_mem_get(mctx, sizeof(*client));
+ if (client == NULL) {
+ isc_mem_detach(&mctx);
+ return (ISC_R_NOMEMORY);
+ }
+ client->mctx = mctx;
+
+ client->task = NULL;
+ result = isc_task_create(manager->taskmgr, 0, &client->task);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_client;
+ isc_task_setname(client->task, "client", client);
+
+ client->timer = NULL;
+ result = isc_timer_create(manager->timermgr, isc_timertype_inactive,
+ NULL, NULL, client->task, client_timeout,
+ client, &client->timer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_task;
+ client->timerset = ISC_FALSE;
+
+ client->message = NULL;
+ result = dns_message_create(client->mctx, DNS_MESSAGE_INTENTPARSE,
+ &client->message);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_timer;
+
+ /* XXXRTH Hardwired constants */
+
+ client->sendevent = (isc_socketevent_t *)
+ isc_event_allocate(client->mctx, client,
+ ISC_SOCKEVENT_SENDDONE,
+ client_senddone, client,
+ sizeof(isc_socketevent_t));
+ if (client->sendevent == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_message;
+ }
+
+ client->recvbuf = isc_mem_get(client->mctx, RECV_BUFFER_SIZE);
+ if (client->recvbuf == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_sendevent;
+ }
+
+ client->recvevent = (isc_socketevent_t *)
+ isc_event_allocate(client->mctx, client,
+ ISC_SOCKEVENT_RECVDONE,
+ client_request, client,
+ sizeof(isc_socketevent_t));
+ if (client->recvevent == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup_recvbuf;
+ }
+
+ client->magic = NS_CLIENT_MAGIC;
+ client->manager = NULL;
+ client->state = NS_CLIENTSTATE_INACTIVE;
+ client->newstate = NS_CLIENTSTATE_MAX;
+ client->naccepts = 0;
+ client->nreads = 0;
+ client->nsends = 0;
+ client->nrecvs = 0;
+ client->nupdates = 0;
+ client->nctls = 0;
+ client->references = 0;
+ client->attributes = 0;
+ client->view = NULL;
+ client->dispatch = NULL;
+ client->udpsocket = NULL;
+ client->tcplistener = NULL;
+ client->tcpsocket = NULL;
+ client->tcpmsg_valid = ISC_FALSE;
+ client->tcpbuf = NULL;
+ client->opt = NULL;
+ client->udpsize = 512;
+ client->extflags = 0;
+ client->ednsversion = -1;
+ client->next = NULL;
+ client->shutdown = NULL;
+ client->shutdown_arg = NULL;
+ dns_name_init(&client->signername, NULL);
+ client->mortal = ISC_FALSE;
+ client->tcpquota = NULL;
+ client->recursionquota = NULL;
+ client->interface = NULL;
+ client->peeraddr_valid = ISC_FALSE;
+ ISC_EVENT_INIT(&client->ctlevent, sizeof(client->ctlevent), 0, NULL,
+ NS_EVENT_CLIENTCONTROL, client_start, client, client,
+ NULL, NULL);
+ /*
+ * Initialize FORMERR cache to sentinel value that will not match
+ * any actual FORMERR response.
+ */
+ isc_sockaddr_any(&client->formerrcache.addr);
+ client->formerrcache.time = 0;
+ client->formerrcache.id = 0;
+ ISC_LINK_INIT(client, link);
+ client->list = NULL;
+
+ /*
+ * We call the init routines for the various kinds of client here,
+ * after we have created an otherwise valid client, because some
+ * of them call routines that REQUIRE(NS_CLIENT_VALID(client)).
+ */
+ result = ns_query_init(client);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_recvevent;
+
+ result = isc_task_onshutdown(client->task, client_shutdown, client);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_query;
+
+ client->needshutdown = ns_g_clienttest;
+
+ CTRACE("create");
+
+ *clientp = client;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_query:
+ ns_query_free(client);
+
+ cleanup_recvevent:
+ isc_event_free((isc_event_t **)&client->recvevent);
+
+ cleanup_recvbuf:
+ isc_mem_put(client->mctx, client->recvbuf, RECV_BUFFER_SIZE);
+
+ cleanup_sendevent:
+ isc_event_free((isc_event_t **)&client->sendevent);
+
+ client->magic = 0;
+
+ cleanup_message:
+ dns_message_destroy(&client->message);
+
+ cleanup_timer:
+ isc_timer_detach(&client->timer);
+
+ cleanup_task:
+ isc_task_detach(&client->task);
+
+ cleanup_client:
+ isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
+
+ return (result);
+}
+
+static void
+client_read(ns_client_t *client) {
+ isc_result_t result;
+
+ CTRACE("read");
+
+ result = dns_tcpmsg_readmessage(&client->tcpmsg, client->task,
+ client_request, client);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ /*
+ * Set a timeout to limit the amount of time we will wait
+ * for a request on this TCP connection.
+ */
+ ns_client_settimeout(client, 30);
+
+ client->state = client->newstate = NS_CLIENTSTATE_READING;
+ INSIST(client->nreads == 0);
+ INSIST(client->recursionquota == NULL);
+ client->nreads++;
+
+ return;
+ fail:
+ ns_client_next(client, result);
+}
+
+static void
+client_newconn(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client = event->ev_arg;
+ isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
+ isc_result_t result;
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_NEWCONN);
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(client->task == task);
+
+ UNUSED(task);
+
+ INSIST(client->state == NS_CLIENTSTATE_READY);
+
+ INSIST(client->naccepts == 1);
+ client->naccepts--;
+
+ LOCK(&client->interface->lock);
+ INSIST(client->interface->ntcpcurrent > 0);
+ client->interface->ntcpcurrent--;
+ UNLOCK(&client->interface->lock);
+
+ /*
+ * We must take ownership of the new socket before the exit
+ * check to make sure it gets destroyed if we decide to exit.
+ */
+ if (nevent->result == ISC_R_SUCCESS) {
+ client->tcpsocket = nevent->newsocket;
+ isc_socket_setname(client->tcpsocket, "client-tcp", NULL);
+ client->state = NS_CLIENTSTATE_READING;
+ INSIST(client->recursionquota == NULL);
+
+ (void)isc_socket_getpeername(client->tcpsocket,
+ &client->peeraddr);
+ client->peeraddr_valid = ISC_TRUE;
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "new TCP connection");
+ } else {
+ /*
+ * XXXRTH What should we do? We're trying to accept but
+ * it didn't work. If we just give up, then TCP
+ * service may eventually stop.
+ *
+ * For now, we just go idle.
+ *
+ * Going idle is probably the right thing if the
+ * I/O was canceled.
+ */
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "accept failed: %s",
+ isc_result_totext(nevent->result));
+ }
+
+ if (exit_check(client))
+ goto freeevent;
+
+ if (nevent->result == ISC_R_SUCCESS) {
+ int match;
+ isc_netaddr_t netaddr;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+ if (ns_g_server->blackholeacl != NULL &&
+ dns_acl_match(&netaddr, NULL,
+ ns_g_server->blackholeacl,
+ &ns_g_server->aclenv,
+ &match, NULL) == ISC_R_SUCCESS &&
+ match > 0)
+ {
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "blackholed connection attempt");
+ client->newstate = NS_CLIENTSTATE_READY;
+ (void)exit_check(client);
+ goto freeevent;
+ }
+
+ INSIST(client->tcpmsg_valid == ISC_FALSE);
+ dns_tcpmsg_init(client->mctx, client->tcpsocket,
+ &client->tcpmsg);
+ client->tcpmsg_valid = ISC_TRUE;
+
+ /*
+ * Let a new client take our place immediately, before
+ * we wait for a request packet. If we don't,
+ * telnetting to port 53 (once per CPU) will
+ * deny service to legititmate TCP clients.
+ */
+ result = isc_quota_attach(&ns_g_server->tcpquota,
+ &client->tcpquota);
+ if (result == ISC_R_SUCCESS)
+ result = ns_client_replace(client);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_WARNING,
+ "no more TCP clients: %s",
+ isc_result_totext(result));
+ }
+
+ client_read(client);
+ }
+
+ freeevent:
+ isc_event_free(&event);
+}
+
+static void
+client_accept(ns_client_t *client) {
+ isc_result_t result;
+
+ CTRACE("accept");
+
+ result = isc_socket_accept(client->tcplistener, client->task,
+ client_newconn, client);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socket_accept() failed: %s",
+ isc_result_totext(result));
+ /*
+ * XXXRTH What should we do? We're trying to accept but
+ * it didn't work. If we just give up, then TCP
+ * service may eventually stop.
+ *
+ * For now, we just go idle.
+ */
+ return;
+ }
+ INSIST(client->naccepts == 0);
+ client->naccepts++;
+ LOCK(&client->interface->lock);
+ client->interface->ntcpcurrent++;
+ UNLOCK(&client->interface->lock);
+}
+
+static void
+client_udprecv(ns_client_t *client) {
+ isc_result_t result;
+ isc_region_t r;
+
+ CTRACE("udprecv");
+
+ r.base = client->recvbuf;
+ r.length = RECV_BUFFER_SIZE;
+ result = isc_socket_recv2(client->udpsocket, &r, 1,
+ client->task, client->recvevent, 0);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socket_recv2() failed: %s",
+ isc_result_totext(result));
+ /*
+ * This cannot happen in the current implementation, since
+ * isc_socket_recv2() cannot fail if flags == 0.
+ *
+ * If this does fail, we just go idle.
+ */
+ return;
+ }
+ INSIST(client->nrecvs == 0);
+ client->nrecvs++;
+}
+
+void
+ns_client_attach(ns_client_t *source, ns_client_t **targetp) {
+ REQUIRE(NS_CLIENT_VALID(source));
+ REQUIRE(targetp != NULL && *targetp == NULL);
+
+ source->references++;
+ ns_client_log(source, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "ns_client_attach: ref = %d", source->references);
+ *targetp = source;
+}
+
+void
+ns_client_detach(ns_client_t **clientp) {
+ ns_client_t *client = *clientp;
+
+ client->references--;
+ INSIST(client->references >= 0);
+ *clientp = NULL;
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(10),
+ "ns_client_detach: ref = %d", client->references);
+ (void)exit_check(client);
+}
+
+isc_boolean_t
+ns_client_shuttingdown(ns_client_t *client) {
+ return (ISC_TF(client->newstate == NS_CLIENTSTATE_FREED));
+}
+
+isc_result_t
+ns_client_replace(ns_client_t *client) {
+ isc_result_t result;
+
+ CTRACE("replace");
+
+ result = ns_clientmgr_createclients(client->manager,
+ 1, client->interface,
+ (TCP_CLIENT(client) ?
+ ISC_TRUE : ISC_FALSE));
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * The responsibility for listening for new requests is hereby
+ * transferred to the new client. Therefore, the old client
+ * should refrain from listening for any more requests.
+ */
+ client->mortal = ISC_TRUE;
+
+ return (ISC_R_SUCCESS);
+}
+
+/***
+ *** Client Manager
+ ***/
+
+static void
+clientmgr_destroy(ns_clientmgr_t *manager) {
+#if NMCTXS > 0
+ int i;
+#endif
+
+ REQUIRE(ISC_LIST_EMPTY(manager->active));
+ REQUIRE(ISC_LIST_EMPTY(manager->inactive));
+ REQUIRE(ISC_LIST_EMPTY(manager->recursing));
+
+ MTRACE("clientmgr_destroy");
+
+#if NMCTXS > 0
+ for (i = 0; i < NMCTXS; i++) {
+ if (manager->mctxpool[i] != NULL)
+ isc_mem_detach(&manager->mctxpool[i]);
+ }
+#endif
+
+ DESTROYLOCK(&manager->lock);
+ manager->magic = 0;
+ isc_mem_put(manager->mctx, manager, sizeof(*manager));
+}
+
+isc_result_t
+ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, ns_clientmgr_t **managerp)
+{
+ ns_clientmgr_t *manager;
+ isc_result_t result;
+#if NMCTXS > 0
+ int i;
+#endif
+
+ manager = isc_mem_get(mctx, sizeof(*manager));
+ if (manager == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&manager->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_manager;
+
+ manager->mctx = mctx;
+ manager->taskmgr = taskmgr;
+ manager->timermgr = timermgr;
+ manager->exiting = ISC_FALSE;
+ ISC_LIST_INIT(manager->active);
+ ISC_LIST_INIT(manager->inactive);
+ ISC_LIST_INIT(manager->recursing);
+#if NMCTXS > 0
+ manager->nextmctx = 0;
+ for (i = 0; i < NMCTXS; i++)
+ manager->mctxpool[i] = NULL; /* will be created on-demand */
+#endif
+ manager->magic = MANAGER_MAGIC;
+
+ MTRACE("create");
+
+ *managerp = manager;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup_manager:
+ isc_mem_put(manager->mctx, manager, sizeof(*manager));
+
+ return (result);
+}
+
+void
+ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
+ ns_clientmgr_t *manager;
+ ns_client_t *client;
+ isc_boolean_t need_destroy = ISC_FALSE;
+
+ REQUIRE(managerp != NULL);
+ manager = *managerp;
+ REQUIRE(VALID_MANAGER(manager));
+
+ MTRACE("destroy");
+
+ LOCK(&manager->lock);
+
+ manager->exiting = ISC_TRUE;
+
+ for (client = ISC_LIST_HEAD(manager->recursing);
+ client != NULL;
+ client = ISC_LIST_NEXT(client, link))
+ isc_task_shutdown(client->task);
+
+ for (client = ISC_LIST_HEAD(manager->active);
+ client != NULL;
+ client = ISC_LIST_NEXT(client, link))
+ isc_task_shutdown(client->task);
+
+ for (client = ISC_LIST_HEAD(manager->inactive);
+ client != NULL;
+ client = ISC_LIST_NEXT(client, link))
+ isc_task_shutdown(client->task);
+
+ if (ISC_LIST_EMPTY(manager->active) &&
+ ISC_LIST_EMPTY(manager->inactive) &&
+ ISC_LIST_EMPTY(manager->recursing))
+ need_destroy = ISC_TRUE;
+
+ UNLOCK(&manager->lock);
+
+ if (need_destroy)
+ clientmgr_destroy(manager);
+
+ *managerp = NULL;
+}
+
+isc_result_t
+ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
+ ns_interface_t *ifp, isc_boolean_t tcp)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned int i;
+ ns_client_t *client;
+
+ REQUIRE(VALID_MANAGER(manager));
+ REQUIRE(n > 0);
+
+ MTRACE("createclients");
+
+ /*
+ * We MUST lock the manager lock for the entire client creation
+ * process. If we didn't do this, then a client could get a
+ * shutdown event and disappear out from under us.
+ */
+
+ LOCK(&manager->lock);
+
+ for (i = 0; i < n; i++) {
+ isc_event_t *ev;
+ /*
+ * Allocate a client. First try to get a recycled one;
+ * if that fails, make a new one.
+ */
+ client = NULL;
+ if (!ns_g_clienttest)
+ client = ISC_LIST_HEAD(manager->inactive);
+ if (client != NULL) {
+ MTRACE("recycle");
+ ISC_LIST_UNLINK(manager->inactive, client, link);
+ client->list = NULL;
+ } else {
+ MTRACE("create new");
+ result = client_create(manager, &client);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+
+ ns_interface_attach(ifp, &client->interface);
+ client->state = NS_CLIENTSTATE_READY;
+ INSIST(client->recursionquota == NULL);
+
+ if (tcp) {
+ client->attributes |= NS_CLIENTATTR_TCP;
+ isc_socket_attach(ifp->tcpsocket,
+ &client->tcplistener);
+ } else {
+ isc_socket_t *sock;
+
+ dns_dispatch_attach(ifp->udpdispatch,
+ &client->dispatch);
+ sock = dns_dispatch_getsocket(client->dispatch);
+ isc_socket_attach(sock, &client->udpsocket);
+ }
+ client->manager = manager;
+ ISC_LIST_APPEND(manager->active, client, link);
+ client->list = &manager->active;
+
+ INSIST(client->nctls == 0);
+ client->nctls++;
+ ev = &client->ctlevent;
+ isc_task_send(client->task, &ev);
+ }
+ if (i != 0) {
+ /*
+ * We managed to create at least one client, so we
+ * declare victory.
+ */
+ result = ISC_R_SUCCESS;
+ }
+
+ UNLOCK(&manager->lock);
+
+ return (result);
+}
+
+isc_sockaddr_t *
+ns_client_getsockaddr(ns_client_t *client) {
+ return (&client->peeraddr);
+}
+
+isc_result_t
+ns_client_checkaclsilent(ns_client_t *client, isc_sockaddr_t *sockaddr,
+ dns_acl_t *acl, isc_boolean_t default_allow)
+{
+ isc_result_t result;
+ int match;
+ isc_netaddr_t netaddr;
+
+ if (acl == NULL) {
+ if (default_allow)
+ goto allow;
+ else
+ goto deny;
+ }
+
+
+ if (sockaddr == NULL)
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ else
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+
+ result = dns_acl_match(&netaddr, client->signer, acl,
+ &ns_g_server->aclenv,
+ &match, NULL);
+
+ if (result != ISC_R_SUCCESS)
+ goto deny; /* Internal error, already logged. */
+ if (match > 0)
+ goto allow;
+ goto deny; /* Negative match or no match. */
+
+ allow:
+ return (ISC_R_SUCCESS);
+
+ deny:
+ return (DNS_R_REFUSED);
+}
+
+isc_result_t
+ns_client_checkacl(ns_client_t *client, isc_sockaddr_t *sockaddr,
+ const char *opname, dns_acl_t *acl,
+ isc_boolean_t default_allow, int log_level)
+{
+ isc_result_t result =
+ ns_client_checkaclsilent(client, sockaddr, acl, default_allow);
+
+ if (result == ISC_R_SUCCESS)
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "%s approved", opname);
+ else
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT,
+ log_level, "%s denied", opname);
+ return (result);
+}
+
+static void
+ns_client_name(ns_client_t *client, char *peerbuf, size_t len) {
+ if (client->peeraddr_valid)
+ isc_sockaddr_format(&client->peeraddr, peerbuf, len);
+ else
+ snprintf(peerbuf, len, "@%p", client);
+}
+
+void
+ns_client_logv(ns_client_t *client, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *fmt, va_list ap)
+{
+ char msgbuf[2048];
+ char peerbuf[ISC_SOCKADDR_FORMATSIZE];
+ const char *name = "";
+ const char *sep = "";
+
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ ns_client_name(client, peerbuf, sizeof(peerbuf));
+ if (client->view != NULL && strcmp(client->view->name, "_bind") != 0 &&
+ strcmp(client->view->name, "_default") != 0) {
+ name = client->view->name;
+ sep = ": view ";
+ }
+
+ isc_log_write(ns_g_lctx, category, module, level,
+ "client %s%s%s: %s", peerbuf, sep, name, msgbuf);
+}
+
+void
+ns_client_log(ns_client_t *client, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (! isc_log_wouldlog(ns_g_lctx, level))
+ return;
+
+ va_start(ap, fmt);
+ ns_client_logv(client, category, module, level, fmt, ap);
+ va_end(ap);
+}
+
+void
+ns_client_aclmsg(const char *msg, dns_name_t *name, dns_rdatatype_t type,
+ dns_rdataclass_t rdclass, char *buf, size_t len)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(type, typebuf, sizeof(typebuf));
+ dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
+ (void)snprintf(buf, len, "%s '%s/%s/%s'", msg, namebuf, typebuf,
+ classbuf);
+}
+
+static void
+ns_client_dumpmessage(ns_client_t *client, const char *reason) {
+ isc_buffer_t buffer;
+ char *buf = NULL;
+ int len = 1024;
+ isc_result_t result;
+
+ /*
+ * Note that these are multiline debug messages. We want a newline
+ * to appear in the log after each message.
+ */
+
+ do {
+ buf = isc_mem_get(client->mctx, len);
+ if (buf == NULL)
+ break;
+ isc_buffer_init(&buffer, buf, len);
+ result = dns_message_totext(client->message,
+ &dns_master_style_debug,
+ 0, &buffer);
+ if (result == ISC_R_NOSPACE) {
+ isc_mem_put(client->mctx, buf, len);
+ len += 1024;
+ } else if (result == ISC_R_SUCCESS)
+ ns_client_log(client, NS_LOGCATEGORY_UNMATCHED,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+ "%s\n%.*s", reason,
+ (int)isc_buffer_usedlength(&buffer),
+ buf);
+ } while (result == ISC_R_NOSPACE);
+
+ if (buf != NULL)
+ isc_mem_put(client->mctx, buf, len);
+}
+
+void
+ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager) {
+ ns_client_t *client;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char peerbuf[ISC_SOCKADDR_FORMATSIZE];
+ const char *name;
+ const char *sep;
+
+ REQUIRE(VALID_MANAGER(manager));
+
+ LOCK(&manager->lock);
+ client = ISC_LIST_HEAD(manager->recursing);
+ while (client != NULL) {
+ ns_client_name(client, peerbuf, sizeof(peerbuf));
+ if (client->view != NULL &&
+ strcmp(client->view->name, "_bind") != 0 &&
+ strcmp(client->view->name, "_default") != 0) {
+ name = client->view->name;
+ sep = ": view ";
+ } else {
+ name = "";
+ sep = "";
+ }
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+ fprintf(f, "; client %s%s%s: '%s' requesttime %d\n",
+ peerbuf, sep, name, namebuf, client->requesttime);
+ client = ISC_LIST_NEXT(client, link);
+ }
+ UNLOCK(&manager->lock);
+}
+
+void
+ns_client_qnamereplace(ns_client_t *client, dns_name_t *name) {
+
+ if (client->manager != NULL)
+ LOCK(&client->manager->lock);
+ if (client->query.restarts > 0) {
+ /*
+ * client->query.qname was dynamically allocated.
+ */
+ dns_message_puttempname(client->message,
+ &client->query.qname);
+ }
+ client->query.qname = name;
+ if (client->manager != NULL)
+ UNLOCK(&client->manager->lock);
+}
diff --git a/bin/named/config.c b/bin/named/config.c
new file mode 100644
index 0000000..96b071d
--- /dev/null
+++ b/bin/named/config.c
@@ -0,0 +1,807 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: config.c,v 1.93 2008/11/06 05:30:24 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/log.h>
+#include <isc/mem.h>
+#include <isc/parseint.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/sockaddr.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <isccfg/namedconf.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/tsig.h>
+#include <dns/zone.h>
+
+#include <named/config.h>
+#include <named/globals.h>
+
+/*% default configuration */
+static char defaultconf[] = "\
+options {\n\
+# blackhole {none;};\n"
+#ifndef WIN32
+" coresize default;\n\
+ datasize default;\n\
+ files unlimited;\n\
+ stacksize default;\n"
+#endif
+" deallocate-on-exit true;\n\
+# directory <none>\n\
+ dump-file \"named_dump.db\";\n\
+ fake-iquery no;\n\
+ has-old-clients false;\n\
+ heartbeat-interval 60;\n\
+ host-statistics no;\n\
+ interface-interval 60;\n\
+ listen-on {any;};\n\
+ listen-on-v6 {none;};\n\
+ match-mapped-addresses no;\n\
+ memstatistics-file \"named.memstats\";\n\
+ multiple-cnames no;\n\
+# named-xfer <obsolete>;\n\
+# pid-file \"" NS_LOCALSTATEDIR "/run/named/named.pid\"; /* or /lwresd.pid */\n\
+ port 53;\n\
+ recursing-file \"named.recursing\";\n\
+"
+#ifdef PATH_RANDOMDEV
+"\
+ random-device \"" PATH_RANDOMDEV "\";\n\
+"
+#endif
+"\
+ recursive-clients 1000;\n\
+ rrset-order {type NS order random; order cyclic; };\n\
+ serial-queries 20;\n\
+ serial-query-rate 20;\n\
+ server-id none;\n\
+ statistics-file \"named.stats\";\n\
+ statistics-interval 60;\n\
+ tcp-clients 100;\n\
+ tcp-listen-queue 3;\n\
+# tkey-dhkey <none>\n\
+# tkey-gssapi-credential <none>\n\
+# tkey-domain <none>\n\
+ transfers-per-ns 2;\n\
+ transfers-in 10;\n\
+ transfers-out 10;\n\
+ treat-cr-as-space true;\n\
+ use-id-pool true;\n\
+ use-ixfr true;\n\
+ edns-udp-size 4096;\n\
+ max-udp-size 4096;\n\
+ request-nsid false;\n\
+ reserved-sockets 512;\n\
+\n\
+ /* view */\n\
+ allow-notify {none;};\n\
+ allow-update-forwarding {none;};\n\
+ allow-query-cache { localnets; localhost; };\n\
+ allow-query-cache-on { any; };\n\
+ allow-recursion { localnets; localhost; };\n\
+ allow-recursion-on { any; };\n\
+# allow-v6-synthesis <obsolete>;\n\
+# sortlist <none>\n\
+# topology <none>\n\
+ auth-nxdomain false;\n\
+ minimal-responses false;\n\
+ recursion true;\n\
+ provide-ixfr true;\n\
+ request-ixfr true;\n\
+ fetch-glue no;\n\
+ rfc2308-type1 no;\n\
+ additional-from-auth true;\n\
+ additional-from-cache true;\n\
+ query-source address *;\n\
+ query-source-v6 address *;\n\
+ notify-source *;\n\
+ notify-source-v6 *;\n\
+ cleaning-interval 0; /* now meaningless */\n\
+ min-roots 2;\n\
+ lame-ttl 600;\n\
+ max-ncache-ttl 10800; /* 3 hours */\n\
+ max-cache-ttl 604800; /* 1 week */\n\
+ transfer-format many-answers;\n\
+ max-cache-size 0;\n\
+ check-names master fail;\n\
+ check-names slave warn;\n\
+ check-names response ignore;\n\
+ check-mx warn;\n\
+ acache-enable no;\n\
+ acache-cleaning-interval 60;\n\
+ max-acache-size 16M;\n\
+ dnssec-enable yes;\n\
+ dnssec-validation yes; \n\
+ dnssec-accept-expired no;\n\
+ clients-per-query 10;\n\
+ max-clients-per-query 100;\n\
+ zero-no-soa-ttl-cache no;\n\
+ nsec3-test-zone no;\n\
+"
+
+" /* zone */\n\
+ allow-query {any;};\n\
+ allow-query-on {any;};\n\
+ allow-transfer {any;};\n\
+ notify yes;\n\
+# also-notify <none>\n\
+ notify-delay 5;\n\
+ notify-to-soa no;\n\
+ dialup no;\n\
+# forward <none>\n\
+# forwarders <none>\n\
+ maintain-ixfr-base no;\n\
+# max-ixfr-log-size <obsolete>\n\
+ transfer-source *;\n\
+ transfer-source-v6 *;\n\
+ alt-transfer-source *;\n\
+ alt-transfer-source-v6 *;\n\
+ max-transfer-time-in 120;\n\
+ max-transfer-time-out 120;\n\
+ max-transfer-idle-in 60;\n\
+ max-transfer-idle-out 60;\n\
+ max-retry-time 1209600; /* 2 weeks */\n\
+ min-retry-time 500;\n\
+ max-refresh-time 2419200; /* 4 weeks */\n\
+ min-refresh-time 300;\n\
+ multi-master no;\n\
+ sig-validity-interval 30; /* days */\n\
+ sig-signing-nodes 100;\n\
+ sig-signing-signatures 10;\n\
+ sig-signing-type 65535;\n\
+ zone-statistics false;\n\
+ max-journal-size unlimited;\n\
+ ixfr-from-differences false;\n\
+ check-wildcard yes;\n\
+ check-sibling yes;\n\
+ check-integrity yes;\n\
+ check-mx-cname warn;\n\
+ check-srv-cname warn;\n\
+ zero-no-soa-ttl yes;\n\
+ update-check-ksk yes;\n\
+ try-tcp-refresh yes; /* BIND 8 compat */\n\
+};\n\
+"
+
+"#\n\
+# Zones in the \"_bind\" view are NOT counted in the count of zones.\n\
+#\n\
+view \"_bind\" chaos {\n\
+ recursion no;\n\
+ notify no;\n\
+\n\
+ zone \"version.bind\" chaos {\n\
+ type master;\n\
+ database \"_builtin version\";\n\
+ };\n\
+\n\
+ zone \"hostname.bind\" chaos {\n\
+ type master;\n\
+ database \"_builtin hostname\";\n\
+ };\n\
+\n\
+ zone \"authors.bind\" chaos {\n\
+ type master;\n\
+ database \"_builtin authors\";\n\
+ };\n\
+ zone \"id.server\" chaos {\n\
+ type master;\n\
+ database \"_builtin id\";\n\
+ };\n\
+};\n\
+";
+
+isc_result_t
+ns_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf) {
+ isc_buffer_t b;
+
+ isc_buffer_init(&b, defaultconf, sizeof(defaultconf) - 1);
+ isc_buffer_add(&b, sizeof(defaultconf) - 1);
+ return (cfg_parse_buffer(parser, &b, &cfg_type_namedconf, conf));
+}
+
+isc_result_t
+ns_config_get(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) {
+ int i;
+
+ for (i = 0;; i++) {
+ if (maps[i] == NULL)
+ return (ISC_R_NOTFOUND);
+ if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+ }
+}
+
+isc_result_t
+ns_checknames_get(const cfg_obj_t **maps, const char *which,
+ const cfg_obj_t **obj)
+{
+ const cfg_listelt_t *element;
+ const cfg_obj_t *checknames;
+ const cfg_obj_t *type;
+ const cfg_obj_t *value;
+ int i;
+
+ for (i = 0;; i++) {
+ if (maps[i] == NULL)
+ return (ISC_R_NOTFOUND);
+ checknames = NULL;
+ if (cfg_map_get(maps[i], "check-names", &checknames) == ISC_R_SUCCESS) {
+ /*
+ * Zone map entry is not a list.
+ */
+ if (checknames != NULL && !cfg_obj_islist(checknames)) {
+ *obj = checknames;
+ return (ISC_R_SUCCESS);
+ }
+ for (element = cfg_list_first(checknames);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ value = cfg_listelt_value(element);
+ type = cfg_tuple_get(value, "type");
+ if (strcasecmp(cfg_obj_asstring(type), which) == 0) {
+ *obj = cfg_tuple_get(value, "mode");
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ }
+ }
+}
+
+int
+ns_config_listcount(const cfg_obj_t *list) {
+ const cfg_listelt_t *e;
+ int i = 0;
+
+ for (e = cfg_list_first(list); e != NULL; e = cfg_list_next(e))
+ i++;
+
+ return (i);
+}
+
+isc_result_t
+ns_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass,
+ dns_rdataclass_t *classp) {
+ isc_textregion_t r;
+ isc_result_t result;
+
+ if (!cfg_obj_isstring(classobj)) {
+ *classp = defclass;
+ return (ISC_R_SUCCESS);
+ }
+ DE_CONST(cfg_obj_asstring(classobj), r.base);
+ r.length = strlen(r.base);
+ result = dns_rdataclass_fromtext(classp, &r);
+ if (result != ISC_R_SUCCESS)
+ cfg_obj_log(classobj, ns_g_lctx, ISC_LOG_ERROR,
+ "unknown class '%s'", r.base);
+ return (result);
+}
+
+isc_result_t
+ns_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype,
+ dns_rdatatype_t *typep) {
+ isc_textregion_t r;
+ isc_result_t result;
+
+ if (!cfg_obj_isstring(typeobj)) {
+ *typep = deftype;
+ return (ISC_R_SUCCESS);
+ }
+ DE_CONST(cfg_obj_asstring(typeobj), r.base);
+ r.length = strlen(r.base);
+ result = dns_rdatatype_fromtext(typep, &r);
+ if (result != ISC_R_SUCCESS)
+ cfg_obj_log(typeobj, ns_g_lctx, ISC_LOG_ERROR,
+ "unknown type '%s'", r.base);
+ return (result);
+}
+
+dns_zonetype_t
+ns_config_getzonetype(const cfg_obj_t *zonetypeobj) {
+ dns_zonetype_t ztype = dns_zone_none;
+ const char *str;
+
+ str = cfg_obj_asstring(zonetypeobj);
+ if (strcasecmp(str, "master") == 0)
+ ztype = dns_zone_master;
+ else if (strcasecmp(str, "slave") == 0)
+ ztype = dns_zone_slave;
+ else if (strcasecmp(str, "stub") == 0)
+ ztype = dns_zone_stub;
+ else
+ INSIST(0);
+ return (ztype);
+}
+
+isc_result_t
+ns_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list,
+ in_port_t defport, isc_mem_t *mctx,
+ isc_sockaddr_t **addrsp, isc_uint32_t *countp)
+{
+ int count, i = 0;
+ const cfg_obj_t *addrlist;
+ const cfg_obj_t *portobj;
+ const cfg_listelt_t *element;
+ isc_sockaddr_t *addrs;
+ in_port_t port;
+ isc_result_t result;
+
+ INSIST(addrsp != NULL && *addrsp == NULL);
+ INSIST(countp != NULL);
+
+ addrlist = cfg_tuple_get(list, "addresses");
+ count = ns_config_listcount(addrlist);
+
+ portobj = cfg_tuple_get(list, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t) val;
+ } else if (defport != 0)
+ port = defport;
+ else {
+ result = ns_config_getport(config, &port);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ addrs = isc_mem_get(mctx, count * sizeof(isc_sockaddr_t));
+ if (addrs == NULL)
+ return (ISC_R_NOMEMORY);
+
+ for (element = cfg_list_first(addrlist);
+ element != NULL;
+ element = cfg_list_next(element), i++)
+ {
+ INSIST(i < count);
+ addrs[i] = *cfg_obj_assockaddr(cfg_listelt_value(element));
+ if (isc_sockaddr_getport(&addrs[i]) == 0)
+ isc_sockaddr_setport(&addrs[i], port);
+ }
+ INSIST(i == count);
+
+ *addrsp = addrs;
+ *countp = count;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ isc_uint32_t count)
+{
+ INSIST(addrsp != NULL && *addrsp != NULL);
+
+ isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t));
+ *addrsp = NULL;
+}
+
+static isc_result_t
+get_masters_def(const cfg_obj_t *cctx, const char *name,
+ const cfg_obj_t **ret)
+{
+ isc_result_t result;
+ const cfg_obj_t *masters = NULL;
+ const cfg_listelt_t *elt;
+
+ result = cfg_map_get(cctx, "masters", &masters);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ for (elt = cfg_list_first(masters);
+ elt != NULL;
+ elt = cfg_list_next(elt)) {
+ const cfg_obj_t *list;
+ const char *listname;
+
+ list = cfg_listelt_value(elt);
+ listname = cfg_obj_asstring(cfg_tuple_get(list, "name"));
+
+ if (strcasecmp(listname, name) == 0) {
+ *ret = list;
+ return (ISC_R_SUCCESS);
+ }
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+isc_result_t
+ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
+ isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ dns_name_t ***keysp, isc_uint32_t *countp)
+{
+ isc_uint32_t addrcount = 0, keycount = 0, i = 0;
+ isc_uint32_t listcount = 0, l = 0, j;
+ isc_uint32_t stackcount = 0, pushed = 0;
+ isc_result_t result;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *addrlist;
+ const cfg_obj_t *portobj;
+ in_port_t port;
+ dns_fixedname_t fname;
+ isc_sockaddr_t *addrs = NULL;
+ dns_name_t **keys = NULL;
+ struct { const char *name; } *lists = NULL;
+ struct {
+ const cfg_listelt_t *element;
+ in_port_t port;
+ } *stack = NULL;
+
+ REQUIRE(addrsp != NULL && *addrsp == NULL);
+ REQUIRE(keysp != NULL && *keysp == NULL);
+ REQUIRE(countp != NULL);
+
+ newlist:
+ addrlist = cfg_tuple_get(list, "addresses");
+ portobj = cfg_tuple_get(list, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ port = (in_port_t) val;
+ } else {
+ result = ns_config_getport(config, &port);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ result = ISC_R_NOMEMORY;
+
+ element = cfg_list_first(addrlist);
+ resume:
+ for ( ;
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *addr;
+ const cfg_obj_t *key;
+ const char *keystr;
+ isc_buffer_t b;
+
+ addr = cfg_tuple_get(cfg_listelt_value(element),
+ "masterselement");
+ key = cfg_tuple_get(cfg_listelt_value(element), "key");
+
+ if (!cfg_obj_issockaddr(addr)) {
+ const char *listname = cfg_obj_asstring(addr);
+ isc_result_t tresult;
+
+ /* Grow lists? */
+ if (listcount == l) {
+ void * new;
+ isc_uint32_t newlen = listcount + 16;
+ size_t newsize, oldsize;
+
+ newsize = newlen * sizeof(*lists);
+ oldsize = listcount * sizeof(*lists);
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ if (listcount != 0) {
+ memcpy(new, lists, oldsize);
+ isc_mem_put(mctx, lists, oldsize);
+ }
+ lists = new;
+ listcount = newlen;
+ }
+ /* Seen? */
+ for (j = 0; j < l; j++)
+ if (strcasecmp(lists[j].name, listname) == 0)
+ break;
+ if (j < l)
+ continue;
+ tresult = get_masters_def(config, listname, &list);
+ if (tresult == ISC_R_NOTFOUND) {
+ cfg_obj_log(addr, ns_g_lctx, ISC_LOG_ERROR,
+ "masters \"%s\" not found", listname);
+
+ result = tresult;
+ goto cleanup;
+ }
+ if (tresult != ISC_R_SUCCESS)
+ goto cleanup;
+ lists[l++].name = listname;
+ /* Grow stack? */
+ if (stackcount == pushed) {
+ void * new;
+ isc_uint32_t newlen = stackcount + 16;
+ size_t newsize, oldsize;
+
+ newsize = newlen * sizeof(*stack);
+ oldsize = stackcount * sizeof(*stack);
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ if (stackcount != 0) {
+ memcpy(new, stack, oldsize);
+ isc_mem_put(mctx, stack, oldsize);
+ }
+ stack = new;
+ stackcount = newlen;
+ }
+ /*
+ * We want to resume processing this list on the
+ * next element.
+ */
+ stack[pushed].element = cfg_list_next(element);
+ stack[pushed].port = port;
+ pushed++;
+ goto newlist;
+ }
+
+ if (i == addrcount) {
+ void * new;
+ isc_uint32_t newlen = addrcount + 16;
+ size_t newsize, oldsize;
+
+ newsize = newlen * sizeof(isc_sockaddr_t);
+ oldsize = addrcount * sizeof(isc_sockaddr_t);
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ if (addrcount != 0) {
+ memcpy(new, addrs, oldsize);
+ isc_mem_put(mctx, addrs, oldsize);
+ }
+ addrs = new;
+ addrcount = newlen;
+
+ newsize = newlen * sizeof(dns_name_t *);
+ oldsize = keycount * sizeof(dns_name_t *);
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ if (keycount != 0) {
+ memcpy(new, keys, oldsize);
+ isc_mem_put(mctx, keys, oldsize);
+ }
+ keys = new;
+ keycount = newlen;
+ }
+
+ addrs[i] = *cfg_obj_assockaddr(addr);
+ if (isc_sockaddr_getport(&addrs[i]) == 0)
+ isc_sockaddr_setport(&addrs[i], port);
+ keys[i] = NULL;
+ if (!cfg_obj_isstring(key)) {
+ i++;
+ continue;
+ }
+ keys[i] = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (keys[i] == NULL)
+ goto cleanup;
+ dns_name_init(keys[i], NULL);
+
+ keystr = cfg_obj_asstring(key);
+ isc_buffer_init(&b, keystr, strlen(keystr));
+ isc_buffer_add(&b, strlen(keystr));
+ dns_fixedname_init(&fname);
+ result = dns_name_fromtext(dns_fixedname_name(&fname), &b,
+ dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_name_dup(dns_fixedname_name(&fname), mctx,
+ keys[i]);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ i++;
+ }
+ if (pushed != 0) {
+ pushed--;
+ element = stack[pushed].element;
+ port = stack[pushed].port;
+ goto resume;
+ }
+ if (i < addrcount) {
+ void * new;
+ size_t newsize, oldsize;
+
+ newsize = i * sizeof(isc_sockaddr_t);
+ oldsize = addrcount * sizeof(isc_sockaddr_t);
+ if (i != 0) {
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ memcpy(new, addrs, newsize);
+ } else
+ new = NULL;
+ isc_mem_put(mctx, addrs, oldsize);
+ addrs = new;
+ addrcount = i;
+
+ newsize = i * sizeof(dns_name_t *);
+ oldsize = keycount * sizeof(dns_name_t *);
+ if (i != 0) {
+ new = isc_mem_get(mctx, newsize);
+ if (new == NULL)
+ goto cleanup;
+ memcpy(new, keys, newsize);
+ } else
+ new = NULL;
+ isc_mem_put(mctx, keys, oldsize);
+ keys = new;
+ keycount = i;
+ }
+
+ if (lists != NULL)
+ isc_mem_put(mctx, lists, listcount * sizeof(*lists));
+ if (stack != NULL)
+ isc_mem_put(mctx, stack, stackcount * sizeof(*stack));
+
+ INSIST(keycount == addrcount);
+
+ *addrsp = addrs;
+ *keysp = keys;
+ *countp = addrcount;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (addrs != NULL)
+ isc_mem_put(mctx, addrs, addrcount * sizeof(isc_sockaddr_t));
+ if (keys != NULL) {
+ for (j = 0; j <= i; j++) {
+ if (keys[j] == NULL)
+ continue;
+ if (dns_name_dynamic(keys[j]))
+ dns_name_free(keys[j], mctx);
+ isc_mem_put(mctx, keys[j], sizeof(dns_name_t));
+ }
+ isc_mem_put(mctx, keys, keycount * sizeof(dns_name_t *));
+ }
+ if (lists != NULL)
+ isc_mem_put(mctx, lists, listcount * sizeof(*lists));
+ if (stack != NULL)
+ isc_mem_put(mctx, stack, stackcount * sizeof(*stack));
+ return (result);
+}
+
+void
+ns_config_putipandkeylist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ dns_name_t ***keysp, isc_uint32_t count)
+{
+ unsigned int i;
+ dns_name_t **keys = *keysp;
+
+ INSIST(addrsp != NULL && *addrsp != NULL);
+
+ isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t));
+ for (i = 0; i < count; i++) {
+ if (keys[i] == NULL)
+ continue;
+ if (dns_name_dynamic(keys[i]))
+ dns_name_free(keys[i], mctx);
+ isc_mem_put(mctx, keys[i], sizeof(dns_name_t));
+ }
+ isc_mem_put(mctx, *keysp, count * sizeof(dns_name_t *));
+ *addrsp = NULL;
+ *keysp = NULL;
+}
+
+isc_result_t
+ns_config_getport(const cfg_obj_t *config, in_port_t *portp) {
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *portobj = NULL;
+ isc_result_t result;
+ int i;
+
+ (void)cfg_map_get(config, "options", &options);
+ i = 0;
+ if (options != NULL)
+ maps[i++] = options;
+ maps[i++] = ns_g_defaults;
+ maps[i] = NULL;
+
+ result = ns_config_get(maps, "port", &portobj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range",
+ cfg_obj_asuint32(portobj));
+ return (ISC_R_RANGE);
+ }
+ *portp = (in_port_t)cfg_obj_asuint32(portobj);
+ return (ISC_R_SUCCESS);
+}
+
+struct keyalgorithms {
+ const char *str;
+ enum { hmacnone, hmacmd5, hmacsha1, hmacsha224,
+ hmacsha256, hmacsha384, hmacsha512 } hmac;
+ isc_uint16_t size;
+} algorithms[] = {
+ { "hmac-md5", hmacmd5, 128 },
+ { "hmac-md5.sig-alg.reg.int", hmacmd5, 0 },
+ { "hmac-md5.sig-alg.reg.int.", hmacmd5, 0 },
+ { "hmac-sha1", hmacsha1, 160 },
+ { "hmac-sha224", hmacsha224, 224 },
+ { "hmac-sha256", hmacsha256, 256 },
+ { "hmac-sha384", hmacsha384, 384 },
+ { "hmac-sha512", hmacsha512, 512 },
+ { NULL, hmacnone, 0 }
+};
+
+isc_result_t
+ns_config_getkeyalgorithm(const char *str, dns_name_t **name,
+ isc_uint16_t *digestbits)
+{
+ int i;
+ size_t len = 0;
+ isc_uint16_t bits;
+ isc_result_t result;
+
+ for (i = 0; algorithms[i].str != NULL; i++) {
+ len = strlen(algorithms[i].str);
+ if (strncasecmp(algorithms[i].str, str, len) == 0 &&
+ (str[len] == '\0' ||
+ (algorithms[i].size != 0 && str[len] == '-')))
+ break;
+ }
+ if (algorithms[i].str == NULL)
+ return (ISC_R_NOTFOUND);
+ if (str[len] == '-') {
+ result = isc_parse_uint16(&bits, str + len + 1, 10);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (bits > algorithms[i].size)
+ return (ISC_R_RANGE);
+ } else if (algorithms[i].size == 0)
+ bits = 128;
+ else
+ bits = algorithms[i].size;
+
+ if (name != NULL) {
+ switch (algorithms[i].hmac) {
+ case hmacmd5: *name = dns_tsig_hmacmd5_name; break;
+ case hmacsha1: *name = dns_tsig_hmacsha1_name; break;
+ case hmacsha224: *name = dns_tsig_hmacsha224_name; break;
+ case hmacsha256: *name = dns_tsig_hmacsha256_name; break;
+ case hmacsha384: *name = dns_tsig_hmacsha384_name; break;
+ case hmacsha512: *name = dns_tsig_hmacsha512_name; break;
+ default:
+ INSIST(0);
+ }
+ }
+ if (digestbits != NULL)
+ *digestbits = bits;
+ return (ISC_R_SUCCESS);
+}
diff --git a/bin/named/control.c b/bin/named/control.c
new file mode 100644
index 0000000..8bd8f6c
--- /dev/null
+++ b/bin/named/control.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: control.c,v 1.33 2007/09/13 04:45:18 each Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+
+#include <isc/app.h>
+#include <isc/event.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/result.h>
+
+#include <isccc/alist.h>
+#include <isccc/cc.h>
+#include <isccc/result.h>
+
+#include <named/control.h>
+#include <named/log.h>
+#include <named/os.h>
+#include <named/server.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#endif
+
+static isc_boolean_t
+command_compare(const char *text, const char *command) {
+ unsigned int commandlen = strlen(command);
+ if (strncasecmp(text, command, commandlen) == 0 &&
+ (text[commandlen] == '\0' ||
+ text[commandlen] == ' ' ||
+ text[commandlen] == '\t'))
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+/*%
+ * This function is called to process the incoming command
+ * when a control channel message is received.
+ */
+isc_result_t
+ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text) {
+ isccc_sexpr_t *data;
+ char *command;
+ isc_result_t result;
+ int log_level;
+#ifdef HAVE_LIBSCF
+ ns_smf_want_disable = 0;
+#endif
+
+ data = isccc_alist_lookup(message, "_data");
+ if (data == NULL) {
+ /*
+ * No data section.
+ */
+ return (ISC_R_FAILURE);
+ }
+
+ result = isccc_cc_lookupstring(data, "type", &command);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * We have no idea what this is.
+ */
+ return (result);
+ }
+
+ /*
+ * Compare the 'command' parameter against all known control commands.
+ */
+ if (command_compare(command, NS_COMMAND_NULL) ||
+ command_compare(command, NS_COMMAND_STATUS)) {
+ log_level = ISC_LOG_DEBUG(1);
+ } else {
+ log_level = ISC_LOG_INFO;
+ }
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, log_level,
+ "received control channel command '%s'",
+ command);
+
+ if (command_compare(command, NS_COMMAND_RELOAD)) {
+ result = ns_server_reloadcommand(ns_g_server, command, text);
+ } else if (command_compare(command, NS_COMMAND_RECONFIG)) {
+ result = ns_server_reconfigcommand(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_REFRESH)) {
+ result = ns_server_refreshcommand(ns_g_server, command, text);
+ } else if (command_compare(command, NS_COMMAND_RETRANSFER)) {
+ result = ns_server_retransfercommand(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_HALT)) {
+#ifdef HAVE_LIBSCF
+ /*
+ * If we are managed by smf(5), AND in chroot, then
+ * we cannot connect to the smf repository, so just
+ * return with an appropriate message back to rndc.
+ */
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 1) {
+ result = ns_smf_add_message(text);
+ return (result);
+ }
+ /*
+ * If we are managed by smf(5) but not in chroot,
+ * try to disable ourselves the smf way.
+ */
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 0)
+ ns_smf_want_disable = 1;
+ /*
+ * If ns_smf_got_instance = 0, ns_smf_chroot
+ * is not relevant and we fall through to
+ * isc_app_shutdown below.
+ */
+#endif
+ ns_server_flushonshutdown(ns_g_server, ISC_FALSE);
+ ns_os_shutdownmsg(command, text);
+ isc_app_shutdown();
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_STOP)) {
+#ifdef HAVE_LIBSCF
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 1) {
+ result = ns_smf_add_message(text);
+ return (result);
+ }
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 0)
+ ns_smf_want_disable = 1;
+#endif
+ ns_server_flushonshutdown(ns_g_server, ISC_TRUE);
+ ns_os_shutdownmsg(command, text);
+ isc_app_shutdown();
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_DUMPSTATS)) {
+ result = ns_server_dumpstats(ns_g_server);
+ } else if (command_compare(command, NS_COMMAND_QUERYLOG)) {
+ result = ns_server_togglequerylog(ns_g_server);
+ } else if (command_compare(command, NS_COMMAND_DUMPDB)) {
+ ns_server_dumpdb(ns_g_server, command);
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_TRACE)) {
+ result = ns_server_setdebuglevel(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_NOTRACE)) {
+ ns_g_debuglevel = 0;
+ isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_FLUSH)) {
+ result = ns_server_flushcache(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_FLUSHNAME)) {
+ result = ns_server_flushname(ns_g_server, command);
+ } else if (command_compare(command, NS_COMMAND_STATUS)) {
+ result = ns_server_status(ns_g_server, text);
+ } else if (command_compare(command, NS_COMMAND_TSIGLIST)) {
+ result = ns_server_tsiglist(ns_g_server, text);
+ } else if (command_compare(command, NS_COMMAND_TSIGDELETE)) {
+ result = ns_server_tsigdelete(ns_g_server, command, text);
+ } else if (command_compare(command, NS_COMMAND_FREEZE)) {
+ result = ns_server_freeze(ns_g_server, ISC_TRUE, command);
+ } else if (command_compare(command, NS_COMMAND_UNFREEZE) ||
+ command_compare(command, NS_COMMAND_THAW)) {
+ result = ns_server_freeze(ns_g_server, ISC_FALSE, command);
+ } else if (command_compare(command, NS_COMMAND_RECURSING)) {
+ result = ns_server_dumprecursing(ns_g_server);
+ } else if (command_compare(command, NS_COMMAND_TIMERPOKE)) {
+ result = ISC_R_SUCCESS;
+ isc_timermgr_poke(ns_g_timermgr);
+ } else if (command_compare(command, NS_COMMAND_NULL)) {
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_NOTIFY)) {
+ result = ns_server_notifycommand(ns_g_server, command, text);
+ } else if (command_compare(command, NS_COMMAND_VALIDATION)) {
+ result = ns_server_validation(ns_g_server, command);
+ } else {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "unknown control channel command '%s'",
+ command);
+ result = DNS_R_UNKNOWNCOMMAND;
+ }
+
+ return (result);
+}
diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c
new file mode 100644
index 0000000..766f013
--- /dev/null
+++ b/bin/named/controlconf.c
@@ -0,0 +1,1459 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: controlconf.c,v 1.60 2008/07/23 23:27:54 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/event.h>
+#include <isc/mem.h>
+#include <isc/net.h>
+#include <isc/netaddr.h>
+#include <isc/random.h>
+#include <isc/result.h>
+#include <isc/stdtime.h>
+#include <isc/string.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <isccfg/namedconf.h>
+
+#include <bind9/check.h>
+
+#include <isccc/alist.h>
+#include <isccc/cc.h>
+#include <isccc/ccmsg.h>
+#include <isccc/events.h>
+#include <isccc/result.h>
+#include <isccc/sexpr.h>
+#include <isccc/symtab.h>
+#include <isccc/util.h>
+
+#include <dns/result.h>
+
+#include <named/config.h>
+#include <named/control.h>
+#include <named/log.h>
+#include <named/server.h>
+
+/*
+ * Note: Listeners and connections are not locked. All event handlers are
+ * executed by the server task, and all callers of exported routines must
+ * be running under the server task.
+ */
+
+typedef struct controlkey controlkey_t;
+typedef ISC_LIST(controlkey_t) controlkeylist_t;
+
+typedef struct controlconnection controlconnection_t;
+typedef ISC_LIST(controlconnection_t) controlconnectionlist_t;
+
+typedef struct controllistener controllistener_t;
+typedef ISC_LIST(controllistener_t) controllistenerlist_t;
+
+struct controlkey {
+ char * keyname;
+ isc_region_t secret;
+ ISC_LINK(controlkey_t) link;
+};
+
+struct controlconnection {
+ isc_socket_t * sock;
+ isccc_ccmsg_t ccmsg;
+ isc_boolean_t ccmsg_valid;
+ isc_boolean_t sending;
+ isc_timer_t * timer;
+ unsigned char buffer[2048];
+ controllistener_t * listener;
+ isc_uint32_t nonce;
+ ISC_LINK(controlconnection_t) link;
+};
+
+struct controllistener {
+ ns_controls_t * controls;
+ isc_mem_t * mctx;
+ isc_task_t * task;
+ isc_sockaddr_t address;
+ isc_socket_t * sock;
+ dns_acl_t * acl;
+ isc_boolean_t listening;
+ isc_boolean_t exiting;
+ controlkeylist_t keys;
+ controlconnectionlist_t connections;
+ isc_sockettype_t type;
+ isc_uint32_t perm;
+ isc_uint32_t owner;
+ isc_uint32_t group;
+ ISC_LINK(controllistener_t) link;
+};
+
+struct ns_controls {
+ ns_server_t *server;
+ controllistenerlist_t listeners;
+ isc_boolean_t shuttingdown;
+ isccc_symtab_t *symtab;
+};
+
+static void control_newconn(isc_task_t *task, isc_event_t *event);
+static void control_recvmessage(isc_task_t *task, isc_event_t *event);
+
+#define CLOCKSKEW 300
+
+static void
+free_controlkey(controlkey_t *key, isc_mem_t *mctx) {
+ if (key->keyname != NULL)
+ isc_mem_free(mctx, key->keyname);
+ if (key->secret.base != NULL)
+ isc_mem_put(mctx, key->secret.base, key->secret.length);
+ isc_mem_put(mctx, key, sizeof(*key));
+}
+
+static void
+free_controlkeylist(controlkeylist_t *keylist, isc_mem_t *mctx) {
+ while (!ISC_LIST_EMPTY(*keylist)) {
+ controlkey_t *key = ISC_LIST_HEAD(*keylist);
+ ISC_LIST_UNLINK(*keylist, key, link);
+ free_controlkey(key, mctx);
+ }
+}
+
+static void
+free_listener(controllistener_t *listener) {
+ INSIST(listener->exiting);
+ INSIST(!listener->listening);
+ INSIST(ISC_LIST_EMPTY(listener->connections));
+
+ if (listener->sock != NULL)
+ isc_socket_detach(&listener->sock);
+
+ free_controlkeylist(&listener->keys, listener->mctx);
+
+ if (listener->acl != NULL)
+ dns_acl_detach(&listener->acl);
+
+ isc_mem_put(listener->mctx, listener, sizeof(*listener));
+}
+
+static void
+maybe_free_listener(controllistener_t *listener) {
+ if (listener->exiting &&
+ !listener->listening &&
+ ISC_LIST_EMPTY(listener->connections))
+ free_listener(listener);
+}
+
+static void
+maybe_free_connection(controlconnection_t *conn) {
+ controllistener_t *listener = conn->listener;
+
+ if (conn->timer != NULL)
+ isc_timer_detach(&conn->timer);
+
+ if (conn->ccmsg_valid) {
+ isccc_ccmsg_cancelread(&conn->ccmsg);
+ return;
+ }
+
+ if (conn->sending) {
+ isc_socket_cancel(conn->sock, listener->task,
+ ISC_SOCKCANCEL_SEND);
+ return;
+ }
+
+ ISC_LIST_UNLINK(listener->connections, conn, link);
+ isc_mem_put(listener->mctx, conn, sizeof(*conn));
+}
+
+static void
+shutdown_listener(controllistener_t *listener) {
+ controlconnection_t *conn;
+ controlconnection_t *next;
+
+ if (!listener->exiting) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+
+ ISC_LIST_UNLINK(listener->controls->listeners, listener, link);
+
+ isc_sockaddr_format(&listener->address, socktext,
+ sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
+ "stopping command channel on %s", socktext);
+ if (listener->type == isc_sockettype_unix)
+ isc_socket_cleanunix(&listener->address, ISC_TRUE);
+ listener->exiting = ISC_TRUE;
+ }
+
+ for (conn = ISC_LIST_HEAD(listener->connections);
+ conn != NULL;
+ conn = next)
+ {
+ next = ISC_LIST_NEXT(conn, link);
+ maybe_free_connection(conn);
+ }
+
+ if (listener->listening)
+ isc_socket_cancel(listener->sock, listener->task,
+ ISC_SOCKCANCEL_ACCEPT);
+
+ maybe_free_listener(listener);
+}
+
+static isc_boolean_t
+address_ok(isc_sockaddr_t *sockaddr, dns_acl_t *acl) {
+ isc_netaddr_t netaddr;
+ isc_result_t result;
+ int match;
+
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+
+ result = dns_acl_match(&netaddr, NULL, acl,
+ &ns_g_server->aclenv, &match, NULL);
+
+ if (result != ISC_R_SUCCESS || match <= 0)
+ return (ISC_FALSE);
+ else
+ return (ISC_TRUE);
+}
+
+static isc_result_t
+control_accept(controllistener_t *listener) {
+ isc_result_t result;
+ result = isc_socket_accept(listener->sock,
+ listener->task,
+ control_newconn, listener);
+ if (result != ISC_R_SUCCESS)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socket_accept() failed: %s",
+ isc_result_totext(result));
+ else
+ listener->listening = ISC_TRUE;
+ return (result);
+}
+
+static isc_result_t
+control_listen(controllistener_t *listener) {
+ isc_result_t result;
+
+ result = isc_socket_listen(listener->sock, 0);
+ if (result != ISC_R_SUCCESS)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socket_listen() failed: %s",
+ isc_result_totext(result));
+ return (result);
+}
+
+static void
+control_next(controllistener_t *listener) {
+ (void)control_accept(listener);
+}
+
+static void
+control_senddone(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sevent = (isc_socketevent_t *) event;
+ controlconnection_t *conn = event->ev_arg;
+ controllistener_t *listener = conn->listener;
+ isc_socket_t *sock = (isc_socket_t *)sevent->ev_sender;
+ isc_result_t result;
+
+ REQUIRE(conn->sending);
+
+ UNUSED(task);
+
+ conn->sending = ISC_FALSE;
+
+ if (sevent->result != ISC_R_SUCCESS &&
+ sevent->result != ISC_R_CANCELED)
+ {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_t peeraddr;
+
+ (void)isc_socket_getpeername(sock, &peeraddr);
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "error sending command response to %s: %s",
+ socktext, isc_result_totext(sevent->result));
+ }
+ isc_event_free(&event);
+
+ result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task,
+ control_recvmessage, conn);
+ if (result != ISC_R_SUCCESS) {
+ isc_socket_detach(&conn->sock);
+ maybe_free_connection(conn);
+ maybe_free_listener(listener);
+ }
+}
+
+static inline void
+log_invalid(isccc_ccmsg_t *ccmsg, isc_result_t result) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_t peeraddr;
+
+ (void)isc_socket_getpeername(ccmsg->sock, &peeraddr);
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_ERROR,
+ "invalid command from %s: %s",
+ socktext, isc_result_totext(result));
+}
+
+static void
+control_recvmessage(isc_task_t *task, isc_event_t *event) {
+ controlconnection_t *conn;
+ controllistener_t *listener;
+ controlkey_t *key;
+ isccc_sexpr_t *request = NULL;
+ isccc_sexpr_t *response = NULL;
+ isccc_region_t ccregion;
+ isccc_region_t secret;
+ isc_stdtime_t now;
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_uint32_t len;
+ isc_buffer_t text;
+ char textarray[1024];
+ isc_result_t result;
+ isc_result_t eresult;
+ isccc_sexpr_t *_ctrl;
+ isccc_time_t sent;
+ isccc_time_t exp;
+ isc_uint32_t nonce;
+
+ REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG);
+
+ conn = event->ev_arg;
+ listener = conn->listener;
+ secret.rstart = NULL;
+
+ /* Is the server shutting down? */
+ if (listener->controls->shuttingdown)
+ goto cleanup;
+
+ if (conn->ccmsg.result != ISC_R_SUCCESS) {
+ if (conn->ccmsg.result != ISC_R_CANCELED &&
+ conn->ccmsg.result != ISC_R_EOF)
+ log_invalid(&conn->ccmsg, conn->ccmsg.result);
+ goto cleanup;
+ }
+
+ request = NULL;
+
+ for (key = ISC_LIST_HEAD(listener->keys);
+ key != NULL;
+ key = ISC_LIST_NEXT(key, link))
+ {
+ ccregion.rstart = isc_buffer_base(&conn->ccmsg.buffer);
+ ccregion.rend = isc_buffer_used(&conn->ccmsg.buffer);
+ secret.rstart = isc_mem_get(listener->mctx, key->secret.length);
+ if (secret.rstart == NULL)
+ goto cleanup;
+ memcpy(secret.rstart, key->secret.base, key->secret.length);
+ secret.rend = secret.rstart + key->secret.length;
+ result = isccc_cc_fromwire(&ccregion, &request, &secret);
+ if (result == ISC_R_SUCCESS)
+ break;
+ isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
+ if (result == ISCCC_R_BADAUTH) {
+ /*
+ * For some reason, request is non-NULL when
+ * isccc_cc_fromwire returns ISCCC_R_BADAUTH.
+ */
+ if (request != NULL)
+ isccc_sexpr_free(&request);
+ } else {
+ log_invalid(&conn->ccmsg, result);
+ goto cleanup;
+ }
+ }
+
+ if (key == NULL) {
+ log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
+ goto cleanup;
+ }
+
+ /* We shouldn't be getting a reply. */
+ if (isccc_cc_isreply(request)) {
+ log_invalid(&conn->ccmsg, ISC_R_FAILURE);
+ goto cleanup_request;
+ }
+
+ isc_stdtime_get(&now);
+
+ /*
+ * Limit exposure to replay attacks.
+ */
+ _ctrl = isccc_alist_lookup(request, "_ctrl");
+ if (_ctrl == NULL) {
+ log_invalid(&conn->ccmsg, ISC_R_FAILURE);
+ goto cleanup_request;
+ }
+
+ if (isccc_cc_lookupuint32(_ctrl, "_tim", &sent) == ISC_R_SUCCESS) {
+ if ((sent + CLOCKSKEW) < now || (sent - CLOCKSKEW) > now) {
+ log_invalid(&conn->ccmsg, ISCCC_R_CLOCKSKEW);
+ goto cleanup_request;
+ }
+ } else {
+ log_invalid(&conn->ccmsg, ISC_R_FAILURE);
+ goto cleanup_request;
+ }
+
+ /*
+ * Expire messages that are too old.
+ */
+ if (isccc_cc_lookupuint32(_ctrl, "_exp", &exp) == ISC_R_SUCCESS &&
+ now > exp) {
+ log_invalid(&conn->ccmsg, ISCCC_R_EXPIRED);
+ goto cleanup_request;
+ }
+
+ /*
+ * Duplicate suppression (required for UDP).
+ */
+ isccc_cc_cleansymtab(listener->controls->symtab, now);
+ result = isccc_cc_checkdup(listener->controls->symtab, request, now);
+ if (result != ISC_R_SUCCESS) {
+ if (result == ISC_R_EXISTS)
+ result = ISCCC_R_DUPLICATE;
+ log_invalid(&conn->ccmsg, result);
+ goto cleanup_request;
+ }
+
+ if (conn->nonce != 0 &&
+ (isccc_cc_lookupuint32(_ctrl, "_nonce", &nonce) != ISC_R_SUCCESS ||
+ conn->nonce != nonce)) {
+ log_invalid(&conn->ccmsg, ISCCC_R_BADAUTH);
+ goto cleanup_request;
+ }
+
+ /*
+ * Establish nonce.
+ */
+ while (conn->nonce == 0)
+ isc_random_get(&conn->nonce);
+
+ isc_buffer_init(&text, textarray, sizeof(textarray));
+ eresult = ns_control_docommand(request, &text);
+
+ result = isccc_cc_createresponse(request, now, now + 60, &response);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_request;
+ if (eresult != ISC_R_SUCCESS) {
+ isccc_sexpr_t *data;
+
+ data = isccc_alist_lookup(response, "_data");
+ if (data != NULL) {
+ const char *estr = isc_result_totext(eresult);
+ if (isccc_cc_definestring(data, "err", estr) == NULL)
+ goto cleanup_response;
+ }
+ }
+
+ if (isc_buffer_usedlength(&text) > 0) {
+ isccc_sexpr_t *data;
+
+ data = isccc_alist_lookup(response, "_data");
+ if (data != NULL) {
+ char *str = (char *)isc_buffer_base(&text);
+ if (isccc_cc_definestring(data, "text", str) == NULL)
+ goto cleanup_response;
+ }
+ }
+
+ _ctrl = isccc_alist_lookup(response, "_ctrl");
+ if (_ctrl == NULL ||
+ isccc_cc_defineuint32(_ctrl, "_nonce", conn->nonce) == NULL)
+ goto cleanup_response;
+
+ ccregion.rstart = conn->buffer + 4;
+ ccregion.rend = conn->buffer + sizeof(conn->buffer);
+ result = isccc_cc_towire(response, &ccregion, &secret);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_response;
+ isc_buffer_init(&b, conn->buffer, 4);
+ len = sizeof(conn->buffer) - REGION_SIZE(ccregion);
+ isc_buffer_putuint32(&b, len - 4);
+ r.base = conn->buffer;
+ r.length = len;
+
+ result = isc_socket_send(conn->sock, &r, task, control_senddone, conn);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_response;
+ conn->sending = ISC_TRUE;
+
+ isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
+ isccc_sexpr_free(&request);
+ isccc_sexpr_free(&response);
+ return;
+
+ cleanup_response:
+ isccc_sexpr_free(&response);
+
+ cleanup_request:
+ isccc_sexpr_free(&request);
+ isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
+
+ cleanup:
+ isc_socket_detach(&conn->sock);
+ isccc_ccmsg_invalidate(&conn->ccmsg);
+ conn->ccmsg_valid = ISC_FALSE;
+ maybe_free_connection(conn);
+ maybe_free_listener(listener);
+}
+
+static void
+control_timeout(isc_task_t *task, isc_event_t *event) {
+ controlconnection_t *conn = event->ev_arg;
+
+ UNUSED(task);
+
+ isc_timer_detach(&conn->timer);
+ maybe_free_connection(conn);
+
+ isc_event_free(&event);
+}
+
+static isc_result_t
+newconnection(controllistener_t *listener, isc_socket_t *sock) {
+ controlconnection_t *conn;
+ isc_interval_t interval;
+ isc_result_t result;
+
+ conn = isc_mem_get(listener->mctx, sizeof(*conn));
+ if (conn == NULL)
+ return (ISC_R_NOMEMORY);
+
+ conn->sock = sock;
+ isccc_ccmsg_init(listener->mctx, sock, &conn->ccmsg);
+ conn->ccmsg_valid = ISC_TRUE;
+ conn->sending = ISC_FALSE;
+ conn->timer = NULL;
+ isc_interval_set(&interval, 60, 0);
+ result = isc_timer_create(ns_g_timermgr, isc_timertype_once,
+ NULL, &interval, listener->task,
+ control_timeout, conn, &conn->timer);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ conn->listener = listener;
+ conn->nonce = 0;
+ ISC_LINK_INIT(conn, link);
+
+ result = isccc_ccmsg_readmessage(&conn->ccmsg, listener->task,
+ control_recvmessage, conn);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isccc_ccmsg_setmaxsize(&conn->ccmsg, 2048);
+
+ ISC_LIST_APPEND(listener->connections, conn, link);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ isccc_ccmsg_invalidate(&conn->ccmsg);
+ if (conn->timer != NULL)
+ isc_timer_detach(&conn->timer);
+ isc_mem_put(listener->mctx, conn, sizeof(*conn));
+ return (result);
+}
+
+static void
+control_newconn(isc_task_t *task, isc_event_t *event) {
+ isc_socket_newconnev_t *nevent = (isc_socket_newconnev_t *)event;
+ controllistener_t *listener = event->ev_arg;
+ isc_socket_t *sock;
+ isc_sockaddr_t peeraddr;
+ isc_result_t result;
+
+ UNUSED(task);
+
+ listener->listening = ISC_FALSE;
+
+ if (nevent->result != ISC_R_SUCCESS) {
+ if (nevent->result == ISC_R_CANCELED) {
+ shutdown_listener(listener);
+ goto cleanup;
+ }
+ goto restart;
+ }
+
+ sock = nevent->newsocket;
+ isc_socket_setname(sock, "control", NULL);
+ (void)isc_socket_getpeername(sock, &peeraddr);
+ if (listener->type == isc_sockettype_tcp &&
+ !address_ok(&peeraddr, listener->acl)) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "rejected command channel message from %s",
+ socktext);
+ isc_socket_detach(&sock);
+ goto restart;
+ }
+
+ result = newconnection(listener, sock);
+ if (result != ISC_R_SUCCESS) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&peeraddr, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "dropped command channel from %s: %s",
+ socktext, isc_result_totext(result));
+ isc_socket_detach(&sock);
+ goto restart;
+ }
+
+ restart:
+ control_next(listener);
+ cleanup:
+ isc_event_free(&event);
+}
+
+static void
+controls_shutdown(ns_controls_t *controls) {
+ controllistener_t *listener;
+ controllistener_t *next;
+
+ for (listener = ISC_LIST_HEAD(controls->listeners);
+ listener != NULL;
+ listener = next)
+ {
+ /*
+ * This is asynchronous. As listeners shut down, they will
+ * call their callbacks.
+ */
+ next = ISC_LIST_NEXT(listener, link);
+ shutdown_listener(listener);
+ }
+}
+
+void
+ns_controls_shutdown(ns_controls_t *controls) {
+ controls_shutdown(controls);
+ controls->shuttingdown = ISC_TRUE;
+}
+
+static isc_result_t
+cfgkeylist_find(const cfg_obj_t *keylist, const char *keyname,
+ const cfg_obj_t **objp)
+{
+ const cfg_listelt_t *element;
+ const char *str;
+ const cfg_obj_t *obj;
+
+ for (element = cfg_list_first(keylist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(cfg_map_getname(obj));
+ if (strcasecmp(str, keyname) == 0)
+ break;
+ }
+ if (element == NULL)
+ return (ISC_R_NOTFOUND);
+ obj = cfg_listelt_value(element);
+ *objp = obj;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+controlkeylist_fromcfg(const cfg_obj_t *keylist, isc_mem_t *mctx,
+ controlkeylist_t *keyids)
+{
+ const cfg_listelt_t *element;
+ char *newstr = NULL;
+ const char *str;
+ const cfg_obj_t *obj;
+ controlkey_t *key;
+
+ for (element = cfg_list_first(keylist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(obj);
+ newstr = isc_mem_strdup(mctx, str);
+ if (newstr == NULL)
+ goto cleanup;
+ key = isc_mem_get(mctx, sizeof(*key));
+ if (key == NULL)
+ goto cleanup;
+ key->keyname = newstr;
+ key->secret.base = NULL;
+ key->secret.length = 0;
+ ISC_LINK_INIT(key, link);
+ ISC_LIST_APPEND(*keyids, key, link);
+ newstr = NULL;
+ }
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (newstr != NULL)
+ isc_mem_free(mctx, newstr);
+ free_controlkeylist(keyids, mctx);
+ return (ISC_R_NOMEMORY);
+}
+
+static void
+register_keys(const cfg_obj_t *control, const cfg_obj_t *keylist,
+ controlkeylist_t *keyids, isc_mem_t *mctx, const char *socktext)
+{
+ controlkey_t *keyid, *next;
+ const cfg_obj_t *keydef;
+ char secret[1024];
+ isc_buffer_t b;
+ isc_result_t result;
+
+ /*
+ * Find the keys corresponding to the keyids used by this listener.
+ */
+ for (keyid = ISC_LIST_HEAD(*keyids); keyid != NULL; keyid = next) {
+ next = ISC_LIST_NEXT(keyid, link);
+
+ result = cfgkeylist_find(keylist, keyid->keyname, &keydef);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't find key '%s' for use with "
+ "command channel %s",
+ keyid->keyname, socktext);
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ } else {
+ const cfg_obj_t *algobj = NULL;
+ const cfg_obj_t *secretobj = NULL;
+ const char *algstr = NULL;
+ const char *secretstr = NULL;
+
+ (void)cfg_map_get(keydef, "algorithm", &algobj);
+ (void)cfg_map_get(keydef, "secret", &secretobj);
+ INSIST(algobj != NULL && secretobj != NULL);
+
+ algstr = cfg_obj_asstring(algobj);
+ secretstr = cfg_obj_asstring(secretobj);
+
+ if (ns_config_getkeyalgorithm(algstr, NULL, NULL) !=
+ ISC_R_SUCCESS)
+ {
+ cfg_obj_log(control, ns_g_lctx,
+ ISC_LOG_WARNING,
+ "unsupported algorithm '%s' in "
+ "key '%s' for use with command "
+ "channel %s",
+ algstr, keyid->keyname, socktext);
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ continue;
+ }
+
+ isc_buffer_init(&b, secret, sizeof(secret));
+ result = isc_base64_decodestring(secretstr, &b);
+
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
+ "secret for key '%s' on "
+ "command channel %s: %s",
+ keyid->keyname, socktext,
+ isc_result_totext(result));
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ continue;
+ }
+
+ keyid->secret.length = isc_buffer_usedlength(&b);
+ keyid->secret.base = isc_mem_get(mctx,
+ keyid->secret.length);
+ if (keyid->secret.base == NULL) {
+ cfg_obj_log(keydef, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't register key '%s': "
+ "out of memory", keyid->keyname);
+ ISC_LIST_UNLINK(*keyids, keyid, link);
+ free_controlkey(keyid, mctx);
+ break;
+ }
+ memcpy(keyid->secret.base, isc_buffer_base(&b),
+ keyid->secret.length);
+ }
+ }
+}
+
+#define CHECK(x) \
+ do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+static isc_result_t
+get_rndckey(isc_mem_t *mctx, controlkeylist_t *keyids) {
+ isc_result_t result;
+ cfg_parser_t *pctx = NULL;
+ cfg_obj_t *config = NULL;
+ const cfg_obj_t *key = NULL;
+ const cfg_obj_t *algobj = NULL;
+ const cfg_obj_t *secretobj = NULL;
+ const char *algstr = NULL;
+ const char *secretstr = NULL;
+ controlkey_t *keyid = NULL;
+ char secret[1024];
+ isc_buffer_t b;
+
+ CHECK(cfg_parser_create(mctx, ns_g_lctx, &pctx));
+ CHECK(cfg_parse_file(pctx, ns_g_keyfile, &cfg_type_rndckey, &config));
+ CHECK(cfg_map_get(config, "key", &key));
+
+ keyid = isc_mem_get(mctx, sizeof(*keyid));
+ if (keyid == NULL)
+ CHECK(ISC_R_NOMEMORY);
+ keyid->keyname = isc_mem_strdup(mctx,
+ cfg_obj_asstring(cfg_map_getname(key)));
+ keyid->secret.base = NULL;
+ keyid->secret.length = 0;
+ ISC_LINK_INIT(keyid, link);
+ if (keyid->keyname == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ CHECK(bind9_check_key(key, ns_g_lctx));
+
+ (void)cfg_map_get(key, "algorithm", &algobj);
+ (void)cfg_map_get(key, "secret", &secretobj);
+ INSIST(algobj != NULL && secretobj != NULL);
+
+ algstr = cfg_obj_asstring(algobj);
+ secretstr = cfg_obj_asstring(secretobj);
+
+ if (ns_config_getkeyalgorithm(algstr, NULL, NULL) != ISC_R_SUCCESS) {
+ cfg_obj_log(key, ns_g_lctx,
+ ISC_LOG_WARNING,
+ "unsupported algorithm '%s' in "
+ "key '%s' for use with command "
+ "channel",
+ algstr, keyid->keyname);
+ goto cleanup;
+ }
+
+ isc_buffer_init(&b, secret, sizeof(secret));
+ result = isc_base64_decodestring(secretstr, &b);
+
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
+ "secret for key '%s' on command channel: %s",
+ keyid->keyname, isc_result_totext(result));
+ CHECK(result);
+ }
+
+ keyid->secret.length = isc_buffer_usedlength(&b);
+ keyid->secret.base = isc_mem_get(mctx,
+ keyid->secret.length);
+ if (keyid->secret.base == NULL) {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't register key '%s': "
+ "out of memory", keyid->keyname);
+ CHECK(ISC_R_NOMEMORY);
+ }
+ memcpy(keyid->secret.base, isc_buffer_base(&b),
+ keyid->secret.length);
+ ISC_LIST_APPEND(*keyids, keyid, link);
+ keyid = NULL;
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (keyid != NULL)
+ free_controlkey(keyid, mctx);
+ if (config != NULL)
+ cfg_obj_destroy(pctx, &config);
+ if (pctx != NULL)
+ cfg_parser_destroy(&pctx);
+ return (result);
+}
+
+/*
+ * Ensures that both '*global_keylistp' and '*control_keylistp' are
+ * valid or both are NULL.
+ */
+static void
+get_key_info(const cfg_obj_t *config, const cfg_obj_t *control,
+ const cfg_obj_t **global_keylistp,
+ const cfg_obj_t **control_keylistp)
+{
+ isc_result_t result;
+ const cfg_obj_t *control_keylist = NULL;
+ const cfg_obj_t *global_keylist = NULL;
+
+ REQUIRE(global_keylistp != NULL && *global_keylistp == NULL);
+ REQUIRE(control_keylistp != NULL && *control_keylistp == NULL);
+
+ control_keylist = cfg_tuple_get(control, "keys");
+
+ if (!cfg_obj_isvoid(control_keylist) &&
+ cfg_list_first(control_keylist) != NULL) {
+ result = cfg_map_get(config, "key", &global_keylist);
+
+ if (result == ISC_R_SUCCESS) {
+ *global_keylistp = global_keylist;
+ *control_keylistp = control_keylist;
+ }
+ }
+}
+
+static void
+update_listener(ns_controls_t *cp, controllistener_t **listenerp,
+ const cfg_obj_t *control, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext, isc_sockettype_t type)
+{
+ controllistener_t *listener;
+ const cfg_obj_t *allow;
+ const cfg_obj_t *global_keylist = NULL;
+ const cfg_obj_t *control_keylist = NULL;
+ dns_acl_t *new_acl = NULL;
+ controlkeylist_t keys;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (listener = ISC_LIST_HEAD(cp->listeners);
+ listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ if (isc_sockaddr_equal(addr, &listener->address))
+ break;
+
+ if (listener == NULL) {
+ *listenerp = NULL;
+ return;
+ }
+
+ /*
+ * There is already a listener for this sockaddr.
+ * Update the access list and key information.
+ *
+ * First try to deal with the key situation. There are a few
+ * possibilities:
+ * (a) It had an explicit keylist and still has an explicit keylist.
+ * (b) It had an automagic key and now has an explicit keylist.
+ * (c) It had an explicit keylist and now needs an automagic key.
+ * (d) It has an automagic key and still needs the automagic key.
+ *
+ * (c) and (d) are the annoying ones. The caller needs to know
+ * that it should use the automagic configuration for key information
+ * in place of the named.conf configuration.
+ *
+ * XXXDCL There is one other hazard that has not been dealt with,
+ * the problem that if a key change is being caused by a control
+ * channel reload, then the response will be with the new key
+ * and not able to be decrypted by the client.
+ */
+ if (control != NULL)
+ get_key_info(config, control, &global_keylist,
+ &control_keylist);
+
+ if (control_keylist != NULL) {
+ INSIST(global_keylist != NULL);
+
+ ISC_LIST_INIT(keys);
+ result = controlkeylist_fromcfg(control_keylist,
+ listener->mctx, &keys);
+ if (result == ISC_R_SUCCESS) {
+ free_controlkeylist(&listener->keys, listener->mctx);
+ listener->keys = keys;
+ register_keys(control, global_keylist, &listener->keys,
+ listener->mctx, socktext);
+ }
+ } else {
+ free_controlkeylist(&listener->keys, listener->mctx);
+ result = get_rndckey(listener->mctx, &listener->keys);
+ }
+
+ if (result != ISC_R_SUCCESS && global_keylist != NULL) {
+ /*
+ * This message might be a little misleading since the
+ * "new keys" might in fact be identical to the old ones,
+ * but tracking whether they are identical just for the
+ * sake of avoiding this message would be too much trouble.
+ */
+ if (control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't install new keys for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "couldn't install new keys for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ }
+
+ /*
+ * Now, keep the old access list unless a new one can be made.
+ */
+ if (control != NULL && type == isc_sockettype_tcp) {
+ allow = cfg_tuple_get(control, "allow");
+ result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
+ aclconfctx, listener->mctx, 0,
+ &new_acl);
+ } else {
+ result = dns_acl_any(listener->mctx, &new_acl);
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ dns_acl_detach(&listener->acl);
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+ /* XXXDCL say the old acl is still used? */
+ } else if (control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't install new acl for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_WARNING,
+ "couldn't install new acl for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+
+ if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
+ isc_uint32_t perm, owner, group;
+ perm = cfg_obj_asuint32(cfg_tuple_get(control, "perm"));
+ owner = cfg_obj_asuint32(cfg_tuple_get(control, "owner"));
+ group = cfg_obj_asuint32(cfg_tuple_get(control, "group"));
+ result = ISC_R_SUCCESS;
+ if (listener->perm != perm || listener->owner != owner ||
+ listener->group != group)
+ result = isc_socket_permunix(&listener->address, perm,
+ owner, group);
+ if (result == ISC_R_SUCCESS) {
+ listener->perm = perm;
+ listener->owner = owner;
+ listener->group = group;
+ } else if (control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't update ownership/permission for "
+ "command channel %s", socktext);
+ }
+
+ *listenerp = listener;
+}
+
+static void
+add_listener(ns_controls_t *cp, controllistener_t **listenerp,
+ const cfg_obj_t *control, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext, isc_sockettype_t type)
+{
+ isc_mem_t *mctx = cp->server->mctx;
+ controllistener_t *listener;
+ const cfg_obj_t *allow;
+ const cfg_obj_t *global_keylist = NULL;
+ const cfg_obj_t *control_keylist = NULL;
+ dns_acl_t *new_acl = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ listener = isc_mem_get(mctx, sizeof(*listener));
+ if (listener == NULL)
+ result = ISC_R_NOMEMORY;
+
+ if (result == ISC_R_SUCCESS) {
+ listener->controls = cp;
+ listener->mctx = mctx;
+ listener->task = cp->server->task;
+ listener->address = *addr;
+ listener->sock = NULL;
+ listener->listening = ISC_FALSE;
+ listener->exiting = ISC_FALSE;
+ listener->acl = NULL;
+ listener->type = type;
+ listener->perm = 0;
+ listener->owner = 0;
+ listener->group = 0;
+ ISC_LINK_INIT(listener, link);
+ ISC_LIST_INIT(listener->keys);
+ ISC_LIST_INIT(listener->connections);
+
+ /*
+ * Make the acl.
+ */
+ if (control != NULL && type == isc_sockettype_tcp) {
+ allow = cfg_tuple_get(control, "allow");
+ result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
+ aclconfctx, mctx, 0,
+ &new_acl);
+ } else {
+ result = dns_acl_any(mctx, &new_acl);
+ }
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+
+ if (config != NULL)
+ get_key_info(config, control, &global_keylist,
+ &control_keylist);
+
+ if (control_keylist != NULL) {
+ result = controlkeylist_fromcfg(control_keylist,
+ listener->mctx,
+ &listener->keys);
+ if (result == ISC_R_SUCCESS)
+ register_keys(control, global_keylist,
+ &listener->keys,
+ listener->mctx, socktext);
+ } else
+ result = get_rndckey(mctx, &listener->keys);
+
+ if (result != ISC_R_SUCCESS && control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't install keys for "
+ "command channel %s: %s",
+ socktext, isc_result_totext(result));
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ int pf = isc_sockaddr_pf(&listener->address);
+ if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
+#ifdef ISC_PLATFORM_HAVESYSUNH
+ (pf == AF_UNIX && isc_net_probeunix() != ISC_R_SUCCESS) ||
+#endif
+ (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
+ result = ISC_R_FAMILYNOSUPPORT;
+ }
+
+ if (result == ISC_R_SUCCESS && type == isc_sockettype_unix)
+ isc_socket_cleanunix(&listener->address, ISC_FALSE);
+
+ if (result == ISC_R_SUCCESS)
+ result = isc_socket_create(ns_g_socketmgr,
+ isc_sockaddr_pf(&listener->address),
+ type, &listener->sock);
+ if (result == ISC_R_SUCCESS)
+ isc_socket_setname(listener->sock, "control", NULL);
+
+ if (result == ISC_R_SUCCESS)
+ result = isc_socket_bind(listener->sock, &listener->address,
+ ISC_SOCKET_REUSEADDRESS);
+
+ if (result == ISC_R_SUCCESS && type == isc_sockettype_unix) {
+ listener->perm = cfg_obj_asuint32(cfg_tuple_get(control,
+ "perm"));
+ listener->owner = cfg_obj_asuint32(cfg_tuple_get(control,
+ "owner"));
+ listener->group = cfg_obj_asuint32(cfg_tuple_get(control,
+ "group"));
+ result = isc_socket_permunix(&listener->address, listener->perm,
+ listener->owner, listener->group);
+ }
+ if (result == ISC_R_SUCCESS)
+ result = control_listen(listener);
+
+ if (result == ISC_R_SUCCESS)
+ result = control_accept(listener);
+
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
+ "command channel listening on %s", socktext);
+ *listenerp = listener;
+
+ } else {
+ if (listener != NULL) {
+ listener->exiting = ISC_TRUE;
+ free_listener(listener);
+ }
+
+ if (control != NULL)
+ cfg_obj_log(control, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't add command channel %s: %s",
+ socktext, isc_result_totext(result));
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_NOTICE,
+ "couldn't add command channel %s: %s",
+ socktext, isc_result_totext(result));
+
+ *listenerp = NULL;
+ }
+
+ /* XXXDCL return error results? fail hard? */
+}
+
+isc_result_t
+ns_controls_configure(ns_controls_t *cp, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx)
+{
+ controllistener_t *listener;
+ controllistenerlist_t new_listeners;
+ const cfg_obj_t *controlslist = NULL;
+ const cfg_listelt_t *element, *element2;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+
+ ISC_LIST_INIT(new_listeners);
+
+ /*
+ * Get the list of named.conf 'controls' statements.
+ */
+ (void)cfg_map_get(config, "controls", &controlslist);
+
+ /*
+ * Run through the new control channel list, noting sockets that
+ * are already being listened on and moving them to the new list.
+ *
+ * Identifying duplicate addr/port combinations is left to either
+ * the underlying config code, or to the bind attempt getting an
+ * address-in-use error.
+ */
+ if (controlslist != NULL) {
+ for (element = cfg_list_first(controlslist);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ const cfg_obj_t *controls;
+ const cfg_obj_t *inetcontrols = NULL;
+
+ controls = cfg_listelt_value(element);
+ (void)cfg_map_get(controls, "inet", &inetcontrols);
+ if (inetcontrols == NULL)
+ continue;
+
+ for (element2 = cfg_list_first(inetcontrols);
+ element2 != NULL;
+ element2 = cfg_list_next(element2)) {
+ const cfg_obj_t *control;
+ const cfg_obj_t *obj;
+ isc_sockaddr_t addr;
+
+ /*
+ * The parser handles BIND 8 configuration file
+ * syntax, so it allows unix phrases as well
+ * inet phrases with no keys{} clause.
+ */
+ control = cfg_listelt_value(element2);
+
+ obj = cfg_tuple_get(control, "address");
+ addr = *cfg_obj_assockaddr(obj);
+ if (isc_sockaddr_getport(&addr) == 0)
+ isc_sockaddr_setport(&addr,
+ NS_CONTROL_PORT);
+
+ isc_sockaddr_format(&addr, socktext,
+ sizeof(socktext));
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL,
+ ISC_LOG_DEBUG(9),
+ "processing control channel %s",
+ socktext);
+
+ update_listener(cp, &listener, control, config,
+ &addr, aclconfctx, socktext,
+ isc_sockettype_tcp);
+
+ if (listener != NULL)
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(cp->listeners,
+ listener, link);
+ else
+ /*
+ * This is a new listener.
+ */
+ add_listener(cp, &listener, control,
+ config, &addr, aclconfctx,
+ socktext,
+ isc_sockettype_tcp);
+
+ if (listener != NULL)
+ ISC_LIST_APPEND(new_listeners,
+ listener, link);
+ }
+ }
+ for (element = cfg_list_first(controlslist);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ const cfg_obj_t *controls;
+ const cfg_obj_t *unixcontrols = NULL;
+
+ controls = cfg_listelt_value(element);
+ (void)cfg_map_get(controls, "unix", &unixcontrols);
+ if (unixcontrols == NULL)
+ continue;
+
+ for (element2 = cfg_list_first(unixcontrols);
+ element2 != NULL;
+ element2 = cfg_list_next(element2)) {
+ const cfg_obj_t *control;
+ const cfg_obj_t *path;
+ isc_sockaddr_t addr;
+ isc_result_t result;
+
+ /*
+ * The parser handles BIND 8 configuration file
+ * syntax, so it allows unix phrases as well
+ * inet phrases with no keys{} clause.
+ */
+ control = cfg_listelt_value(element2);
+
+ path = cfg_tuple_get(control, "path");
+ result = isc_sockaddr_frompath(&addr,
+ cfg_obj_asstring(path));
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL,
+ ISC_LOG_DEBUG(9),
+ "control channel '%s': %s",
+ cfg_obj_asstring(path),
+ isc_result_totext(result));
+ continue;
+ }
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL,
+ ISC_LOG_DEBUG(9),
+ "processing control channel '%s'",
+ cfg_obj_asstring(path));
+
+ update_listener(cp, &listener, control, config,
+ &addr, aclconfctx,
+ cfg_obj_asstring(path),
+ isc_sockettype_unix);
+
+ if (listener != NULL)
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(cp->listeners,
+ listener, link);
+ else
+ /*
+ * This is a new listener.
+ */
+ add_listener(cp, &listener, control,
+ config, &addr, aclconfctx,
+ cfg_obj_asstring(path),
+ isc_sockettype_unix);
+
+ if (listener != NULL)
+ ISC_LIST_APPEND(new_listeners,
+ listener, link);
+ }
+ }
+ } else {
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ isc_sockaddr_t addr;
+
+ if (i == 0) {
+ struct in_addr localhost;
+
+ if (isc_net_probeipv4() != ISC_R_SUCCESS)
+ continue;
+ localhost.s_addr = htonl(INADDR_LOOPBACK);
+ isc_sockaddr_fromin(&addr, &localhost, 0);
+ } else {
+ if (isc_net_probeipv6() != ISC_R_SUCCESS)
+ continue;
+ isc_sockaddr_fromin6(&addr,
+ &in6addr_loopback, 0);
+ }
+ isc_sockaddr_setport(&addr, NS_CONTROL_PORT);
+
+ isc_sockaddr_format(&addr, socktext, sizeof(socktext));
+
+ update_listener(cp, &listener, NULL, NULL,
+ &addr, NULL, socktext,
+ isc_sockettype_tcp);
+
+ if (listener != NULL)
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(cp->listeners,
+ listener, link);
+ else
+ /*
+ * This is a new listener.
+ */
+ add_listener(cp, &listener, NULL, NULL,
+ &addr, NULL, socktext,
+ isc_sockettype_tcp);
+
+ if (listener != NULL)
+ ISC_LIST_APPEND(new_listeners,
+ listener, link);
+ }
+ }
+
+ /*
+ * ns_control_shutdown() will stop whatever is on the global
+ * listeners list, which currently only has whatever sockaddrs
+ * were in the previous configuration (if any) that do not
+ * remain in the current configuration.
+ */
+ controls_shutdown(cp);
+
+ /*
+ * Put all of the valid listeners on the listeners list.
+ * Anything already on listeners in the process of shutting
+ * down will be taken care of by listen_done().
+ */
+ ISC_LIST_APPENDLIST(cp->listeners, new_listeners, link);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp) {
+ isc_mem_t *mctx = server->mctx;
+ isc_result_t result;
+ ns_controls_t *controls = isc_mem_get(mctx, sizeof(*controls));
+
+ if (controls == NULL)
+ return (ISC_R_NOMEMORY);
+ controls->server = server;
+ ISC_LIST_INIT(controls->listeners);
+ controls->shuttingdown = ISC_FALSE;
+ controls->symtab = NULL;
+ result = isccc_cc_createsymtab(&controls->symtab);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(server->mctx, controls, sizeof(*controls));
+ return (result);
+ }
+ *ctrlsp = controls;
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_controls_destroy(ns_controls_t **ctrlsp) {
+ ns_controls_t *controls = *ctrlsp;
+
+ REQUIRE(ISC_LIST_EMPTY(controls->listeners));
+
+ isccc_symtab_destroy(&controls->symtab);
+ isc_mem_put(controls->server->mctx, controls, sizeof(*controls));
+ *ctrlsp = NULL;
+}
diff --git a/bin/named/convertxsl.pl b/bin/named/convertxsl.pl
new file mode 100755
index 0000000..87550b3
--- /dev/null
+++ b/bin/named/convertxsl.pl
@@ -0,0 +1,57 @@
+#!/usr/bin/env perl
+#
+# Copyright (C) 2006-2008 Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: convertxsl.pl,v 1.14 2008/07/17 23:43:26 jinmei Exp $
+
+use strict;
+use warnings;
+
+my $rev = '$Id: convertxsl.pl,v 1.14 2008/07/17 23:43:26 jinmei Exp $';
+$rev =~ s/\$//g;
+$rev =~ s/,v//g;
+$rev =~ s/Id: //;
+
+my $xsl = "unknown";
+my $lines = '';
+
+while (<>) {
+ chomp;
+ # pickout the id for comment.
+ $xsl = $_ if (/<!-- .Id:.* -->/);
+ # convert Id string to a form not recognisable by cvs.
+ $_ =~ s/<!-- .Id:(.*). -->/<!-- \\045Id: $1\\045 -->/;
+ s/[\ \t]+/ /g;
+ s/\>\ \</\>\</g;
+ s/\"/\\\"/g;
+ s/^/\t\"/;
+ s/$/\\n\"/;
+ if ($lines eq "") {
+ $lines .= $_;
+ } else {
+ $lines .= "\n" . $_;
+ }
+}
+
+$xsl =~ s/\$//g;
+$xsl =~ s/<!-- Id: //;
+$xsl =~ s/ -->.*//;
+$xsl =~ s/,v//;
+
+print "/*\n * Generated by $rev \n * From $xsl\n */\n";
+print 'static char xslmsg[] =',"\n";
+print $lines;
+
+print ';', "\n";
diff --git a/bin/named/include/named/builtin.h b/bin/named/include/named/builtin.h
new file mode 100644
index 0000000..a5185ba
--- /dev/null
+++ b/bin/named/include/named/builtin.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: builtin.h,v 1.6 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_BUILTIN_H
+#define NAMED_BUILTIN_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+
+isc_result_t ns_builtin_init(void);
+
+void ns_builtin_deinit(void);
+
+#endif /* NAMED_BUILTIN_H */
diff --git a/bin/named/include/named/client.h b/bin/named/include/named/client.h
new file mode 100644
index 0000000..b4be639
--- /dev/null
+++ b/bin/named/include/named/client.h
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: client.h,v 1.86 2008/04/03 02:01:08 marka Exp $ */
+
+#ifndef NAMED_CLIENT_H
+#define NAMED_CLIENT_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * This module defines two objects, ns_client_t and ns_clientmgr_t.
+ *
+ * An ns_client_t object handles incoming DNS requests from clients
+ * on a given network interface.
+ *
+ * Each ns_client_t object can handle only one TCP connection or UDP
+ * request at a time. Therefore, several ns_client_t objects are
+ * typically created to serve each network interface, e.g., one
+ * for handling TCP requests and a few (one per CPU) for handling
+ * UDP requests.
+ *
+ * Incoming requests are classified as queries, zone transfer
+ * requests, update requests, notify requests, etc, and handed off
+ * to the appropriate request handler. When the request has been
+ * fully handled (which can be much later), the ns_client_t must be
+ * notified of this by calling one of the following functions
+ * exactly once in the context of its task:
+ * \code
+ * ns_client_send() (sending a non-error response)
+ * ns_client_sendraw() (sending a raw response)
+ * ns_client_error() (sending an error response)
+ * ns_client_next() (sending no response)
+ *\endcode
+ * This will release any resources used by the request and
+ * and allow the ns_client_t to listen for the next request.
+ *
+ * A ns_clientmgr_t manages a number of ns_client_t objects.
+ * New ns_client_t objects are created by calling
+ * ns_clientmgr_createclients(). They are destroyed by
+ * destroying their manager.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/stdtime.h>
+#include <isc/quota.h>
+
+#include <dns/fixedname.h>
+#include <dns/name.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/tcpmsg.h>
+#include <dns/types.h>
+
+#include <named/types.h>
+#include <named/query.h>
+
+/***
+ *** Types
+ ***/
+
+typedef ISC_LIST(ns_client_t) client_list_t;
+
+/*% nameserver client structure */
+struct ns_client {
+ unsigned int magic;
+ isc_mem_t * mctx;
+ ns_clientmgr_t * manager;
+ int state;
+ int newstate;
+ int naccepts;
+ int nreads;
+ int nsends;
+ int nrecvs;
+ int nupdates;
+ int nctls;
+ int references;
+ isc_boolean_t needshutdown; /*
+ * Used by clienttest to get
+ * the client to go from
+ * inactive to free state
+ * by shutting down the
+ * client's task.
+ */
+ unsigned int attributes;
+ isc_task_t * task;
+ dns_view_t * view;
+ dns_dispatch_t * dispatch;
+ isc_socket_t * udpsocket;
+ isc_socket_t * tcplistener;
+ isc_socket_t * tcpsocket;
+ unsigned char * tcpbuf;
+ dns_tcpmsg_t tcpmsg;
+ isc_boolean_t tcpmsg_valid;
+ isc_timer_t * timer;
+ isc_boolean_t timerset;
+ dns_message_t * message;
+ isc_socketevent_t * sendevent;
+ isc_socketevent_t * recvevent;
+ unsigned char * recvbuf;
+ dns_rdataset_t * opt;
+ isc_uint16_t udpsize;
+ isc_uint16_t extflags;
+ isc_int16_t ednsversion; /* -1 noedns */
+ void (*next)(ns_client_t *);
+ void (*shutdown)(void *arg, isc_result_t result);
+ void *shutdown_arg;
+ ns_query_t query;
+ isc_stdtime_t requesttime;
+ isc_stdtime_t now;
+ dns_name_t signername; /*%< [T]SIG key name */
+ dns_name_t * signer; /*%< NULL if not valid sig */
+ isc_boolean_t mortal; /*%< Die after handling request */
+ isc_quota_t *tcpquota;
+ isc_quota_t *recursionquota;
+ ns_interface_t *interface;
+ isc_sockaddr_t peeraddr;
+ isc_boolean_t peeraddr_valid;
+ struct in6_pktinfo pktinfo;
+ isc_event_t ctlevent;
+ /*%
+ * Information about recent FORMERR response(s), for
+ * FORMERR loop avoidance. This is separate for each
+ * client object rather than global only to avoid
+ * the need for locking.
+ */
+ struct {
+ isc_sockaddr_t addr;
+ isc_stdtime_t time;
+ dns_messageid_t id;
+ } formerrcache;
+ ISC_LINK(ns_client_t) link;
+ /*%
+ * The list 'link' is part of, or NULL if not on any list.
+ */
+ client_list_t *list;
+};
+
+#define NS_CLIENT_MAGIC ISC_MAGIC('N','S','C','c')
+#define NS_CLIENT_VALID(c) ISC_MAGIC_VALID(c, NS_CLIENT_MAGIC)
+
+#define NS_CLIENTATTR_TCP 0x01
+#define NS_CLIENTATTR_RA 0x02 /*%< Client gets recusive service */
+#define NS_CLIENTATTR_PKTINFO 0x04 /*%< pktinfo is valid */
+#define NS_CLIENTATTR_MULTICAST 0x08 /*%< recv'd from multicast */
+#define NS_CLIENTATTR_WANTDNSSEC 0x10 /*%< include dnssec records */
+#define NS_CLIENTATTR_WANTNSID 0x20 /*%< include nameserver ID */
+
+extern unsigned int ns_client_requests;
+
+/***
+ *** Functions
+ ***/
+
+/*%
+ * Note! These ns_client_ routines MUST be called ONLY from the client's
+ * task in order to ensure synchronization.
+ */
+
+void
+ns_client_send(ns_client_t *client);
+/*%
+ * Finish processing the current client request and
+ * send client->message as a response.
+ * \brief
+ * Note! These ns_client_ routines MUST be called ONLY from the client's
+ * task in order to ensure synchronization.
+ */
+
+void
+ns_client_sendraw(ns_client_t *client, dns_message_t *msg);
+/*%
+ * Finish processing the current client request and
+ * send msg as a response using client->message->id for the id.
+ */
+
+void
+ns_client_error(ns_client_t *client, isc_result_t result);
+/*%
+ * Finish processing the current client request and return
+ * an error response to the client. The error response
+ * will have an RCODE determined by 'result'.
+ */
+
+void
+ns_client_next(ns_client_t *client, isc_result_t result);
+/*%
+ * Finish processing the current client request,
+ * return no response to the client.
+ */
+
+isc_boolean_t
+ns_client_shuttingdown(ns_client_t *client);
+/*%
+ * Return ISC_TRUE iff the client is currently shutting down.
+ */
+
+void
+ns_client_attach(ns_client_t *source, ns_client_t **target);
+/*%
+ * Attach '*targetp' to 'source'.
+ */
+
+void
+ns_client_detach(ns_client_t **clientp);
+/*%
+ * Detach '*clientp' from its client.
+ */
+
+isc_result_t
+ns_client_replace(ns_client_t *client);
+/*%
+ * Try to replace the current client with a new one, so that the
+ * current one can go off and do some lengthy work without
+ * leaving the dispatch/socket without service.
+ */
+
+void
+ns_client_settimeout(ns_client_t *client, unsigned int seconds);
+/*%
+ * Set a timer in the client to go off in the specified amount of time.
+ */
+
+isc_result_t
+ns_clientmgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_timermgr_t *timermgr, ns_clientmgr_t **managerp);
+/*%
+ * Create a client manager.
+ */
+
+void
+ns_clientmgr_destroy(ns_clientmgr_t **managerp);
+/*%
+ * Destroy a client manager and all ns_client_t objects
+ * managed by it.
+ */
+
+isc_result_t
+ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
+ ns_interface_t *ifp, isc_boolean_t tcp);
+/*%
+ * Create up to 'n' clients listening on interface 'ifp'.
+ * If 'tcp' is ISC_TRUE, the clients will listen for TCP connections,
+ * otherwise for UDP requests.
+ */
+
+isc_sockaddr_t *
+ns_client_getsockaddr(ns_client_t *client);
+/*%
+ * Get the socket address of the client whose request is
+ * currently being processed.
+ */
+
+isc_result_t
+ns_client_checkaclsilent(ns_client_t *client,
+ isc_sockaddr_t *sockaddr,
+ dns_acl_t *acl,
+ isc_boolean_t default_allow);
+
+/*%
+ * Convenience function for client request ACL checking.
+ *
+ * Check the current client request against 'acl'. If 'acl'
+ * is NULL, allow the request iff 'default_allow' is ISC_TRUE.
+ * If netaddr is NULL, check the ACL against client->peeraddr;
+ * otherwise check it against netaddr.
+ *
+ * Notes:
+ *\li This is appropriate for checking allow-update,
+ * allow-query, allow-transfer, etc. It is not appropriate
+ * for checking the blackhole list because we treat positive
+ * matches as "allow" and negative matches as "deny"; in
+ * the case of the blackhole list this would be backwards.
+ *
+ * Requires:
+ *\li 'client' points to a valid client.
+ *\li 'sockaddr' points to a valid address, or is NULL.
+ *\li 'acl' points to a valid ACL, or is NULL.
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS if the request should be allowed
+ * \li ISC_R_REFUSED if the request should be denied
+ *\li No other return values are possible.
+ */
+
+isc_result_t
+ns_client_checkacl(ns_client_t *client,
+ isc_sockaddr_t *sockaddr,
+ const char *opname, dns_acl_t *acl,
+ isc_boolean_t default_allow,
+ int log_level);
+/*%
+ * Like ns_client_checkaclsilent, except the outcome of the check is
+ * logged at log level 'log_level' if denied, and at debug 3 if approved.
+ * Log messages will refer to the request as an 'opname' request.
+ *
+ * Requires:
+ *\li 'client' points to a valid client.
+ *\li 'sockaddr' points to a valid address, or is NULL.
+ *\li 'acl' points to a valid ACL, or is NULL.
+ *\li 'opname' points to a null-terminated string.
+ */
+
+void
+ns_client_log(ns_client_t *client, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level,
+ const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6);
+
+void
+ns_client_logv(ns_client_t *client, isc_logcategory_t *category,
+ isc_logmodule_t *module, int level, const char *fmt, va_list ap) ISC_FORMAT_PRINTF(5, 0);
+
+void
+ns_client_aclmsg(const char *msg, dns_name_t *name, dns_rdatatype_t type,
+ dns_rdataclass_t rdclass, char *buf, size_t len);
+
+#define NS_CLIENT_ACLMSGSIZE(x) \
+ (DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE + \
+ DNS_RDATACLASS_FORMATSIZE + sizeof(x) + sizeof("'/'"))
+
+void
+ns_client_recursing(ns_client_t *client);
+/*%
+ * Add client to end of th recursing list.
+ */
+
+void
+ns_client_killoldestquery(ns_client_t *client);
+/*%
+ * Kill the oldest recursive query (recursing list head).
+ */
+
+void
+ns_client_dumprecursing(FILE *f, ns_clientmgr_t *manager);
+/*%
+ * Dump the outstanding recursive queries to 'f'.
+ */
+
+void
+ns_client_qnamereplace(ns_client_t *client, dns_name_t *name);
+/*%
+ * Replace the qname.
+ */
+
+isc_boolean_t
+ns_client_isself(dns_view_t *myview, dns_tsigkey_t *mykey,
+ isc_sockaddr_t *srcaddr, isc_sockaddr_t *destaddr,
+ dns_rdataclass_t rdclass, void *arg);
+/*%
+ * Isself callback.
+ */
+
+#endif /* NAMED_CLIENT_H */
diff --git a/bin/named/include/named/config.h b/bin/named/include/named/config.h
new file mode 100644
index 0000000..f7ceed8
--- /dev/null
+++ b/bin/named/include/named/config.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001, 2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: config.h,v 1.14 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_CONFIG_H
+#define NAMED_CONFIG_H 1
+
+/*! \file */
+
+#include <isccfg/cfg.h>
+
+#include <dns/types.h>
+#include <dns/zone.h>
+
+isc_result_t
+ns_config_parsedefaults(cfg_parser_t *parser, cfg_obj_t **conf);
+
+isc_result_t
+ns_config_get(const cfg_obj_t **maps, const char* name, const cfg_obj_t **obj);
+
+isc_result_t
+ns_checknames_get(const cfg_obj_t **maps, const char* name,
+ const cfg_obj_t **obj);
+
+int
+ns_config_listcount(const cfg_obj_t *list);
+
+isc_result_t
+ns_config_getclass(const cfg_obj_t *classobj, dns_rdataclass_t defclass,
+ dns_rdataclass_t *classp);
+
+isc_result_t
+ns_config_gettype(const cfg_obj_t *typeobj, dns_rdatatype_t deftype,
+ dns_rdatatype_t *typep);
+
+dns_zonetype_t
+ns_config_getzonetype(const cfg_obj_t *zonetypeobj);
+
+isc_result_t
+ns_config_getiplist(const cfg_obj_t *config, const cfg_obj_t *list,
+ in_port_t defport, isc_mem_t *mctx,
+ isc_sockaddr_t **addrsp, isc_uint32_t *countp);
+
+void
+ns_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ isc_uint32_t count);
+
+isc_result_t
+ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
+ isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ dns_name_t ***keys, isc_uint32_t *countp);
+
+void
+ns_config_putipandkeylist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ dns_name_t ***keys, isc_uint32_t count);
+
+isc_result_t
+ns_config_getport(const cfg_obj_t *config, in_port_t *portp);
+
+isc_result_t
+ns_config_getkeyalgorithm(const char *str, dns_name_t **name,
+ isc_uint16_t *digestbits);
+
+#endif /* NAMED_CONFIG_H */
diff --git a/bin/named/include/named/control.h b/bin/named/include/named/control.h
new file mode 100644
index 0000000..d382ffe
--- /dev/null
+++ b/bin/named/include/named/control.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2001-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: control.h,v 1.25 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_CONTROL_H
+#define NAMED_CONTROL_H 1
+
+/*! \file
+ * \brief
+ * The name server command channel.
+ */
+
+#include <isccc/types.h>
+
+#include <isccfg/aclconf.h>
+
+#include <named/types.h>
+
+#define NS_CONTROL_PORT 953
+
+#define NS_COMMAND_STOP "stop"
+#define NS_COMMAND_HALT "halt"
+#define NS_COMMAND_RELOAD "reload"
+#define NS_COMMAND_RECONFIG "reconfig"
+#define NS_COMMAND_REFRESH "refresh"
+#define NS_COMMAND_RETRANSFER "retransfer"
+#define NS_COMMAND_DUMPSTATS "stats"
+#define NS_COMMAND_QUERYLOG "querylog"
+#define NS_COMMAND_DUMPDB "dumpdb"
+#define NS_COMMAND_TRACE "trace"
+#define NS_COMMAND_NOTRACE "notrace"
+#define NS_COMMAND_FLUSH "flush"
+#define NS_COMMAND_FLUSHNAME "flushname"
+#define NS_COMMAND_STATUS "status"
+#define NS_COMMAND_TSIGLIST "tsig-list"
+#define NS_COMMAND_TSIGDELETE "tsig-delete"
+#define NS_COMMAND_FREEZE "freeze"
+#define NS_COMMAND_UNFREEZE "unfreeze"
+#define NS_COMMAND_THAW "thaw"
+#define NS_COMMAND_TIMERPOKE "timerpoke"
+#define NS_COMMAND_RECURSING "recursing"
+#define NS_COMMAND_NULL "null"
+#define NS_COMMAND_NOTIFY "notify"
+#define NS_COMMAND_VALIDATION "validation"
+
+isc_result_t
+ns_controls_create(ns_server_t *server, ns_controls_t **ctrlsp);
+/*%<
+ * Create an initial, empty set of command channels for 'server'.
+ */
+
+void
+ns_controls_destroy(ns_controls_t **ctrlsp);
+/*%<
+ * Destroy a set of command channels.
+ *
+ * Requires:
+ * Shutdown of the channels has completed.
+ */
+
+isc_result_t
+ns_controls_configure(ns_controls_t *controls, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx);
+/*%<
+ * Configure zero or more command channels into 'controls'
+ * as defined in the configuration parse tree 'config'.
+ * The channels will evaluate ACLs in the context of
+ * 'aclconfctx'.
+ */
+
+void
+ns_controls_shutdown(ns_controls_t *controls);
+/*%<
+ * Initiate shutdown of all the command channels in 'controls'.
+ */
+
+isc_result_t
+ns_control_docommand(isccc_sexpr_t *message, isc_buffer_t *text);
+
+#endif /* NAMED_CONTROL_H */
diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h
new file mode 100644
index 0000000..6040dc3
--- /dev/null
+++ b/bin/named/include/named/globals.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: globals.h,v 1.80 2008/11/16 22:49:18 marka Exp $ */
+
+#ifndef NAMED_GLOBALS_H
+#define NAMED_GLOBALS_H 1
+
+/*! \file */
+
+#include <isc/rwlock.h>
+#include <isc/log.h>
+#include <isc/net.h>
+
+#include <isccfg/cfg.h>
+
+#include <dns/zone.h>
+
+#include <named/types.h>
+
+#undef EXTERN
+#undef INIT
+#ifdef NS_MAIN
+#define EXTERN
+#define INIT(v) = (v)
+#else
+#define EXTERN extern
+#define INIT(v)
+#endif
+
+#ifndef NS_RUN_PID_DIR
+#define NS_RUN_PID_DIR 1
+#endif
+
+EXTERN isc_mem_t * ns_g_mctx INIT(NULL);
+EXTERN unsigned int ns_g_cpus INIT(0);
+EXTERN isc_taskmgr_t * ns_g_taskmgr INIT(NULL);
+EXTERN dns_dispatchmgr_t * ns_g_dispatchmgr INIT(NULL);
+EXTERN isc_entropy_t * ns_g_entropy INIT(NULL);
+EXTERN isc_entropy_t * ns_g_fallbackentropy INIT(NULL);
+EXTERN unsigned int ns_g_cpus_detected INIT(1);
+
+/*
+ * XXXRTH We're going to want multiple timer managers eventually. One
+ * for really short timers, another for client timers, and one
+ * for zone timers.
+ */
+EXTERN isc_timermgr_t * ns_g_timermgr INIT(NULL);
+EXTERN isc_socketmgr_t * ns_g_socketmgr INIT(NULL);
+EXTERN cfg_parser_t * ns_g_parser INIT(NULL);
+EXTERN const char * ns_g_version INIT(VERSION);
+EXTERN const char * ns_g_configargs INIT(CONFIGARGS);
+EXTERN in_port_t ns_g_port INIT(0);
+EXTERN in_port_t lwresd_g_listenport INIT(0);
+
+EXTERN ns_server_t * ns_g_server INIT(NULL);
+
+EXTERN isc_boolean_t ns_g_lwresdonly INIT(ISC_FALSE);
+
+/*
+ * Logging.
+ */
+EXTERN isc_log_t * ns_g_lctx INIT(NULL);
+EXTERN isc_logcategory_t * ns_g_categories INIT(NULL);
+EXTERN isc_logmodule_t * ns_g_modules INIT(NULL);
+EXTERN unsigned int ns_g_debuglevel INIT(0);
+
+/*
+ * Current configuration information.
+ */
+EXTERN cfg_obj_t * ns_g_config INIT(NULL);
+EXTERN const cfg_obj_t * ns_g_defaults INIT(NULL);
+EXTERN const char * ns_g_conffile INIT(NS_SYSCONFDIR
+ "/named.conf");
+EXTERN const char * ns_g_keyfile INIT(NS_SYSCONFDIR
+ "/rndc.key");
+EXTERN const char * lwresd_g_conffile INIT(NS_SYSCONFDIR
+ "/lwresd.conf");
+EXTERN const char * lwresd_g_resolvconffile INIT("/etc"
+ "/resolv.conf");
+EXTERN isc_boolean_t ns_g_conffileset INIT(ISC_FALSE);
+EXTERN isc_boolean_t lwresd_g_useresolvconf INIT(ISC_FALSE);
+EXTERN isc_uint16_t ns_g_udpsize INIT(4096);
+
+/*
+ * Initial resource limits.
+ */
+EXTERN isc_resourcevalue_t ns_g_initstacksize INIT(0);
+EXTERN isc_resourcevalue_t ns_g_initdatasize INIT(0);
+EXTERN isc_resourcevalue_t ns_g_initcoresize INIT(0);
+EXTERN isc_resourcevalue_t ns_g_initopenfiles INIT(0);
+
+/*
+ * Misc.
+ */
+EXTERN isc_boolean_t ns_g_coreok INIT(ISC_TRUE);
+EXTERN const char * ns_g_chrootdir INIT(NULL);
+EXTERN isc_boolean_t ns_g_foreground INIT(ISC_FALSE);
+EXTERN isc_boolean_t ns_g_logstderr INIT(ISC_FALSE);
+
+#if NS_RUN_PID_DIR
+EXTERN const char * ns_g_defaultpidfile INIT(NS_LOCALSTATEDIR
+ "/run/named/"
+ "named.pid");
+EXTERN const char * lwresd_g_defaultpidfile INIT(NS_LOCALSTATEDIR
+ "/run/lwresd/"
+ "lwresd.pid");
+#else
+EXTERN const char * ns_g_defaultpidfile INIT(NS_LOCALSTATEDIR
+ "/run/named.pid");
+EXTERN const char * lwresd_g_defaultpidfile INIT(NS_LOCALSTATEDIR
+ "/run/lwresd.pid");
+#endif
+
+EXTERN const char * ns_g_username INIT(NULL);
+
+EXTERN int ns_g_listen INIT(3);
+EXTERN isc_time_t ns_g_boottime;
+EXTERN isc_boolean_t ns_g_memstatistics INIT(ISC_FALSE);
+EXTERN isc_boolean_t ns_g_clienttest INIT(ISC_FALSE);
+
+#undef EXTERN
+#undef INIT
+
+#endif /* NAMED_GLOBALS_H */
diff --git a/bin/named/include/named/interfacemgr.h b/bin/named/include/named/interfacemgr.h
new file mode 100644
index 0000000..2724c39
--- /dev/null
+++ b/bin/named/include/named/interfacemgr.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: interfacemgr.h,v 1.33 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_INTERFACEMGR_H
+#define NAMED_INTERFACEMGR_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * The interface manager monitors the operating system's list
+ * of network interfaces, creating and destroying listeners
+ * as needed.
+ *
+ * Reliability:
+ *\li No impact expected.
+ *
+ * Resources:
+ *
+ * Security:
+ * \li The server will only be able to bind to the DNS port on
+ * newly discovered interfaces if it is running as root.
+ *
+ * Standards:
+ *\li The API for scanning varies greatly among operating systems.
+ * This module attempts to hide the differences.
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/socket.h>
+
+#include <dns/result.h>
+
+#include <named/listenlist.h>
+#include <named/types.h>
+
+/***
+ *** Types
+ ***/
+
+#define IFACE_MAGIC ISC_MAGIC('I',':','-',')')
+#define NS_INTERFACE_VALID(t) ISC_MAGIC_VALID(t, IFACE_MAGIC)
+
+#define NS_INTERFACEFLAG_ANYADDR 0x01U /*%< bound to "any" address */
+
+/*% The nameserver interface structure */
+struct ns_interface {
+ unsigned int magic; /*%< Magic number. */
+ ns_interfacemgr_t * mgr; /*%< Interface manager. */
+ isc_mutex_t lock;
+ int references; /*%< Locked */
+ unsigned int generation; /*%< Generation number. */
+ isc_sockaddr_t addr; /*%< Address and port. */
+ unsigned int flags; /*%< Interface characteristics */
+ char name[32]; /*%< Null terminated. */
+ dns_dispatch_t * udpdispatch; /*%< UDP dispatcher. */
+ isc_socket_t * tcpsocket; /*%< TCP socket. */
+ int ntcptarget; /*%< Desired number of concurrent
+ TCP accepts */
+ int ntcpcurrent; /*%< Current ditto, locked */
+ ns_clientmgr_t * clientmgr; /*%< Client manager. */
+ ISC_LINK(ns_interface_t) link;
+};
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+ns_interfacemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_socketmgr_t *socketmgr,
+ dns_dispatchmgr_t *dispatchmgr,
+ ns_interfacemgr_t **mgrp);
+/*%
+ * Create a new interface manager.
+ *
+ * Initially, the new manager will not listen on any interfaces.
+ * Call ns_interfacemgr_setlistenon() and/or ns_interfacemgr_setlistenon6()
+ * to set nonempty listen-on lists.
+ */
+
+void
+ns_interfacemgr_attach(ns_interfacemgr_t *source, ns_interfacemgr_t **target);
+
+void
+ns_interfacemgr_detach(ns_interfacemgr_t **targetp);
+
+void
+ns_interfacemgr_shutdown(ns_interfacemgr_t *mgr);
+
+void
+ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose);
+/*%
+ * Scan the operatings system's list of network interfaces
+ * and create listeners when new interfaces are discovered.
+ * Shut down the sockets for interfaces that go away.
+ *
+ * This should be called once on server startup and then
+ * periodically according to the 'interface-interval' option
+ * in named.conf.
+ */
+
+void
+ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list,
+ isc_boolean_t verbose);
+/*%
+ * Similar to ns_interfacemgr_scan(), but this function also tries to see the
+ * need for an explicit listen-on when a list element in 'list' is going to
+ * override an already-listening a wildcard interface.
+ *
+ * This function does not update localhost and localnets ACLs.
+ *
+ * This should be called once on server startup, after configuring views and
+ * zones.
+ */
+
+void
+ns_interfacemgr_setlistenon4(ns_interfacemgr_t *mgr, ns_listenlist_t *value);
+/*%
+ * Set the IPv4 "listen-on" list of 'mgr' to 'value'.
+ * The previous IPv4 listen-on list is freed.
+ */
+
+void
+ns_interfacemgr_setlistenon6(ns_interfacemgr_t *mgr, ns_listenlist_t *value);
+/*%
+ * Set the IPv6 "listen-on" list of 'mgr' to 'value'.
+ * The previous IPv6 listen-on list is freed.
+ */
+
+dns_aclenv_t *
+ns_interfacemgr_getaclenv(ns_interfacemgr_t *mgr);
+
+void
+ns_interface_attach(ns_interface_t *source, ns_interface_t **target);
+
+void
+ns_interface_detach(ns_interface_t **targetp);
+
+void
+ns_interface_shutdown(ns_interface_t *ifp);
+/*%
+ * Stop listening for queries on interface 'ifp'.
+ * May safely be called multiple times.
+ */
+
+void
+ns_interfacemgr_dumprecursing(FILE *f, ns_interfacemgr_t *mgr);
+
+isc_boolean_t
+ns_interfacemgr_listeningon(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr);
+
+#endif /* NAMED_INTERFACEMGR_H */
diff --git a/bin/named/include/named/listenlist.h b/bin/named/include/named/listenlist.h
new file mode 100644
index 0000000..9e65d5d
--- /dev/null
+++ b/bin/named/include/named/listenlist.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: listenlist.h,v 1.15 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_LISTENLIST_H
+#define NAMED_LISTENLIST_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * "Listen lists", as in the "listen-on" configuration statement.
+ */
+
+/***
+ *** Imports
+ ***/
+#include <isc/net.h>
+
+#include <dns/types.h>
+
+/***
+ *** Types
+ ***/
+
+typedef struct ns_listenelt ns_listenelt_t;
+typedef struct ns_listenlist ns_listenlist_t;
+
+struct ns_listenelt {
+ isc_mem_t * mctx;
+ in_port_t port;
+ dns_acl_t * acl;
+ ISC_LINK(ns_listenelt_t) link;
+};
+
+struct ns_listenlist {
+ isc_mem_t * mctx;
+ int refcount;
+ ISC_LIST(ns_listenelt_t) elts;
+};
+
+/***
+ *** Functions
+ ***/
+
+isc_result_t
+ns_listenelt_create(isc_mem_t *mctx, in_port_t port,
+ dns_acl_t *acl, ns_listenelt_t **target);
+/*%
+ * Create a listen-on list element.
+ */
+
+void
+ns_listenelt_destroy(ns_listenelt_t *elt);
+/*%
+ * Destroy a listen-on list element.
+ */
+
+isc_result_t
+ns_listenlist_create(isc_mem_t *mctx, ns_listenlist_t **target);
+/*%
+ * Create a new, empty listen-on list.
+ */
+
+void
+ns_listenlist_attach(ns_listenlist_t *source, ns_listenlist_t **target);
+/*%
+ * Attach '*target' to '*source'.
+ */
+
+void
+ns_listenlist_detach(ns_listenlist_t **listp);
+/*%
+ * Detach 'listp'.
+ */
+
+isc_result_t
+ns_listenlist_default(isc_mem_t *mctx, in_port_t port,
+ isc_boolean_t enabled, ns_listenlist_t **target);
+/*%
+ * Create a listen-on list with default contents, matching
+ * all addresses with port 'port' (if 'enabled' is ISC_TRUE),
+ * or no addresses (if 'enabled' is ISC_FALSE).
+ */
+
+#endif /* NAMED_LISTENLIST_H */
+
+
diff --git a/bin/named/include/named/log.h b/bin/named/include/named/log.h
new file mode 100644
index 0000000..e8a4f32
--- /dev/null
+++ b/bin/named/include/named/log.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: log.h,v 1.25 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_LOG_H
+#define NAMED_LOG_H 1
+
+/*! \file */
+
+#include <isc/log.h>
+#include <isc/types.h>
+
+#include <dns/log.h>
+
+#include <named/globals.h> /* Required for ns_g_(categories|modules). */
+
+/* Unused slot 0. */
+#define NS_LOGCATEGORY_CLIENT (&ns_g_categories[1])
+#define NS_LOGCATEGORY_NETWORK (&ns_g_categories[2])
+#define NS_LOGCATEGORY_UPDATE (&ns_g_categories[3])
+#define NS_LOGCATEGORY_QUERIES (&ns_g_categories[4])
+#define NS_LOGCATEGORY_UNMATCHED (&ns_g_categories[5])
+#define NS_LOGCATEGORY_UPDATE_SECURITY (&ns_g_categories[6])
+
+/*
+ * Backwards compatibility.
+ */
+#define NS_LOGCATEGORY_GENERAL ISC_LOGCATEGORY_GENERAL
+
+#define NS_LOGMODULE_MAIN (&ns_g_modules[0])
+#define NS_LOGMODULE_CLIENT (&ns_g_modules[1])
+#define NS_LOGMODULE_SERVER (&ns_g_modules[2])
+#define NS_LOGMODULE_QUERY (&ns_g_modules[3])
+#define NS_LOGMODULE_INTERFACEMGR (&ns_g_modules[4])
+#define NS_LOGMODULE_UPDATE (&ns_g_modules[5])
+#define NS_LOGMODULE_XFER_IN (&ns_g_modules[6])
+#define NS_LOGMODULE_XFER_OUT (&ns_g_modules[7])
+#define NS_LOGMODULE_NOTIFY (&ns_g_modules[8])
+#define NS_LOGMODULE_CONTROL (&ns_g_modules[9])
+#define NS_LOGMODULE_LWRESD (&ns_g_modules[10])
+
+isc_result_t
+ns_log_init(isc_boolean_t safe);
+/*%
+ * Initialize the logging system and set up an initial default
+ * logging default configuration that will be used until the
+ * config file has been read.
+ *
+ * If 'safe' is true, use a default configuration that refrains
+ * from opening files. This is to avoid creating log files
+ * as root.
+ */
+
+isc_result_t
+ns_log_setdefaultchannels(isc_logconfig_t *lcfg);
+/*%
+ * Set up logging channels according to the named defaults, which
+ * may differ from the logging library defaults. Currently,
+ * this just means setting up default_debug.
+ */
+
+isc_result_t
+ns_log_setsafechannels(isc_logconfig_t *lcfg);
+/*%
+ * Like ns_log_setdefaultchannels(), but omits any logging to files.
+ */
+
+isc_result_t
+ns_log_setdefaultcategory(isc_logconfig_t *lcfg);
+/*%
+ * Set up "category default" to go to the right places.
+ */
+
+isc_result_t
+ns_log_setunmatchedcategory(isc_logconfig_t *lcfg);
+/*%
+ * Set up "category unmatched" to go to the right places.
+ */
+
+void
+ns_log_shutdown(void);
+
+#endif /* NAMED_LOG_H */
diff --git a/bin/named/include/named/logconf.h b/bin/named/include/named/logconf.h
new file mode 100644
index 0000000..0354345
--- /dev/null
+++ b/bin/named/include/named/logconf.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: logconf.h,v 1.17 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_LOGCONF_H
+#define NAMED_LOGCONF_H 1
+
+/*! \file */
+
+#include <isc/log.h>
+
+isc_result_t
+ns_log_configure(isc_logconfig_t *logconf, const cfg_obj_t *logstmt);
+/*%<
+ * Set up the logging configuration in '*logconf' according to
+ * the named.conf data in 'logstmt'.
+ */
+
+#endif /* NAMED_LOGCONF_H */
diff --git a/bin/named/include/named/lwaddr.h b/bin/named/include/named/lwaddr.h
new file mode 100644
index 0000000..962aa91
--- /dev/null
+++ b/bin/named/include/named/lwaddr.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwaddr.h,v 1.8 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <lwres/lwres.h>
+#include <lwres/net.h>
+
+isc_result_t
+lwaddr_netaddr_fromlwresaddr(isc_netaddr_t *na, lwres_addr_t *la);
+
+isc_result_t
+lwaddr_sockaddr_fromlwresaddr(isc_sockaddr_t *sa, lwres_addr_t *la,
+ in_port_t port);
+
+isc_result_t
+lwaddr_lwresaddr_fromnetaddr(lwres_addr_t *la, isc_netaddr_t *na);
+
+isc_result_t
+lwaddr_lwresaddr_fromsockaddr(lwres_addr_t *la, isc_sockaddr_t *sa);
diff --git a/bin/named/include/named/lwdclient.h b/bin/named/include/named/lwdclient.h
new file mode 100644
index 0000000..176134a
--- /dev/null
+++ b/bin/named/include/named/lwdclient.h
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwdclient.h,v 1.18 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_LWDCLIENT_H
+#define NAMED_LWDCLIENT_H 1
+
+/*! \file */
+
+#include <isc/event.h>
+#include <isc/eventclass.h>
+#include <isc/netaddr.h>
+#include <isc/sockaddr.h>
+#include <isc/types.h>
+
+#include <dns/fixedname.h>
+#include <dns/types.h>
+
+#include <lwres/lwres.h>
+
+#include <named/lwsearch.h>
+
+#define LWRD_EVENTCLASS ISC_EVENTCLASS(4242)
+
+#define LWRD_SHUTDOWN (LWRD_EVENTCLASS + 0x0001)
+
+/*% Lighweight Resolver Daemon Client */
+struct ns_lwdclient {
+ isc_sockaddr_t address; /*%< where to reply */
+ struct in6_pktinfo pktinfo;
+ isc_boolean_t pktinfo_valid;
+ ns_lwdclientmgr_t *clientmgr; /*%< our parent */
+ ISC_LINK(ns_lwdclient_t) link;
+ unsigned int state;
+ void *arg; /*%< packet processing state */
+
+ /*
+ * Received data info.
+ */
+ unsigned char buffer[LWRES_RECVLENGTH]; /*%< receive buffer */
+ isc_uint32_t recvlength; /*%< length recv'd */
+ lwres_lwpacket_t pkt;
+
+ /*%
+ * Send data state. If sendbuf != buffer (that is, the send buffer
+ * isn't our receive buffer) it will be freed to the lwres_context_t.
+ */
+ unsigned char *sendbuf;
+ isc_uint32_t sendlength;
+ isc_buffer_t recv_buffer;
+
+ /*%
+ * gabn (get address by name) state info.
+ */
+ dns_adbfind_t *find;
+ dns_adbfind_t *v4find;
+ dns_adbfind_t *v6find;
+ unsigned int find_wanted; /*%< Addresses we want */
+ dns_fixedname_t query_name;
+ dns_fixedname_t target_name;
+ ns_lwsearchctx_t searchctx;
+ lwres_gabnresponse_t gabn;
+
+ /*%
+ * gnba (get name by address) state info.
+ */
+ lwres_gnbaresponse_t gnba;
+ dns_byaddr_t *byaddr;
+ unsigned int options;
+ isc_netaddr_t na;
+
+ /*%
+ * grbn (get rrset by name) state info.
+ *
+ * Note: this also uses target_name and searchctx.
+ */
+ lwres_grbnresponse_t grbn;
+ dns_lookup_t *lookup;
+ dns_rdatatype_t rdtype;
+
+ /*%
+ * Alias and address info. This is copied up to the gabn/gnba
+ * structures eventually.
+ *
+ * XXXMLG We can keep all of this in a client since we only service
+ * three packet types right now. If we started handling more,
+ * we'd need to use "arg" above and allocate/destroy things.
+ */
+ char *aliases[LWRES_MAX_ALIASES];
+ isc_uint16_t aliaslen[LWRES_MAX_ALIASES];
+ lwres_addr_t addrs[LWRES_MAX_ADDRS];
+};
+
+/*%
+ * Client states.
+ *
+ * _IDLE The client is not doing anything at all.
+ *
+ * _RECV The client is waiting for data after issuing a socket recv().
+ *
+ * _RECVDONE Data has been received, and is being processed.
+ *
+ * _FINDWAIT An adb (or other) request was made that cannot be satisfied
+ * immediately. An event will wake the client up.
+ *
+ * _SEND All data for a response has completed, and a reply was
+ * sent via a socket send() call.
+ *
+ * Badly formatted state table:
+ *
+ * IDLE -> RECV when client has a recv() queued.
+ *
+ * RECV -> RECVDONE when recvdone event received.
+ *
+ * RECVDONE -> SEND if the data for a reply is at hand.
+ * RECVDONE -> FINDWAIT if more searching is needed, and events will
+ * eventually wake us up again.
+ *
+ * FINDWAIT -> SEND when enough data was received to reply.
+ *
+ * SEND -> IDLE when a senddone event was received.
+ *
+ * At any time -> IDLE on error. Sometimes this will be -> SEND
+ * instead, if enough data is on hand to reply with a meaningful
+ * error.
+ *
+ * Packets which are badly formatted may or may not get error returns.
+ */
+#define NS_LWDCLIENT_STATEIDLE 1
+#define NS_LWDCLIENT_STATERECV 2
+#define NS_LWDCLIENT_STATERECVDONE 3
+#define NS_LWDCLIENT_STATEFINDWAIT 4
+#define NS_LWDCLIENT_STATESEND 5
+#define NS_LWDCLIENT_STATESENDDONE 6
+
+#define NS_LWDCLIENT_ISIDLE(c) \
+ ((c)->state == NS_LWDCLIENT_STATEIDLE)
+#define NS_LWDCLIENT_ISRECV(c) \
+ ((c)->state == NS_LWDCLIENT_STATERECV)
+#define NS_LWDCLIENT_ISRECVDONE(c) \
+ ((c)->state == NS_LWDCLIENT_STATERECVDONE)
+#define NS_LWDCLIENT_ISFINDWAIT(c) \
+ ((c)->state == NS_LWDCLIENT_STATEFINDWAIT)
+#define NS_LWDCLIENT_ISSEND(c) \
+ ((c)->state == NS_LWDCLIENT_STATESEND)
+
+/*%
+ * Overall magic test that means we're not idle.
+ */
+#define NS_LWDCLIENT_ISRUNNING(c) (!NS_LWDCLIENT_ISIDLE(c))
+
+#define NS_LWDCLIENT_SETIDLE(c) \
+ ((c)->state = NS_LWDCLIENT_STATEIDLE)
+#define NS_LWDCLIENT_SETRECV(c) \
+ ((c)->state = NS_LWDCLIENT_STATERECV)
+#define NS_LWDCLIENT_SETRECVDONE(c) \
+ ((c)->state = NS_LWDCLIENT_STATERECVDONE)
+#define NS_LWDCLIENT_SETFINDWAIT(c) \
+ ((c)->state = NS_LWDCLIENT_STATEFINDWAIT)
+#define NS_LWDCLIENT_SETSEND(c) \
+ ((c)->state = NS_LWDCLIENT_STATESEND)
+#define NS_LWDCLIENT_SETSENDDONE(c) \
+ ((c)->state = NS_LWDCLIENT_STATESENDDONE)
+
+/*% lightweight daemon client manager */
+struct ns_lwdclientmgr {
+ ns_lwreslistener_t *listener;
+ isc_mem_t *mctx;
+ isc_socket_t *sock; /*%< socket to use */
+ dns_view_t *view;
+ lwres_context_t *lwctx; /*%< lightweight proto context */
+ isc_task_t *task; /*%< owning task */
+ unsigned int flags;
+ ISC_LINK(ns_lwdclientmgr_t) link;
+ ISC_LIST(ns_lwdclient_t) idle; /*%< idle client slots */
+ ISC_LIST(ns_lwdclient_t) running; /*%< running clients */
+};
+
+#define NS_LWDCLIENTMGR_FLAGRECVPENDING 0x00000001
+#define NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN 0x00000002
+
+isc_result_t
+ns_lwdclientmgr_create(ns_lwreslistener_t *, unsigned int, isc_taskmgr_t *);
+
+void
+ns_lwdclient_initialize(ns_lwdclient_t *, ns_lwdclientmgr_t *);
+
+isc_result_t
+ns_lwdclient_startrecv(ns_lwdclientmgr_t *);
+
+void
+ns_lwdclient_stateidle(ns_lwdclient_t *);
+
+void
+ns_lwdclient_recv(isc_task_t *, isc_event_t *);
+
+void
+ns_lwdclient_shutdown(isc_task_t *, isc_event_t *);
+
+void
+ns_lwdclient_send(isc_task_t *, isc_event_t *);
+
+isc_result_t
+ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r);
+
+/*
+ * Processing functions of various types.
+ */
+void ns_lwdclient_processgabn(ns_lwdclient_t *, lwres_buffer_t *);
+void ns_lwdclient_processgnba(ns_lwdclient_t *, lwres_buffer_t *);
+void ns_lwdclient_processgrbn(ns_lwdclient_t *, lwres_buffer_t *);
+void ns_lwdclient_processnoop(ns_lwdclient_t *, lwres_buffer_t *);
+
+void ns_lwdclient_errorpktsend(ns_lwdclient_t *, isc_uint32_t);
+
+void ns_lwdclient_log(int level, const char *format, ...)
+ ISC_FORMAT_PRINTF(2, 3);
+
+#endif /* NAMED_LWDCLIENT_H */
diff --git a/bin/named/include/named/lwresd.h b/bin/named/include/named/lwresd.h
new file mode 100644
index 0000000..565e58d
--- /dev/null
+++ b/bin/named/include/named/lwresd.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwresd.h,v 1.19 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_LWRESD_H
+#define NAMED_LWRESD_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+#include <isc/sockaddr.h>
+
+#include <isccfg/cfg.h>
+
+#include <dns/types.h>
+
+struct ns_lwresd {
+ unsigned int magic;
+
+ isc_mutex_t lock;
+ dns_view_t *view;
+ ns_lwsearchlist_t *search;
+ unsigned int ndots;
+ isc_mem_t *mctx;
+ isc_boolean_t shutting_down;
+ unsigned int refs;
+};
+
+struct ns_lwreslistener {
+ unsigned int magic;
+
+ isc_mutex_t lock;
+ isc_mem_t *mctx;
+ isc_sockaddr_t address;
+ ns_lwresd_t *manager;
+ isc_socket_t *sock;
+ unsigned int refs;
+ ISC_LIST(ns_lwdclientmgr_t) cmgrs;
+ ISC_LINK(ns_lwreslistener_t) link;
+};
+
+/*%
+ * Configure lwresd.
+ */
+isc_result_t
+ns_lwresd_configure(isc_mem_t *mctx, const cfg_obj_t *config);
+
+isc_result_t
+ns_lwresd_parseeresolvconf(isc_mem_t *mctx, cfg_parser_t *pctx,
+ cfg_obj_t **configp);
+
+/*%
+ * Trigger shutdown.
+ */
+void
+ns_lwresd_shutdown(void);
+
+/*
+ * Manager functions
+ */
+/*% create manager */
+isc_result_t
+ns_lwdmanager_create(isc_mem_t *mctx, const cfg_obj_t *lwres,
+ ns_lwresd_t **lwresdp);
+
+/*% attach to manager */
+void
+ns_lwdmanager_attach(ns_lwresd_t *source, ns_lwresd_t **targetp);
+
+/*% detach from manager */
+void
+ns_lwdmanager_detach(ns_lwresd_t **lwresdp);
+
+/*
+ * Listener functions
+ */
+/*% attach to listener */
+void
+ns_lwreslistener_attach(ns_lwreslistener_t *source,
+ ns_lwreslistener_t **targetp);
+
+/*% detach from lister */
+void
+ns_lwreslistener_detach(ns_lwreslistener_t **listenerp);
+
+/*% link client manager */
+void
+ns_lwreslistener_unlinkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm);
+
+/*% unlink client manager */
+void
+ns_lwreslistener_linkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm);
+
+
+
+
+/*
+ * INTERNAL FUNCTIONS.
+ */
+void *
+ns__lwresd_memalloc(void *arg, size_t size);
+
+void
+ns__lwresd_memfree(void *arg, void *mem, size_t size);
+
+#endif /* NAMED_LWRESD_H */
diff --git a/bin/named/include/named/lwsearch.h b/bin/named/include/named/lwsearch.h
new file mode 100644
index 0000000..c1b4f48
--- /dev/null
+++ b/bin/named/include/named/lwsearch.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwsearch.h,v 1.9 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_LWSEARCH_H
+#define NAMED_LWSEARCH_H 1
+
+#include <isc/mutex.h>
+#include <isc/result.h>
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+#include <named/types.h>
+
+/*! \file
+ * \brief
+ * Lightweight resolver search list types and routines.
+ *
+ * An ns_lwsearchlist_t holds a list of search path elements.
+ *
+ * An ns_lwsearchctx stores the state of search list during a lookup
+ * operation.
+ */
+
+/*% An ns_lwsearchlist_t holds a list of search path elements. */
+struct ns_lwsearchlist {
+ unsigned int magic;
+
+ isc_mutex_t lock;
+ isc_mem_t *mctx;
+ unsigned int refs;
+ dns_namelist_t names;
+};
+/*% An ns_lwsearchctx stores the state of search list during a lookup operation. */
+struct ns_lwsearchctx {
+ dns_name_t *relname;
+ dns_name_t *searchname;
+ unsigned int ndots;
+ ns_lwsearchlist_t *list;
+ isc_boolean_t doneexact;
+ isc_boolean_t exactfirst;
+};
+
+isc_result_t
+ns_lwsearchlist_create(isc_mem_t *mctx, ns_lwsearchlist_t **listp);
+/*%<
+ * Create an empty search list object.
+ */
+
+void
+ns_lwsearchlist_attach(ns_lwsearchlist_t *source, ns_lwsearchlist_t **target);
+/*%<
+ * Attach to a search list object.
+ */
+
+void
+ns_lwsearchlist_detach(ns_lwsearchlist_t **listp);
+/*%<
+ * Detach from a search list object.
+ */
+
+isc_result_t
+ns_lwsearchlist_append(ns_lwsearchlist_t *list, dns_name_t *name);
+/*%<
+ * Append an element to a search list. This creates a copy of the name.
+ */
+
+void
+ns_lwsearchctx_init(ns_lwsearchctx_t *sctx, ns_lwsearchlist_t *list,
+ dns_name_t *name, unsigned int ndots);
+/*%<
+ * Creates a search list context structure.
+ */
+
+void
+ns_lwsearchctx_first(ns_lwsearchctx_t *sctx);
+/*%<
+ * Moves the search list context iterator to the first element, which
+ * is usually the exact name.
+ */
+
+isc_result_t
+ns_lwsearchctx_next(ns_lwsearchctx_t *sctx);
+/*%<
+ * Moves the search list context iterator to the next element.
+ */
+
+isc_result_t
+ns_lwsearchctx_current(ns_lwsearchctx_t *sctx, dns_name_t *absname);
+/*%<
+ * Obtains the current name to be looked up. This involves either
+ * concatenating the name with a search path element, making an
+ * exact name absolute, or doing nothing.
+ */
+
+#endif /* NAMED_LWSEARCH_H */
diff --git a/bin/named/include/named/main.h b/bin/named/include/named/main.h
new file mode 100644
index 0000000..e834539
--- /dev/null
+++ b/bin/named/include/named/main.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: main.h,v 1.15 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_MAIN_H
+#define NAMED_MAIN_H 1
+
+/*! \file */
+
+void
+ns_main_earlyfatal(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
+
+void
+ns_main_earlywarning(const char *format, ...) ISC_FORMAT_PRINTF(1, 2);
+
+void
+ns_main_setmemstats(const char *);
+
+#endif /* NAMED_MAIN_H */
diff --git a/bin/named/include/named/notify.h b/bin/named/include/named/notify.h
new file mode 100644
index 0000000..6205d7d
--- /dev/null
+++ b/bin/named/include/named/notify.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: notify.h,v 1.14 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_NOTIFY_H
+#define NAMED_NOTIFY_H 1
+
+#include <named/types.h>
+#include <named/client.h>
+
+/***
+ *** Module Info
+ ***/
+
+/*! \file
+ * \brief
+ * RFC1996
+ * A Mechanism for Prompt Notification of Zone Changes (DNS NOTIFY)
+ */
+
+/***
+ *** Functions.
+ ***/
+
+void
+ns_notify_start(ns_client_t *client);
+
+/*%<
+ * Examines the incoming message to determine apporiate zone.
+ * Returns FORMERR if there is not exactly one question.
+ * Returns REFUSED if we do not serve the listed zone.
+ * Pass the message to the zone module for processing
+ * and returns the return status.
+ *
+ * Requires
+ *\li client to be valid.
+ */
+
+#endif /* NAMED_NOTIFY_H */
+
diff --git a/bin/named/include/named/ns_smf_globals.h b/bin/named/include/named/ns_smf_globals.h
new file mode 100644
index 0000000..3a35743
--- /dev/null
+++ b/bin/named/include/named/ns_smf_globals.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: ns_smf_globals.h,v 1.7 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NS_SMF_GLOBALS_H
+#define NS_SMF_GLOBALS_H 1
+
+#include <libscf.h>
+
+#undef EXTERN
+#undef INIT
+#ifdef NS_MAIN
+#define EXTERN
+#define INIT(v) = (v)
+#else
+#define EXTERN extern
+#define INIT(v)
+#endif
+
+EXTERN unsigned int ns_smf_got_instance INIT(0);
+EXTERN unsigned int ns_smf_chroot INIT(0);
+EXTERN unsigned int ns_smf_want_disable INIT(0);
+
+isc_result_t ns_smf_add_message(isc_buffer_t *text);
+isc_result_t ns_smf_get_instance(char **name, int debug, isc_mem_t *mctx);
+
+#undef EXTERN
+#undef INIT
+
+#endif /* NS_SMF_GLOBALS_H */
diff --git a/bin/named/include/named/query.h b/bin/named/include/named/query.h
new file mode 100644
index 0000000..500b577
--- /dev/null
+++ b/bin/named/include/named/query.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: query.h,v 1.40 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_QUERY_H
+#define NAMED_QUERY_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+#include <isc/buffer.h>
+#include <isc/netaddr.h>
+
+#include <dns/types.h>
+
+#include <named/types.h>
+
+/*% nameserver database version structure */
+typedef struct ns_dbversion {
+ dns_db_t *db;
+ dns_dbversion_t *version;
+ isc_boolean_t queryok;
+ ISC_LINK(struct ns_dbversion) link;
+} ns_dbversion_t;
+
+/*% nameserver query structure */
+struct ns_query {
+ unsigned int attributes;
+ unsigned int restarts;
+ isc_boolean_t timerset;
+ dns_name_t * qname;
+ dns_name_t * origqname;
+ unsigned int dboptions;
+ unsigned int fetchoptions;
+ dns_db_t * gluedb;
+ dns_db_t * authdb;
+ dns_zone_t * authzone;
+ isc_boolean_t authdbset;
+ isc_boolean_t isreferral;
+ isc_mutex_t fetchlock;
+ dns_fetch_t * fetch;
+ isc_bufferlist_t namebufs;
+ ISC_LIST(ns_dbversion_t) activeversions;
+ ISC_LIST(ns_dbversion_t) freeversions;
+};
+
+#define NS_QUERYATTR_RECURSIONOK 0x0001
+#define NS_QUERYATTR_CACHEOK 0x0002
+#define NS_QUERYATTR_PARTIALANSWER 0x0004
+#define NS_QUERYATTR_NAMEBUFUSED 0x0008
+#define NS_QUERYATTR_RECURSING 0x0010
+#define NS_QUERYATTR_CACHEGLUEOK 0x0020
+#define NS_QUERYATTR_QUERYOKVALID 0x0040
+#define NS_QUERYATTR_QUERYOK 0x0080
+#define NS_QUERYATTR_WANTRECURSION 0x0100
+#define NS_QUERYATTR_SECURE 0x0200
+#define NS_QUERYATTR_NOAUTHORITY 0x0400
+#define NS_QUERYATTR_NOADDITIONAL 0x0800
+
+isc_result_t
+ns_query_init(ns_client_t *client);
+
+void
+ns_query_free(ns_client_t *client);
+
+void
+ns_query_start(ns_client_t *client);
+
+void
+ns_query_cancel(ns_client_t *client);
+
+#endif /* NAMED_QUERY_H */
diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h
new file mode 100644
index 0000000..d3a75fd
--- /dev/null
+++ b/bin/named/include/named/server.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: server.h,v 1.93 2008/04/03 05:55:51 marka Exp $ */
+
+#ifndef NAMED_SERVER_H
+#define NAMED_SERVER_H 1
+
+/*! \file */
+
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/quota.h>
+#include <isc/sockaddr.h>
+#include <isc/types.h>
+#include <isc/xml.h>
+
+#include <dns/acl.h>
+#include <dns/types.h>
+
+#include <named/types.h>
+
+#define NS_EVENTCLASS ISC_EVENTCLASS(0x4E43)
+#define NS_EVENT_RELOAD (NS_EVENTCLASS + 0)
+#define NS_EVENT_CLIENTCONTROL (NS_EVENTCLASS + 1)
+
+/*%
+ * Name server state. Better here than in lots of separate global variables.
+ */
+struct ns_server {
+ unsigned int magic;
+ isc_mem_t * mctx;
+
+ isc_task_t * task;
+
+ /* Configurable data. */
+ isc_quota_t xfroutquota;
+ isc_quota_t tcpquota;
+ isc_quota_t recursionquota;
+ dns_acl_t *blackholeacl;
+ char * statsfile; /*%< Statistics file name */
+ char * dumpfile; /*%< Dump file name */
+ char * recfile; /*%< Recursive file name */
+ isc_boolean_t version_set; /*%< User has set version */
+ char * version; /*%< User-specified version */
+ isc_boolean_t hostname_set; /*%< User has set hostname */
+ char * hostname; /*%< User-specified hostname */
+ /*% Use hostname for server id */
+ isc_boolean_t server_usehostname;
+ char * server_id; /*%< User-specified server id */
+
+ /*%
+ * Current ACL environment. This defines the
+ * current values of the localhost and localnets
+ * ACLs.
+ */
+ dns_aclenv_t aclenv;
+
+ /* Server data structures. */
+ dns_loadmgr_t * loadmgr;
+ dns_zonemgr_t * zonemgr;
+ dns_viewlist_t viewlist;
+ ns_interfacemgr_t * interfacemgr;
+ dns_db_t * in_roothints;
+ dns_tkeyctx_t * tkeyctx;
+
+ isc_timer_t * interface_timer;
+ isc_timer_t * heartbeat_timer;
+ isc_timer_t * pps_timer;
+
+ isc_uint32_t interface_interval;
+ isc_uint32_t heartbeat_interval;
+
+ isc_mutex_t reload_event_lock;
+ isc_event_t * reload_event;
+
+ isc_boolean_t flushonshutdown;
+ isc_boolean_t log_queries; /*%< For BIND 8 compatibility */
+
+ dns_stats_t * nsstats; /*%< Server statistics */
+ dns_stats_t * rcvquerystats; /*% Incoming query statistics */
+ dns_stats_t * opcodestats; /*%< Incoming message statistics */
+ dns_stats_t * zonestats; /*% Zone management statistics */
+ dns_stats_t * resolverstats; /*% Resolver statistics */
+
+ ns_controls_t * controls; /*%< Control channels */
+ unsigned int dispatchgen;
+ ns_dispatchlist_t dispatches;
+
+ dns_acache_t *acache;
+
+ ns_statschannellist_t statschannels;
+};
+
+#define NS_SERVER_MAGIC ISC_MAGIC('S','V','E','R')
+#define NS_SERVER_VALID(s) ISC_MAGIC_VALID(s, NS_SERVER_MAGIC)
+
+/*%
+ * Server statistics counters. Used as dns_statscounter_t values.
+ */
+enum {
+ dns_nsstatscounter_requestv4 = 0,
+ dns_nsstatscounter_requestv6 = 1,
+ dns_nsstatscounter_edns0in = 2,
+ dns_nsstatscounter_badednsver = 3,
+ dns_nsstatscounter_tsigin = 4,
+ dns_nsstatscounter_sig0in = 5,
+ dns_nsstatscounter_invalidsig = 6,
+ dns_nsstatscounter_tcp = 7,
+
+ dns_nsstatscounter_authrej = 8,
+ dns_nsstatscounter_recurserej = 9,
+ dns_nsstatscounter_xfrrej = 10,
+ dns_nsstatscounter_updaterej = 11,
+
+ dns_nsstatscounter_response = 12,
+ dns_nsstatscounter_truncatedresp = 13,
+ dns_nsstatscounter_edns0out = 14,
+ dns_nsstatscounter_tsigout = 15,
+ dns_nsstatscounter_sig0out = 16,
+
+ dns_nsstatscounter_success = 17,
+ dns_nsstatscounter_authans = 18,
+ dns_nsstatscounter_nonauthans = 19,
+ dns_nsstatscounter_referral = 20,
+ dns_nsstatscounter_nxrrset = 21,
+ dns_nsstatscounter_servfail = 22,
+ dns_nsstatscounter_formerr = 23,
+ dns_nsstatscounter_nxdomain = 24,
+ dns_nsstatscounter_recursion = 25,
+ dns_nsstatscounter_duplicate = 26,
+ dns_nsstatscounter_dropped = 27,
+ dns_nsstatscounter_failure = 28,
+
+ dns_nsstatscounter_xfrdone = 29,
+
+ dns_nsstatscounter_updatereqfwd = 30,
+ dns_nsstatscounter_updaterespfwd = 31,
+ dns_nsstatscounter_updatefwdfail = 32,
+ dns_nsstatscounter_updatedone = 33,
+ dns_nsstatscounter_updatefail = 34,
+ dns_nsstatscounter_updatebadprereq = 35,
+
+ dns_nsstatscounter_max = 36
+};
+
+void
+ns_server_create(isc_mem_t *mctx, ns_server_t **serverp);
+/*%<
+ * Create a server object with default settings.
+ * This function either succeeds or causes the program to exit
+ * with a fatal error.
+ */
+
+void
+ns_server_destroy(ns_server_t **serverp);
+/*%<
+ * Destroy a server object, freeing its memory.
+ */
+
+void
+ns_server_reloadwanted(ns_server_t *server);
+/*%<
+ * Inform a server that a reload is wanted. This function
+ * may be called asynchronously, from outside the server's task.
+ * If a reload is already scheduled or in progress, the call
+ * is ignored.
+ */
+
+void
+ns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush);
+/*%<
+ * Inform the server that the zones should be flushed to disk on shutdown.
+ */
+
+isc_result_t
+ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text);
+/*%<
+ * Act on a "reload" command from the command channel.
+ */
+
+isc_result_t
+ns_server_reconfigcommand(ns_server_t *server, char *args);
+/*%<
+ * Act on a "reconfig" command from the command channel.
+ */
+
+isc_result_t
+ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text);
+/*%<
+ * Act on a "notify" command from the command channel.
+ */
+
+isc_result_t
+ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text);
+/*%<
+ * Act on a "refresh" command from the command channel.
+ */
+
+isc_result_t
+ns_server_retransfercommand(ns_server_t *server, char *args);
+/*%<
+ * Act on a "retransfer" command from the command channel.
+ */
+
+isc_result_t
+ns_server_togglequerylog(ns_server_t *server);
+/*%<
+ * Toggle logging of queries, as in BIND 8.
+ */
+
+/*%
+ * Dump the current statistics to the statistics file.
+ */
+isc_result_t
+ns_server_dumpstats(ns_server_t *server);
+
+/*%
+ * Dump the current cache to the dump file.
+ */
+isc_result_t
+ns_server_dumpdb(ns_server_t *server, char *args);
+
+/*%
+ * Change or increment the server debug level.
+ */
+isc_result_t
+ns_server_setdebuglevel(ns_server_t *server, char *args);
+
+/*%
+ * Flush the server's cache(s)
+ */
+isc_result_t
+ns_server_flushcache(ns_server_t *server, char *args);
+
+/*%
+ * Flush a particular name from the server's cache(s)
+ */
+isc_result_t
+ns_server_flushname(ns_server_t *server, char *args);
+
+/*%
+ * Report the server's status.
+ */
+isc_result_t
+ns_server_status(ns_server_t *server, isc_buffer_t *text);
+
+/*%
+ * Report a list of dynamic and static tsig keys, per view.
+ */
+isc_result_t
+ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text);
+
+/*%
+ * Delete a specific key (with optional view).
+ */
+isc_result_t
+ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text);
+
+/*%
+ * Enable or disable updates for a zone.
+ */
+isc_result_t
+ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args);
+
+/*%
+ * Dump the current recursive queries.
+ */
+isc_result_t
+ns_server_dumprecursing(ns_server_t *server);
+
+/*%
+ * Maintain a list of dispatches that require reserved ports.
+ */
+void
+ns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr);
+
+/*%
+ * Enable or disable dnssec validation.
+ */
+isc_result_t
+ns_server_validation(ns_server_t *server, char *args);
+
+#endif /* NAMED_SERVER_H */
diff --git a/bin/named/include/named/sortlist.h b/bin/named/include/named/sortlist.h
new file mode 100644
index 0000000..b9f6076
--- /dev/null
+++ b/bin/named/include/named/sortlist.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: sortlist.h,v 1.11 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_SORTLIST_H
+#define NAMED_SORTLIST_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+
+#include <dns/types.h>
+
+/*%
+ * Type for callback functions that rank addresses.
+ */
+typedef int
+(*dns_addressorderfunc_t)(const isc_netaddr_t *address, const void *arg);
+
+/*%
+ * Return value type for setup_sortlist.
+ */
+typedef enum {
+ NS_SORTLISTTYPE_NONE,
+ NS_SORTLISTTYPE_1ELEMENT,
+ NS_SORTLISTTYPE_2ELEMENT
+} ns_sortlisttype_t;
+
+ns_sortlisttype_t
+ns_sortlist_setup(dns_acl_t *acl, isc_netaddr_t *clientaddr,
+ const void **argp);
+/*%<
+ * Find the sortlist statement in 'acl' that applies to 'clientaddr', if any.
+ *
+ * If a 1-element sortlist item applies, return NS_SORTLISTTYPE_1ELEMENT and
+ * make '*argp' point to the matching subelement.
+ *
+ * If a 2-element sortlist item applies, return NS_SORTLISTTYPE_2ELEMENT and
+ * make '*argp' point to ACL that forms the second element.
+ *
+ * If no sortlist item applies, return NS_SORTLISTTYPE_NONE and set '*argp'
+ * to NULL.
+ */
+
+int
+ns_sortlist_addrorder1(const isc_netaddr_t *addr, const void *arg);
+/*%<
+ * Find the sort order of 'addr' in 'arg', the matching element
+ * of a 1-element top-level sortlist statement.
+ */
+
+int
+ns_sortlist_addrorder2(const isc_netaddr_t *addr, const void *arg);
+/*%<
+ * Find the sort order of 'addr' in 'arg', a topology-like
+ * ACL forming the second element in a 2-element top-level
+ * sortlist statement.
+ */
+
+void
+ns_sortlist_byaddrsetup(dns_acl_t *sortlist_acl, isc_netaddr_t *client_addr,
+ dns_addressorderfunc_t *orderp,
+ const void **argp);
+/*%<
+ * Find the sortlist statement in 'acl' that applies to 'clientaddr', if any.
+ * If a sortlist statement applies, return in '*orderp' a pointer to a function
+ * for ranking network addresses based on that sortlist statement, and in
+ * '*argp' an argument to pass to said function. If no sortlist statement
+ * applies, set '*orderp' and '*argp' to NULL.
+ */
+
+#endif /* NAMED_SORTLIST_H */
diff --git a/bin/named/include/named/statschannel.h b/bin/named/include/named/statschannel.h
new file mode 100644
index 0000000..0c36d8c
--- /dev/null
+++ b/bin/named/include/named/statschannel.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: statschannel.h,v 1.3 2008/04/03 05:55:51 marka Exp $ */
+
+#ifndef NAMED_STATSCHANNEL_H
+#define NAMED_STATSCHANNEL_H 1
+
+/*! \file
+ * \brief
+ * The statistics channels built-in the name server.
+ */
+
+#include <isccc/types.h>
+
+#include <isccfg/aclconf.h>
+
+#include <named/types.h>
+
+#define NS_STATSCHANNEL_HTTPPORT 80
+
+isc_result_t
+ns_statschannels_configure(ns_server_t *server, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx);
+/*%<
+ * [Re]configure the statistics channels.
+ *
+ * If it is no longer there but was previously configured, destroy
+ * it here.
+ *
+ * If the IP address or port has changed, destroy the old server
+ * and create a new one.
+ */
+
+
+void
+ns_statschannels_shutdown(ns_server_t *server);
+/*%<
+ * Initiate shutdown of all the statistics channel listeners.
+ */
+
+isc_result_t
+ns_stats_dump(ns_server_t *server, FILE *fp);
+/*%<
+ * Dump statistics counters managed by the server to the file fp.
+ */
+
+#endif /* NAMED_STATSCHANNEL_H */
diff --git a/bin/named/include/named/tkeyconf.h b/bin/named/include/named/tkeyconf.h
new file mode 100644
index 0000000..02bd718
--- /dev/null
+++ b/bin/named/include/named/tkeyconf.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: tkeyconf.h,v 1.16 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NS_TKEYCONF_H
+#define NS_TKEYCONF_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+#include <isc/lang.h>
+
+#include <isccfg/cfg.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+ns_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx,
+ isc_entropy_t *ectx, dns_tkeyctx_t **tctxp);
+/*%<
+ * Create a TKEY context and configure it, including the default DH key
+ * and default domain, according to 'options'.
+ *
+ * Requires:
+ *\li 'cfg' is a valid configuration options object.
+ *\li 'mctx' is not NULL
+ *\li 'ectx' is not NULL
+ *\li 'tctx' is not NULL
+ *\li '*tctx' is NULL
+ *
+ * Returns:
+ *\li ISC_R_SUCCESS
+ *\li ISC_R_NOMEMORY
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* NS_TKEYCONF_H */
diff --git a/bin/named/include/named/tsigconf.h b/bin/named/include/named/tsigconf.h
new file mode 100644
index 0000000..49ad82a
--- /dev/null
+++ b/bin/named/include/named/tsigconf.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: tsigconf.h,v 1.16 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NS_TSIGCONF_H
+#define NS_TSIGCONF_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+ns_tsigkeyring_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_tsig_keyring_t **ringp);
+/*%<
+ * Create a TSIG key ring and configure it according to the 'key'
+ * statements in the global and view configuration objects.
+ *
+ * Requires:
+ * \li 'config' is not NULL.
+ * \li 'mctx' is not NULL
+ * \li 'ring' is not NULL, and '*ring' is NULL
+ *
+ * Returns:
+ * \li ISC_R_SUCCESS
+ * \li ISC_R_NOMEMORY
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* NS_TSIGCONF_H */
diff --git a/bin/named/include/named/types.h b/bin/named/include/named/types.h
new file mode 100644
index 0000000..eb25520
--- /dev/null
+++ b/bin/named/include/named/types.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: types.h,v 1.29 2008/01/17 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_TYPES_H
+#define NAMED_TYPES_H 1
+
+/*! \file */
+
+#include <dns/types.h>
+
+typedef struct ns_client ns_client_t;
+typedef struct ns_clientmgr ns_clientmgr_t;
+typedef struct ns_query ns_query_t;
+typedef struct ns_server ns_server_t;
+typedef struct ns_xmld ns_xmld_t;
+typedef struct ns_xmldmgr ns_xmldmgr_t;
+typedef struct ns_interface ns_interface_t;
+typedef struct ns_interfacemgr ns_interfacemgr_t;
+typedef struct ns_lwresd ns_lwresd_t;
+typedef struct ns_lwreslistener ns_lwreslistener_t;
+typedef struct ns_lwdclient ns_lwdclient_t;
+typedef struct ns_lwdclientmgr ns_lwdclientmgr_t;
+typedef struct ns_lwsearchlist ns_lwsearchlist_t;
+typedef struct ns_lwsearchctx ns_lwsearchctx_t;
+typedef struct ns_controls ns_controls_t;
+typedef struct ns_dispatch ns_dispatch_t;
+typedef ISC_LIST(ns_dispatch_t) ns_dispatchlist_t;
+typedef struct ns_statschannel ns_statschannel_t;
+typedef ISC_LIST(ns_statschannel_t) ns_statschannellist_t;
+#endif /* NAMED_TYPES_H */
diff --git a/bin/named/include/named/update.h b/bin/named/include/named/update.h
new file mode 100644
index 0000000..a34570c
--- /dev/null
+++ b/bin/named/include/named/update.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: update.h,v 1.13 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_UPDATE_H
+#define NAMED_UPDATE_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * RFC2136 Dynamic Update
+ */
+
+/***
+ *** Imports
+ ***/
+
+#include <dns/types.h>
+#include <dns/result.h>
+
+/***
+ *** Types.
+ ***/
+
+/***
+ *** Functions
+ ***/
+
+void
+ns_update_start(ns_client_t *client, isc_result_t sigresult);
+
+#endif /* NAMED_UPDATE_H */
diff --git a/bin/named/include/named/xfrout.h b/bin/named/include/named/xfrout.h
new file mode 100644
index 0000000..4bb79a3
--- /dev/null
+++ b/bin/named/include/named/xfrout.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: xfrout.h,v 1.12 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NAMED_XFROUT_H
+#define NAMED_XFROUT_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*! \file
+ * \brief
+ * Outgoing zone transfers (AXFR + IXFR).
+ */
+
+/***
+ *** Functions
+ ***/
+
+void
+ns_xfr_start(ns_client_t *client, dns_rdatatype_t xfrtype);
+
+#endif /* NAMED_XFROUT_H */
diff --git a/bin/named/include/named/zoneconf.h b/bin/named/include/named/zoneconf.h
new file mode 100644
index 0000000..b973013
--- /dev/null
+++ b/bin/named/include/named/zoneconf.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: zoneconf.h,v 1.26 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NS_ZONECONF_H
+#define NS_ZONECONF_H 1
+
+/*! \file */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+#include <isccfg/aclconf.h>
+#include <isccfg/cfg.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac,
+ dns_zone_t *zone);
+/*%<
+ * Configure or reconfigure a zone according to the named.conf
+ * data in 'cctx' and 'czone'.
+ *
+ * The zone origin is not configured, it is assumed to have been set
+ * at zone creation time.
+ *
+ * Require:
+ * \li 'lctx' to be initialized or NULL.
+ * \li 'cctx' to be initialized or NULL.
+ * \li 'ac' to point to an initialized ns_aclconfctx_t.
+ * \li 'czone' to be initialized.
+ * \li 'zone' to be initialized.
+ */
+
+isc_boolean_t
+ns_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig);
+/*%<
+ * If 'zone' can be safely reconfigured according to the configuration
+ * data in 'zconfig', return ISC_TRUE. If the configuration data is so
+ * different from the current zone state that the zone needs to be destroyed
+ * and recreated, return ISC_FALSE.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* NS_ZONECONF_H */
diff --git a/bin/named/interfacemgr.c b/bin/named/interfacemgr.c
new file mode 100644
index 0000000..897de28
--- /dev/null
+++ b/bin/named/interfacemgr.c
@@ -0,0 +1,974 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: interfacemgr.c,v 1.93 2008/07/23 23:27:54 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/interfaceiter.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/dispatch.h>
+
+#include <named/client.h>
+#include <named/log.h>
+#include <named/interfacemgr.h>
+
+#define IFMGR_MAGIC ISC_MAGIC('I', 'F', 'M', 'G')
+#define NS_INTERFACEMGR_VALID(t) ISC_MAGIC_VALID(t, IFMGR_MAGIC)
+
+#define IFMGR_COMMON_LOGARGS \
+ ns_g_lctx, NS_LOGCATEGORY_NETWORK, NS_LOGMODULE_INTERFACEMGR
+
+/*% nameserver interface manager structure */
+struct ns_interfacemgr {
+ unsigned int magic; /*%< Magic number. */
+ int references;
+ isc_mutex_t lock;
+ isc_mem_t * mctx; /*%< Memory context. */
+ isc_taskmgr_t * taskmgr; /*%< Task manager. */
+ isc_socketmgr_t * socketmgr; /*%< Socket manager. */
+ dns_dispatchmgr_t * dispatchmgr;
+ unsigned int generation; /*%< Current generation no. */
+ ns_listenlist_t * listenon4;
+ ns_listenlist_t * listenon6;
+ dns_aclenv_t aclenv; /*%< Localhost/localnets ACLs */
+ ISC_LIST(ns_interface_t) interfaces; /*%< List of interfaces. */
+ ISC_LIST(isc_sockaddr_t) listenon;
+};
+
+static void
+purge_old_interfaces(ns_interfacemgr_t *mgr);
+
+static void
+clearlistenon(ns_interfacemgr_t *mgr);
+
+isc_result_t
+ns_interfacemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_socketmgr_t *socketmgr,
+ dns_dispatchmgr_t *dispatchmgr,
+ ns_interfacemgr_t **mgrp)
+{
+ isc_result_t result;
+ ns_interfacemgr_t *mgr;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(mgrp != NULL);
+ REQUIRE(*mgrp == NULL);
+
+ mgr = isc_mem_get(mctx, sizeof(*mgr));
+ if (mgr == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&mgr->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mem;
+
+ mgr->mctx = mctx;
+ mgr->taskmgr = taskmgr;
+ mgr->socketmgr = socketmgr;
+ mgr->dispatchmgr = dispatchmgr;
+ mgr->generation = 1;
+ mgr->listenon4 = NULL;
+ mgr->listenon6 = NULL;
+
+ ISC_LIST_INIT(mgr->interfaces);
+ ISC_LIST_INIT(mgr->listenon);
+
+ /*
+ * The listen-on lists are initially empty.
+ */
+ result = ns_listenlist_create(mctx, &mgr->listenon4);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mem;
+ ns_listenlist_attach(mgr->listenon4, &mgr->listenon6);
+
+ result = dns_aclenv_init(mctx, &mgr->aclenv);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_listenon;
+
+ mgr->references = 1;
+ mgr->magic = IFMGR_MAGIC;
+ *mgrp = mgr;
+ return (ISC_R_SUCCESS);
+
+ cleanup_listenon:
+ ns_listenlist_detach(&mgr->listenon4);
+ ns_listenlist_detach(&mgr->listenon6);
+ cleanup_mem:
+ isc_mem_put(mctx, mgr, sizeof(*mgr));
+ return (result);
+}
+
+static void
+ns_interfacemgr_destroy(ns_interfacemgr_t *mgr) {
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+ dns_aclenv_destroy(&mgr->aclenv);
+ ns_listenlist_detach(&mgr->listenon4);
+ ns_listenlist_detach(&mgr->listenon6);
+ clearlistenon(mgr);
+ DESTROYLOCK(&mgr->lock);
+ mgr->magic = 0;
+ isc_mem_put(mgr->mctx, mgr, sizeof(*mgr));
+}
+
+dns_aclenv_t *
+ns_interfacemgr_getaclenv(ns_interfacemgr_t *mgr) {
+ return (&mgr->aclenv);
+}
+
+void
+ns_interfacemgr_attach(ns_interfacemgr_t *source, ns_interfacemgr_t **target) {
+ REQUIRE(NS_INTERFACEMGR_VALID(source));
+ LOCK(&source->lock);
+ INSIST(source->references > 0);
+ source->references++;
+ UNLOCK(&source->lock);
+ *target = source;
+}
+
+void
+ns_interfacemgr_detach(ns_interfacemgr_t **targetp) {
+ isc_result_t need_destroy = ISC_FALSE;
+ ns_interfacemgr_t *target = *targetp;
+ REQUIRE(target != NULL);
+ REQUIRE(NS_INTERFACEMGR_VALID(target));
+ LOCK(&target->lock);
+ REQUIRE(target->references > 0);
+ target->references--;
+ if (target->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&target->lock);
+ if (need_destroy)
+ ns_interfacemgr_destroy(target);
+ *targetp = NULL;
+}
+
+void
+ns_interfacemgr_shutdown(ns_interfacemgr_t *mgr) {
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+
+ /*%
+ * Shut down and detach all interfaces.
+ * By incrementing the generation count, we make purge_old_interfaces()
+ * consider all interfaces "old".
+ */
+ mgr->generation++;
+ purge_old_interfaces(mgr);
+}
+
+
+static isc_result_t
+ns_interface_create(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
+ const char *name, ns_interface_t **ifpret)
+{
+ ns_interface_t *ifp;
+ isc_result_t result;
+
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+ ifp = isc_mem_get(mgr->mctx, sizeof(*ifp));
+ if (ifp == NULL)
+ return (ISC_R_NOMEMORY);
+ ifp->mgr = NULL;
+ ifp->generation = mgr->generation;
+ ifp->addr = *addr;
+ ifp->flags = 0;
+ strncpy(ifp->name, name, sizeof(ifp->name));
+ ifp->name[sizeof(ifp->name)-1] = '\0';
+ ifp->clientmgr = NULL;
+
+ result = isc_mutex_init(&ifp->lock);
+ if (result != ISC_R_SUCCESS)
+ goto lock_create_failure;
+
+ result = ns_clientmgr_create(mgr->mctx, mgr->taskmgr,
+ ns_g_timermgr,
+ &ifp->clientmgr);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "ns_clientmgr_create() failed: %s",
+ isc_result_totext(result));
+ goto clientmgr_create_failure;
+ }
+
+ ifp->udpdispatch = NULL;
+
+ ifp->tcpsocket = NULL;
+ /*
+ * Create a single TCP client object. It will replace itself
+ * with a new one as soon as it gets a connection, so the actual
+ * connections will be handled in parallel even though there is
+ * only one client initially.
+ */
+ ifp->ntcptarget = 1;
+ ifp->ntcpcurrent = 0;
+
+ ISC_LINK_INIT(ifp, link);
+
+ ns_interfacemgr_attach(mgr, &ifp->mgr);
+ ISC_LIST_APPEND(mgr->interfaces, ifp, link);
+
+ ifp->references = 1;
+ ifp->magic = IFACE_MAGIC;
+ *ifpret = ifp;
+
+ return (ISC_R_SUCCESS);
+
+ clientmgr_create_failure:
+ DESTROYLOCK(&ifp->lock);
+ lock_create_failure:
+ ifp->magic = 0;
+ isc_mem_put(mgr->mctx, ifp, sizeof(*ifp));
+
+ return (ISC_R_UNEXPECTED);
+}
+
+static isc_result_t
+ns_interface_listenudp(ns_interface_t *ifp) {
+ isc_result_t result;
+ unsigned int attrs;
+ unsigned int attrmask;
+
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_UDP;
+ if (isc_sockaddr_pf(&ifp->addr) == AF_INET)
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ else
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ attrs |= DNS_DISPATCHATTR_NOLISTEN;
+ attrmask = 0;
+ attrmask |= DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP;
+ attrmask |= DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
+ result = dns_dispatch_getudp(ifp->mgr->dispatchmgr, ns_g_socketmgr,
+ ns_g_taskmgr, &ifp->addr,
+ 4096, 1000, 32768, 8219, 8237,
+ attrs, attrmask, &ifp->udpdispatch);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "could not listen on UDP socket: %s",
+ isc_result_totext(result));
+ goto udp_dispatch_failure;
+ }
+
+ result = ns_clientmgr_createclients(ifp->clientmgr, ns_g_cpus,
+ ifp, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "UDP ns_clientmgr_createclients(): %s",
+ isc_result_totext(result));
+ goto addtodispatch_failure;
+ }
+ return (ISC_R_SUCCESS);
+
+ addtodispatch_failure:
+ dns_dispatch_changeattributes(ifp->udpdispatch, 0,
+ DNS_DISPATCHATTR_NOLISTEN);
+ dns_dispatch_detach(&ifp->udpdispatch);
+ udp_dispatch_failure:
+ return (result);
+}
+
+static isc_result_t
+ns_interface_accepttcp(ns_interface_t *ifp) {
+ isc_result_t result;
+
+ /*
+ * Open a TCP socket.
+ */
+ result = isc_socket_create(ifp->mgr->socketmgr,
+ isc_sockaddr_pf(&ifp->addr),
+ isc_sockettype_tcp,
+ &ifp->tcpsocket);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "creating TCP socket: %s",
+ isc_result_totext(result));
+ goto tcp_socket_failure;
+ }
+ isc_socket_setname(ifp->tcpsocket, "dispatcher", NULL);
+#ifndef ISC_ALLOW_MAPPED
+ isc_socket_ipv6only(ifp->tcpsocket, ISC_TRUE);
+#endif
+ result = isc_socket_bind(ifp->tcpsocket, &ifp->addr,
+ ISC_SOCKET_REUSEADDRESS);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "binding TCP socket: %s",
+ isc_result_totext(result));
+ goto tcp_bind_failure;
+ }
+ result = isc_socket_listen(ifp->tcpsocket, ns_g_listen);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "listening on TCP socket: %s",
+ isc_result_totext(result));
+ goto tcp_listen_failure;
+ }
+
+ /*
+ * If/when there a multiple filters listen to the
+ * result.
+ */
+ (void)isc_socket_filter(ifp->tcpsocket, "dataready");
+
+ result = ns_clientmgr_createclients(ifp->clientmgr,
+ ifp->ntcptarget, ifp,
+ ISC_TRUE);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "TCP ns_clientmgr_createclients(): %s",
+ isc_result_totext(result));
+ goto accepttcp_failure;
+ }
+ return (ISC_R_SUCCESS);
+
+ accepttcp_failure:
+ tcp_listen_failure:
+ tcp_bind_failure:
+ isc_socket_detach(&ifp->tcpsocket);
+ tcp_socket_failure:
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
+ const char *name, ns_interface_t **ifpret,
+ isc_boolean_t accept_tcp)
+{
+ isc_result_t result;
+ ns_interface_t *ifp = NULL;
+ REQUIRE(ifpret != NULL && *ifpret == NULL);
+
+ result = ns_interface_create(mgr, addr, name, &ifp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = ns_interface_listenudp(ifp);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_interface;
+
+ if (accept_tcp == ISC_TRUE) {
+ result = ns_interface_accepttcp(ifp);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * XXXRTH We don't currently have a way to easily stop
+ * dispatch service, so we currently return
+ * ISC_R_SUCCESS (the UDP stuff will work even if TCP
+ * creation failed). This will be fixed later.
+ */
+ result = ISC_R_SUCCESS;
+ }
+ }
+ *ifpret = ifp;
+ return (ISC_R_SUCCESS);
+
+ cleanup_interface:
+ ISC_LIST_UNLINK(ifp->mgr->interfaces, ifp, link);
+ ns_interface_detach(&ifp);
+ return (result);
+}
+
+void
+ns_interface_shutdown(ns_interface_t *ifp) {
+ if (ifp->clientmgr != NULL)
+ ns_clientmgr_destroy(&ifp->clientmgr);
+}
+
+static void
+ns_interface_destroy(ns_interface_t *ifp) {
+ isc_mem_t *mctx = ifp->mgr->mctx;
+ REQUIRE(NS_INTERFACE_VALID(ifp));
+
+ ns_interface_shutdown(ifp);
+
+ if (ifp->udpdispatch != NULL) {
+ dns_dispatch_changeattributes(ifp->udpdispatch, 0,
+ DNS_DISPATCHATTR_NOLISTEN);
+ dns_dispatch_detach(&ifp->udpdispatch);
+ }
+ if (ifp->tcpsocket != NULL)
+ isc_socket_detach(&ifp->tcpsocket);
+
+ DESTROYLOCK(&ifp->lock);
+
+ ns_interfacemgr_detach(&ifp->mgr);
+
+ ifp->magic = 0;
+ isc_mem_put(mctx, ifp, sizeof(*ifp));
+}
+
+void
+ns_interface_attach(ns_interface_t *source, ns_interface_t **target) {
+ REQUIRE(NS_INTERFACE_VALID(source));
+ LOCK(&source->lock);
+ INSIST(source->references > 0);
+ source->references++;
+ UNLOCK(&source->lock);
+ *target = source;
+}
+
+void
+ns_interface_detach(ns_interface_t **targetp) {
+ isc_result_t need_destroy = ISC_FALSE;
+ ns_interface_t *target = *targetp;
+ REQUIRE(target != NULL);
+ REQUIRE(NS_INTERFACE_VALID(target));
+ LOCK(&target->lock);
+ REQUIRE(target->references > 0);
+ target->references--;
+ if (target->references == 0)
+ need_destroy = ISC_TRUE;
+ UNLOCK(&target->lock);
+ if (need_destroy)
+ ns_interface_destroy(target);
+ *targetp = NULL;
+}
+
+/*%
+ * Search the interface list for an interface whose address and port
+ * both match those of 'addr'. Return a pointer to it, or NULL if not found.
+ */
+static ns_interface_t *
+find_matching_interface(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr) {
+ ns_interface_t *ifp;
+ for (ifp = ISC_LIST_HEAD(mgr->interfaces); ifp != NULL;
+ ifp = ISC_LIST_NEXT(ifp, link)) {
+ if (isc_sockaddr_equal(&ifp->addr, addr))
+ break;
+ }
+ return (ifp);
+}
+
+/*%
+ * Remove any interfaces whose generation number is not the current one.
+ */
+static void
+purge_old_interfaces(ns_interfacemgr_t *mgr) {
+ ns_interface_t *ifp, *next;
+ for (ifp = ISC_LIST_HEAD(mgr->interfaces); ifp != NULL; ifp = next) {
+ INSIST(NS_INTERFACE_VALID(ifp));
+ next = ISC_LIST_NEXT(ifp, link);
+ if (ifp->generation != mgr->generation) {
+ char sabuf[256];
+ ISC_LIST_UNLINK(ifp->mgr->interfaces, ifp, link);
+ isc_sockaddr_format(&ifp->addr, sabuf, sizeof(sabuf));
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_INFO,
+ "no longer listening on %s", sabuf);
+ ns_interface_shutdown(ifp);
+ ns_interface_detach(&ifp);
+ }
+ }
+}
+
+static isc_result_t
+clearacl(isc_mem_t *mctx, dns_acl_t **aclp) {
+ dns_acl_t *newacl = NULL;
+ isc_result_t result;
+ result = dns_acl_create(mctx, 0, &newacl);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_acl_detach(aclp);
+ dns_acl_attach(newacl, aclp);
+ dns_acl_detach(&newacl);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_boolean_t
+listenon_is_ip6_any(ns_listenelt_t *elt) {
+ REQUIRE(elt && elt->acl);
+ return dns_acl_isany(elt->acl);
+}
+
+static isc_result_t
+setup_locals(ns_interfacemgr_t *mgr, isc_interface_t *interface) {
+ isc_result_t result;
+ unsigned int prefixlen;
+ isc_netaddr_t *netaddr;
+
+ netaddr = &interface->address;
+
+ /* First add localhost address */
+ prefixlen = (netaddr->family == AF_INET) ? 32 : 128;
+ result = dns_iptable_addprefix(mgr->aclenv.localhost->iptable,
+ netaddr, prefixlen, ISC_TRUE);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* Then add localnets prefix */
+ result = isc_netaddr_masktoprefixlen(&interface->netmask,
+ &prefixlen);
+
+ /* Non contigious netmasks not allowed by IPv6 arch. */
+ if (result != ISC_R_SUCCESS && netaddr->family == AF_INET6)
+ return (result);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_WARNING,
+ "omitting IPv4 interface %s from "
+ "localnets ACL: %s",
+ interface->name,
+ isc_result_totext(result));
+ return (ISC_R_SUCCESS);
+ }
+
+ result = dns_iptable_addprefix(mgr->aclenv.localnets->iptable,
+ netaddr, prefixlen, ISC_TRUE);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+setup_listenon(ns_interfacemgr_t *mgr, isc_interface_t *interface,
+ in_port_t port)
+{
+ isc_sockaddr_t *addr;
+ isc_sockaddr_t *old;
+
+ addr = isc_mem_get(mgr->mctx, sizeof(*addr));
+ if (addr == NULL)
+ return;
+
+ isc_sockaddr_fromnetaddr(addr, &interface->address, port);
+
+ for (old = ISC_LIST_HEAD(mgr->listenon);
+ old != NULL;
+ old = ISC_LIST_NEXT(old, link))
+ if (isc_sockaddr_equal(addr, old))
+ break;
+
+ if (old != NULL)
+ isc_mem_put(mgr->mctx, addr, sizeof(*addr));
+ else
+ ISC_LIST_APPEND(mgr->listenon, addr, link);
+}
+
+static void
+clearlistenon(ns_interfacemgr_t *mgr) {
+ isc_sockaddr_t *old;
+
+ old = ISC_LIST_HEAD(mgr->listenon);
+ while (old != NULL) {
+ ISC_LIST_UNLINK(mgr->listenon, old, link);
+ isc_mem_put(mgr->mctx, old, sizeof(*old));
+ old = ISC_LIST_HEAD(mgr->listenon);
+ }
+}
+
+static isc_result_t
+do_scan(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
+ isc_boolean_t verbose)
+{
+ isc_interfaceiter_t *iter = NULL;
+ isc_boolean_t scan_ipv4 = ISC_FALSE;
+ isc_boolean_t scan_ipv6 = ISC_FALSE;
+ isc_boolean_t adjusting = ISC_FALSE;
+ isc_boolean_t ipv6only = ISC_TRUE;
+ isc_boolean_t ipv6pktinfo = ISC_TRUE;
+ isc_result_t result;
+ isc_netaddr_t zero_address, zero_address6;
+ ns_listenelt_t *le;
+ isc_sockaddr_t listen_addr;
+ ns_interface_t *ifp;
+ isc_boolean_t log_explicit = ISC_FALSE;
+ isc_boolean_t dolistenon;
+
+ if (ext_listen != NULL)
+ adjusting = ISC_TRUE;
+
+ if (isc_net_probeipv6() == ISC_R_SUCCESS)
+ scan_ipv6 = ISC_TRUE;
+#ifdef WANT_IPV6
+ else
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ verbose ? ISC_LOG_INFO : ISC_LOG_DEBUG(1),
+ "no IPv6 interfaces found");
+#endif
+
+ if (isc_net_probeipv4() == ISC_R_SUCCESS)
+ scan_ipv4 = ISC_TRUE;
+ else
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ verbose ? ISC_LOG_INFO : ISC_LOG_DEBUG(1),
+ "no IPv4 interfaces found");
+
+ /*
+ * A special, but typical case; listen-on-v6 { any; }.
+ * When we can make the socket IPv6-only, open a single wildcard
+ * socket for IPv6 communication. Otherwise, make separate socket
+ * for each IPv6 address in order to avoid accepting IPv4 packets
+ * as the form of mapped addresses unintentionally unless explicitly
+ * allowed.
+ */
+#ifndef ISC_ALLOW_MAPPED
+ if (scan_ipv6 == ISC_TRUE &&
+ isc_net_probe_ipv6only() != ISC_R_SUCCESS) {
+ ipv6only = ISC_FALSE;
+ log_explicit = ISC_TRUE;
+ }
+#endif
+ if (scan_ipv6 == ISC_TRUE &&
+ isc_net_probe_ipv6pktinfo() != ISC_R_SUCCESS) {
+ ipv6pktinfo = ISC_FALSE;
+ log_explicit = ISC_TRUE;
+ }
+ if (scan_ipv6 == ISC_TRUE && ipv6only && ipv6pktinfo) {
+ for (le = ISC_LIST_HEAD(mgr->listenon6->elts);
+ le != NULL;
+ le = ISC_LIST_NEXT(le, link)) {
+ struct in6_addr in6a;
+
+ if (!listenon_is_ip6_any(le))
+ continue;
+
+ in6a = in6addr_any;
+ isc_sockaddr_fromin6(&listen_addr, &in6a, le->port);
+
+ ifp = find_matching_interface(mgr, &listen_addr);
+ if (ifp != NULL) {
+ ifp->generation = mgr->generation;
+ } else {
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_INFO,
+ "listening on IPv6 "
+ "interfaces, port %u",
+ le->port);
+ result = ns_interface_setup(mgr, &listen_addr,
+ "<any>", &ifp,
+ ISC_TRUE);
+ if (result == ISC_R_SUCCESS)
+ ifp->flags |= NS_INTERFACEFLAG_ANYADDR;
+ else
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_ERROR,
+ "listening on all IPv6 "
+ "interfaces failed");
+ /* Continue. */
+ }
+ }
+ }
+
+ isc_netaddr_any(&zero_address);
+ isc_netaddr_any6(&zero_address6);
+
+ result = isc_interfaceiter_create(mgr->mctx, &iter);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (adjusting == ISC_FALSE) {
+ result = clearacl(mgr->mctx, &mgr->aclenv.localhost);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iter;
+ result = clearacl(mgr->mctx, &mgr->aclenv.localnets);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iter;
+ clearlistenon(mgr);
+ }
+
+ for (result = isc_interfaceiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = isc_interfaceiter_next(iter))
+ {
+ isc_interface_t interface;
+ ns_listenlist_t *ll;
+ unsigned int family;
+
+ result = isc_interfaceiter_current(iter, &interface);
+ if (result != ISC_R_SUCCESS)
+ break;
+
+ family = interface.address.family;
+ if (family != AF_INET && family != AF_INET6)
+ continue;
+ if (scan_ipv4 == ISC_FALSE && family == AF_INET)
+ continue;
+ if (scan_ipv6 == ISC_FALSE && family == AF_INET6)
+ continue;
+
+ /*
+ * Test for the address being nonzero rather than testing
+ * INTERFACE_F_UP, because on some systems the latter
+ * follows the media state and we could end up ignoring
+ * the interface for an entire rescan interval due to
+ * a temporary media glitch at rescan time.
+ */
+ if (family == AF_INET &&
+ isc_netaddr_equal(&interface.address, &zero_address)) {
+ continue;
+ }
+ if (family == AF_INET6 &&
+ isc_netaddr_equal(&interface.address, &zero_address6)) {
+ continue;
+ }
+
+ if (adjusting == ISC_FALSE) {
+ result = setup_locals(mgr, &interface);
+ if (result != ISC_R_SUCCESS)
+ goto ignore_interface;
+ }
+
+ ll = (family == AF_INET) ? mgr->listenon4 : mgr->listenon6;
+ dolistenon = ISC_TRUE;
+ for (le = ISC_LIST_HEAD(ll->elts);
+ le != NULL;
+ le = ISC_LIST_NEXT(le, link))
+ {
+ int match;
+ isc_boolean_t ipv6_wildcard = ISC_FALSE;
+ isc_netaddr_t listen_netaddr;
+ isc_sockaddr_t listen_sockaddr;
+
+ /*
+ * Construct a socket address for this IP/port
+ * combination.
+ */
+ if (family == AF_INET) {
+ isc_netaddr_fromin(&listen_netaddr,
+ &interface.address.type.in);
+ } else {
+ isc_netaddr_fromin6(&listen_netaddr,
+ &interface.address.type.in6);
+ isc_netaddr_setzone(&listen_netaddr,
+ interface.address.zone);
+ }
+ isc_sockaddr_fromnetaddr(&listen_sockaddr,
+ &listen_netaddr,
+ le->port);
+
+ /*
+ * See if the address matches the listen-on statement;
+ * if not, ignore the interface.
+ */
+ (void)dns_acl_match(&listen_netaddr, NULL, le->acl,
+ &mgr->aclenv, &match, NULL);
+ if (match <= 0)
+ continue;
+
+ if (adjusting == ISC_FALSE && dolistenon == ISC_TRUE) {
+ setup_listenon(mgr, &interface, le->port);
+ dolistenon = ISC_FALSE;
+ }
+
+ /*
+ * The case of "any" IPv6 address will require
+ * special considerations later, so remember it.
+ */
+ if (family == AF_INET6 && ipv6only && ipv6pktinfo &&
+ listenon_is_ip6_any(le))
+ ipv6_wildcard = ISC_TRUE;
+
+ /*
+ * When adjusting interfaces with extra a listening
+ * list, see if the address matches the extra list.
+ * If it does, and is also covered by a wildcard
+ * interface, we need to listen on the address
+ * explicitly.
+ */
+ if (adjusting == ISC_TRUE) {
+ ns_listenelt_t *ele;
+
+ match = 0;
+ for (ele = ISC_LIST_HEAD(ext_listen->elts);
+ ele != NULL;
+ ele = ISC_LIST_NEXT(ele, link)) {
+ (void)dns_acl_match(&listen_netaddr,
+ NULL, ele->acl,
+ NULL, &match, NULL);
+ if (match > 0 &&
+ (ele->port == le->port ||
+ ele->port == 0))
+ break;
+ else
+ match = 0;
+ }
+ if (ipv6_wildcard == ISC_TRUE && match == 0)
+ continue;
+ }
+
+ ifp = find_matching_interface(mgr, &listen_sockaddr);
+ if (ifp != NULL) {
+ ifp->generation = mgr->generation;
+ } else {
+ char sabuf[ISC_SOCKADDR_FORMATSIZE];
+
+ if (adjusting == ISC_FALSE &&
+ ipv6_wildcard == ISC_TRUE)
+ continue;
+
+ if (log_explicit && family == AF_INET6 &&
+ !adjusting && listenon_is_ip6_any(le)) {
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ verbose ? ISC_LOG_INFO :
+ ISC_LOG_DEBUG(1),
+ "IPv6 socket API is "
+ "incomplete; explicitly "
+ "binding to each IPv6 "
+ "address separately");
+ log_explicit = ISC_FALSE;
+ }
+ isc_sockaddr_format(&listen_sockaddr,
+ sabuf, sizeof(sabuf));
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_INFO,
+ "%s"
+ "listening on %s interface "
+ "%s, %s",
+ (adjusting == ISC_TRUE) ?
+ "additionally " : "",
+ (family == AF_INET) ?
+ "IPv4" : "IPv6",
+ interface.name, sabuf);
+
+ result = ns_interface_setup(mgr,
+ &listen_sockaddr,
+ interface.name,
+ &ifp,
+ (adjusting == ISC_TRUE) ?
+ ISC_FALSE :
+ ISC_TRUE);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_ERROR,
+ "creating %s interface "
+ "%s failed; interface "
+ "ignored",
+ (family == AF_INET) ?
+ "IPv4" : "IPv6",
+ interface.name);
+ }
+ /* Continue. */
+ }
+
+ }
+ continue;
+
+ ignore_interface:
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_ERROR,
+ "ignoring %s interface %s: %s",
+ (family == AF_INET) ? "IPv4" : "IPv6",
+ interface.name, isc_result_totext(result));
+ continue;
+ }
+ if (result != ISC_R_NOMORE)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "interface iteration failed: %s",
+ isc_result_totext(result));
+ else
+ result = ISC_R_SUCCESS;
+ cleanup_iter:
+ isc_interfaceiter_destroy(&iter);
+ return (result);
+}
+
+static void
+ns_interfacemgr_scan0(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
+ isc_boolean_t verbose)
+{
+ isc_boolean_t purge = ISC_TRUE;
+
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+
+ mgr->generation++; /* Increment the generation count. */
+
+ if (do_scan(mgr, ext_listen, verbose) != ISC_R_SUCCESS)
+ purge = ISC_FALSE;
+
+ /*
+ * Now go through the interface list and delete anything that
+ * does not have the current generation number. This is
+ * how we catch interfaces that go away or change their
+ * addresses.
+ */
+ if (purge)
+ purge_old_interfaces(mgr);
+
+ /*
+ * Warn if we are not listening on any interface, unless
+ * we're in lwresd-only mode, in which case that is to
+ * be expected.
+ */
+ if (ext_listen == NULL &&
+ ISC_LIST_EMPTY(mgr->interfaces) && ! ns_g_lwresdonly) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_WARNING,
+ "not listening on any interfaces");
+ }
+}
+
+void
+ns_interfacemgr_scan(ns_interfacemgr_t *mgr, isc_boolean_t verbose) {
+ ns_interfacemgr_scan0(mgr, NULL, verbose);
+}
+
+void
+ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list,
+ isc_boolean_t verbose)
+{
+ ns_interfacemgr_scan0(mgr, list, verbose);
+}
+
+void
+ns_interfacemgr_setlistenon4(ns_interfacemgr_t *mgr, ns_listenlist_t *value) {
+ LOCK(&mgr->lock);
+ ns_listenlist_detach(&mgr->listenon4);
+ ns_listenlist_attach(value, &mgr->listenon4);
+ UNLOCK(&mgr->lock);
+}
+
+void
+ns_interfacemgr_setlistenon6(ns_interfacemgr_t *mgr, ns_listenlist_t *value) {
+ LOCK(&mgr->lock);
+ ns_listenlist_detach(&mgr->listenon6);
+ ns_listenlist_attach(value, &mgr->listenon6);
+ UNLOCK(&mgr->lock);
+}
+
+void
+ns_interfacemgr_dumprecursing(FILE *f, ns_interfacemgr_t *mgr) {
+ ns_interface_t *interface;
+
+ LOCK(&mgr->lock);
+ interface = ISC_LIST_HEAD(mgr->interfaces);
+ while (interface != NULL) {
+ if (interface->clientmgr != NULL)
+ ns_client_dumprecursing(f, interface->clientmgr);
+ interface = ISC_LIST_NEXT(interface, link);
+ }
+ UNLOCK(&mgr->lock);
+}
+
+isc_boolean_t
+ns_interfacemgr_listeningon(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr) {
+ isc_sockaddr_t *old;
+
+ old = ISC_LIST_HEAD(mgr->listenon);
+ for (old = ISC_LIST_HEAD(mgr->listenon);
+ old != NULL;
+ old = ISC_LIST_NEXT(old, link))
+ if (isc_sockaddr_equal(old, addr))
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
diff --git a/bin/named/listenlist.c b/bin/named/listenlist.c
new file mode 100644
index 0000000..513fe9c
--- /dev/null
+++ b/bin/named/listenlist.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: listenlist.c,v 1.14 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+
+#include <named/listenlist.h>
+
+static void
+destroy(ns_listenlist_t *list);
+
+isc_result_t
+ns_listenelt_create(isc_mem_t *mctx, in_port_t port,
+ dns_acl_t *acl, ns_listenelt_t **target)
+{
+ ns_listenelt_t *elt = NULL;
+ REQUIRE(target != NULL && *target == NULL);
+ elt = isc_mem_get(mctx, sizeof(*elt));
+ if (elt == NULL)
+ return (ISC_R_NOMEMORY);
+ elt->mctx = mctx;
+ ISC_LINK_INIT(elt, link);
+ elt->port = port;
+ elt->acl = acl;
+ *target = elt;
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_listenelt_destroy(ns_listenelt_t *elt) {
+ if (elt->acl != NULL)
+ dns_acl_detach(&elt->acl);
+ isc_mem_put(elt->mctx, elt, sizeof(*elt));
+}
+
+isc_result_t
+ns_listenlist_create(isc_mem_t *mctx, ns_listenlist_t **target) {
+ ns_listenlist_t *list = NULL;
+ REQUIRE(target != NULL && *target == NULL);
+ list = isc_mem_get(mctx, sizeof(*list));
+ if (list == NULL)
+ return (ISC_R_NOMEMORY);
+ list->mctx = mctx;
+ list->refcount = 1;
+ ISC_LIST_INIT(list->elts);
+ *target = list;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+destroy(ns_listenlist_t *list) {
+ ns_listenelt_t *elt, *next;
+ for (elt = ISC_LIST_HEAD(list->elts);
+ elt != NULL;
+ elt = next)
+ {
+ next = ISC_LIST_NEXT(elt, link);
+ ns_listenelt_destroy(elt);
+ }
+ isc_mem_put(list->mctx, list, sizeof(*list));
+}
+
+void
+ns_listenlist_attach(ns_listenlist_t *source, ns_listenlist_t **target) {
+ INSIST(source->refcount > 0);
+ source->refcount++;
+ *target = source;
+}
+
+void
+ns_listenlist_detach(ns_listenlist_t **listp) {
+ ns_listenlist_t *list = *listp;
+ INSIST(list->refcount > 0);
+ list->refcount--;
+ if (list->refcount == 0)
+ destroy(list);
+ *listp = NULL;
+}
+
+isc_result_t
+ns_listenlist_default(isc_mem_t *mctx, in_port_t port,
+ isc_boolean_t enabled, ns_listenlist_t **target)
+{
+ isc_result_t result;
+ dns_acl_t *acl = NULL;
+ ns_listenelt_t *elt = NULL;
+ ns_listenlist_t *list = NULL;
+
+ REQUIRE(target != NULL && *target == NULL);
+ if (enabled)
+ result = dns_acl_any(mctx, &acl);
+ else
+ result = dns_acl_none(mctx, &acl);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = ns_listenelt_create(mctx, port, acl, &elt);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_acl;
+
+ result = ns_listenlist_create(mctx, &list);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_listenelt;
+
+ ISC_LIST_APPEND(list->elts, elt, link);
+
+ *target = list;
+ return (ISC_R_SUCCESS);
+
+ cleanup_listenelt:
+ ns_listenelt_destroy(elt);
+ cleanup_acl:
+ dns_acl_detach(&acl);
+ cleanup:
+ return (result);
+}
diff --git a/bin/named/log.c b/bin/named/log.c
new file mode 100644
index 0000000..fdb4e0c
--- /dev/null
+++ b/bin/named/log.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: log.c,v 1.46 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/result.h>
+
+#include <isccfg/log.h>
+
+#include <named/log.h>
+
+#ifndef ISC_FACILITY
+#define ISC_FACILITY LOG_DAEMON
+#endif
+
+/*%
+ * When adding a new category, be sure to add the appropriate
+ * \#define to <named/log.h> and to update the list in
+ * bin/check/check-tool.c.
+ */
+static isc_logcategory_t categories[] = {
+ { "", 0 },
+ { "client", 0 },
+ { "network", 0 },
+ { "update", 0 },
+ { "queries", 0 },
+ { "unmatched", 0 },
+ { "update-security", 0 },
+ { NULL, 0 }
+};
+
+/*%
+ * When adding a new module, be sure to add the appropriate
+ * \#define to <dns/log.h>.
+ */
+static isc_logmodule_t modules[] = {
+ { "main", 0 },
+ { "client", 0 },
+ { "server", 0 },
+ { "query", 0 },
+ { "interfacemgr", 0 },
+ { "update", 0 },
+ { "xfer-in", 0 },
+ { "xfer-out", 0 },
+ { "notify", 0 },
+ { "control", 0 },
+ { "lwresd", 0 },
+ { NULL, 0 }
+};
+
+isc_result_t
+ns_log_init(isc_boolean_t safe) {
+ isc_result_t result;
+ isc_logconfig_t *lcfg = NULL;
+
+ ns_g_categories = categories;
+ ns_g_modules = modules;
+
+ /*
+ * Setup a logging context.
+ */
+ result = isc_log_create(ns_g_mctx, &ns_g_lctx, &lcfg);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * named-checktool.c:setup_logging() needs to be kept in sync.
+ */
+ isc_log_registercategories(ns_g_lctx, ns_g_categories);
+ isc_log_registermodules(ns_g_lctx, ns_g_modules);
+ isc_log_setcontext(ns_g_lctx);
+ dns_log_init(ns_g_lctx);
+ dns_log_setcontext(ns_g_lctx);
+ cfg_log_init(ns_g_lctx);
+
+ if (safe)
+ result = ns_log_setsafechannels(lcfg);
+ else
+ result = ns_log_setdefaultchannels(lcfg);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = ns_log_setdefaultcategory(lcfg);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ isc_log_destroy(&ns_g_lctx);
+ isc_log_setcontext(NULL);
+ dns_log_setcontext(NULL);
+
+ return (result);
+}
+
+isc_result_t
+ns_log_setdefaultchannels(isc_logconfig_t *lcfg) {
+ isc_result_t result;
+ isc_logdestination_t destination;
+
+ /*
+ * By default, the logging library makes "default_debug" log to
+ * stderr. In BIND, we want to override this and log to named.run
+ * instead, unless the the -g option was given.
+ */
+ if (! ns_g_logstderr) {
+ destination.file.stream = NULL;
+ destination.file.name = "named.run";
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.maximum_size = 0;
+ result = isc_log_createchannel(lcfg, "default_debug",
+ ISC_LOG_TOFILE,
+ ISC_LOG_DYNAMIC,
+ &destination,
+ ISC_LOG_PRINTTIME|
+ ISC_LOG_DEBUGONLY);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+#if ISC_FACILITY != LOG_DAEMON
+ destination.facility = ISC_FACILITY;
+ result = isc_log_createchannel(lcfg, "default_syslog",
+ ISC_LOG_TOSYSLOG, ISC_LOG_INFO,
+ &destination, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#endif
+
+ /*
+ * Set the initial debug level.
+ */
+ isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+ns_log_setsafechannels(isc_logconfig_t *lcfg) {
+ isc_result_t result;
+#if ISC_FACILITY != LOG_DAEMON
+ isc_logdestination_t destination;
+#endif
+
+ if (! ns_g_logstderr) {
+ result = isc_log_createchannel(lcfg, "default_debug",
+ ISC_LOG_TONULL,
+ ISC_LOG_DYNAMIC,
+ NULL, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Setting the debug level to zero should get the output
+ * discarded a bit faster.
+ */
+ isc_log_setdebuglevel(ns_g_lctx, 0);
+ } else {
+ isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
+ }
+
+#if ISC_FACILITY != LOG_DAEMON
+ destination.facility = ISC_FACILITY;
+ result = isc_log_createchannel(lcfg, "default_syslog",
+ ISC_LOG_TOSYSLOG, ISC_LOG_INFO,
+ &destination, 0);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+#endif
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+ns_log_setdefaultcategory(isc_logconfig_t *lcfg) {
+ isc_result_t result;
+
+ if (! ns_g_logstderr) {
+ result = isc_log_usechannel(lcfg, "default_syslog",
+ ISC_LOGCATEGORY_DEFAULT, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ result = isc_log_usechannel(lcfg, "default_debug",
+ ISC_LOGCATEGORY_DEFAULT, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+ns_log_setunmatchedcategory(isc_logconfig_t *lcfg) {
+ isc_result_t result;
+
+ result = isc_log_usechannel(lcfg, "null",
+ NS_LOGCATEGORY_UNMATCHED, NULL);
+ return (result);
+}
+
+void
+ns_log_shutdown(void) {
+ isc_log_destroy(&ns_g_lctx);
+ isc_log_setcontext(NULL);
+ dns_log_setcontext(NULL);
+}
diff --git a/bin/named/logconf.c b/bin/named/logconf.c
new file mode 100644
index 0000000..e324965
--- /dev/null
+++ b/bin/named/logconf.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: logconf.c,v 1.42 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/offset.h>
+#include <isc/result.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/syslog.h>
+
+#include <isccfg/cfg.h>
+#include <isccfg/log.h>
+
+#include <named/log.h>
+#include <named/logconf.h>
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+/*%
+ * Set up a logging category according to the named.conf data
+ * in 'ccat' and add it to 'lctx'.
+ */
+static isc_result_t
+category_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *lctx) {
+ isc_result_t result;
+ const char *catname;
+ isc_logcategory_t *category;
+ isc_logmodule_t *module;
+ const cfg_obj_t *destinations = NULL;
+ const cfg_listelt_t *element = NULL;
+
+ catname = cfg_obj_asstring(cfg_tuple_get(ccat, "name"));
+ category = isc_log_categorybyname(ns_g_lctx, catname);
+ if (category == NULL) {
+ cfg_obj_log(ccat, ns_g_lctx, ISC_LOG_ERROR,
+ "unknown logging category '%s' ignored",
+ catname);
+ /*
+ * Allow further processing by returning success.
+ */
+ return (ISC_R_SUCCESS);
+ }
+
+ module = NULL;
+
+ destinations = cfg_tuple_get(ccat, "destinations");
+ for (element = cfg_list_first(destinations);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *channel = cfg_listelt_value(element);
+ const char *channelname = cfg_obj_asstring(channel);
+
+ result = isc_log_usechannel(lctx, channelname, category,
+ module);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "logging channel '%s': %s", channelname,
+ isc_result_totext(result));
+ return (result);
+ }
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Set up a logging channel according to the named.conf data
+ * in 'cchan' and add it to 'lctx'.
+ */
+static isc_result_t
+channel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *lctx) {
+ isc_result_t result;
+ isc_logdestination_t dest;
+ unsigned int type;
+ unsigned int flags = 0;
+ int level;
+ const char *channelname;
+ const cfg_obj_t *fileobj = NULL;
+ const cfg_obj_t *syslogobj = NULL;
+ const cfg_obj_t *nullobj = NULL;
+ const cfg_obj_t *stderrobj = NULL;
+ const cfg_obj_t *severity = NULL;
+ int i;
+
+ channelname = cfg_obj_asstring(cfg_map_getname(channel));
+
+ (void)cfg_map_get(channel, "file", &fileobj);
+ (void)cfg_map_get(channel, "syslog", &syslogobj);
+ (void)cfg_map_get(channel, "null", &nullobj);
+ (void)cfg_map_get(channel, "stderr", &stderrobj);
+
+ i = 0;
+ if (fileobj != NULL)
+ i++;
+ if (syslogobj != NULL)
+ i++;
+ if (nullobj != NULL)
+ i++;
+ if (stderrobj != NULL)
+ i++;
+
+ if (i != 1) {
+ cfg_obj_log(channel, ns_g_lctx, ISC_LOG_ERROR,
+ "channel '%s': exactly one of file, syslog, "
+ "null, and stderr must be present", channelname);
+ return (ISC_R_FAILURE);
+ }
+
+ type = ISC_LOG_TONULL;
+
+ if (fileobj != NULL) {
+ const cfg_obj_t *pathobj = cfg_tuple_get(fileobj, "file");
+ const cfg_obj_t *sizeobj = cfg_tuple_get(fileobj, "size");
+ const cfg_obj_t *versionsobj =
+ cfg_tuple_get(fileobj, "versions");
+ isc_int32_t versions = ISC_LOG_ROLLNEVER;
+ isc_offset_t size = 0;
+
+ type = ISC_LOG_TOFILE;
+
+ if (versionsobj != NULL && cfg_obj_isuint32(versionsobj))
+ versions = cfg_obj_asuint32(versionsobj);
+ if (versionsobj != NULL && cfg_obj_isstring(versionsobj) &&
+ strcasecmp(cfg_obj_asstring(versionsobj), "unlimited") == 0)
+ versions = ISC_LOG_ROLLINFINITE;
+ if (sizeobj != NULL &&
+ cfg_obj_isuint64(sizeobj) &&
+ cfg_obj_asuint64(sizeobj) < ISC_OFFSET_MAXIMUM)
+ size = (isc_offset_t)cfg_obj_asuint64(sizeobj);
+ dest.file.stream = NULL;
+ dest.file.name = cfg_obj_asstring(pathobj);
+ dest.file.versions = versions;
+ dest.file.maximum_size = size;
+ } else if (syslogobj != NULL) {
+ int facility = LOG_DAEMON;
+
+ type = ISC_LOG_TOSYSLOG;
+
+ if (cfg_obj_isstring(syslogobj)) {
+ const char *facilitystr = cfg_obj_asstring(syslogobj);
+ (void)isc_syslog_facilityfromstring(facilitystr,
+ &facility);
+ }
+ dest.facility = facility;
+ } else if (stderrobj != NULL) {
+ type = ISC_LOG_TOFILEDESC;
+ dest.file.stream = stderr;
+ dest.file.name = NULL;
+ dest.file.versions = ISC_LOG_ROLLNEVER;
+ dest.file.maximum_size = 0;
+ }
+
+ /*
+ * Munge flags.
+ */
+ {
+ const cfg_obj_t *printcat = NULL;
+ const cfg_obj_t *printsev = NULL;
+ const cfg_obj_t *printtime = NULL;
+
+ (void)cfg_map_get(channel, "print-category", &printcat);
+ (void)cfg_map_get(channel, "print-severity", &printsev);
+ (void)cfg_map_get(channel, "print-time", &printtime);
+
+ if (printcat != NULL && cfg_obj_asboolean(printcat))
+ flags |= ISC_LOG_PRINTCATEGORY;
+ if (printtime != NULL && cfg_obj_asboolean(printtime))
+ flags |= ISC_LOG_PRINTTIME;
+ if (printsev != NULL && cfg_obj_asboolean(printsev))
+ flags |= ISC_LOG_PRINTLEVEL;
+ }
+
+ level = ISC_LOG_INFO;
+ if (cfg_map_get(channel, "severity", &severity) == ISC_R_SUCCESS) {
+ if (cfg_obj_isstring(severity)) {
+ const char *str = cfg_obj_asstring(severity);
+ if (strcasecmp(str, "critical") == 0)
+ level = ISC_LOG_CRITICAL;
+ else if (strcasecmp(str, "error") == 0)
+ level = ISC_LOG_ERROR;
+ else if (strcasecmp(str, "warning") == 0)
+ level = ISC_LOG_WARNING;
+ else if (strcasecmp(str, "notice") == 0)
+ level = ISC_LOG_NOTICE;
+ else if (strcasecmp(str, "info") == 0)
+ level = ISC_LOG_INFO;
+ else if (strcasecmp(str, "dynamic") == 0)
+ level = ISC_LOG_DYNAMIC;
+ } else
+ /* debug */
+ level = cfg_obj_asuint32(severity);
+ }
+
+ result = isc_log_createchannel(lctx, channelname,
+ type, level, &dest, flags);
+
+ if (result == ISC_R_SUCCESS && type == ISC_LOG_TOFILE) {
+ FILE *fp;
+
+ /*
+ * Test that the file can be opened, since isc_log_open()
+ * can't effectively report failures when called in
+ * isc_log_doit().
+ */
+ result = isc_stdio_open(dest.file.name, "a", &fp);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, CFG_LOGCATEGORY_CONFIG,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "logging channel '%s' file '%s': %s",
+ channelname, dest.file.name,
+ isc_result_totext(result));
+ else
+ (void)isc_stdio_close(fp);
+
+ /*
+ * Allow named to continue by returning success.
+ */
+ result = ISC_R_SUCCESS;
+ }
+
+ return (result);
+}
+
+isc_result_t
+ns_log_configure(isc_logconfig_t *logconf, const cfg_obj_t *logstmt) {
+ isc_result_t result;
+ const cfg_obj_t *channels = NULL;
+ const cfg_obj_t *categories = NULL;
+ const cfg_listelt_t *element;
+ isc_boolean_t default_set = ISC_FALSE;
+ isc_boolean_t unmatched_set = ISC_FALSE;
+ const cfg_obj_t *catname;
+
+ CHECK(ns_log_setdefaultchannels(logconf));
+
+ (void)cfg_map_get(logstmt, "channel", &channels);
+ for (element = cfg_list_first(channels);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *channel = cfg_listelt_value(element);
+ CHECK(channel_fromconf(channel, logconf));
+ }
+
+ (void)cfg_map_get(logstmt, "category", &categories);
+ for (element = cfg_list_first(categories);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *category = cfg_listelt_value(element);
+ CHECK(category_fromconf(category, logconf));
+ if (!default_set) {
+ catname = cfg_tuple_get(category, "name");
+ if (strcmp(cfg_obj_asstring(catname), "default") == 0)
+ default_set = ISC_TRUE;
+ }
+ if (!unmatched_set) {
+ catname = cfg_tuple_get(category, "name");
+ if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0)
+ unmatched_set = ISC_TRUE;
+ }
+ }
+
+ if (!default_set)
+ CHECK(ns_log_setdefaultcategory(logconf));
+
+ if (!unmatched_set)
+ CHECK(ns_log_setunmatchedcategory(logconf));
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (logconf != NULL)
+ isc_logconfig_destroy(&logconf);
+ return (result);
+}
diff --git a/bin/named/lwaddr.c b/bin/named/lwaddr.c
new file mode 100644
index 0000000..ed7880a
--- /dev/null
+++ b/bin/named/lwaddr.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwaddr.c,v 1.10 2008/01/11 23:46:56 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <isc/result.h>
+#include <isc/netaddr.h>
+#include <isc/sockaddr.h>
+
+#include <lwres/lwres.h>
+
+#include <named/lwaddr.h>
+
+/*%
+ * Convert addresses from lwres to isc format.
+ */
+isc_result_t
+lwaddr_netaddr_fromlwresaddr(isc_netaddr_t *na, lwres_addr_t *la) {
+ if (la->family != LWRES_ADDRTYPE_V4 && la->family != LWRES_ADDRTYPE_V6)
+ return (ISC_R_FAMILYNOSUPPORT);
+
+ if (la->family == LWRES_ADDRTYPE_V4) {
+ struct in_addr ina;
+ memcpy(&ina.s_addr, la->address, 4);
+ isc_netaddr_fromin(na, &ina);
+ } else {
+ struct in6_addr ina6;
+ memcpy(&ina6.s6_addr, la->address, 16);
+ isc_netaddr_fromin6(na, &ina6);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+lwaddr_sockaddr_fromlwresaddr(isc_sockaddr_t *sa, lwres_addr_t *la,
+ in_port_t port)
+{
+ isc_netaddr_t na;
+ isc_result_t result;
+
+ result = lwaddr_netaddr_fromlwresaddr(&na, la);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ isc_sockaddr_fromnetaddr(sa, &na, port);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Convert addresses from isc to lwres format.
+ */
+
+isc_result_t
+lwaddr_lwresaddr_fromnetaddr(lwres_addr_t *la, isc_netaddr_t *na) {
+ if (na->family != AF_INET && na->family != AF_INET6)
+ return (ISC_R_FAMILYNOSUPPORT);
+
+ if (na->family == AF_INET) {
+ la->family = LWRES_ADDRTYPE_V4;
+ la->length = 4;
+ memcpy(la->address, &na->type.in, 4);
+ } else {
+ la->family = LWRES_ADDRTYPE_V6;
+ la->length = 16;
+ memcpy(la->address, &na->type.in6, 16);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+lwaddr_lwresaddr_fromsockaddr(lwres_addr_t *la, isc_sockaddr_t *sa) {
+ isc_netaddr_t na;
+ isc_netaddr_fromsockaddr(&na, sa);
+ return (lwaddr_lwresaddr_fromnetaddr(la, &na));
+}
diff --git a/bin/named/lwdclient.c b/bin/named/lwdclient.c
new file mode 100644
index 0000000..a843134
--- /dev/null
+++ b/bin/named/lwdclient.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwdclient.c,v 1.22 2007/06/18 23:47:18 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/socket.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/view.h>
+#include <dns/log.h>
+
+#include <named/types.h>
+#include <named/log.h>
+#include <named/lwresd.h>
+#include <named/lwdclient.h>
+
+#define SHUTTINGDOWN(cm) ((cm->flags & NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN) != 0)
+
+static void
+lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev);
+
+void
+ns_lwdclient_log(int level, const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ isc_log_vwrite(dns_lctx,
+ DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_ADB,
+ ISC_LOG_DEBUG(level), format, args);
+ va_end(args);
+}
+
+isc_result_t
+ns_lwdclientmgr_create(ns_lwreslistener_t *listener, unsigned int nclients,
+ isc_taskmgr_t *taskmgr)
+{
+ ns_lwresd_t *lwresd = listener->manager;
+ ns_lwdclientmgr_t *cm;
+ ns_lwdclient_t *client;
+ unsigned int i;
+ isc_result_t result = ISC_R_FAILURE;
+
+ cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
+ if (cm == NULL)
+ return (ISC_R_NOMEMORY);
+
+ cm->listener = NULL;
+ ns_lwreslistener_attach(listener, &cm->listener);
+ cm->mctx = lwresd->mctx;
+ cm->sock = NULL;
+ isc_socket_attach(listener->sock, &cm->sock);
+ cm->view = lwresd->view;
+ cm->lwctx = NULL;
+ cm->task = NULL;
+ cm->flags = 0;
+ ISC_LINK_INIT(cm, link);
+ ISC_LIST_INIT(cm->idle);
+ ISC_LIST_INIT(cm->running);
+
+ if (lwres_context_create(&cm->lwctx, cm->mctx,
+ ns__lwresd_memalloc, ns__lwresd_memfree,
+ LWRES_CONTEXT_SERVERMODE)
+ != ISC_R_SUCCESS)
+ goto errout;
+
+ for (i = 0; i < nclients; i++) {
+ client = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclient_t));
+ if (client != NULL) {
+ ns_lwdclient_log(50, "created client %p, manager %p",
+ client, cm);
+ ns_lwdclient_initialize(client, cm);
+ }
+ }
+
+ /*
+ * If we could create no clients, clean up and return.
+ */
+ if (ISC_LIST_EMPTY(cm->idle))
+ goto errout;
+
+ result = isc_task_create(taskmgr, 0, &cm->task);
+ if (result != ISC_R_SUCCESS)
+ goto errout;
+ isc_task_setname(cm->task, "lwdclient", NULL);
+
+ /*
+ * This MUST be last, since there is no way to cancel an onshutdown...
+ */
+ result = isc_task_onshutdown(cm->task, lwdclientmgr_shutdown_callback,
+ cm);
+ if (result != ISC_R_SUCCESS)
+ goto errout;
+
+ ns_lwreslistener_linkcm(listener, cm);
+
+ return (ISC_R_SUCCESS);
+
+ errout:
+ client = ISC_LIST_HEAD(cm->idle);
+ while (client != NULL) {
+ ISC_LIST_UNLINK(cm->idle, client, link);
+ isc_mem_put(lwresd->mctx, client, sizeof(*client));
+ client = ISC_LIST_HEAD(cm->idle);
+ }
+
+ if (cm->task != NULL)
+ isc_task_detach(&cm->task);
+
+ if (cm->lwctx != NULL)
+ lwres_context_destroy(&cm->lwctx);
+
+ isc_mem_put(lwresd->mctx, cm, sizeof(*cm));
+ return (result);
+}
+
+static void
+lwdclientmgr_destroy(ns_lwdclientmgr_t *cm) {
+ ns_lwdclient_t *client;
+ ns_lwreslistener_t *listener;
+
+ if (!SHUTTINGDOWN(cm))
+ return;
+
+ /*
+ * run through the idle list and free the clients there. Idle
+ * clients do not have a recv running nor do they have any finds
+ * or similar running.
+ */
+ client = ISC_LIST_HEAD(cm->idle);
+ while (client != NULL) {
+ ns_lwdclient_log(50, "destroying client %p, manager %p",
+ client, cm);
+ ISC_LIST_UNLINK(cm->idle, client, link);
+ isc_mem_put(cm->mctx, client, sizeof(*client));
+ client = ISC_LIST_HEAD(cm->idle);
+ }
+
+ if (!ISC_LIST_EMPTY(cm->running))
+ return;
+
+ lwres_context_destroy(&cm->lwctx);
+ cm->view = NULL;
+ isc_socket_detach(&cm->sock);
+ isc_task_detach(&cm->task);
+
+ listener = cm->listener;
+ ns_lwreslistener_unlinkcm(listener, cm);
+ ns_lwdclient_log(50, "destroying manager %p", cm);
+ isc_mem_put(cm->mctx, cm, sizeof(*cm));
+ ns_lwreslistener_detach(&listener);
+}
+
+static void
+process_request(ns_lwdclient_t *client) {
+ lwres_buffer_t b;
+ isc_result_t result;
+
+ lwres_buffer_init(&b, client->buffer, client->recvlength);
+ lwres_buffer_add(&b, client->recvlength);
+
+ result = lwres_lwpacket_parseheader(&b, &client->pkt);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_log(50, "invalid packet header received");
+ goto restart;
+ }
+
+ ns_lwdclient_log(50, "opcode %08x", client->pkt.opcode);
+
+ switch (client->pkt.opcode) {
+ case LWRES_OPCODE_GETADDRSBYNAME:
+ ns_lwdclient_processgabn(client, &b);
+ return;
+ case LWRES_OPCODE_GETNAMEBYADDR:
+ ns_lwdclient_processgnba(client, &b);
+ return;
+ case LWRES_OPCODE_GETRDATABYNAME:
+ ns_lwdclient_processgrbn(client, &b);
+ return;
+ case LWRES_OPCODE_NOOP:
+ ns_lwdclient_processnoop(client, &b);
+ return;
+ default:
+ ns_lwdclient_log(50, "unknown opcode %08x", client->pkt.opcode);
+ goto restart;
+ }
+
+ /*
+ * Drop the packet.
+ */
+ restart:
+ ns_lwdclient_log(50, "restarting client %p...", client);
+ ns_lwdclient_stateidle(client);
+}
+
+void
+ns_lwdclient_recv(isc_task_t *task, isc_event_t *ev) {
+ isc_result_t result;
+ ns_lwdclient_t *client = ev->ev_arg;
+ ns_lwdclientmgr_t *cm = client->clientmgr;
+ isc_socketevent_t *dev = (isc_socketevent_t *)ev;
+
+ INSIST(dev->region.base == client->buffer);
+ INSIST(NS_LWDCLIENT_ISRECV(client));
+
+ NS_LWDCLIENT_SETRECVDONE(client);
+
+ INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
+ cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
+
+ ns_lwdclient_log(50,
+ "event received: task %p, length %u, result %u (%s)",
+ task, dev->n, dev->result,
+ isc_result_totext(dev->result));
+
+ if (dev->result != ISC_R_SUCCESS) {
+ isc_event_free(&ev);
+ dev = NULL;
+
+ /*
+ * Go idle.
+ */
+ ns_lwdclient_stateidle(client);
+
+ return;
+ }
+
+ client->recvlength = dev->n;
+ client->address = dev->address;
+ if ((dev->attributes & ISC_SOCKEVENTATTR_PKTINFO) != 0) {
+ client->pktinfo = dev->pktinfo;
+ client->pktinfo_valid = ISC_TRUE;
+ } else
+ client->pktinfo_valid = ISC_FALSE;
+ isc_event_free(&ev);
+ dev = NULL;
+
+ result = ns_lwdclient_startrecv(cm);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
+ "could not start lwres "
+ "client handler: %s",
+ isc_result_totext(result));
+
+ process_request(client);
+}
+
+/*
+ * This function will start a new recv() on a socket for this client manager.
+ */
+isc_result_t
+ns_lwdclient_startrecv(ns_lwdclientmgr_t *cm) {
+ ns_lwdclient_t *client;
+ isc_result_t result;
+ isc_region_t r;
+
+ if (SHUTTINGDOWN(cm)) {
+ lwdclientmgr_destroy(cm);
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * If a recv is already running, don't bother.
+ */
+ if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * If we have no idle slots, just return success.
+ */
+ client = ISC_LIST_HEAD(cm->idle);
+ if (client == NULL)
+ return (ISC_R_SUCCESS);
+ INSIST(NS_LWDCLIENT_ISIDLE(client));
+
+ /*
+ * Issue the recv. If it fails, return that it did.
+ */
+ r.base = client->buffer;
+ r.length = LWRES_RECVLENGTH;
+ result = isc_socket_recv(cm->sock, &r, 0, cm->task, ns_lwdclient_recv,
+ client);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Set the flag to say we've issued a recv() call.
+ */
+ cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
+
+ /*
+ * Remove the client from the idle list, and put it on the running
+ * list.
+ */
+ NS_LWDCLIENT_SETRECV(client);
+ ISC_LIST_UNLINK(cm->idle, client, link);
+ ISC_LIST_APPEND(cm->running, client, link);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+lwdclientmgr_shutdown_callback(isc_task_t *task, isc_event_t *ev) {
+ ns_lwdclientmgr_t *cm = ev->ev_arg;
+ ns_lwdclient_t *client;
+
+ REQUIRE(!SHUTTINGDOWN(cm));
+
+ ns_lwdclient_log(50, "got shutdown event, task %p, lwdclientmgr %p",
+ task, cm);
+
+ /*
+ * run through the idle list and free the clients there. Idle
+ * clients do not have a recv running nor do they have any finds
+ * or similar running.
+ */
+ client = ISC_LIST_HEAD(cm->idle);
+ while (client != NULL) {
+ ns_lwdclient_log(50, "destroying client %p, manager %p",
+ client, cm);
+ ISC_LIST_UNLINK(cm->idle, client, link);
+ isc_mem_put(cm->mctx, client, sizeof(*client));
+ client = ISC_LIST_HEAD(cm->idle);
+ }
+
+ /*
+ * Cancel any pending I/O.
+ */
+ isc_socket_cancel(cm->sock, task, ISC_SOCKCANCEL_ALL);
+
+ /*
+ * Run through the running client list and kill off any finds
+ * in progress.
+ */
+ client = ISC_LIST_HEAD(cm->running);
+ while (client != NULL) {
+ if (client->find != client->v4find
+ && client->find != client->v6find)
+ dns_adb_cancelfind(client->find);
+ if (client->v4find != NULL)
+ dns_adb_cancelfind(client->v4find);
+ if (client->v6find != NULL)
+ dns_adb_cancelfind(client->v6find);
+ client = ISC_LIST_NEXT(client, link);
+ }
+
+ cm->flags |= NS_LWDCLIENTMGR_FLAGSHUTTINGDOWN;
+
+ isc_event_free(&ev);
+}
+
+/*
+ * Do all the crap needed to move a client from the run queue to the idle
+ * queue.
+ */
+void
+ns_lwdclient_stateidle(ns_lwdclient_t *client) {
+ ns_lwdclientmgr_t *cm;
+ isc_result_t result;
+
+ cm = client->clientmgr;
+
+ INSIST(client->sendbuf == NULL);
+ INSIST(client->sendlength == 0);
+ INSIST(client->arg == NULL);
+ INSIST(client->v4find == NULL);
+ INSIST(client->v6find == NULL);
+
+ ISC_LIST_UNLINK(cm->running, client, link);
+ ISC_LIST_PREPEND(cm->idle, client, link);
+
+ NS_LWDCLIENT_SETIDLE(client);
+
+ result = ns_lwdclient_startrecv(cm);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
+ "could not start lwres "
+ "client handler: %s",
+ isc_result_totext(result));
+}
+
+void
+ns_lwdclient_send(isc_task_t *task, isc_event_t *ev) {
+ ns_lwdclient_t *client = ev->ev_arg;
+ ns_lwdclientmgr_t *cm = client->clientmgr;
+ isc_socketevent_t *dev = (isc_socketevent_t *)ev;
+
+ UNUSED(task);
+ UNUSED(dev);
+
+ INSIST(NS_LWDCLIENT_ISSEND(client));
+ INSIST(client->sendbuf == dev->region.base);
+
+ ns_lwdclient_log(50, "task %p for client %p got send-done event",
+ task, client);
+
+ if (client->sendbuf != client->buffer)
+ lwres_context_freemem(cm->lwctx, client->sendbuf,
+ client->sendlength);
+ client->sendbuf = NULL;
+ client->sendlength = 0;
+
+ ns_lwdclient_stateidle(client);
+
+ isc_event_free(&ev);
+}
+
+isc_result_t
+ns_lwdclient_sendreply(ns_lwdclient_t *client, isc_region_t *r) {
+ struct in6_pktinfo *pktinfo;
+ ns_lwdclientmgr_t *cm = client->clientmgr;
+
+ if (client->pktinfo_valid)
+ pktinfo = &client->pktinfo;
+ else
+ pktinfo = NULL;
+ return (isc_socket_sendto(cm->sock, r, cm->task, ns_lwdclient_send,
+ client, &client->address, pktinfo));
+}
+
+void
+ns_lwdclient_initialize(ns_lwdclient_t *client, ns_lwdclientmgr_t *cmgr) {
+ client->clientmgr = cmgr;
+ ISC_LINK_INIT(client, link);
+ NS_LWDCLIENT_SETIDLE(client);
+ client->arg = NULL;
+
+ client->recvlength = 0;
+
+ client->sendbuf = NULL;
+ client->sendlength = 0;
+
+ client->find = NULL;
+ client->v4find = NULL;
+ client->v6find = NULL;
+ client->find_wanted = 0;
+
+ client->options = 0;
+ client->byaddr = NULL;
+
+ client->lookup = NULL;
+
+ client->pktinfo_valid = ISC_FALSE;
+
+ ISC_LIST_APPEND(cmgr->idle, client, link);
+}
diff --git a/bin/named/lwderror.c b/bin/named/lwderror.c
new file mode 100644
index 0000000..33f247a
--- /dev/null
+++ b/bin/named/lwderror.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwderror.c,v 1.12 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/socket.h>
+#include <isc/util.h>
+
+#include <named/types.h>
+#include <named/lwdclient.h>
+
+/*%
+ * Generate an error packet for the client, schedule a send, and put us in
+ * the SEND state.
+ *
+ * The client->pkt structure will be modified to form an error return.
+ * The receiver needs to verify that it is in fact an error, and do the
+ * right thing with it. The opcode will be unchanged. The result needs
+ * to be set before calling this function.
+ *
+ * The only change this code makes is to set the receive buffer size to the
+ * size we use, set the reply bit, and recompute any security information.
+ */
+void
+ns_lwdclient_errorpktsend(ns_lwdclient_t *client, isc_uint32_t _result) {
+ isc_result_t result;
+ int lwres;
+ isc_region_t r;
+ lwres_buffer_t b;
+
+ REQUIRE(NS_LWDCLIENT_ISRUNNING(client));
+
+ /*
+ * Since we are only sending the packet header, we can safely toss
+ * the receive buffer. This means we won't need to allocate space
+ * for sending an error reply. This is a Good Thing.
+ */
+ client->pkt.length = LWRES_LWPACKET_LENGTH;
+ client->pkt.pktflags |= LWRES_LWPACKETFLAG_RESPONSE;
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+ client->pkt.result = _result;
+
+ lwres_buffer_init(&b, client->buffer, LWRES_RECVLENGTH);
+ lwres = lwres_lwpacket_renderheader(&b, &client->pkt);
+ if (lwres != LWRES_R_SUCCESS) {
+ ns_lwdclient_stateidle(client);
+ return;
+ }
+
+ r.base = client->buffer;
+ r.length = b.used;
+ client->sendbuf = client->buffer;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_stateidle(client);
+ return;
+ }
+
+ NS_LWDCLIENT_SETSEND(client);
+}
diff --git a/bin/named/lwdgabn.c b/bin/named/lwdgabn.c
new file mode 100644
index 0000000..dec1e1a
--- /dev/null
+++ b/bin/named/lwdgabn.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwdgabn.c,v 1.22 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include <isc/netaddr.h>
+#include <isc/sockaddr.h>
+#include <isc/socket.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/events.h>
+#include <dns/result.h>
+
+#include <named/types.h>
+#include <named/lwaddr.h>
+#include <named/lwdclient.h>
+#include <named/lwresd.h>
+#include <named/lwsearch.h>
+#include <named/sortlist.h>
+
+#define NEED_V4(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V4) != 0) \
+ && ((c)->v4find == NULL))
+#define NEED_V6(c) ((((c)->find_wanted & LWRES_ADDRTYPE_V6) != 0) \
+ && ((c)->v6find == NULL))
+
+static isc_result_t start_find(ns_lwdclient_t *);
+static void restart_find(ns_lwdclient_t *);
+static void init_gabn(ns_lwdclient_t *);
+
+/*%
+ * Destroy any finds. This can be used to "start over from scratch" and
+ * should only be called when events are _not_ being generated by the finds.
+ */
+static void
+cleanup_gabn(ns_lwdclient_t *client) {
+ ns_lwdclient_log(50, "cleaning up client %p", client);
+
+ if (client->v6find != NULL) {
+ if (client->v6find == client->v4find)
+ client->v6find = NULL;
+ else
+ dns_adb_destroyfind(&client->v6find);
+ }
+ if (client->v4find != NULL)
+ dns_adb_destroyfind(&client->v4find);
+}
+
+static void
+setup_addresses(ns_lwdclient_t *client, dns_adbfind_t *find, unsigned int at) {
+ dns_adbaddrinfo_t *ai;
+ lwres_addr_t *addr;
+ int af;
+ const struct sockaddr *sa;
+ isc_result_t result;
+
+ if (at == DNS_ADBFIND_INET)
+ af = AF_INET;
+ else
+ af = AF_INET6;
+
+ ai = ISC_LIST_HEAD(find->list);
+ while (ai != NULL && client->gabn.naddrs < LWRES_MAX_ADDRS) {
+ sa = &ai->sockaddr.type.sa;
+ if (sa->sa_family != af)
+ goto next;
+
+ addr = &client->addrs[client->gabn.naddrs];
+
+ result = lwaddr_lwresaddr_fromsockaddr(addr, &ai->sockaddr);
+ if (result != ISC_R_SUCCESS)
+ goto next;
+
+ ns_lwdclient_log(50, "adding address %p, family %d, length %d",
+ addr->address, addr->family, addr->length);
+
+ client->gabn.naddrs++;
+ REQUIRE(!LWRES_LINK_LINKED(addr, link));
+ LWRES_LIST_APPEND(client->gabn.addrs, addr, link);
+
+ next:
+ ai = ISC_LIST_NEXT(ai, publink);
+ }
+}
+
+typedef struct {
+ isc_netaddr_t address;
+ int rank;
+} rankedaddress;
+
+static int
+addr_compare(const void *av, const void *bv) {
+ const rankedaddress *a = (const rankedaddress *) av;
+ const rankedaddress *b = (const rankedaddress *) bv;
+ return (a->rank - b->rank);
+}
+
+static void
+sort_addresses(ns_lwdclient_t *client) {
+ unsigned int naddrs;
+ rankedaddress *addrs;
+ isc_netaddr_t remote;
+ dns_addressorderfunc_t order;
+ const void *arg;
+ ns_lwresd_t *lwresd = client->clientmgr->listener->manager;
+ unsigned int i;
+ isc_result_t result;
+
+ naddrs = client->gabn.naddrs;
+
+ if (naddrs <= 1 || lwresd->view->sortlist == NULL)
+ return;
+
+ addrs = isc_mem_get(lwresd->mctx, sizeof(rankedaddress) * naddrs);
+ if (addrs == NULL)
+ return;
+
+ isc_netaddr_fromsockaddr(&remote, &client->address);
+ ns_sortlist_byaddrsetup(lwresd->view->sortlist,
+ &remote, &order, &arg);
+ if (order == NULL) {
+ isc_mem_put(lwresd->mctx, addrs,
+ sizeof(rankedaddress) * naddrs);
+ return;
+ }
+ for (i = 0; i < naddrs; i++) {
+ result = lwaddr_netaddr_fromlwresaddr(&addrs[i].address,
+ &client->addrs[i]);
+ INSIST(result == ISC_R_SUCCESS);
+ addrs[i].rank = (*order)(&addrs[i].address, arg);
+ }
+ qsort(addrs, naddrs, sizeof(rankedaddress), addr_compare);
+ for (i = 0; i < naddrs; i++) {
+ result = lwaddr_lwresaddr_fromnetaddr(&client->addrs[i],
+ &addrs[i].address);
+ INSIST(result == ISC_R_SUCCESS);
+ }
+
+ isc_mem_put(lwresd->mctx, addrs, sizeof(rankedaddress) * naddrs);
+}
+
+static void
+generate_reply(ns_lwdclient_t *client) {
+ isc_result_t result;
+ int lwres;
+ isc_region_t r;
+ lwres_buffer_t lwb;
+ ns_lwdclientmgr_t *cm;
+
+ cm = client->clientmgr;
+ lwb.base = NULL;
+
+ ns_lwdclient_log(50, "generating gabn reply for client %p", client);
+
+ /*
+ * We must make certain the client->find is not still active.
+ * If it is either the v4 or v6 answer, just set it to NULL and
+ * let the cleanup code destroy it. Otherwise, destroy it now.
+ */
+ if (client->find == client->v4find || client->find == client->v6find)
+ client->find = NULL;
+ else
+ if (client->find != NULL)
+ dns_adb_destroyfind(&client->find);
+
+ /*
+ * perhaps there are some here?
+ */
+ if (NEED_V6(client) && client->v4find != NULL)
+ client->v6find = client->v4find;
+
+ /*
+ * Run through the finds we have and wire them up to the gabn
+ * structure.
+ */
+ LWRES_LIST_INIT(client->gabn.addrs);
+ if (client->v4find != NULL)
+ setup_addresses(client, client->v4find, DNS_ADBFIND_INET);
+ if (client->v6find != NULL)
+ setup_addresses(client, client->v6find, DNS_ADBFIND_INET6);
+
+ /*
+ * If there are no addresses, try the next element in the search
+ * path, if there are any more. Otherwise, fall through into
+ * the error handling code below.
+ */
+ if (client->gabn.naddrs == 0) {
+ do {
+ result = ns_lwsearchctx_next(&client->searchctx);
+ if (result == ISC_R_SUCCESS) {
+ cleanup_gabn(client);
+ result = start_find(client);
+ if (result == ISC_R_SUCCESS)
+ return;
+ }
+ } while (result == ISC_R_SUCCESS);
+ }
+
+ /*
+ * Render the packet.
+ */
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+
+ /*
+ * If there are no addresses, return failure.
+ */
+ if (client->gabn.naddrs != 0)
+ client->pkt.result = LWRES_R_SUCCESS;
+ else
+ client->pkt.result = LWRES_R_NOTFOUND;
+
+ sort_addresses(client);
+
+ lwres = lwres_gabnresponse_render(cm->lwctx, &client->gabn,
+ &client->pkt, &lwb);
+ if (lwres != LWRES_R_SUCCESS)
+ goto out;
+
+ r.base = lwb.base;
+ r.length = lwb.used;
+ client->sendbuf = r.base;
+ client->sendlength = r.length;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ NS_LWDCLIENT_SETSEND(client);
+
+ /*
+ * All done!
+ */
+ cleanup_gabn(client);
+
+ return;
+
+ out:
+ cleanup_gabn(client);
+
+ if (lwb.base != NULL)
+ lwres_context_freemem(client->clientmgr->lwctx,
+ lwb.base, lwb.length);
+
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
+
+/*
+ * Take the current real name, move it to an alias slot (if any are
+ * open) then put this new name in as the real name for the target.
+ *
+ * Return success if it can be rendered, otherwise failure. Note that
+ * not having enough alias slots open is NOT a failure.
+ */
+static isc_result_t
+add_alias(ns_lwdclient_t *client) {
+ isc_buffer_t b;
+ isc_result_t result;
+ isc_uint16_t naliases;
+
+ b = client->recv_buffer;
+
+ /*
+ * Render the new name to the buffer.
+ */
+ result = dns_name_totext(dns_fixedname_name(&client->target_name),
+ ISC_TRUE, &client->recv_buffer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Are there any open slots?
+ */
+ naliases = client->gabn.naliases;
+ if (naliases < LWRES_MAX_ALIASES) {
+ client->gabn.aliases[naliases] = client->gabn.realname;
+ client->gabn.aliaslen[naliases] = client->gabn.realnamelen;
+ client->gabn.naliases++;
+ }
+
+ /*
+ * Save this name away as the current real name.
+ */
+ client->gabn.realname = (char *)(b.base) + b.used;
+ client->gabn.realnamelen = client->recv_buffer.used - b.used;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+store_realname(ns_lwdclient_t *client) {
+ isc_buffer_t b;
+ isc_result_t result;
+ dns_name_t *tname;
+
+ b = client->recv_buffer;
+
+ tname = dns_fixedname_name(&client->target_name);
+ result = ns_lwsearchctx_current(&client->searchctx, tname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Render the new name to the buffer.
+ */
+ result = dns_name_totext(tname, ISC_TRUE, &client->recv_buffer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Save this name away as the current real name.
+ */
+ client->gabn.realname = (char *) b.base + b.used;
+ client->gabn.realnamelen = client->recv_buffer.used - b.used;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+process_gabn_finddone(isc_task_t *task, isc_event_t *ev) {
+ ns_lwdclient_t *client = ev->ev_arg;
+ isc_eventtype_t evtype;
+ isc_boolean_t claimed;
+
+ ns_lwdclient_log(50, "find done for task %p, client %p", task, client);
+
+ evtype = ev->ev_type;
+ isc_event_free(&ev);
+
+ /*
+ * No more info to be had? If so, we have all the good stuff
+ * right now, so we can render things.
+ */
+ claimed = ISC_FALSE;
+ if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
+ if (NEED_V4(client)) {
+ client->v4find = client->find;
+ claimed = ISC_TRUE;
+ }
+ if (NEED_V6(client)) {
+ client->v6find = client->find;
+ claimed = ISC_TRUE;
+ }
+ if (client->find != NULL) {
+ if (claimed)
+ client->find = NULL;
+ else
+ dns_adb_destroyfind(&client->find);
+
+ }
+ generate_reply(client);
+ return;
+ }
+
+ /*
+ * We probably don't need this find anymore. We're either going to
+ * reissue it, or an error occurred. Either way, we're done with
+ * it.
+ */
+ if ((client->find != client->v4find)
+ && (client->find != client->v6find)) {
+ dns_adb_destroyfind(&client->find);
+ } else {
+ client->find = NULL;
+ }
+
+ /*
+ * We have some new information we can gather. Run off and fetch
+ * it.
+ */
+ if (evtype == DNS_EVENT_ADBMOREADDRESSES) {
+ restart_find(client);
+ return;
+ }
+
+ /*
+ * An error or other strangeness happened. Drop this query.
+ */
+ cleanup_gabn(client);
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
+
+static void
+restart_find(ns_lwdclient_t *client) {
+ unsigned int options;
+ isc_result_t result;
+ isc_boolean_t claimed;
+
+ ns_lwdclient_log(50, "starting find for client %p", client);
+
+ /*
+ * Issue a find for the name contained in the request. We won't
+ * set the bit that says "anything is good enough" -- we want it
+ * all.
+ */
+ options = 0;
+ options |= DNS_ADBFIND_WANTEVENT;
+ options |= DNS_ADBFIND_RETURNLAME;
+
+ /*
+ * Set the bits up here to mark that we want this address family
+ * and that we do not currently have a find pending. We will
+ * set that bit again below if it turns out we will get an event.
+ */
+ if (NEED_V4(client))
+ options |= DNS_ADBFIND_INET;
+ if (NEED_V6(client))
+ options |= DNS_ADBFIND_INET6;
+
+ find_again:
+ INSIST(client->find == NULL);
+ result = dns_adb_createfind(client->clientmgr->view->adb,
+ client->clientmgr->task,
+ process_gabn_finddone, client,
+ dns_fixedname_name(&client->target_name),
+ dns_rootname, 0, options, 0,
+ dns_fixedname_name(&client->target_name),
+ client->clientmgr->view->dstport,
+ &client->find);
+
+ /*
+ * Did we get an alias? If so, save it and re-issue the query.
+ */
+ if (result == DNS_R_ALIAS) {
+ ns_lwdclient_log(50, "found alias, restarting query");
+ dns_adb_destroyfind(&client->find);
+ cleanup_gabn(client);
+ result = add_alias(client);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_log(50,
+ "out of buffer space adding alias");
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+ return;
+ }
+ goto find_again;
+ }
+
+ ns_lwdclient_log(50, "find returned %d (%s)", result,
+ isc_result_totext(result));
+
+ /*
+ * Did we get an error?
+ */
+ if (result != ISC_R_SUCCESS) {
+ if (client->find != NULL)
+ dns_adb_destroyfind(&client->find);
+ cleanup_gabn(client);
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+ return;
+ }
+
+ claimed = ISC_FALSE;
+
+ /*
+ * Did we get our answer to V4 addresses?
+ */
+ if (NEED_V4(client)
+ && ((client->find->query_pending & DNS_ADBFIND_INET) == 0)) {
+ ns_lwdclient_log(50, "client %p ipv4 satisfied by find %p",
+ client, client->find);
+ claimed = ISC_TRUE;
+ client->v4find = client->find;
+ }
+
+ /*
+ * Did we get our answer to V6 addresses?
+ */
+ if (NEED_V6(client)
+ && ((client->find->query_pending & DNS_ADBFIND_INET6) == 0)) {
+ ns_lwdclient_log(50, "client %p ipv6 satisfied by find %p",
+ client, client->find);
+ claimed = ISC_TRUE;
+ client->v6find = client->find;
+ }
+
+ /*
+ * If we're going to get an event, set our internal pending flag
+ * and return. When we get an event back we'll do the right
+ * thing, basically by calling this function again, perhaps with a
+ * new target name.
+ *
+ * If we have both v4 and v6, and we are still getting an event,
+ * we have a programming error, so die hard.
+ */
+ if ((client->find->options & DNS_ADBFIND_WANTEVENT) != 0) {
+ ns_lwdclient_log(50, "event will be sent");
+ INSIST(client->v4find == NULL || client->v6find == NULL);
+ return;
+ }
+ ns_lwdclient_log(50, "no event will be sent");
+ if (claimed)
+ client->find = NULL;
+ else
+ dns_adb_destroyfind(&client->find);
+
+ /*
+ * We seem to have everything we asked for, or at least we are
+ * able to respond with things we've learned.
+ */
+
+ generate_reply(client);
+}
+
+static isc_result_t
+start_find(ns_lwdclient_t *client) {
+ isc_result_t result;
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ init_gabn(client);
+
+ result = store_realname(client);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ restart_find(client);
+ return (ISC_R_SUCCESS);
+
+}
+
+static void
+init_gabn(ns_lwdclient_t *client) {
+ int i;
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ for (i = 0; i < LWRES_MAX_ALIASES; i++) {
+ client->aliases[i] = NULL;
+ client->aliaslen[i] = 0;
+ }
+ for (i = 0; i < LWRES_MAX_ADDRS; i++) {
+ client->addrs[i].family = 0;
+ client->addrs[i].length = 0;
+ memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
+ LWRES_LINK_INIT(&client->addrs[i], link);
+ }
+
+ client->gabn.naliases = 0;
+ client->gabn.naddrs = 0;
+ client->gabn.realname = NULL;
+ client->gabn.aliases = client->aliases;
+ client->gabn.realnamelen = 0;
+ client->gabn.aliaslen = client->aliaslen;
+ LWRES_LIST_INIT(client->gabn.addrs);
+ client->gabn.base = NULL;
+ client->gabn.baselen = 0;
+
+ /*
+ * Set up the internal buffer to point to the receive region.
+ */
+ isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
+}
+
+/*
+ * When we are called, we can be assured that:
+ *
+ * client->sockaddr contains the address we need to reply to,
+ *
+ * client->pkt contains the packet header data,
+ *
+ * the packet "checks out" overall -- any MD5 hashes or crypto
+ * bits have been verified,
+ *
+ * "b" points to the remaining data after the packet header
+ * was parsed off.
+ *
+ * We are in a the RECVDONE state.
+ *
+ * From this state we will enter the SEND state if we happen to have
+ * everything we need or we need to return an error packet, or to the
+ * FINDWAIT state if we need to look things up.
+ */
+void
+ns_lwdclient_processgabn(ns_lwdclient_t *client, lwres_buffer_t *b) {
+ isc_result_t result;
+ lwres_gabnrequest_t *req;
+ ns_lwdclientmgr_t *cm;
+ isc_buffer_t namebuf;
+
+ REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
+
+ cm = client->clientmgr;
+ req = NULL;
+
+ result = lwres_gabnrequest_parse(client->clientmgr->lwctx,
+ b, &client->pkt, &req);
+ if (result != LWRES_R_SUCCESS)
+ goto out;
+ if (req->name == NULL)
+ goto out;
+
+ isc_buffer_init(&namebuf, req->name, req->namelen);
+ isc_buffer_add(&namebuf, req->namelen);
+
+ dns_fixedname_init(&client->target_name);
+ dns_fixedname_init(&client->query_name);
+ result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
+ &namebuf, NULL, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ ns_lwsearchctx_init(&client->searchctx,
+ cm->listener->manager->search,
+ dns_fixedname_name(&client->query_name),
+ cm->listener->manager->ndots);
+ ns_lwsearchctx_first(&client->searchctx);
+
+ client->find_wanted = req->addrtypes;
+ ns_lwdclient_log(50, "client %p looking for addrtypes %08x",
+ client, client->find_wanted);
+
+ /*
+ * We no longer need to keep this around.
+ */
+ lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
+
+ /*
+ * Start the find.
+ */
+ result = start_find(client);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ return;
+
+ /*
+ * We're screwed. Return an error packet to our caller.
+ */
+ out:
+ if (req != NULL)
+ lwres_gabnrequest_free(client->clientmgr->lwctx, &req);
+
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
diff --git a/bin/named/lwdgnba.c b/bin/named/lwdgnba.c
new file mode 100644
index 0000000..dfc2ad6
--- /dev/null
+++ b/bin/named/lwdgnba.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwdgnba.c,v 1.22 2008/01/14 23:46:56 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/socket.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/byaddr.h>
+#include <dns/result.h>
+
+#include <named/types.h>
+#include <named/lwdclient.h>
+
+static void start_byaddr(ns_lwdclient_t *);
+
+static void
+byaddr_done(isc_task_t *task, isc_event_t *event) {
+ ns_lwdclient_t *client;
+ ns_lwdclientmgr_t *cm;
+ dns_byaddrevent_t *bevent;
+ int lwres;
+ lwres_buffer_t lwb;
+ dns_name_t *name;
+ isc_result_t result;
+ lwres_result_t lwresult;
+ isc_region_t r;
+ isc_buffer_t b;
+ lwres_gnbaresponse_t *gnba;
+ isc_uint16_t naliases;
+
+ UNUSED(task);
+
+ lwb.base = NULL;
+ client = event->ev_arg;
+ cm = client->clientmgr;
+ INSIST(client->byaddr == (dns_byaddr_t *)event->ev_sender);
+
+ bevent = (dns_byaddrevent_t *)event;
+ gnba = &client->gnba;
+
+ ns_lwdclient_log(50, "byaddr event result = %s",
+ isc_result_totext(bevent->result));
+
+ result = bevent->result;
+ if (result != ISC_R_SUCCESS) {
+ dns_byaddr_destroy(&client->byaddr);
+ isc_event_free(&event);
+ bevent = NULL;
+
+ if (client->na.family != AF_INET6 ||
+ (client->options & DNS_BYADDROPT_IPV6INT) != 0) {
+ if (result == DNS_R_NCACHENXDOMAIN ||
+ result == DNS_R_NCACHENXRRSET ||
+ result == DNS_R_NXDOMAIN ||
+ result == DNS_R_NXRRSET)
+ lwresult = LWRES_R_NOTFOUND;
+ else
+ lwresult = LWRES_R_FAILURE;
+ ns_lwdclient_errorpktsend(client, lwresult);
+ return;
+ }
+
+ /*
+ * Fall back to ip6.int reverse if the default ip6.arpa
+ * fails.
+ */
+ client->options |= DNS_BYADDROPT_IPV6INT;
+
+ start_byaddr(client);
+ return;
+ }
+
+ for (name = ISC_LIST_HEAD(bevent->names);
+ name != NULL;
+ name = ISC_LIST_NEXT(name, link))
+ {
+ b = client->recv_buffer;
+
+ result = dns_name_totext(name, ISC_TRUE, &client->recv_buffer);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ ns_lwdclient_log(50, "found name '%.*s'",
+ (int)(client->recv_buffer.used - b.used),
+ (char *)(b.base) + b.used);
+ if (gnba->realname == NULL) {
+ gnba->realname = (char *)(b.base) + b.used;
+ gnba->realnamelen = client->recv_buffer.used - b.used;
+ } else {
+ naliases = gnba->naliases;
+ if (naliases >= LWRES_MAX_ALIASES)
+ break;
+ gnba->aliases[naliases] = (char *)(b.base) + b.used;
+ gnba->aliaslen[naliases] =
+ client->recv_buffer.used - b.used;
+ gnba->naliases++;
+ }
+ }
+
+ dns_byaddr_destroy(&client->byaddr);
+ isc_event_free(&event);
+
+ /*
+ * Render the packet.
+ */
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+ client->pkt.result = LWRES_R_SUCCESS;
+
+ lwres = lwres_gnbaresponse_render(cm->lwctx,
+ gnba, &client->pkt, &lwb);
+ if (lwres != LWRES_R_SUCCESS)
+ goto out;
+
+ r.base = lwb.base;
+ r.length = lwb.used;
+ client->sendbuf = r.base;
+ client->sendlength = r.length;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ NS_LWDCLIENT_SETSEND(client);
+
+ return;
+
+ out:
+ if (client->byaddr != NULL)
+ dns_byaddr_destroy(&client->byaddr);
+ if (lwb.base != NULL)
+ lwres_context_freemem(cm->lwctx,
+ lwb.base, lwb.length);
+
+ if (event != NULL)
+ isc_event_free(&event);
+}
+
+static void
+start_byaddr(ns_lwdclient_t *client) {
+ isc_result_t result;
+ ns_lwdclientmgr_t *cm;
+
+ cm = client->clientmgr;
+
+ INSIST(client->byaddr == NULL);
+
+ result = dns_byaddr_create(cm->mctx, &client->na, cm->view,
+ client->options, cm->task, byaddr_done,
+ client, &client->byaddr);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+ return;
+ }
+}
+
+static void
+init_gnba(ns_lwdclient_t *client) {
+ int i;
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ for (i = 0; i < LWRES_MAX_ALIASES; i++) {
+ client->aliases[i] = NULL;
+ client->aliaslen[i] = 0;
+ }
+ for (i = 0; i < LWRES_MAX_ADDRS; i++) {
+ client->addrs[i].family = 0;
+ client->addrs[i].length = 0;
+ memset(client->addrs[i].address, 0, LWRES_ADDR_MAXLEN);
+ LWRES_LINK_INIT(&client->addrs[i], link);
+ }
+
+ client->gnba.naliases = 0;
+ client->gnba.realname = NULL;
+ client->gnba.aliases = client->aliases;
+ client->gnba.realnamelen = 0;
+ client->gnba.aliaslen = client->aliaslen;
+ client->gnba.base = NULL;
+ client->gnba.baselen = 0;
+ isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
+}
+
+void
+ns_lwdclient_processgnba(ns_lwdclient_t *client, lwres_buffer_t *b) {
+ lwres_gnbarequest_t *req;
+ isc_result_t result;
+ isc_sockaddr_t sa;
+ ns_lwdclientmgr_t *cm;
+
+ REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
+ INSIST(client->byaddr == NULL);
+
+ cm = client->clientmgr;
+ req = NULL;
+
+ result = lwres_gnbarequest_parse(cm->lwctx,
+ b, &client->pkt, &req);
+ if (result != LWRES_R_SUCCESS)
+ goto out;
+
+ client->options = 0;
+ if (req->addr.family == LWRES_ADDRTYPE_V4) {
+ client->na.family = AF_INET;
+ if (req->addr.length != 4)
+ goto out;
+ memcpy(&client->na.type.in, req->addr.address, 4);
+ } else if (req->addr.family == LWRES_ADDRTYPE_V6) {
+ client->na.family = AF_INET6;
+ if (req->addr.length != 16)
+ goto out;
+ memcpy(&client->na.type.in6, req->addr.address, 16);
+ } else {
+ goto out;
+ }
+ isc_sockaddr_fromnetaddr(&sa, &client->na, 53);
+
+ ns_lwdclient_log(50, "client %p looking for addrtype %08x",
+ client, req->addr.family);
+
+ /*
+ * We no longer need to keep this around.
+ */
+ lwres_gnbarequest_free(cm->lwctx, &req);
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ init_gnba(client);
+ client->options = 0;
+
+ /*
+ * Start the find.
+ */
+ start_byaddr(client);
+
+ return;
+
+ /*
+ * We're screwed. Return an error packet to our caller.
+ */
+ out:
+ if (req != NULL)
+ lwres_gnbarequest_free(cm->lwctx, &req);
+
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
diff --git a/bin/named/lwdgrbn.c b/bin/named/lwdgrbn.c
new file mode 100644
index 0000000..b54e83d
--- /dev/null
+++ b/bin/named/lwdgrbn.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwdgrbn.c,v 1.20 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/socket.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/lookup.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+#include <named/types.h>
+#include <named/lwdclient.h>
+#include <named/lwresd.h>
+#include <named/lwsearch.h>
+
+static void start_lookup(ns_lwdclient_t *);
+
+static isc_result_t
+fill_array(int *pos, dns_rdataset_t *rdataset,
+ int size, unsigned char **rdatas, lwres_uint16_t *rdatalen)
+{
+ dns_rdata_t rdata;
+ isc_result_t result;
+ isc_region_t r;
+
+ UNUSED(size);
+
+ dns_rdata_init(&rdata);
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ INSIST(*pos < size);
+ dns_rdataset_current(rdataset, &rdata);
+ dns_rdata_toregion(&rdata, &r);
+ rdatas[*pos] = r.base;
+ rdatalen[*pos] = r.length;
+ dns_rdata_reset(&rdata);
+ (*pos)++;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+static isc_result_t
+iterate_node(lwres_grbnresponse_t *grbn, dns_db_t *db, dns_dbnode_t *node,
+ isc_mem_t *mctx)
+{
+ int used = 0, count;
+ int size = 8, oldsize = 0;
+ unsigned char **rdatas = NULL, **oldrdatas = NULL, **newrdatas = NULL;
+ lwres_uint16_t *lens = NULL, *oldlens = NULL, *newlens = NULL;
+ dns_rdatasetiter_t *iter = NULL;
+ dns_rdataset_t set;
+ dns_ttl_t ttl = ISC_INT32_MAX;
+ lwres_uint32_t flags = LWRDATA_VALIDATED;
+ isc_result_t result = ISC_R_NOMEMORY;
+
+ result = dns_db_allrdatasets(db, node, NULL, 0, &iter);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+
+ rdatas = isc_mem_get(mctx, size * sizeof(*rdatas));
+ if (rdatas == NULL)
+ goto out;
+ lens = isc_mem_get(mctx, size * sizeof(*lens));
+ if (lens == NULL)
+ goto out;
+
+ for (result = dns_rdatasetiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iter))
+ {
+ result = ISC_R_NOMEMORY;
+ dns_rdataset_init(&set);
+ dns_rdatasetiter_current(iter, &set);
+
+ if (set.type != dns_rdatatype_rrsig) {
+ dns_rdataset_disassociate(&set);
+ continue;
+ }
+
+ count = dns_rdataset_count(&set);
+ if (used + count > size) {
+ /* copy & reallocate */
+ oldsize = size;
+ oldrdatas = rdatas;
+ oldlens = lens;
+ rdatas = NULL;
+ lens = NULL;
+
+ size *= 2;
+
+ rdatas = isc_mem_get(mctx, size * sizeof(*rdatas));
+ if (rdatas == NULL)
+ goto out;
+ lens = isc_mem_get(mctx, size * sizeof(*lens));
+ if (lens == NULL)
+ goto out;
+ memcpy(rdatas, oldrdatas, used * sizeof(*rdatas));
+ memcpy(lens, oldlens, used * sizeof(*lens));
+ isc_mem_put(mctx, oldrdatas,
+ oldsize * sizeof(*oldrdatas));
+ isc_mem_put(mctx, oldlens, oldsize * sizeof(*oldlens));
+ oldrdatas = NULL;
+ oldlens = NULL;
+ }
+ if (set.ttl < ttl)
+ ttl = set.ttl;
+ if (set.trust != dns_trust_secure)
+ flags &= (~LWRDATA_VALIDATED);
+ result = fill_array(&used, &set, size, rdatas, lens);
+ dns_rdataset_disassociate(&set);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ dns_rdatasetiter_destroy(&iter);
+
+ /*
+ * If necessary, shrink and copy the arrays.
+ */
+ if (size != used) {
+ result = ISC_R_NOMEMORY;
+ newrdatas = isc_mem_get(mctx, used * sizeof(*rdatas));
+ if (newrdatas == NULL)
+ goto out;
+ newlens = isc_mem_get(mctx, used * sizeof(*lens));
+ if (newlens == NULL)
+ goto out;
+ memcpy(newrdatas, rdatas, used * sizeof(*rdatas));
+ memcpy(newlens, lens, used * sizeof(*lens));
+ isc_mem_put(mctx, rdatas, size * sizeof(*rdatas));
+ isc_mem_put(mctx, lens, size * sizeof(*lens));
+ grbn->rdatas = newrdatas;
+ grbn->rdatalen = newlens;
+ } else {
+ grbn->rdatas = rdatas;
+ grbn->rdatalen = lens;
+ }
+ grbn->nrdatas = used;
+ grbn->ttl = ttl;
+ grbn->flags = flags;
+ return (ISC_R_SUCCESS);
+
+ out:
+ dns_rdatasetiter_destroy(&iter);
+ if (rdatas != NULL)
+ isc_mem_put(mctx, rdatas, size * sizeof(*rdatas));
+ if (lens != NULL)
+ isc_mem_put(mctx, lens, size * sizeof(*lens));
+ if (oldrdatas != NULL)
+ isc_mem_put(mctx, oldrdatas, oldsize * sizeof(*oldrdatas));
+ if (oldlens != NULL)
+ isc_mem_put(mctx, oldlens, oldsize * sizeof(*oldlens));
+ if (newrdatas != NULL)
+ isc_mem_put(mctx, newrdatas, used * sizeof(*oldrdatas));
+ return (result);
+}
+
+static void
+lookup_done(isc_task_t *task, isc_event_t *event) {
+ ns_lwdclient_t *client;
+ ns_lwdclientmgr_t *cm;
+ dns_lookupevent_t *levent;
+ lwres_buffer_t lwb;
+ dns_name_t *name;
+ dns_rdataset_t *rdataset;
+ dns_rdataset_t *sigrdataset;
+ isc_result_t result;
+ lwres_result_t lwresult;
+ isc_region_t r;
+ isc_buffer_t b;
+ lwres_grbnresponse_t *grbn;
+ int i;
+
+ UNUSED(task);
+
+ lwb.base = NULL;
+ client = event->ev_arg;
+ cm = client->clientmgr;
+ INSIST(client->lookup == (dns_lookup_t *)event->ev_sender);
+
+ levent = (dns_lookupevent_t *)event;
+ grbn = &client->grbn;
+
+ ns_lwdclient_log(50, "lookup event result = %s",
+ isc_result_totext(levent->result));
+
+ result = levent->result;
+ if (result != ISC_R_SUCCESS) {
+ dns_lookup_destroy(&client->lookup);
+ isc_event_free(&event);
+ levent = NULL;
+
+ switch (result) {
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NCACHENXDOMAIN:
+ result = ns_lwsearchctx_next(&client->searchctx);
+ if (result != ISC_R_SUCCESS)
+ lwresult = LWRES_R_NOTFOUND;
+ else {
+ start_lookup(client);
+ return;
+ }
+ break;
+ case DNS_R_NXRRSET:
+ case DNS_R_NCACHENXRRSET:
+ lwresult = LWRES_R_TYPENOTFOUND;
+ break;
+ default:
+ lwresult = LWRES_R_FAILURE;
+ }
+ ns_lwdclient_errorpktsend(client, lwresult);
+ return;
+ }
+
+ name = levent->name;
+ b = client->recv_buffer;
+
+ grbn->flags = 0;
+
+ grbn->nrdatas = 0;
+ grbn->rdatas = NULL;
+ grbn->rdatalen = NULL;
+
+ grbn->nsigs = 0;
+ grbn->sigs = NULL;
+ grbn->siglen = NULL;
+
+ result = dns_name_totext(name, ISC_TRUE, &client->recv_buffer);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ grbn->realname = (char *)isc_buffer_used(&b);
+ grbn->realnamelen = isc_buffer_usedlength(&client->recv_buffer) -
+ isc_buffer_usedlength(&b);
+ ns_lwdclient_log(50, "found name '%.*s'", grbn->realnamelen,
+ grbn->realname);
+
+ grbn->rdclass = cm->view->rdclass;
+ grbn->rdtype = client->rdtype;
+
+ rdataset = levent->rdataset;
+ if (rdataset != NULL) {
+ /* The normal case */
+ grbn->nrdatas = dns_rdataset_count(rdataset);
+ grbn->rdatas = isc_mem_get(cm->mctx, grbn->nrdatas *
+ sizeof(unsigned char *));
+ if (grbn->rdatas == NULL)
+ goto out;
+ grbn->rdatalen = isc_mem_get(cm->mctx, grbn->nrdatas *
+ sizeof(lwres_uint16_t));
+ if (grbn->rdatalen == NULL)
+ goto out;
+
+ i = 0;
+ result = fill_array(&i, rdataset, grbn->nrdatas, grbn->rdatas,
+ grbn->rdatalen);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ INSIST(i == grbn->nrdatas);
+ grbn->ttl = rdataset->ttl;
+ if (rdataset->trust == dns_trust_secure)
+ grbn->flags |= LWRDATA_VALIDATED;
+ } else {
+ /* The SIG query case */
+ result = iterate_node(grbn, levent->db, levent->node,
+ cm->mctx);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ }
+ ns_lwdclient_log(50, "filled in %d rdata%s", grbn->nrdatas,
+ (grbn->nrdatas == 1) ? "" : "s");
+
+ sigrdataset = levent->sigrdataset;
+ if (sigrdataset != NULL) {
+ grbn->nsigs = dns_rdataset_count(sigrdataset);
+ grbn->sigs = isc_mem_get(cm->mctx, grbn->nsigs *
+ sizeof(unsigned char *));
+ if (grbn->sigs == NULL)
+ goto out;
+ grbn->siglen = isc_mem_get(cm->mctx, grbn->nsigs *
+ sizeof(lwres_uint16_t));
+ if (grbn->siglen == NULL)
+ goto out;
+
+ i = 0;
+ result = fill_array(&i, sigrdataset, grbn->nsigs, grbn->sigs,
+ grbn->siglen);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ INSIST(i == grbn->nsigs);
+ ns_lwdclient_log(50, "filled in %d signature%s", grbn->nsigs,
+ (grbn->nsigs == 1) ? "" : "s");
+ }
+
+ dns_lookup_destroy(&client->lookup);
+ isc_event_free(&event);
+
+ /*
+ * Render the packet.
+ */
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+ client->pkt.result = LWRES_R_SUCCESS;
+
+ lwresult = lwres_grbnresponse_render(cm->lwctx,
+ grbn, &client->pkt, &lwb);
+ if (lwresult != LWRES_R_SUCCESS)
+ goto out;
+
+ isc_mem_put(cm->mctx, grbn->rdatas,
+ grbn->nrdatas * sizeof(unsigned char *));
+ isc_mem_put(cm->mctx, grbn->rdatalen,
+ grbn->nrdatas * sizeof(lwres_uint16_t));
+
+ if (grbn->sigs != NULL)
+ isc_mem_put(cm->mctx, grbn->sigs,
+ grbn->nsigs * sizeof(unsigned char *));
+ if (grbn->siglen != NULL)
+ isc_mem_put(cm->mctx, grbn->siglen,
+ grbn->nsigs * sizeof(lwres_uint16_t));
+
+ r.base = lwb.base;
+ r.length = lwb.used;
+ client->sendbuf = r.base;
+ client->sendlength = r.length;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS)
+ goto out2;
+
+ NS_LWDCLIENT_SETSEND(client);
+
+ return;
+
+ out:
+ if (grbn->rdatas != NULL)
+ isc_mem_put(cm->mctx, grbn->rdatas,
+ grbn->nrdatas * sizeof(unsigned char *));
+ if (grbn->rdatalen != NULL)
+ isc_mem_put(cm->mctx, grbn->rdatalen,
+ grbn->nrdatas * sizeof(lwres_uint16_t));
+
+ if (grbn->sigs != NULL)
+ isc_mem_put(cm->mctx, grbn->sigs,
+ grbn->nsigs * sizeof(unsigned char *));
+ if (grbn->siglen != NULL)
+ isc_mem_put(cm->mctx, grbn->siglen,
+ grbn->nsigs * sizeof(lwres_uint16_t));
+ out2:
+ if (client->lookup != NULL)
+ dns_lookup_destroy(&client->lookup);
+ if (lwb.base != NULL)
+ lwres_context_freemem(cm->lwctx, lwb.base, lwb.length);
+
+ if (event != NULL)
+ isc_event_free(&event);
+
+ ns_lwdclient_log(50, "error constructing getrrsetbyname response");
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
+
+static void
+start_lookup(ns_lwdclient_t *client) {
+ isc_result_t result;
+ ns_lwdclientmgr_t *cm;
+ dns_fixedname_t absname;
+
+ cm = client->clientmgr;
+
+ INSIST(client->lookup == NULL);
+
+ dns_fixedname_init(&absname);
+ result = ns_lwsearchctx_current(&client->searchctx,
+ dns_fixedname_name(&absname));
+ /*
+ * This will return failure if relative name + suffix is too long.
+ * In this case, just go on to the next entry in the search path.
+ */
+ if (result != ISC_R_SUCCESS)
+ start_lookup(client);
+
+ result = dns_lookup_create(cm->mctx,
+ dns_fixedname_name(&absname),
+ client->rdtype, cm->view,
+ client->options, cm->task, lookup_done,
+ client, &client->lookup);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+ return;
+ }
+}
+
+static void
+init_grbn(ns_lwdclient_t *client) {
+ client->grbn.rdclass = 0;
+ client->grbn.rdtype = 0;
+ client->grbn.ttl = 0;
+ client->grbn.nrdatas = 0;
+ client->grbn.realname = NULL;
+ client->grbn.realnamelen = 0;
+ client->grbn.rdatas = 0;
+ client->grbn.rdatalen = 0;
+ client->grbn.base = NULL;
+ client->grbn.baselen = 0;
+ isc_buffer_init(&client->recv_buffer, client->buffer, LWRES_RECVLENGTH);
+}
+
+void
+ns_lwdclient_processgrbn(ns_lwdclient_t *client, lwres_buffer_t *b) {
+ lwres_grbnrequest_t *req;
+ isc_result_t result;
+ ns_lwdclientmgr_t *cm;
+ isc_buffer_t namebuf;
+
+ REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
+ INSIST(client->byaddr == NULL);
+
+ cm = client->clientmgr;
+ req = NULL;
+
+ result = lwres_grbnrequest_parse(cm->lwctx,
+ b, &client->pkt, &req);
+ if (result != LWRES_R_SUCCESS)
+ goto out;
+ if (req->name == NULL)
+ goto out;
+
+ client->options = 0;
+ if (req->rdclass != cm->view->rdclass)
+ goto out;
+
+ if (req->rdclass == dns_rdataclass_any ||
+ req->rdtype == dns_rdatatype_any)
+ goto out;
+
+ client->rdtype = req->rdtype;
+
+ isc_buffer_init(&namebuf, req->name, req->namelen);
+ isc_buffer_add(&namebuf, req->namelen);
+
+ dns_fixedname_init(&client->query_name);
+ result = dns_name_fromtext(dns_fixedname_name(&client->query_name),
+ &namebuf, NULL, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ ns_lwsearchctx_init(&client->searchctx,
+ cm->listener->manager->search,
+ dns_fixedname_name(&client->query_name),
+ cm->listener->manager->ndots);
+ ns_lwsearchctx_first(&client->searchctx);
+
+ ns_lwdclient_log(50, "client %p looking for type %d",
+ client, client->rdtype);
+
+ /*
+ * We no longer need to keep this around.
+ */
+ lwres_grbnrequest_free(cm->lwctx, &req);
+
+ /*
+ * Initialize the real name and alias arrays in the reply we're
+ * going to build up.
+ */
+ init_grbn(client);
+
+ /*
+ * Start the find.
+ */
+ start_lookup(client);
+
+ return;
+
+ /*
+ * We're screwed. Return an error packet to our caller.
+ */
+ out:
+ if (req != NULL)
+ lwres_grbnrequest_free(cm->lwctx, &req);
+
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
diff --git a/bin/named/lwdnoop.c b/bin/named/lwdnoop.c
new file mode 100644
index 0000000..14d8e0c
--- /dev/null
+++ b/bin/named/lwdnoop.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwdnoop.c,v 1.13 2008/01/22 23:28:04 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/socket.h>
+#include <isc/util.h>
+
+#include <named/types.h>
+#include <named/lwdclient.h>
+
+void
+ns_lwdclient_processnoop(ns_lwdclient_t *client, lwres_buffer_t *b) {
+ lwres_nooprequest_t *req;
+ lwres_noopresponse_t resp;
+ isc_result_t result;
+ lwres_result_t lwres;
+ isc_region_t r;
+ lwres_buffer_t lwb;
+
+ REQUIRE(NS_LWDCLIENT_ISRECVDONE(client));
+ INSIST(client->byaddr == NULL);
+
+ req = NULL;
+
+ result = lwres_nooprequest_parse(client->clientmgr->lwctx,
+ b, &client->pkt, &req);
+ if (result != LWRES_R_SUCCESS)
+ goto send_error;
+
+ client->pkt.recvlength = LWRES_RECVLENGTH;
+ client->pkt.authtype = 0; /* XXXMLG */
+ client->pkt.authlength = 0;
+ client->pkt.result = LWRES_R_SUCCESS;
+
+ resp.datalength = req->datalength;
+ resp.data = req->data;
+
+ lwres = lwres_noopresponse_render(client->clientmgr->lwctx, &resp,
+ &client->pkt, &lwb);
+ if (lwres != LWRES_R_SUCCESS)
+ goto cleanup_req;
+
+ r.base = lwb.base;
+ r.length = lwb.used;
+ client->sendbuf = r.base;
+ client->sendlength = r.length;
+ result = ns_lwdclient_sendreply(client, &r);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lwb;
+
+ /*
+ * We can now destroy request.
+ */
+ lwres_nooprequest_free(client->clientmgr->lwctx, &req);
+
+ NS_LWDCLIENT_SETSEND(client);
+
+ return;
+
+ cleanup_lwb:
+ lwres_context_freemem(client->clientmgr->lwctx, lwb.base, lwb.length);
+
+ cleanup_req:
+ lwres_nooprequest_free(client->clientmgr->lwctx, &req);
+
+ send_error:
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+}
diff --git a/bin/named/lwresd.8 b/bin/named/lwresd.8
new file mode 100644
index 0000000..2c4e3bc
--- /dev/null
+++ b/bin/named/lwresd.8
@@ -0,0 +1,223 @@
+.\" Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (C) 2000, 2001 Internet Software Consortium.
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+.\" PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $Id: lwresd.8,v 1.29 2008/11/07 01:11:19 tbox Exp $
+.\"
+.hy 0
+.ad l
+.\" Title: lwresd
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.71.1 <http://docbook.sf.net/>
+.\" Date: June 30, 2000
+.\" Manual: BIND9
+.\" Source: BIND9
+.\"
+.TH "LWRESD" "8" "June 30, 2000" "BIND9" "BIND9"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+lwresd \- lightweight resolver daemon
+.SH "SYNOPSIS"
+.HP 7
+\fBlwresd\fR [\fB\-c\ \fR\fB\fIconfig\-file\fR\fR] [\fB\-C\ \fR\fB\fIconfig\-file\fR\fR] [\fB\-d\ \fR\fB\fIdebug\-level\fR\fR] [\fB\-f\fR] [\fB\-g\fR] [\fB\-i\ \fR\fB\fIpid\-file\fR\fR] [\fB\-m\ \fR\fB\fIflag\fR\fR] [\fB\-n\ \fR\fB\fI#cpus\fR\fR] [\fB\-P\ \fR\fB\fIport\fR\fR] [\fB\-p\ \fR\fB\fIport\fR\fR] [\fB\-s\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-4\fR] [\fB\-6\fR]
+.SH "DESCRIPTION"
+.PP
+\fBlwresd\fR
+is the daemon providing name lookup services to clients that use the BIND 9 lightweight resolver library. It is essentially a stripped\-down, caching\-only name server that answers queries using the BIND 9 lightweight resolver protocol rather than the DNS protocol.
+.PP
+\fBlwresd\fR
+listens for resolver queries on a UDP port on the IPv4 loopback interface, 127.0.0.1. This means that
+\fBlwresd\fR
+can only be used by processes running on the local machine. By default UDP port number 921 is used for lightweight resolver requests and responses.
+.PP
+Incoming lightweight resolver requests are decoded by the server which then resolves them using the DNS protocol. When the DNS lookup completes,
+\fBlwresd\fR
+encodes the answers in the lightweight resolver format and returns them to the client that made the request.
+.PP
+If
+\fI/etc/resolv.conf\fR
+contains any
+\fBnameserver\fR
+entries,
+\fBlwresd\fR
+sends recursive DNS queries to those servers. This is similar to the use of forwarders in a caching name server. If no
+\fBnameserver\fR
+entries are present, or if forwarding fails,
+\fBlwresd\fR
+resolves the queries autonomously starting at the root name servers, using a built\-in list of root server hints.
+.SH "OPTIONS"
+.PP
+\-4
+.RS 4
+Use IPv4 only even if the host machine is capable of IPv6.
+\fB\-4\fR
+and
+\fB\-6\fR
+are mutually exclusive.
+.RE
+.PP
+\-6
+.RS 4
+Use IPv6 only even if the host machine is capable of IPv4.
+\fB\-4\fR
+and
+\fB\-6\fR
+are mutually exclusive.
+.RE
+.PP
+\-c \fIconfig\-file\fR
+.RS 4
+Use
+\fIconfig\-file\fR
+as the configuration file instead of the default,
+\fI/etc/lwresd.conf\fR.
+\fB\-c\fR
+can not be used with
+\fB\-C\fR.
+.RE
+.PP
+\-C \fIconfig\-file\fR
+.RS 4
+Use
+\fIconfig\-file\fR
+as the configuration file instead of the default,
+\fI/etc/resolv.conf\fR.
+\fB\-C\fR
+can not be used with
+\fB\-c\fR.
+.RE
+.PP
+\-d \fIdebug\-level\fR
+.RS 4
+Set the daemon's debug level to
+\fIdebug\-level\fR. Debugging traces from
+\fBlwresd\fR
+become more verbose as the debug level increases.
+.RE
+.PP
+\-f
+.RS 4
+Run the server in the foreground (i.e. do not daemonize).
+.RE
+.PP
+\-g
+.RS 4
+Run the server in the foreground and force all logging to
+\fIstderr\fR.
+.RE
+.PP
+\-i \fIpid\-file\fR
+.RS 4
+Use
+\fIpid\-file\fR
+as the PID file instead of the default,
+\fI/var/run/lwresd/lwresd.pid\fR.
+.RE
+.PP
+\-m \fIflag\fR
+.RS 4
+Turn on memory usage debugging flags. Possible flags are
+\fIusage\fR,
+\fItrace\fR,
+\fIrecord\fR,
+\fIsize\fR, and
+\fImctx\fR. These correspond to the ISC_MEM_DEBUGXXXX flags described in
+\fI<isc/mem.h>\fR.
+.RE
+.PP
+\-n \fI#cpus\fR
+.RS 4
+Create
+\fI#cpus\fR
+worker threads to take advantage of multiple CPUs. If not specified,
+\fBlwresd\fR
+will try to determine the number of CPUs present and create one thread per CPU. If it is unable to determine the number of CPUs, a single worker thread will be created.
+.RE
+.PP
+\-P \fIport\fR
+.RS 4
+Listen for lightweight resolver queries on port
+\fIport\fR. If not specified, the default is port 921.
+.RE
+.PP
+\-p \fIport\fR
+.RS 4
+Send DNS lookups to port
+\fIport\fR. If not specified, the default is port 53. This provides a way of testing the lightweight resolver daemon with a name server that listens for queries on a non\-standard port number.
+.RE
+.PP
+\-s
+.RS 4
+Write memory usage statistics to
+\fIstdout\fR
+on exit.
+.RS
+.B "Note:"
+This option is mainly of interest to BIND 9 developers and may be removed or changed in a future release.
+.RE
+.RE
+.PP
+\-t \fIdirectory\fR
+.RS 4
+Chroot to
+\fIdirectory\fR
+after processing the command line arguments, but before reading the configuration file.
+.RS
+.B "Warning:"
+This option should be used in conjunction with the
+\fB\-u\fR
+option, as chrooting a process running as root doesn't enhance security on most systems; the way
+\fBchroot(2)\fR
+is defined allows a process with root privileges to escape a chroot jail.
+.RE
+.RE
+.PP
+\-u \fIuser\fR
+.RS 4
+Setuid to
+\fIuser\fR
+after completing privileged operations, such as creating sockets that listen on privileged ports.
+.RE
+.PP
+\-v
+.RS 4
+Report the version number and exit.
+.RE
+.SH "FILES"
+.PP
+\fI/etc/resolv.conf\fR
+.RS 4
+The default configuration file.
+.RE
+.PP
+\fI/var/run/lwresd.pid\fR
+.RS 4
+The default process\-id file.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBnamed\fR(8),
+\fBlwres\fR(3),
+\fBresolver\fR(5).
+.SH "AUTHOR"
+.PP
+Internet Systems Consortium
+.SH "COPYRIGHT"
+Copyright \(co 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+.br
+Copyright \(co 2000, 2001 Internet Software Consortium.
+.br
diff --git a/bin/named/lwresd.c b/bin/named/lwresd.c
new file mode 100644
index 0000000..4e245fd
--- /dev/null
+++ b/bin/named/lwresd.c
@@ -0,0 +1,870 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwresd.c,v 1.58 2008/07/23 23:27:54 marka Exp $ */
+
+/*! \file
+ * \brief
+ * Main program for the Lightweight Resolver Daemon.
+ *
+ * To paraphrase the old saying about X11, "It's not a lightweight deamon
+ * for resolvers, it's a deamon for lightweight resolvers".
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/list.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/socket.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <isccfg/namedconf.h>
+
+#include <dns/log.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+#include <named/config.h>
+#include <named/globals.h>
+#include <named/log.h>
+#include <named/lwaddr.h>
+#include <named/lwresd.h>
+#include <named/lwdclient.h>
+#include <named/lwsearch.h>
+#include <named/server.h>
+
+#define LWRESD_MAGIC ISC_MAGIC('L', 'W', 'R', 'D')
+#define VALID_LWRESD(l) ISC_MAGIC_VALID(l, LWRESD_MAGIC)
+
+#define LWRESLISTENER_MAGIC ISC_MAGIC('L', 'W', 'R', 'L')
+#define VALID_LWRESLISTENER(l) ISC_MAGIC_VALID(l, LWRESLISTENER_MAGIC)
+
+/*!
+ * The total number of clients we can handle will be NTASKS * NRECVS.
+ */
+#define NTASKS 2 /*%< tasks to create to handle lwres queries */
+#define NRECVS 2 /*%< max clients per task */
+
+typedef ISC_LIST(ns_lwreslistener_t) ns_lwreslistenerlist_t;
+
+static ns_lwreslistenerlist_t listeners;
+static isc_mutex_t listeners_lock;
+static isc_once_t once = ISC_ONCE_INIT;
+
+
+static void
+initialize_mutex(void) {
+ RUNTIME_CHECK(isc_mutex_init(&listeners_lock) == ISC_R_SUCCESS);
+}
+
+
+/*%
+ * Wrappers around our memory management stuff, for the lwres functions.
+ */
+void *
+ns__lwresd_memalloc(void *arg, size_t size) {
+ return (isc_mem_get(arg, size));
+}
+
+void
+ns__lwresd_memfree(void *arg, void *mem, size_t size) {
+ isc_mem_put(arg, mem, size);
+}
+
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+static isc_result_t
+buffer_putstr(isc_buffer_t *b, const char *s) {
+ unsigned int len = strlen(s);
+ if (isc_buffer_availablelength(b) <= len)
+ return (ISC_R_NOSPACE);
+ isc_buffer_putmem(b, (const unsigned char *)s, len);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Convert a resolv.conf file into a config structure.
+ */
+isc_result_t
+ns_lwresd_parseeresolvconf(isc_mem_t *mctx, cfg_parser_t *pctx,
+ cfg_obj_t **configp)
+{
+ char text[4096];
+ char str[16];
+ isc_buffer_t b;
+ lwres_context_t *lwctx = NULL;
+ lwres_conf_t *lwc = NULL;
+ isc_sockaddr_t sa;
+ isc_netaddr_t na;
+ int i;
+ isc_result_t result;
+ lwres_result_t lwresult;
+
+ lwctx = NULL;
+ lwresult = lwres_context_create(&lwctx, mctx, ns__lwresd_memalloc,
+ ns__lwresd_memfree,
+ LWRES_CONTEXT_SERVERMODE);
+ if (lwresult != LWRES_R_SUCCESS) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ lwresult = lwres_conf_parse(lwctx, lwresd_g_resolvconffile);
+ if (lwresult != LWRES_R_SUCCESS) {
+ result = DNS_R_SYNTAX;
+ goto cleanup;
+ }
+
+ lwc = lwres_conf_get(lwctx);
+ INSIST(lwc != NULL);
+
+ isc_buffer_init(&b, text, sizeof(text));
+
+ CHECK(buffer_putstr(&b, "options {\n"));
+
+ /*
+ * Build the list of forwarders.
+ */
+ if (lwc->nsnext > 0) {
+ CHECK(buffer_putstr(&b, "\tforwarders {\n"));
+
+ for (i = 0; i < lwc->nsnext; i++) {
+ CHECK(lwaddr_sockaddr_fromlwresaddr(
+ &sa,
+ &lwc->nameservers[i],
+ ns_g_port));
+ isc_netaddr_fromsockaddr(&na, &sa);
+ CHECK(buffer_putstr(&b, "\t\t"));
+ CHECK(isc_netaddr_totext(&na, &b));
+ CHECK(buffer_putstr(&b, ";\n"));
+ }
+ CHECK(buffer_putstr(&b, "\t};\n"));
+ }
+
+ /*
+ * Build the sortlist
+ */
+ if (lwc->sortlistnxt > 0) {
+ CHECK(buffer_putstr(&b, "\tsortlist {\n"));
+ CHECK(buffer_putstr(&b, "\t\t{\n"));
+ CHECK(buffer_putstr(&b, "\t\t\tany;\n"));
+ CHECK(buffer_putstr(&b, "\t\t\t{\n"));
+ for (i = 0; i < lwc->sortlistnxt; i++) {
+ lwres_addr_t *lwaddr = &lwc->sortlist[i].addr;
+ lwres_addr_t *lwmask = &lwc->sortlist[i].mask;
+ unsigned int mask;
+
+ CHECK(lwaddr_sockaddr_fromlwresaddr(&sa, lwmask, 0));
+ isc_netaddr_fromsockaddr(&na, &sa);
+ result = isc_netaddr_masktoprefixlen(&na, &mask);
+ if (result != ISC_R_SUCCESS) {
+ char addrtext[ISC_NETADDR_FORMATSIZE];
+ isc_netaddr_format(&na, addrtext,
+ sizeof(addrtext));
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD,
+ ISC_LOG_ERROR,
+ "processing sortlist: '%s' is "
+ "not a valid netmask",
+ addrtext);
+ goto cleanup;
+ }
+
+ CHECK(lwaddr_sockaddr_fromlwresaddr(&sa, lwaddr, 0));
+ isc_netaddr_fromsockaddr(&na, &sa);
+
+ CHECK(buffer_putstr(&b, "\t\t\t\t"));
+ CHECK(isc_netaddr_totext(&na, &b));
+ snprintf(str, sizeof(str), "%u", mask);
+ CHECK(buffer_putstr(&b, "/"));
+ CHECK(buffer_putstr(&b, str));
+ CHECK(buffer_putstr(&b, ";\n"));
+ }
+ CHECK(buffer_putstr(&b, "\t\t\t};\n"));
+ CHECK(buffer_putstr(&b, "\t\t};\n"));
+ CHECK(buffer_putstr(&b, "\t};\n"));
+ }
+
+ CHECK(buffer_putstr(&b, "};\n\n"));
+
+ CHECK(buffer_putstr(&b, "lwres {\n"));
+
+ /*
+ * Build the search path
+ */
+ if (lwc->searchnxt > 0) {
+ if (lwc->searchnxt > 0) {
+ CHECK(buffer_putstr(&b, "\tsearch {\n"));
+ for (i = 0; i < lwc->searchnxt; i++) {
+ CHECK(buffer_putstr(&b, "\t\t\""));
+ CHECK(buffer_putstr(&b, lwc->search[i]));
+ CHECK(buffer_putstr(&b, "\";\n"));
+ }
+ CHECK(buffer_putstr(&b, "\t};\n"));
+ }
+ }
+
+ /*
+ * Build the ndots line
+ */
+ if (lwc->ndots != 1) {
+ CHECK(buffer_putstr(&b, "\tndots "));
+ snprintf(str, sizeof(str), "%u", lwc->ndots);
+ CHECK(buffer_putstr(&b, str));
+ CHECK(buffer_putstr(&b, ";\n"));
+ }
+
+ /*
+ * Build the listen-on line
+ */
+ if (lwc->lwnext > 0) {
+ CHECK(buffer_putstr(&b, "\tlisten-on {\n"));
+
+ for (i = 0; i < lwc->lwnext; i++) {
+ CHECK(lwaddr_sockaddr_fromlwresaddr(&sa,
+ &lwc->lwservers[i],
+ 0));
+ isc_netaddr_fromsockaddr(&na, &sa);
+ CHECK(buffer_putstr(&b, "\t\t"));
+ CHECK(isc_netaddr_totext(&na, &b));
+ CHECK(buffer_putstr(&b, ";\n"));
+ }
+ CHECK(buffer_putstr(&b, "\t};\n"));
+ }
+
+ CHECK(buffer_putstr(&b, "};\n"));
+
+#if 0
+ printf("%.*s\n",
+ (int)isc_buffer_usedlength(&b),
+ (char *)isc_buffer_base(&b));
+#endif
+
+ lwres_conf_clear(lwctx);
+ lwres_context_destroy(&lwctx);
+
+ return (cfg_parse_buffer(pctx, &b, &cfg_type_namedconf, configp));
+
+ cleanup:
+
+ if (lwctx != NULL) {
+ lwres_conf_clear(lwctx);
+ lwres_context_destroy(&lwctx);
+ }
+
+ return (result);
+}
+
+
+/*
+ * Handle lwresd manager objects
+ */
+isc_result_t
+ns_lwdmanager_create(isc_mem_t *mctx, const cfg_obj_t *lwres,
+ ns_lwresd_t **lwresdp)
+{
+ ns_lwresd_t *lwresd;
+ const char *vname;
+ dns_rdataclass_t vclass;
+ const cfg_obj_t *obj, *viewobj, *searchobj;
+ const cfg_listelt_t *element;
+ isc_result_t result;
+
+ INSIST(lwresdp != NULL && *lwresdp == NULL);
+
+ lwresd = isc_mem_get(mctx, sizeof(ns_lwresd_t));
+ if (lwresd == NULL)
+ return (ISC_R_NOMEMORY);
+
+ lwresd->mctx = NULL;
+ isc_mem_attach(mctx, &lwresd->mctx);
+ lwresd->view = NULL;
+ lwresd->search = NULL;
+ lwresd->refs = 1;
+
+ obj = NULL;
+ (void)cfg_map_get(lwres, "ndots", &obj);
+ if (obj != NULL)
+ lwresd->ndots = cfg_obj_asuint32(obj);
+ else
+ lwresd->ndots = 1;
+
+ RUNTIME_CHECK(isc_mutex_init(&lwresd->lock) == ISC_R_SUCCESS);
+
+ lwresd->shutting_down = ISC_FALSE;
+
+ viewobj = NULL;
+ (void)cfg_map_get(lwres, "view", &viewobj);
+ if (viewobj != NULL) {
+ vname = cfg_obj_asstring(cfg_tuple_get(viewobj, "name"));
+ obj = cfg_tuple_get(viewobj, "class");
+ result = ns_config_getclass(obj, dns_rdataclass_in, &vclass);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ } else {
+ vname = "_default";
+ vclass = dns_rdataclass_in;
+ }
+
+ result = dns_viewlist_find(&ns_g_server->viewlist, vname, vclass,
+ &lwresd->view);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "couldn't find view %s", vname);
+ goto fail;
+ }
+
+ searchobj = NULL;
+ (void)cfg_map_get(lwres, "search", &searchobj);
+ if (searchobj != NULL) {
+ lwresd->search = NULL;
+ result = ns_lwsearchlist_create(lwresd->mctx,
+ &lwresd->search);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "couldn't create searchlist");
+ goto fail;
+ }
+ for (element = cfg_list_first(searchobj);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *search;
+ const char *searchstr;
+ isc_buffer_t namebuf;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+
+ search = cfg_listelt_value(element);
+ searchstr = cfg_obj_asstring(search);
+
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ isc_buffer_init(&namebuf, searchstr,
+ strlen(searchstr));
+ isc_buffer_add(&namebuf, strlen(searchstr));
+ result = dns_name_fromtext(name, &namebuf,
+ dns_rootname, ISC_FALSE,
+ NULL);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD,
+ ISC_LOG_WARNING,
+ "invalid name %s in searchlist",
+ searchstr);
+ continue;
+ }
+
+ result = ns_lwsearchlist_append(lwresd->search, name);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD,
+ ISC_LOG_WARNING,
+ "couldn't update searchlist");
+ goto fail;
+ }
+ }
+ }
+
+ lwresd->magic = LWRESD_MAGIC;
+
+ *lwresdp = lwresd;
+ return (ISC_R_SUCCESS);
+
+ fail:
+ if (lwresd->view != NULL)
+ dns_view_detach(&lwresd->view);
+ if (lwresd->search != NULL)
+ ns_lwsearchlist_detach(&lwresd->search);
+ if (lwresd->mctx != NULL)
+ isc_mem_detach(&lwresd->mctx);
+ isc_mem_put(mctx, lwresd, sizeof(ns_lwresd_t));
+ return (result);
+}
+
+void
+ns_lwdmanager_attach(ns_lwresd_t *source, ns_lwresd_t **targetp) {
+ INSIST(VALID_LWRESD(source));
+ INSIST(targetp != NULL && *targetp == NULL);
+
+ LOCK(&source->lock);
+ source->refs++;
+ UNLOCK(&source->lock);
+
+ *targetp = source;
+}
+
+void
+ns_lwdmanager_detach(ns_lwresd_t **lwresdp) {
+ ns_lwresd_t *lwresd;
+ isc_mem_t *mctx;
+ isc_boolean_t done = ISC_FALSE;
+
+ INSIST(lwresdp != NULL && *lwresdp != NULL);
+ INSIST(VALID_LWRESD(*lwresdp));
+
+ lwresd = *lwresdp;
+ *lwresdp = NULL;
+
+ LOCK(&lwresd->lock);
+ INSIST(lwresd->refs > 0);
+ lwresd->refs--;
+ if (lwresd->refs == 0)
+ done = ISC_TRUE;
+ UNLOCK(&lwresd->lock);
+
+ if (!done)
+ return;
+
+ dns_view_detach(&lwresd->view);
+ if (lwresd->search != NULL)
+ ns_lwsearchlist_detach(&lwresd->search);
+ mctx = lwresd->mctx;
+ lwresd->magic = 0;
+ isc_mem_put(mctx, lwresd, sizeof(*lwresd));
+ isc_mem_detach(&mctx);
+}
+
+
+/*
+ * Handle listener objects
+ */
+void
+ns_lwreslistener_attach(ns_lwreslistener_t *source,
+ ns_lwreslistener_t **targetp)
+{
+ INSIST(VALID_LWRESLISTENER(source));
+ INSIST(targetp != NULL && *targetp == NULL);
+
+ LOCK(&source->lock);
+ source->refs++;
+ UNLOCK(&source->lock);
+
+ *targetp = source;
+}
+
+void
+ns_lwreslistener_detach(ns_lwreslistener_t **listenerp) {
+ ns_lwreslistener_t *listener;
+ isc_mem_t *mctx;
+ isc_boolean_t done = ISC_FALSE;
+
+ INSIST(listenerp != NULL && *listenerp != NULL);
+ INSIST(VALID_LWRESLISTENER(*listenerp));
+
+ listener = *listenerp;
+
+ LOCK(&listener->lock);
+ INSIST(listener->refs > 0);
+ listener->refs--;
+ if (listener->refs == 0)
+ done = ISC_TRUE;
+ UNLOCK(&listener->lock);
+
+ if (!done)
+ return;
+
+ if (listener->manager != NULL)
+ ns_lwdmanager_detach(&listener->manager);
+
+ if (listener->sock != NULL)
+ isc_socket_detach(&listener->sock);
+
+ listener->magic = 0;
+ mctx = listener->mctx;
+ isc_mem_put(mctx, listener, sizeof(*listener));
+ isc_mem_detach(&mctx);
+ listenerp = NULL;
+}
+
+static isc_result_t
+listener_create(isc_mem_t *mctx, ns_lwresd_t *lwresd,
+ ns_lwreslistener_t **listenerp)
+{
+ ns_lwreslistener_t *listener;
+ isc_result_t result;
+
+ REQUIRE(listenerp != NULL && *listenerp == NULL);
+
+ listener = isc_mem_get(mctx, sizeof(ns_lwreslistener_t));
+ if (listener == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&listener->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, listener, sizeof(ns_lwreslistener_t));
+ return (result);
+ }
+
+ listener->magic = LWRESLISTENER_MAGIC;
+ listener->refs = 1;
+
+ listener->sock = NULL;
+
+ listener->manager = NULL;
+ ns_lwdmanager_attach(lwresd, &listener->manager);
+
+ listener->mctx = NULL;
+ isc_mem_attach(mctx, &listener->mctx);
+
+ ISC_LINK_INIT(listener, link);
+ ISC_LIST_INIT(listener->cmgrs);
+
+ *listenerp = listener;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+listener_bind(ns_lwreslistener_t *listener, isc_sockaddr_t *address) {
+ isc_socket_t *sock = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ int pf;
+
+ pf = isc_sockaddr_pf(address);
+ if ((pf == AF_INET && isc_net_probeipv4() != ISC_R_SUCCESS) ||
+ (pf == AF_INET6 && isc_net_probeipv6() != ISC_R_SUCCESS))
+ return (ISC_R_FAMILYNOSUPPORT);
+
+ listener->address = *address;
+
+ if (isc_sockaddr_getport(&listener->address) == 0) {
+ in_port_t port;
+ port = lwresd_g_listenport;
+ if (port == 0)
+ port = LWRES_UDP_PORT;
+ isc_sockaddr_setport(&listener->address, port);
+ }
+
+ sock = NULL;
+ result = isc_socket_create(ns_g_socketmgr, pf,
+ isc_sockettype_udp, &sock);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "failed to create lwres socket: %s",
+ isc_result_totext(result));
+ return (result);
+ }
+
+ result = isc_socket_bind(sock, &listener->address,
+ ISC_SOCKET_REUSEADDRESS);
+ if (result != ISC_R_SUCCESS) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&listener->address, socktext,
+ sizeof(socktext));
+ isc_socket_detach(&sock);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "failed to add lwres socket: %s: %s",
+ socktext, isc_result_totext(result));
+ return (result);
+ }
+ listener->sock = sock;
+ return (ISC_R_SUCCESS);
+}
+
+static void
+listener_copysock(ns_lwreslistener_t *oldlistener,
+ ns_lwreslistener_t *newlistener)
+{
+ newlistener->address = oldlistener->address;
+ isc_socket_attach(oldlistener->sock, &newlistener->sock);
+}
+
+static isc_result_t
+listener_startclients(ns_lwreslistener_t *listener) {
+ ns_lwdclientmgr_t *cm;
+ unsigned int i;
+ isc_result_t result;
+
+ /*
+ * Create the client managers.
+ */
+ result = ISC_R_SUCCESS;
+ for (i = 0; i < NTASKS && result == ISC_R_SUCCESS; i++)
+ result = ns_lwdclientmgr_create(listener, NRECVS,
+ ns_g_taskmgr);
+
+ /*
+ * Ensure that we have created at least one.
+ */
+ if (ISC_LIST_EMPTY(listener->cmgrs))
+ return (result);
+
+ /*
+ * Walk the list of clients and start each one up.
+ */
+ LOCK(&listener->lock);
+ cm = ISC_LIST_HEAD(listener->cmgrs);
+ while (cm != NULL) {
+ result = ns_lwdclient_startrecv(cm);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_ERROR,
+ "could not start lwres "
+ "client handler: %s",
+ isc_result_totext(result));
+ cm = ISC_LIST_NEXT(cm, link);
+ }
+ UNLOCK(&listener->lock);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+listener_shutdown(ns_lwreslistener_t *listener) {
+ ns_lwdclientmgr_t *cm;
+
+ cm = ISC_LIST_HEAD(listener->cmgrs);
+ while (cm != NULL) {
+ isc_task_shutdown(cm->task);
+ cm = ISC_LIST_NEXT(cm, link);
+ }
+}
+
+static isc_result_t
+find_listener(isc_sockaddr_t *address, ns_lwreslistener_t **listenerp) {
+ ns_lwreslistener_t *listener;
+
+ INSIST(listenerp != NULL && *listenerp == NULL);
+
+ for (listener = ISC_LIST_HEAD(listeners);
+ listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ {
+ if (!isc_sockaddr_equal(address, &listener->address))
+ continue;
+ *listenerp = listener;
+ return (ISC_R_SUCCESS);
+ }
+ return (ISC_R_NOTFOUND);
+}
+
+void
+ns_lwreslistener_unlinkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm)
+{
+ REQUIRE(VALID_LWRESLISTENER(listener));
+
+ LOCK(&listener->lock);
+ ISC_LIST_UNLINK(listener->cmgrs, cm, link);
+ UNLOCK(&listener->lock);
+}
+
+void
+ns_lwreslistener_linkcm(ns_lwreslistener_t *listener, ns_lwdclientmgr_t *cm) {
+ REQUIRE(VALID_LWRESLISTENER(listener));
+
+ /*
+ * This does no locking, since it's called early enough that locking
+ * isn't needed.
+ */
+ ISC_LIST_APPEND(listener->cmgrs, cm, link);
+}
+
+static isc_result_t
+configure_listener(isc_sockaddr_t *address, ns_lwresd_t *lwresd,
+ isc_mem_t *mctx, ns_lwreslistenerlist_t *newlisteners)
+{
+ ns_lwreslistener_t *listener, *oldlistener = NULL;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_result_t result;
+
+ (void)find_listener(address, &oldlistener);
+ listener = NULL;
+ result = listener_create(mctx, lwresd, &listener);
+ if (result != ISC_R_SUCCESS) {
+ isc_sockaddr_format(address, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "lwres failed to configure %s: %s",
+ socktext, isc_result_totext(result));
+ return (result);
+ }
+
+ /*
+ * If there's already a listener, don't rebind the socket.
+ */
+ if (oldlistener == NULL) {
+ result = listener_bind(listener, address);
+ if (result != ISC_R_SUCCESS) {
+ ns_lwreslistener_detach(&listener);
+ return (ISC_R_SUCCESS);
+ }
+ } else
+ listener_copysock(oldlistener, listener);
+
+ result = listener_startclients(listener);
+ if (result != ISC_R_SUCCESS) {
+ isc_sockaddr_format(address, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_WARNING,
+ "lwres: failed to start %s: %s", socktext,
+ isc_result_totext(result));
+ ns_lwreslistener_detach(&listener);
+ return (ISC_R_SUCCESS);
+ }
+
+ if (oldlistener != NULL) {
+ /*
+ * Remove the old listener from the old list and shut it down.
+ */
+ ISC_LIST_UNLINK(listeners, oldlistener, link);
+ listener_shutdown(oldlistener);
+ ns_lwreslistener_detach(&oldlistener);
+ } else {
+ isc_sockaddr_format(address, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_NOTICE,
+ "lwres listening on %s", socktext);
+ }
+
+ ISC_LIST_APPEND(*newlisteners, listener, link);
+ return (result);
+}
+
+isc_result_t
+ns_lwresd_configure(isc_mem_t *mctx, const cfg_obj_t *config) {
+ const cfg_obj_t *lwreslist = NULL;
+ const cfg_obj_t *lwres = NULL;
+ const cfg_obj_t *listenerslist = NULL;
+ const cfg_listelt_t *element = NULL;
+ ns_lwreslistener_t *listener;
+ ns_lwreslistenerlist_t newlisteners;
+ isc_result_t result;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_t *addrs = NULL;
+ ns_lwresd_t *lwresd = NULL;
+ isc_uint32_t count = 0;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(config != NULL);
+
+ RUNTIME_CHECK(isc_once_do(&once, initialize_mutex) == ISC_R_SUCCESS);
+
+ ISC_LIST_INIT(newlisteners);
+
+ result = cfg_map_get(config, "lwres", &lwreslist);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ LOCK(&listeners_lock);
+ /*
+ * Run through the new lwres address list, noting sockets that
+ * are already being listened on and moving them to the new list.
+ *
+ * Identifying duplicates addr/port combinations is left to either
+ * the underlying config code, or to the bind attempt getting an
+ * address-in-use error.
+ */
+ for (element = cfg_list_first(lwreslist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ in_port_t port;
+
+ lwres = cfg_listelt_value(element);
+ CHECK(ns_lwdmanager_create(mctx, lwres, &lwresd));
+
+ port = lwresd_g_listenport;
+ if (port == 0)
+ port = LWRES_UDP_PORT;
+
+ listenerslist = NULL;
+ (void)cfg_map_get(lwres, "listen-on", &listenerslist);
+ if (listenerslist == NULL) {
+ struct in_addr localhost;
+ isc_sockaddr_t address;
+
+ localhost.s_addr = htonl(INADDR_LOOPBACK);
+ isc_sockaddr_fromin(&address, &localhost, port);
+ CHECK(configure_listener(&address, lwresd, mctx,
+ &newlisteners));
+ } else {
+ isc_uint32_t i;
+
+ CHECK(ns_config_getiplist(config, listenerslist,
+ port, mctx, &addrs, &count));
+ for (i = 0; i < count; i++)
+ CHECK(configure_listener(&addrs[i], lwresd,
+ mctx, &newlisteners));
+ ns_config_putiplist(mctx, &addrs, count);
+ }
+ ns_lwdmanager_detach(&lwresd);
+ }
+
+ /*
+ * Shutdown everything on the listeners list, and remove them from
+ * the list. Then put all of the new listeners on it.
+ */
+
+ while (!ISC_LIST_EMPTY(listeners)) {
+ listener = ISC_LIST_HEAD(listeners);
+ ISC_LIST_UNLINK(listeners, listener, link);
+
+ isc_sockaddr_format(&listener->address,
+ socktext, sizeof(socktext));
+
+ listener_shutdown(listener);
+ ns_lwreslistener_detach(&listener);
+
+ isc_log_write(ns_g_lctx, ISC_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_NOTICE,
+ "lwres no longer listening on %s", socktext);
+ }
+
+ cleanup:
+ ISC_LIST_APPENDLIST(listeners, newlisteners, link);
+
+ if (addrs != NULL)
+ ns_config_putiplist(mctx, &addrs, count);
+
+ if (lwresd != NULL)
+ ns_lwdmanager_detach(&lwresd);
+
+ UNLOCK(&listeners_lock);
+
+ return (result);
+}
+
+void
+ns_lwresd_shutdown(void) {
+ ns_lwreslistener_t *listener;
+
+ RUNTIME_CHECK(isc_once_do(&once, initialize_mutex) == ISC_R_SUCCESS);
+
+ while (!ISC_LIST_EMPTY(listeners)) {
+ listener = ISC_LIST_HEAD(listeners);
+ ISC_LIST_UNLINK(listeners, listener, link);
+ ns_lwreslistener_detach(&listener);
+ }
+}
diff --git a/bin/named/lwresd.docbook b/bin/named/lwresd.docbook
new file mode 100644
index 0000000..3e72fd3
--- /dev/null
+++ b/bin/named/lwresd.docbook
@@ -0,0 +1,373 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2000, 2001 Internet Software Consortium.
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id: lwresd.docbook,v 1.18 2008/11/06 05:30:24 marka Exp $ -->
+<refentry>
+ <refentryinfo>
+ <date>June 30, 2000</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle><application>lwresd</application></refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo>BIND9</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname><application>lwresd</application></refname>
+ <refpurpose>lightweight resolver daemon</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2007</year>
+ <year>2008</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ <copyright>
+ <year>2000</year>
+ <year>2001</year>
+ <holder>Internet Software Consortium.</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>lwresd</command>
+ <arg><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
+ <arg><option>-C <replaceable class="parameter">config-file</replaceable></option></arg>
+ <arg><option>-d <replaceable class="parameter">debug-level</replaceable></option></arg>
+ <arg><option>-f</option></arg>
+ <arg><option>-g</option></arg>
+ <arg><option>-i <replaceable class="parameter">pid-file</replaceable></option></arg>
+ <arg><option>-m <replaceable class="parameter">flag</replaceable></option></arg>
+ <arg><option>-n <replaceable class="parameter">#cpus</replaceable></option></arg>
+ <arg><option>-P <replaceable class="parameter">port</replaceable></option></arg>
+ <arg><option>-p <replaceable class="parameter">port</replaceable></option></arg>
+ <arg><option>-s</option></arg>
+ <arg><option>-t <replaceable class="parameter">directory</replaceable></option></arg>
+ <arg><option>-u <replaceable class="parameter">user</replaceable></option></arg>
+ <arg><option>-v</option></arg>
+ <arg><option>-4</option></arg>
+ <arg><option>-6</option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+
+ <para><command>lwresd</command>
+ is the daemon providing name lookup
+ services to clients that use the BIND 9 lightweight resolver
+ library. It is essentially a stripped-down, caching-only name
+ server that answers queries using the BIND 9 lightweight
+ resolver protocol rather than the DNS protocol.
+ </para>
+
+ <para><command>lwresd</command>
+ listens for resolver queries on a
+ UDP port on the IPv4 loopback interface, 127.0.0.1. This
+ means that <command>lwresd</command> can only be used by
+ processes running on the local machine. By default UDP port
+ number 921 is used for lightweight resolver requests and
+ responses.
+ </para>
+ <para>
+ Incoming lightweight resolver requests are decoded by the
+ server which then resolves them using the DNS protocol. When
+ the DNS lookup completes, <command>lwresd</command> encodes
+ the answers in the lightweight resolver format and returns
+ them to the client that made the request.
+ </para>
+ <para>
+ If <filename>/etc/resolv.conf</filename> contains any
+ <option>nameserver</option> entries, <command>lwresd</command>
+ sends recursive DNS queries to those servers. This is similar
+ to the use of forwarders in a caching name server. If no
+ <option>nameserver</option> entries are present, or if
+ forwarding fails, <command>lwresd</command> resolves the
+ queries autonomously starting at the root name servers, using
+ a built-in list of root server hints.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>-4</term>
+ <listitem>
+ <para>
+ Use IPv4 only even if the host machine is capable of IPv6.
+ <option>-4</option> and <option>-6</option> are mutually
+ exclusive.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-6</term>
+ <listitem>
+ <para>
+ Use IPv6 only even if the host machine is capable of IPv4.
+ <option>-4</option> and <option>-6</option> are mutually
+ exclusive.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <!-- this is in source but not mentioned? does this matter? -->
+ <varlistentry>
+ <term>-c <replaceable class="parameter">config-file</replaceable></term>
+ <listitem>
+ <para>
+ Use <replaceable class="parameter">config-file</replaceable> as the
+ configuration file instead of the default,
+ <filename>/etc/lwresd.conf</filename>.
+ <!-- Should this be an absolute path name? -->
+ <option>-c</option> can not be used with <option>-C</option>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-C <replaceable class="parameter">config-file</replaceable></term>
+ <listitem>
+ <para>
+ Use <replaceable class="parameter">config-file</replaceable> as the
+ configuration file instead of the default,
+ <filename>/etc/resolv.conf</filename>.
+ <option>-C</option> can not be used with <option>-c</option>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-d <replaceable class="parameter">debug-level</replaceable></term>
+ <listitem>
+ <para>
+ Set the daemon's debug level to <replaceable class="parameter">debug-level</replaceable>.
+ Debugging traces from <command>lwresd</command> become
+ more verbose as the debug level increases.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-f</term>
+ <listitem>
+ <para>
+ Run the server in the foreground (i.e. do not daemonize).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-g</term>
+ <listitem>
+ <para>
+ Run the server in the foreground and force all logging
+ to <filename>stderr</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-i <replaceable class="parameter">pid-file</replaceable></term>
+ <listitem>
+ <para>
+ Use <replaceable class="parameter">pid-file</replaceable> as the
+ PID file instead of the default,
+ <filename>/var/run/lwresd/lwresd.pid</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-m <replaceable class="parameter">flag</replaceable></term>
+ <listitem>
+ <para>
+ Turn on memory usage debugging flags. Possible flags are
+ <replaceable class="parameter">usage</replaceable>,
+ <replaceable class="parameter">trace</replaceable>,
+ <replaceable class="parameter">record</replaceable>,
+ <replaceable class="parameter">size</replaceable>, and
+ <replaceable class="parameter">mctx</replaceable>.
+ These correspond to the ISC_MEM_DEBUGXXXX flags described in
+ <filename>&lt;isc/mem.h&gt;</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-n <replaceable class="parameter">#cpus</replaceable></term>
+ <listitem>
+ <para>
+ Create <replaceable class="parameter">#cpus</replaceable> worker threads
+ to take advantage of multiple CPUs. If not specified,
+ <command>lwresd</command> will try to determine the
+ number of CPUs present and create one thread per CPU.
+ If it is unable to determine the number of CPUs, a
+ single worker thread will be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-P <replaceable class="parameter">port</replaceable></term>
+ <listitem>
+ <para>
+ Listen for lightweight resolver queries on port
+ <replaceable class="parameter">port</replaceable>. If
+ not specified, the default is port 921.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-p <replaceable class="parameter">port</replaceable></term>
+ <listitem>
+ <para>
+ Send DNS lookups to port <replaceable class="parameter">port</replaceable>. If not
+ specified, the default is port 53. This provides a
+ way of testing the lightweight resolver daemon with a
+ name server that listens for queries on a non-standard
+ port number.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s</term>
+ <listitem>
+ <para>
+ Write memory usage statistics to <filename>stdout</filename>
+ on exit.
+ </para>
+ <note>
+ <para>
+ This option is mainly of interest to BIND 9 developers
+ and may be removed or changed in a future release.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-t <replaceable class="parameter">directory</replaceable></term>
+ <listitem>
+ <para>Chroot
+ to <replaceable class="parameter">directory</replaceable> after
+ processing the command line arguments, but before
+ reading the configuration file.
+ </para>
+ <warning>
+ <para>
+ This option should be used in conjunction with the
+ <option>-u</option> option, as chrooting a process
+ running as root doesn't enhance security on most
+ systems; the way <function>chroot(2)</function> is
+ defined allows a process with root privileges to
+ escape a chroot jail.
+ </para>
+ </warning>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-u <replaceable class="parameter">user</replaceable></term>
+ <listitem>
+ <para>Setuid
+ to <replaceable class="parameter">user</replaceable> after completing
+ privileged operations, such as creating sockets that
+ listen on privileged ports.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-v</term>
+ <listitem>
+ <para>
+ Report the version number and exit.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>FILES</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><filename>/etc/resolv.conf</filename></term>
+ <listitem>
+ <para>
+ The default configuration file.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>/var/run/lwresd.pid</filename></term>
+ <listitem>
+ <para>
+ The default process-id file.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para><citerefentry>
+ <refentrytitle>named</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>lwres</refentrytitle><manvolnum>3</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>resolver</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>AUTHOR</title>
+ <para><corpauthor>Internet Systems Consortium</corpauthor>
+ </para>
+ </refsect1>
+
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/bin/named/lwresd.html b/bin/named/lwresd.html
new file mode 100644
index 0000000..65f65e3
--- /dev/null
+++ b/bin/named/lwresd.html
@@ -0,0 +1,225 @@
+<!--
+ - Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2000, 2001 Internet Software Consortium.
+ -
+ - Permission to use, copy, modify, and distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+<!-- $Id: lwresd.html,v 1.25 2008/11/07 01:11:19 tbox Exp $ -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>lwresd</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.71.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry" lang="en">
+<a name="id2476275"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><span class="application">lwresd</span> &#8212; lightweight resolver daemon</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">lwresd</code> [<code class="option">-c <em class="replaceable"><code>config-file</code></em></code>] [<code class="option">-C <em class="replaceable"><code>config-file</code></em></code>] [<code class="option">-d <em class="replaceable"><code>debug-level</code></em></code>] [<code class="option">-f</code>] [<code class="option">-g</code>] [<code class="option">-i <em class="replaceable"><code>pid-file</code></em></code>] [<code class="option">-m <em class="replaceable"><code>flag</code></em></code>] [<code class="option">-n <em class="replaceable"><code>#cpus</code></em></code>] [<code class="option">-P <em class="replaceable"><code>port</code></em></code>] [<code class="option">-p <em class="replaceable"><code>port</code></em></code>] [<code class="option">-s</code>] [<code class="option">-t <em class="replaceable"><code>directory</code></em></code>] [<code class="option">-u <em class="replaceable"><code>user</code></em></code>] [<code class="option">-v</code>] [<code class="option">-4</code>] [<code class="option">-6</code>]</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543464"></a><h2>DESCRIPTION</h2>
+<p><span><strong class="command">lwresd</strong></span>
+ is the daemon providing name lookup
+ services to clients that use the BIND 9 lightweight resolver
+ library. It is essentially a stripped-down, caching-only name
+ server that answers queries using the BIND 9 lightweight
+ resolver protocol rather than the DNS protocol.
+ </p>
+<p><span><strong class="command">lwresd</strong></span>
+ listens for resolver queries on a
+ UDP port on the IPv4 loopback interface, 127.0.0.1. This
+ means that <span><strong class="command">lwresd</strong></span> can only be used by
+ processes running on the local machine. By default UDP port
+ number 921 is used for lightweight resolver requests and
+ responses.
+ </p>
+<p>
+ Incoming lightweight resolver requests are decoded by the
+ server which then resolves them using the DNS protocol. When
+ the DNS lookup completes, <span><strong class="command">lwresd</strong></span> encodes
+ the answers in the lightweight resolver format and returns
+ them to the client that made the request.
+ </p>
+<p>
+ If <code class="filename">/etc/resolv.conf</code> contains any
+ <code class="option">nameserver</code> entries, <span><strong class="command">lwresd</strong></span>
+ sends recursive DNS queries to those servers. This is similar
+ to the use of forwarders in a caching name server. If no
+ <code class="option">nameserver</code> entries are present, or if
+ forwarding fails, <span><strong class="command">lwresd</strong></span> resolves the
+ queries autonomously starting at the root name servers, using
+ a built-in list of root server hints.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543511"></a><h2>OPTIONS</h2>
+<div class="variablelist"><dl>
+<dt><span class="term">-4</span></dt>
+<dd><p>
+ Use IPv4 only even if the host machine is capable of IPv6.
+ <code class="option">-4</code> and <code class="option">-6</code> are mutually
+ exclusive.
+ </p></dd>
+<dt><span class="term">-6</span></dt>
+<dd><p>
+ Use IPv6 only even if the host machine is capable of IPv4.
+ <code class="option">-4</code> and <code class="option">-6</code> are mutually
+ exclusive.
+ </p></dd>
+<dt><span class="term">-c <em class="replaceable"><code>config-file</code></em></span></dt>
+<dd><p>
+ Use <em class="replaceable"><code>config-file</code></em> as the
+ configuration file instead of the default,
+ <code class="filename">/etc/lwresd.conf</code>.
+
+ <code class="option">-c</code> can not be used with <code class="option">-C</code>.
+ </p></dd>
+<dt><span class="term">-C <em class="replaceable"><code>config-file</code></em></span></dt>
+<dd><p>
+ Use <em class="replaceable"><code>config-file</code></em> as the
+ configuration file instead of the default,
+ <code class="filename">/etc/resolv.conf</code>.
+ <code class="option">-C</code> can not be used with <code class="option">-c</code>.
+ </p></dd>
+<dt><span class="term">-d <em class="replaceable"><code>debug-level</code></em></span></dt>
+<dd><p>
+ Set the daemon's debug level to <em class="replaceable"><code>debug-level</code></em>.
+ Debugging traces from <span><strong class="command">lwresd</strong></span> become
+ more verbose as the debug level increases.
+ </p></dd>
+<dt><span class="term">-f</span></dt>
+<dd><p>
+ Run the server in the foreground (i.e. do not daemonize).
+ </p></dd>
+<dt><span class="term">-g</span></dt>
+<dd><p>
+ Run the server in the foreground and force all logging
+ to <code class="filename">stderr</code>.
+ </p></dd>
+<dt><span class="term">-i <em class="replaceable"><code>pid-file</code></em></span></dt>
+<dd><p>
+ Use <em class="replaceable"><code>pid-file</code></em> as the
+ PID file instead of the default,
+ <code class="filename">/var/run/lwresd/lwresd.pid</code>.
+ </p></dd>
+<dt><span class="term">-m <em class="replaceable"><code>flag</code></em></span></dt>
+<dd><p>
+ Turn on memory usage debugging flags. Possible flags are
+ <em class="replaceable"><code>usage</code></em>,
+ <em class="replaceable"><code>trace</code></em>,
+ <em class="replaceable"><code>record</code></em>,
+ <em class="replaceable"><code>size</code></em>, and
+ <em class="replaceable"><code>mctx</code></em>.
+ These correspond to the ISC_MEM_DEBUGXXXX flags described in
+ <code class="filename">&lt;isc/mem.h&gt;</code>.
+ </p></dd>
+<dt><span class="term">-n <em class="replaceable"><code>#cpus</code></em></span></dt>
+<dd><p>
+ Create <em class="replaceable"><code>#cpus</code></em> worker threads
+ to take advantage of multiple CPUs. If not specified,
+ <span><strong class="command">lwresd</strong></span> will try to determine the
+ number of CPUs present and create one thread per CPU.
+ If it is unable to determine the number of CPUs, a
+ single worker thread will be created.
+ </p></dd>
+<dt><span class="term">-P <em class="replaceable"><code>port</code></em></span></dt>
+<dd><p>
+ Listen for lightweight resolver queries on port
+ <em class="replaceable"><code>port</code></em>. If
+ not specified, the default is port 921.
+ </p></dd>
+<dt><span class="term">-p <em class="replaceable"><code>port</code></em></span></dt>
+<dd><p>
+ Send DNS lookups to port <em class="replaceable"><code>port</code></em>. If not
+ specified, the default is port 53. This provides a
+ way of testing the lightweight resolver daemon with a
+ name server that listens for queries on a non-standard
+ port number.
+ </p></dd>
+<dt><span class="term">-s</span></dt>
+<dd>
+<p>
+ Write memory usage statistics to <code class="filename">stdout</code>
+ on exit.
+ </p>
+<div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Note</h3>
+<p>
+ This option is mainly of interest to BIND 9 developers
+ and may be removed or changed in a future release.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-t <em class="replaceable"><code>directory</code></em></span></dt>
+<dd>
+<p>Chroot
+ to <em class="replaceable"><code>directory</code></em> after
+ processing the command line arguments, but before
+ reading the configuration file.
+ </p>
+<div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Warning</h3>
+<p>
+ This option should be used in conjunction with the
+ <code class="option">-u</code> option, as chrooting a process
+ running as root doesn't enhance security on most
+ systems; the way <code class="function">chroot(2)</code> is
+ defined allows a process with root privileges to
+ escape a chroot jail.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-u <em class="replaceable"><code>user</code></em></span></dt>
+<dd><p>Setuid
+ to <em class="replaceable"><code>user</code></em> after completing
+ privileged operations, such as creating sockets that
+ listen on privileged ports.
+ </p></dd>
+<dt><span class="term">-v</span></dt>
+<dd><p>
+ Report the version number and exit.
+ </p></dd>
+</dl></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543928"></a><h2>FILES</h2>
+<div class="variablelist"><dl>
+<dt><span class="term"><code class="filename">/etc/resolv.conf</code></span></dt>
+<dd><p>
+ The default configuration file.
+ </p></dd>
+<dt><span class="term"><code class="filename">/var/run/lwresd.pid</code></span></dt>
+<dd><p>
+ The default process-id file.
+ </p></dd>
+</dl></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543968"></a><h2>SEE ALSO</h2>
+<p><span class="citerefentry"><span class="refentrytitle">named</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">lwres</span>(3)</span>,
+ <span class="citerefentry"><span class="refentrytitle">resolver</span>(5)</span>.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2544002"></a><h2>AUTHOR</h2>
+<p><span class="corpauthor">Internet Systems Consortium</span>
+ </p>
+</div>
+</div></body>
+</html>
diff --git a/bin/named/lwsearch.c b/bin/named/lwsearch.c
new file mode 100644
index 0000000..6754c98
--- /dev/null
+++ b/bin/named/lwsearch.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: lwsearch.c,v 1.13 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/result.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/name.h>
+#include <dns/types.h>
+
+#include <named/lwsearch.h>
+#include <named/types.h>
+
+#define LWSEARCHLIST_MAGIC ISC_MAGIC('L', 'W', 'S', 'L')
+#define VALID_LWSEARCHLIST(l) ISC_MAGIC_VALID(l, LWSEARCHLIST_MAGIC)
+
+isc_result_t
+ns_lwsearchlist_create(isc_mem_t *mctx, ns_lwsearchlist_t **listp) {
+ ns_lwsearchlist_t *list;
+ isc_result_t result;
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(listp != NULL && *listp == NULL);
+
+ list = isc_mem_get(mctx, sizeof(ns_lwsearchlist_t));
+ if (list == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&list->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(mctx, list, sizeof(ns_lwsearchlist_t));
+ return (result);
+ }
+ list->mctx = NULL;
+ isc_mem_attach(mctx, &list->mctx);
+ list->refs = 1;
+ ISC_LIST_INIT(list->names);
+ list->magic = LWSEARCHLIST_MAGIC;
+
+ *listp = list;
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_lwsearchlist_attach(ns_lwsearchlist_t *source, ns_lwsearchlist_t **target) {
+ REQUIRE(VALID_LWSEARCHLIST(source));
+ REQUIRE(target != NULL && *target == NULL);
+
+ LOCK(&source->lock);
+ INSIST(source->refs > 0);
+ source->refs++;
+ INSIST(source->refs != 0);
+ UNLOCK(&source->lock);
+
+ *target = source;
+}
+
+void
+ns_lwsearchlist_detach(ns_lwsearchlist_t **listp) {
+ ns_lwsearchlist_t *list;
+ isc_mem_t *mctx;
+
+ REQUIRE(listp != NULL);
+ list = *listp;
+ REQUIRE(VALID_LWSEARCHLIST(list));
+
+ LOCK(&list->lock);
+ INSIST(list->refs > 0);
+ list->refs--;
+ UNLOCK(&list->lock);
+
+ *listp = NULL;
+ if (list->refs != 0)
+ return;
+
+ mctx = list->mctx;
+ while (!ISC_LIST_EMPTY(list->names)) {
+ dns_name_t *name = ISC_LIST_HEAD(list->names);
+ ISC_LIST_UNLINK(list->names, name, link);
+ dns_name_free(name, list->mctx);
+ isc_mem_put(list->mctx, name, sizeof(dns_name_t));
+ }
+ list->magic = 0;
+ isc_mem_put(mctx, list, sizeof(ns_lwsearchlist_t));
+ isc_mem_detach(&mctx);
+}
+
+isc_result_t
+ns_lwsearchlist_append(ns_lwsearchlist_t *list, dns_name_t *name) {
+ dns_name_t *newname;
+ isc_result_t result;
+
+ REQUIRE(VALID_LWSEARCHLIST(list));
+ REQUIRE(name != NULL);
+
+ newname = isc_mem_get(list->mctx, sizeof(dns_name_t));
+ if (newname == NULL)
+ return (ISC_R_NOMEMORY);
+ dns_name_init(newname, NULL);
+ result = dns_name_dup(name, list->mctx, newname);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(list->mctx, newname, sizeof(dns_name_t));
+ return (result);
+ }
+ ISC_LINK_INIT(newname, link);
+ ISC_LIST_APPEND(list->names, newname, link);
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_lwsearchctx_init(ns_lwsearchctx_t *sctx, ns_lwsearchlist_t *list,
+ dns_name_t *name, unsigned int ndots)
+{
+ INSIST(sctx != NULL);
+ sctx->relname = name;
+ sctx->searchname = NULL;
+ sctx->doneexact = ISC_FALSE;
+ sctx->exactfirst = ISC_FALSE;
+ sctx->ndots = ndots;
+ if (dns_name_isabsolute(name) || list == NULL) {
+ sctx->list = NULL;
+ return;
+ }
+ sctx->list = list;
+ sctx->searchname = ISC_LIST_HEAD(sctx->list->names);
+ if (dns_name_countlabels(name) > ndots)
+ sctx->exactfirst = ISC_TRUE;
+}
+
+void
+ns_lwsearchctx_first(ns_lwsearchctx_t *sctx) {
+ REQUIRE(sctx != NULL);
+ UNUSED(sctx);
+}
+
+isc_result_t
+ns_lwsearchctx_next(ns_lwsearchctx_t *sctx) {
+ REQUIRE(sctx != NULL);
+
+ if (sctx->list == NULL)
+ return (ISC_R_NOMORE);
+
+ if (sctx->searchname == NULL) {
+ INSIST (!sctx->exactfirst || sctx->doneexact);
+ if (sctx->exactfirst || sctx->doneexact)
+ return (ISC_R_NOMORE);
+ sctx->doneexact = ISC_TRUE;
+ } else {
+ if (sctx->exactfirst && !sctx->doneexact)
+ sctx->doneexact = ISC_TRUE;
+ else {
+ sctx->searchname = ISC_LIST_NEXT(sctx->searchname,
+ link);
+ if (sctx->searchname == NULL && sctx->doneexact)
+ return (ISC_R_NOMORE);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_lwsearchctx_current(ns_lwsearchctx_t *sctx, dns_name_t *absname) {
+ dns_name_t *tname;
+ isc_boolean_t useexact = ISC_FALSE;
+
+ REQUIRE(sctx != NULL);
+
+ if (sctx->list == NULL ||
+ sctx->searchname == NULL ||
+ (sctx->exactfirst && !sctx->doneexact))
+ useexact = ISC_TRUE;
+
+ if (useexact) {
+ if (dns_name_isabsolute(sctx->relname))
+ tname = NULL;
+ else
+ tname = dns_rootname;
+ } else
+ tname = sctx->searchname;
+
+ return (dns_name_concatenate(sctx->relname, tname, absname, NULL));
+}
diff --git a/bin/named/main.c b/bin/named/main.c
new file mode 100644
index 0000000..aa6575a
--- /dev/null
+++ b/bin/named/main.c
@@ -0,0 +1,991 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: main.c,v 1.166 2008/10/24 01:24:54 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/app.h>
+#include <isc/commandline.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/os.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/resource.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <isccc/result.h>
+
+#include <dns/dispatch.h>
+#include <dns/name.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+#include <dst/result.h>
+
+/*
+ * Defining NS_MAIN provides storage declarations (rather than extern)
+ * for variables in named/globals.h.
+ */
+#define NS_MAIN 1
+
+#include <named/builtin.h>
+#include <named/control.h>
+#include <named/globals.h> /* Explicit, though named/log.h includes it. */
+#include <named/interfacemgr.h>
+#include <named/log.h>
+#include <named/os.h>
+#include <named/server.h>
+#include <named/lwresd.h>
+#include <named/main.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#endif
+
+/*
+ * Include header files for database drivers here.
+ */
+/* #include "xxdb.h" */
+
+/*
+ * Include DLZ drivers if appropriate.
+ */
+#ifdef DLZ
+#include <dlz/dlz_drivers.h>
+#endif
+
+static isc_boolean_t want_stats = ISC_FALSE;
+static char program_name[ISC_DIR_NAMEMAX] = "named";
+static char absolute_conffile[ISC_DIR_PATHMAX];
+static char saved_command_line[512];
+static char version[512];
+static unsigned int maxsocks = 0;
+
+void
+ns_main_earlywarning(const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ if (ns_g_lctx != NULL) {
+ isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_WARNING,
+ format, args);
+ } else {
+ fprintf(stderr, "%s: ", program_name);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+ va_end(args);
+}
+
+void
+ns_main_earlyfatal(const char *format, ...) {
+ va_list args;
+
+ va_start(args, format);
+ if (ns_g_lctx != NULL) {
+ isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ format, args);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "exiting (due to early fatal error)");
+ } else {
+ fprintf(stderr, "%s: ", program_name);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+ va_end(args);
+
+ exit(1);
+}
+
+static void
+assertion_failed(const char *file, int line, isc_assertiontype_t type,
+ const char *cond)
+{
+ /*
+ * Handle assertion failures.
+ */
+
+ if (ns_g_lctx != NULL) {
+ /*
+ * Reset the assetion callback in case it is the log
+ * routines causing the assertion.
+ */
+ isc_assertion_setcallback(NULL);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "%s:%d: %s(%s) failed", file, line,
+ isc_assertion_typetotext(type), cond);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "exiting (due to assertion failure)");
+ } else {
+ fprintf(stderr, "%s:%d: %s(%s) failed\n",
+ file, line, isc_assertion_typetotext(type), cond);
+ fflush(stderr);
+ }
+
+ if (ns_g_coreok)
+ abort();
+ exit(1);
+}
+
+static void
+library_fatal_error(const char *file, int line, const char *format,
+ va_list args) ISC_FORMAT_PRINTF(3, 0);
+
+static void
+library_fatal_error(const char *file, int line, const char *format,
+ va_list args)
+{
+ /*
+ * Handle isc_error_fatal() calls from our libraries.
+ */
+
+ if (ns_g_lctx != NULL) {
+ /*
+ * Reset the error callback in case it is the log
+ * routines causing the assertion.
+ */
+ isc_error_setfatal(NULL);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "%s:%d: fatal error:", file, line);
+ isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ format, args);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "exiting (due to fatal error in library)");
+ } else {
+ fprintf(stderr, "%s:%d: fatal error: ", file, line);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+
+ if (ns_g_coreok)
+ abort();
+ exit(1);
+}
+
+static void
+library_unexpected_error(const char *file, int line, const char *format,
+ va_list args) ISC_FORMAT_PRINTF(3, 0);
+
+static void
+library_unexpected_error(const char *file, int line, const char *format,
+ va_list args)
+{
+ /*
+ * Handle isc_error_unexpected() calls from our libraries.
+ */
+
+ if (ns_g_lctx != NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_ERROR,
+ "%s:%d: unexpected error:", file, line);
+ isc_log_vwrite(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_ERROR,
+ format, args);
+ } else {
+ fprintf(stderr, "%s:%d: fatal error: ", file, line);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ }
+}
+
+static void
+lwresd_usage(void) {
+ fprintf(stderr,
+ "usage: lwresd [-4|-6] [-c conffile | -C resolvconffile] "
+ "[-d debuglevel]\n"
+ " [-f|-g] [-n number_of_cpus] [-p port] "
+ "[-P listen-port] [-s]\n"
+ " [-t chrootdir] [-u username] [-i pidfile]\n"
+ " [-m {usage|trace|record|size|mctx}]\n");
+}
+
+static void
+usage(void) {
+ if (ns_g_lwresdonly) {
+ lwresd_usage();
+ return;
+ }
+ fprintf(stderr,
+ "usage: named [-4|-6] [-c conffile] [-d debuglevel] "
+ "[-f|-g] [-n number_of_cpus]\n"
+ " [-p port] [-s] [-t chrootdir] [-u username]\n"
+ " [-m {usage|trace|record|size|mctx}]\n");
+}
+
+static void
+save_command_line(int argc, char *argv[]) {
+ int i;
+ char *src;
+ char *dst;
+ char *eob;
+ const char truncated[] = "...";
+ isc_boolean_t quoted = ISC_FALSE;
+
+ dst = saved_command_line;
+ eob = saved_command_line + sizeof(saved_command_line);
+
+ for (i = 1; i < argc && dst < eob; i++) {
+ *dst++ = ' ';
+
+ src = argv[i];
+ while (*src != '\0' && dst < eob) {
+ /*
+ * This won't perfectly produce a shell-independent
+ * pastable command line in all circumstances, but
+ * comes close, and for practical purposes will
+ * nearly always be fine.
+ */
+ if (quoted || isalnum(*src & 0xff) ||
+ *src == '-' || *src == '_' ||
+ *src == '.' || *src == '/') {
+ *dst++ = *src++;
+ quoted = ISC_FALSE;
+ } else {
+ *dst++ = '\\';
+ quoted = ISC_TRUE;
+ }
+ }
+ }
+
+ INSIST(sizeof(saved_command_line) >= sizeof(truncated));
+
+ if (dst == eob)
+ strcpy(eob - sizeof(truncated), truncated);
+ else
+ *dst = '\0';
+}
+
+static int
+parse_int(char *arg, const char *desc) {
+ char *endp;
+ int tmp;
+ long int ltmp;
+
+ ltmp = strtol(arg, &endp, 10);
+ tmp = (int) ltmp;
+ if (*endp != '\0')
+ ns_main_earlyfatal("%s '%s' must be numeric", desc, arg);
+ if (tmp < 0 || tmp != ltmp)
+ ns_main_earlyfatal("%s '%s' out of range", desc, arg);
+ return (tmp);
+}
+
+static struct flag_def {
+ const char *name;
+ unsigned int value;
+} mem_debug_flags[] = {
+ { "trace", ISC_MEM_DEBUGTRACE },
+ { "record", ISC_MEM_DEBUGRECORD },
+ { "usage", ISC_MEM_DEBUGUSAGE },
+ { "size", ISC_MEM_DEBUGSIZE },
+ { "mctx", ISC_MEM_DEBUGCTX },
+ { NULL, 0 }
+};
+
+static void
+set_flags(const char *arg, struct flag_def *defs, unsigned int *ret) {
+ for (;;) {
+ const struct flag_def *def;
+ const char *end = strchr(arg, ',');
+ int arglen;
+ if (end == NULL)
+ end = arg + strlen(arg);
+ arglen = end - arg;
+ for (def = defs; def->name != NULL; def++) {
+ if (arglen == (int)strlen(def->name) &&
+ memcmp(arg, def->name, arglen) == 0) {
+ *ret |= def->value;
+ goto found;
+ }
+ }
+ ns_main_earlyfatal("unrecognized flag '%.*s'", arglen, arg);
+ found:
+ if (*end == '\0')
+ break;
+ arg = end + 1;
+ }
+}
+
+static void
+parse_command_line(int argc, char *argv[]) {
+ int ch;
+ int port;
+ isc_boolean_t disable6 = ISC_FALSE;
+ isc_boolean_t disable4 = ISC_FALSE;
+
+ save_command_line(argc, argv);
+
+ isc_commandline_errprint = ISC_FALSE;
+ while ((ch = isc_commandline_parse(argc, argv,
+ "46c:C:d:fgi:lm:n:N:p:P:"
+ "sS:t:T:u:vVx:")) != -1) {
+ switch (ch) {
+ case '4':
+ if (disable4)
+ ns_main_earlyfatal("cannot specify -4 and -6");
+ if (isc_net_probeipv4() != ISC_R_SUCCESS)
+ ns_main_earlyfatal("IPv4 not supported by OS");
+ isc_net_disableipv6();
+ disable6 = ISC_TRUE;
+ break;
+ case '6':
+ if (disable6)
+ ns_main_earlyfatal("cannot specify -4 and -6");
+ if (isc_net_probeipv6() != ISC_R_SUCCESS)
+ ns_main_earlyfatal("IPv6 not supported by OS");
+ isc_net_disableipv4();
+ disable4 = ISC_TRUE;
+ break;
+ case 'c':
+ ns_g_conffile = isc_commandline_argument;
+ lwresd_g_conffile = isc_commandline_argument;
+ if (lwresd_g_useresolvconf)
+ ns_main_earlyfatal("cannot specify -c and -C");
+ ns_g_conffileset = ISC_TRUE;
+ break;
+ case 'C':
+ lwresd_g_resolvconffile = isc_commandline_argument;
+ if (ns_g_conffileset)
+ ns_main_earlyfatal("cannot specify -c and -C");
+ lwresd_g_useresolvconf = ISC_TRUE;
+ break;
+ case 'd':
+ ns_g_debuglevel = parse_int(isc_commandline_argument,
+ "debug level");
+ break;
+ case 'f':
+ ns_g_foreground = ISC_TRUE;
+ break;
+ case 'g':
+ ns_g_foreground = ISC_TRUE;
+ ns_g_logstderr = ISC_TRUE;
+ break;
+ /* XXXBEW -i should be removed */
+ case 'i':
+ lwresd_g_defaultpidfile = isc_commandline_argument;
+ break;
+ case 'l':
+ ns_g_lwresdonly = ISC_TRUE;
+ break;
+ case 'm':
+ set_flags(isc_commandline_argument, mem_debug_flags,
+ &isc_mem_debugging);
+ break;
+ case 'N': /* Deprecated. */
+ case 'n':
+ ns_g_cpus = parse_int(isc_commandline_argument,
+ "number of cpus");
+ if (ns_g_cpus == 0)
+ ns_g_cpus = 1;
+ break;
+ case 'p':
+ port = parse_int(isc_commandline_argument, "port");
+ if (port < 1 || port > 65535)
+ ns_main_earlyfatal("port '%s' out of range",
+ isc_commandline_argument);
+ ns_g_port = port;
+ break;
+ /* XXXBEW Should -P be removed? */
+ case 'P':
+ port = parse_int(isc_commandline_argument, "port");
+ if (port < 1 || port > 65535)
+ ns_main_earlyfatal("port '%s' out of range",
+ isc_commandline_argument);
+ lwresd_g_listenport = port;
+ break;
+ case 's':
+ /* XXXRTH temporary syntax */
+ want_stats = ISC_TRUE;
+ break;
+ case 'S':
+ maxsocks = parse_int(isc_commandline_argument,
+ "max number of sockets");
+ break;
+ case 't':
+ /* XXXJAB should we make a copy? */
+ ns_g_chrootdir = isc_commandline_argument;
+ break;
+ case 'T':
+ /*
+ * clienttest: make clients single shot with their
+ * own memory context.
+ */
+ if (strcmp(isc_commandline_argument, "clienttest") == 0)
+ ns_g_clienttest = ISC_TRUE;
+ else
+ fprintf(stderr, "unknown -T flag '%s\n",
+ isc_commandline_argument);
+ break;
+ case 'u':
+ ns_g_username = isc_commandline_argument;
+ break;
+ case 'v':
+ printf("BIND %s\n", ns_g_version);
+ exit(0);
+ case 'V':
+ printf("BIND %s built with %s\n", ns_g_version,
+ ns_g_configargs);
+ exit(0);
+ case '?':
+ usage();
+ if (isc_commandline_option == '?')
+ exit(0);
+ ns_main_earlyfatal("unknown option '-%c'",
+ isc_commandline_option);
+ default:
+ ns_main_earlyfatal("parsing options returned %d", ch);
+ }
+ }
+
+ argc -= isc_commandline_index;
+ argv += isc_commandline_index;
+
+ if (argc > 0) {
+ usage();
+ ns_main_earlyfatal("extra command line arguments");
+ }
+}
+
+static isc_result_t
+create_managers(void) {
+ isc_result_t result;
+ unsigned int socks;
+
+#ifdef ISC_PLATFORM_USETHREADS
+ if (ns_g_cpus == 0)
+ ns_g_cpus = ns_g_cpus_detected;
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "found %u CPU%s, using %u worker thread%s",
+ ns_g_cpus_detected, ns_g_cpus_detected == 1 ? "" : "s",
+ ns_g_cpus, ns_g_cpus == 1 ? "" : "s");
+#else
+ ns_g_cpus = 1;
+#endif
+ result = isc_taskmgr_create(ns_g_mctx, ns_g_cpus, 0, &ns_g_taskmgr);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_taskmgr_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = isc_timermgr_create(ns_g_mctx, &ns_g_timermgr);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_timermgr_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = isc_socketmgr_create2(ns_g_mctx, &ns_g_socketmgr, maxsocks);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_socketmgr_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+ result = isc_socketmgr_getmaxsockets(ns_g_socketmgr, &socks);
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "using up to %u sockets", socks);
+ }
+
+ result = isc_entropy_create(ns_g_mctx, &ns_g_entropy);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_entropy_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = isc_hash_create(ns_g_mctx, ns_g_entropy, DNS_NAME_MAXWIRE);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_hash_create() failed: %s",
+ isc_result_totext(result));
+ return (ISC_R_UNEXPECTED);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+destroy_managers(void) {
+ ns_lwresd_shutdown();
+
+ isc_entropy_detach(&ns_g_entropy);
+ if (ns_g_fallbackentropy != NULL)
+ isc_entropy_detach(&ns_g_fallbackentropy);
+
+ /*
+ * isc_taskmgr_destroy() will block until all tasks have exited,
+ */
+ isc_taskmgr_destroy(&ns_g_taskmgr);
+ isc_timermgr_destroy(&ns_g_timermgr);
+ isc_socketmgr_destroy(&ns_g_socketmgr);
+
+ /*
+ * isc_hash_destroy() cannot be called as long as a resolver may be
+ * running. Calling this after isc_taskmgr_destroy() ensures the
+ * call is safe.
+ */
+ isc_hash_destroy();
+}
+
+static void
+setup(void) {
+ isc_result_t result;
+ isc_resourcevalue_t old_openfiles;
+#ifdef HAVE_LIBSCF
+ char *instance = NULL;
+#endif
+
+ /*
+ * Get the user and group information before changing the root
+ * directory, so the administrator does not need to keep a copy
+ * of the user and group databases in the chroot'ed environment.
+ */
+ ns_os_inituserinfo(ns_g_username);
+
+ /*
+ * Initialize time conversion information
+ */
+ ns_os_tzset();
+
+ ns_os_opendevnull();
+
+#ifdef HAVE_LIBSCF
+ /* Check if named is under smf control, before chroot. */
+ result = ns_smf_get_instance(&instance, 0, ns_g_mctx);
+ /* We don't care about instance, just check if we got one. */
+ if (result == ISC_R_SUCCESS)
+ ns_smf_got_instance = 1;
+ else
+ ns_smf_got_instance = 0;
+ if (instance != NULL)
+ isc_mem_free(ns_g_mctx, instance);
+#endif /* HAVE_LIBSCF */
+
+#ifdef PATH_RANDOMDEV
+ /*
+ * Initialize system's random device as fallback entropy source
+ * if running chroot'ed.
+ */
+ if (ns_g_chrootdir != NULL) {
+ result = isc_entropy_create(ns_g_mctx, &ns_g_fallbackentropy);
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("isc_entropy_create() failed: %s",
+ isc_result_totext(result));
+
+ result = isc_entropy_createfilesource(ns_g_fallbackentropy,
+ PATH_RANDOMDEV);
+ if (result != ISC_R_SUCCESS) {
+ ns_main_earlywarning("could not open pre-chroot "
+ "entropy source %s: %s",
+ PATH_RANDOMDEV,
+ isc_result_totext(result));
+ isc_entropy_detach(&ns_g_fallbackentropy);
+ }
+ }
+#endif
+
+#ifdef ISC_PLATFORM_USETHREADS
+ /*
+ * Check for the number of cpu's before ns_os_chroot().
+ */
+ ns_g_cpus_detected = isc_os_ncpus();
+#endif
+
+ ns_os_chroot(ns_g_chrootdir);
+
+ /*
+ * For operating systems which have a capability mechanism, now
+ * is the time to switch to minimal privs and change our user id.
+ * On traditional UNIX systems, this call will be a no-op, and we
+ * will change the user ID after reading the config file the first
+ * time. (We need to read the config file to know which possibly
+ * privileged ports to bind() to.)
+ */
+ ns_os_minprivs();
+
+ result = ns_log_init(ISC_TF(ns_g_username != NULL));
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("ns_log_init() failed: %s",
+ isc_result_totext(result));
+
+ /*
+ * Now is the time to daemonize (if we're not running in the
+ * foreground). We waited until now because we wanted to get
+ * a valid logging context setup. We cannot daemonize any later,
+ * because calling create_managers() will create threads, which
+ * would be lost after fork().
+ */
+ if (!ns_g_foreground)
+ ns_os_daemonize();
+
+ /*
+ * We call isc_app_start() here as some versions of FreeBSD's fork()
+ * destroys all the signal handling it sets up.
+ */
+ result = isc_app_start();
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("isc_app_start() failed: %s",
+ isc_result_totext(result));
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE, "starting BIND %s%s", ns_g_version,
+ saved_command_line);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE, "built with %s", ns_g_configargs);
+
+ /*
+ * Get the initial resource limits.
+ */
+ (void)isc_resource_getlimit(isc_resource_stacksize,
+ &ns_g_initstacksize);
+ (void)isc_resource_getlimit(isc_resource_datasize,
+ &ns_g_initdatasize);
+ (void)isc_resource_getlimit(isc_resource_coresize,
+ &ns_g_initcoresize);
+ (void)isc_resource_getlimit(isc_resource_openfiles,
+ &ns_g_initopenfiles);
+
+ /*
+ * System resources cannot effectively be tuned on some systems.
+ * Raise the limit in such cases for safety.
+ */
+ old_openfiles = ns_g_initopenfiles;
+ ns_os_adjustnofile();
+ (void)isc_resource_getlimit(isc_resource_openfiles,
+ &ns_g_initopenfiles);
+ if (old_openfiles != ns_g_initopenfiles) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "adjusted limit on open files from "
+ "%" ISC_PRINT_QUADFORMAT "u to "
+ "%" ISC_PRINT_QUADFORMAT "u",
+ old_openfiles, ns_g_initopenfiles);
+ }
+
+ /*
+ * If the named configuration filename is relative, prepend the current
+ * directory's name before possibly changing to another directory.
+ */
+ if (! isc_file_isabsolute(ns_g_conffile)) {
+ result = isc_file_absolutepath(ns_g_conffile,
+ absolute_conffile,
+ sizeof(absolute_conffile));
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("could not construct absolute path of "
+ "configuration file: %s",
+ isc_result_totext(result));
+ ns_g_conffile = absolute_conffile;
+ }
+
+ /*
+ * Record the server's startup time.
+ */
+ result = isc_time_now(&ns_g_boottime);
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("isc_time_now() failed: %s",
+ isc_result_totext(result));
+
+ result = create_managers();
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("create_managers() failed: %s",
+ isc_result_totext(result));
+
+ ns_builtin_init();
+
+ /*
+ * Add calls to register sdb drivers here.
+ */
+ /* xxdb_init(); */
+
+#ifdef DLZ
+ /*
+ * Registyer any DLZ drivers.
+ */
+ result = dlz_drivers_init();
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("dlz_drivers_init() failed: %s",
+ isc_result_totext(result));
+#endif
+
+ ns_server_create(ns_g_mctx, &ns_g_server);
+}
+
+static void
+cleanup(void) {
+ destroy_managers();
+
+ ns_server_destroy(&ns_g_server);
+
+ ns_builtin_deinit();
+
+ /*
+ * Add calls to unregister sdb drivers here.
+ */
+ /* xxdb_clear(); */
+
+#ifdef DLZ
+ /*
+ * Unregister any DLZ drivers.
+ */
+ dlz_drivers_clear();
+#endif
+
+ dns_name_destroy();
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE, "exiting");
+ ns_log_shutdown();
+}
+
+static char *memstats = NULL;
+
+void
+ns_main_setmemstats(const char *filename) {
+ /*
+ * Caller has to ensure locking.
+ */
+
+ if (memstats != NULL) {
+ free(memstats);
+ memstats = NULL;
+ }
+ if (filename == NULL)
+ return;
+ memstats = malloc(strlen(filename) + 1);
+ if (memstats)
+ strcpy(memstats, filename);
+}
+
+#ifdef HAVE_LIBSCF
+/*
+ * Get FMRI for the named process.
+ */
+isc_result_t
+ns_smf_get_instance(char **ins_name, int debug, isc_mem_t *mctx) {
+ scf_handle_t *h = NULL;
+ int namelen;
+ char *instance;
+
+ REQUIRE(ins_name != NULL && *ins_name == NULL);
+
+ if ((h = scf_handle_create(SCF_VERSION)) == NULL) {
+ if (debug)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "scf_handle_create() failed: %s",
+ scf_strerror(scf_error()));
+ return (ISC_R_FAILURE);
+ }
+
+ if (scf_handle_bind(h) == -1) {
+ if (debug)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "scf_handle_bind() failed: %s",
+ scf_strerror(scf_error()));
+ scf_handle_destroy(h);
+ return (ISC_R_FAILURE);
+ }
+
+ if ((namelen = scf_myname(h, NULL, 0)) == -1) {
+ if (debug)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "scf_myname() failed: %s",
+ scf_strerror(scf_error()));
+ scf_handle_destroy(h);
+ return (ISC_R_FAILURE);
+ }
+
+ if ((instance = isc_mem_allocate(mctx, namelen + 1)) == NULL) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "ns_smf_get_instance memory "
+ "allocation failed: %s",
+ isc_result_totext(ISC_R_NOMEMORY));
+ scf_handle_destroy(h);
+ return (ISC_R_FAILURE);
+ }
+
+ if (scf_myname(h, instance, namelen + 1) == -1) {
+ if (debug)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "scf_myname() failed: %s",
+ scf_strerror(scf_error()));
+ scf_handle_destroy(h);
+ isc_mem_free(mctx, instance);
+ return (ISC_R_FAILURE);
+ }
+
+ scf_handle_destroy(h);
+ *ins_name = instance;
+ return (ISC_R_SUCCESS);
+}
+#endif /* HAVE_LIBSCF */
+
+int
+main(int argc, char *argv[]) {
+ isc_result_t result;
+#ifdef HAVE_LIBSCF
+ char *instance = NULL;
+#endif
+
+ /*
+ * Record version in core image.
+ * strings named.core | grep "named version:"
+ */
+ strlcat(version,
+#ifdef __DATE__
+ "named version: BIND " VERSION " (" __DATE__ ")",
+#else
+ "named version: BIND " VERSION,
+#endif
+ sizeof(version));
+ result = isc_file_progname(*argv, program_name, sizeof(program_name));
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("program name too long");
+
+ if (strcmp(program_name, "lwresd") == 0)
+ ns_g_lwresdonly = ISC_TRUE;
+
+ isc_assertion_setcallback(assertion_failed);
+ isc_error_setfatal(library_fatal_error);
+ isc_error_setunexpected(library_unexpected_error);
+
+ ns_os_init(program_name);
+
+ dns_result_register();
+ dst_result_register();
+ isccc_result_register();
+
+ parse_command_line(argc, argv);
+
+ /*
+ * Warn about common configuration error.
+ */
+ if (ns_g_chrootdir != NULL) {
+ int len = strlen(ns_g_chrootdir);
+ if (strncmp(ns_g_chrootdir, ns_g_conffile, len) == 0 &&
+ (ns_g_conffile[len] == '/' || ns_g_conffile[len] == '\\'))
+ ns_main_earlywarning("config filename (-c %s) contains "
+ "chroot path (-t %s)",
+ ns_g_conffile, ns_g_chrootdir);
+ }
+
+ result = isc_mem_create(0, 0, &ns_g_mctx);
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("isc_mem_create() failed: %s",
+ isc_result_totext(result));
+ isc_mem_setname(ns_g_mctx, "main", NULL);
+
+ setup();
+
+ /*
+ * Start things running and then wait for a shutdown request
+ * or reload.
+ */
+ do {
+ result = isc_app_run();
+
+ if (result == ISC_R_RELOAD) {
+ ns_server_reloadwanted(ns_g_server);
+ } else if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "isc_app_run(): %s",
+ isc_result_totext(result));
+ /*
+ * Force exit.
+ */
+ result = ISC_R_SUCCESS;
+ }
+ } while (result != ISC_R_SUCCESS);
+
+#ifdef HAVE_LIBSCF
+ if (ns_smf_want_disable == 1) {
+ result = ns_smf_get_instance(&instance, 1, ns_g_mctx);
+ if (result == ISC_R_SUCCESS && instance != NULL) {
+ if (smf_disable_instance(instance, 0) != 0)
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "smf_disable_instance() "
+ "failed for %s : %s",
+ instance,
+ scf_strerror(scf_error()));
+ }
+ if (instance != NULL)
+ isc_mem_free(ns_g_mctx, instance);
+ }
+#endif /* HAVE_LIBSCF */
+
+ cleanup();
+
+ if (want_stats) {
+ isc_mem_stats(ns_g_mctx, stdout);
+ isc_mutex_stats(stdout);
+ }
+
+ if (ns_g_memstatistics && memstats != NULL) {
+ FILE *fp = NULL;
+ result = isc_stdio_open(memstats, "w", &fp);
+ if (result == ISC_R_SUCCESS) {
+ isc_mem_stats(ns_g_mctx, fp);
+ isc_mutex_stats(fp);
+ isc_stdio_close(fp);
+ }
+ }
+ isc_mem_destroy(&ns_g_mctx);
+ isc_mem_checkdestroyed(stderr);
+
+ ns_main_setmemstats(NULL);
+
+ isc_app_finish();
+
+ ns_os_closedevnull();
+
+ ns_os_shutdown();
+
+ return (0);
+}
diff --git a/bin/named/named.8 b/bin/named/named.8
new file mode 100644
index 0000000..3408403
--- /dev/null
+++ b/bin/named/named.8
@@ -0,0 +1,256 @@
+.\" Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+.\" Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+.\" PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $Id: named.8,v 1.38 2008/11/07 01:11:19 tbox Exp $
+.\"
+.hy 0
+.ad l
+.\" Title: named
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.71.1 <http://docbook.sf.net/>
+.\" Date: June 30, 2000
+.\" Manual: BIND9
+.\" Source: BIND9
+.\"
+.TH "NAMED" "8" "June 30, 2000" "BIND9" "BIND9"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+named \- Internet domain name server
+.SH "SYNOPSIS"
+.HP 6
+\fBnamed\fR [\fB\-4\fR] [\fB\-6\fR] [\fB\-c\ \fR\fB\fIconfig\-file\fR\fR] [\fB\-d\ \fR\fB\fIdebug\-level\fR\fR] [\fB\-f\fR] [\fB\-g\fR] [\fB\-m\ \fR\fB\fIflag\fR\fR] [\fB\-n\ \fR\fB\fI#cpus\fR\fR] [\fB\-p\ \fR\fB\fIport\fR\fR] [\fB\-s\fR] [\fB\-S\ \fR\fB\fI#max\-socks\fR\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-V\fR] [\fB\-x\ \fR\fB\fIcache\-file\fR\fR]
+.SH "DESCRIPTION"
+.PP
+\fBnamed\fR
+is a Domain Name System (DNS) server, part of the BIND 9 distribution from ISC. For more information on the DNS, see RFCs 1033, 1034, and 1035.
+.PP
+When invoked without arguments,
+\fBnamed\fR
+will read the default configuration file
+\fI/etc/named.conf\fR, read any initial data, and listen for queries.
+.SH "OPTIONS"
+.PP
+\-4
+.RS 4
+Use IPv4 only even if the host machine is capable of IPv6.
+\fB\-4\fR
+and
+\fB\-6\fR
+are mutually exclusive.
+.RE
+.PP
+\-6
+.RS 4
+Use IPv6 only even if the host machine is capable of IPv4.
+\fB\-4\fR
+and
+\fB\-6\fR
+are mutually exclusive.
+.RE
+.PP
+\-c \fIconfig\-file\fR
+.RS 4
+Use
+\fIconfig\-file\fR
+as the configuration file instead of the default,
+\fI/etc/named.conf\fR. To ensure that reloading the configuration file continues to work after the server has changed its working directory due to to a possible
+\fBdirectory\fR
+option in the configuration file,
+\fIconfig\-file\fR
+should be an absolute pathname.
+.RE
+.PP
+\-d \fIdebug\-level\fR
+.RS 4
+Set the daemon's debug level to
+\fIdebug\-level\fR. Debugging traces from
+\fBnamed\fR
+become more verbose as the debug level increases.
+.RE
+.PP
+\-f
+.RS 4
+Run the server in the foreground (i.e. do not daemonize).
+.RE
+.PP
+\-g
+.RS 4
+Run the server in the foreground and force all logging to
+\fIstderr\fR.
+.RE
+.PP
+\-m \fIflag\fR
+.RS 4
+Turn on memory usage debugging flags. Possible flags are
+\fIusage\fR,
+\fItrace\fR,
+\fIrecord\fR,
+\fIsize\fR, and
+\fImctx\fR. These correspond to the ISC_MEM_DEBUGXXXX flags described in
+\fI<isc/mem.h>\fR.
+.RE
+.PP
+\-n \fI#cpus\fR
+.RS 4
+Create
+\fI#cpus\fR
+worker threads to take advantage of multiple CPUs. If not specified,
+\fBnamed\fR
+will try to determine the number of CPUs present and create one thread per CPU. If it is unable to determine the number of CPUs, a single worker thread will be created.
+.RE
+.PP
+\-p \fIport\fR
+.RS 4
+Listen for queries on port
+\fIport\fR. If not specified, the default is port 53.
+.RE
+.PP
+\-s
+.RS 4
+Write memory usage statistics to
+\fIstdout\fR
+on exit.
+.RS
+.B "Note:"
+This option is mainly of interest to BIND 9 developers and may be removed or changed in a future release.
+.RE
+.RE
+.PP
+\-S \fI#max\-socks\fR
+.RS 4
+Allow
+\fBnamed\fR
+to use up to
+\fI#max\-socks\fR
+sockets.
+.RS
+.B "Warning:"
+This option should be unnecessary for the vast majority of users. The use of this option could even be harmful because the specified value may exceed the limitation of the underlying system API. It is therefore set only when the default configuration causes exhaustion of file descriptors and the operational environment is known to support the specified number of sockets. Note also that the actual maximum number is normally a little fewer than the specified value because
+\fBnamed\fR
+reserves some file descriptors for its internal use.
+.RE
+.RE
+.PP
+\-t \fIdirectory\fR
+.RS 4
+Chroot to
+\fIdirectory\fR
+after processing the command line arguments, but before reading the configuration file.
+.RS
+.B "Warning:"
+This option should be used in conjunction with the
+\fB\-u\fR
+option, as chrooting a process running as root doesn't enhance security on most systems; the way
+\fBchroot(2)\fR
+is defined allows a process with root privileges to escape a chroot jail.
+.RE
+.RE
+.PP
+\-u \fIuser\fR
+.RS 4
+Setuid to
+\fIuser\fR
+after completing privileged operations, such as creating sockets that listen on privileged ports.
+.RS
+.B "Note:"
+On Linux,
+\fBnamed\fR
+uses the kernel's capability mechanism to drop all root privileges except the ability to
+\fBbind(2)\fR
+to a privileged port and set process resource limits. Unfortunately, this means that the
+\fB\-u\fR
+option only works when
+\fBnamed\fR
+is run on kernel 2.2.18 or later, or kernel 2.3.99\-pre3 or later, since previous kernels did not allow privileges to be retained after
+\fBsetuid(2)\fR.
+.RE
+.RE
+.PP
+\-v
+.RS 4
+Report the version number and exit.
+.RE
+.PP
+\-V
+.RS 4
+Report the version number and build options, and exit.
+.RE
+.PP
+\-x \fIcache\-file\fR
+.RS 4
+Load data from
+\fIcache\-file\fR
+into the cache of the default view.
+.RS
+.B "Warning:"
+This option must not be used. It is only of interest to BIND 9 developers and may be removed or changed in a future release.
+.RE
+.RE
+.SH "SIGNALS"
+.PP
+In routine operation, signals should not be used to control the nameserver;
+\fBrndc\fR
+should be used instead.
+.PP
+SIGHUP
+.RS 4
+Force a reload of the server.
+.RE
+.PP
+SIGINT, SIGTERM
+.RS 4
+Shut down the server.
+.RE
+.PP
+The result of sending any other signals to the server is undefined.
+.SH "CONFIGURATION"
+.PP
+The
+\fBnamed\fR
+configuration file is too complex to describe in detail here. A complete description is provided in the
+BIND 9 Administrator Reference Manual.
+.SH "FILES"
+.PP
+\fI/etc/named.conf\fR
+.RS 4
+The default configuration file.
+.RE
+.PP
+\fI/var/run/named/named.pid\fR
+.RS 4
+The default process\-id file.
+.RE
+.SH "SEE ALSO"
+.PP
+RFC 1033,
+RFC 1034,
+RFC 1035,
+\fBnamed\-checkconf\fR(8),
+\fBnamed\-checkzone\fR(8),
+\fBrndc\fR(8),
+\fBlwresd\fR(8),
+\fBnamed.conf\fR(5),
+BIND 9 Administrator Reference Manual.
+.SH "AUTHOR"
+.PP
+Internet Systems Consortium
+.SH "COPYRIGHT"
+Copyright \(co 2004\-2008 Internet Systems Consortium, Inc. ("ISC")
+.br
+Copyright \(co 2000, 2001, 2003 Internet Software Consortium.
+.br
diff --git a/bin/named/named.conf.5 b/bin/named/named.conf.5
new file mode 100644
index 0000000..039c795
--- /dev/null
+++ b/bin/named/named.conf.5
@@ -0,0 +1,548 @@
+.\" Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+.\" REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+.\" INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+.\" LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+.\" OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+.\" PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" $Id: named.conf.5,v 1.36 2008/09/25 04:45:04 tbox Exp $
+.\"
+.hy 0
+.ad l
+.\" Title: \fInamed.conf\fR
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.71.1 <http://docbook.sf.net/>
+.\" Date: Aug 13, 2004
+.\" Manual: BIND9
+.\" Source: BIND9
+.\"
+.TH "\fINAMED.CONF\fR" "5" "Aug 13, 2004" "BIND9" "BIND9"
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.SH "NAME"
+named.conf \- configuration file for named
+.SH "SYNOPSIS"
+.HP 11
+\fBnamed.conf\fR
+.SH "DESCRIPTION"
+.PP
+\fInamed.conf\fR
+is the configuration file for
+\fBnamed\fR. Statements are enclosed in braces and terminated with a semi\-colon. Clauses in the statements are also semi\-colon terminated. The usual comment styles are supported:
+.PP
+C style: /* */
+.PP
+C++ style: // to end of line
+.PP
+Unix style: # to end of line
+.SH "ACL"
+.sp
+.RS 4
+.nf
+acl \fIstring\fR { \fIaddress_match_element\fR; ... };
+.fi
+.RE
+.SH "KEY"
+.sp
+.RS 4
+.nf
+key \fIdomain_name\fR {
+ algorithm \fIstring\fR;
+ secret \fIstring\fR;
+};
+.fi
+.RE
+.SH "MASTERS"
+.sp
+.RS 4
+.nf
+masters \fIstring\fR [ port \fIinteger\fR ] {
+ ( \fImasters\fR | \fIipv4_address\fR [port \fIinteger\fR] |
+ \fIipv6_address\fR [port \fIinteger\fR] ) [ key \fIstring\fR ]; ...
+};
+.fi
+.RE
+.SH "SERVER"
+.sp
+.RS 4
+.nf
+server ( \fIipv4_address\fR\fI[/prefixlen]\fR | \fIipv6_address\fR\fI[/prefixlen]\fR ) {
+ bogus \fIboolean\fR;
+ edns \fIboolean\fR;
+ edns\-udp\-size \fIinteger\fR;
+ max\-udp\-size \fIinteger\fR;
+ provide\-ixfr \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ keys \fIserver_key\fR;
+ transfers \fIinteger\fR;
+ transfer\-format ( many\-answers | one\-answer );
+ transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ support\-ixfr \fIboolean\fR; // obsolete
+};
+.fi
+.RE
+.SH "TRUSTED\-KEYS"
+.sp
+.RS 4
+.nf
+trusted\-keys {
+ \fIdomain_name\fR \fIflags\fR \fIprotocol\fR \fIalgorithm\fR \fIkey\fR; ...
+};
+.fi
+.RE
+.SH "CONTROLS"
+.sp
+.RS 4
+.nf
+controls {
+ inet ( \fIipv4_address\fR | \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ]
+ allow { \fIaddress_match_element\fR; ... }
+ [ keys { \fIstring\fR; ... } ];
+ unix \fIunsupported\fR; // not implemented
+};
+.fi
+.RE
+.SH "LOGGING"
+.sp
+.RS 4
+.nf
+logging {
+ channel \fIstring\fR {
+ file \fIlog_file\fR;
+ syslog \fIoptional_facility\fR;
+ null;
+ stderr;
+ severity \fIlog_severity\fR;
+ print\-time \fIboolean\fR;
+ print\-severity \fIboolean\fR;
+ print\-category \fIboolean\fR;
+ };
+ category \fIstring\fR { \fIstring\fR; ... };
+};
+.fi
+.RE
+.SH "LWRES"
+.sp
+.RS 4
+.nf
+lwres {
+ listen\-on [ port \fIinteger\fR ] {
+ ( \fIipv4_address\fR | \fIipv6_address\fR ) [ port \fIinteger\fR ]; ...
+ };
+ view \fIstring\fR \fIoptional_class\fR;
+ search { \fIstring\fR; ... };
+ ndots \fIinteger\fR;
+};
+.fi
+.RE
+.SH "OPTIONS"
+.sp
+.RS 4
+.nf
+options {
+ avoid\-v4\-udp\-ports { \fIport\fR; ... };
+ avoid\-v6\-udp\-ports { \fIport\fR; ... };
+ blackhole { \fIaddress_match_element\fR; ... };
+ coresize \fIsize\fR;
+ datasize \fIsize\fR;
+ directory \fIquoted_string\fR;
+ dump\-file \fIquoted_string\fR;
+ files \fIsize\fR;
+ heartbeat\-interval \fIinteger\fR;
+ host\-statistics \fIboolean\fR; // not implemented
+ host\-statistics\-max \fInumber\fR; // not implemented
+ hostname ( \fIquoted_string\fR | none );
+ interface\-interval \fIinteger\fR;
+ listen\-on [ port \fIinteger\fR ] { \fIaddress_match_element\fR; ... };
+ listen\-on\-v6 [ port \fIinteger\fR ] { \fIaddress_match_element\fR; ... };
+ match\-mapped\-addresses \fIboolean\fR;
+ memstatistics\-file \fIquoted_string\fR;
+ pid\-file ( \fIquoted_string\fR | none );
+ port \fIinteger\fR;
+ querylog \fIboolean\fR;
+ recursing\-file \fIquoted_string\fR;
+ reserved\-sockets \fIinteger\fR;
+ random\-device \fIquoted_string\fR;
+ recursive\-clients \fIinteger\fR;
+ serial\-query\-rate \fIinteger\fR;
+ server\-id ( \fIquoted_string\fR | none |;
+ stacksize \fIsize\fR;
+ statistics\-file \fIquoted_string\fR;
+ statistics\-interval \fIinteger\fR; // not yet implemented
+ tcp\-clients \fIinteger\fR;
+ tcp\-listen\-queue \fIinteger\fR;
+ tkey\-dhkey \fIquoted_string\fR \fIinteger\fR;
+ tkey\-gssapi\-credential \fIquoted_string\fR;
+ tkey\-domain \fIquoted_string\fR;
+ transfers\-per\-ns \fIinteger\fR;
+ transfers\-in \fIinteger\fR;
+ transfers\-out \fIinteger\fR;
+ use\-ixfr \fIboolean\fR;
+ version ( \fIquoted_string\fR | none );
+ allow\-recursion { \fIaddress_match_element\fR; ... };
+ allow\-recursion\-on { \fIaddress_match_element\fR; ... };
+ sortlist { \fIaddress_match_element\fR; ... };
+ topology { \fIaddress_match_element\fR; ... }; // not implemented
+ auth\-nxdomain \fIboolean\fR; // default changed
+ minimal\-responses \fIboolean\fR;
+ recursion \fIboolean\fR;
+ rrset\-order {
+ [ class \fIstring\fR ] [ type \fIstring\fR ]
+ [ name \fIquoted_string\fR ] \fIstring\fR \fIstring\fR; ...
+ };
+ provide\-ixfr \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ rfc2308\-type1 \fIboolean\fR; // not yet implemented
+ additional\-from\-auth \fIboolean\fR;
+ additional\-from\-cache \fIboolean\fR;
+ query\-source ( ( \fIipv4_address\fR | * ) | [ address ( \fIipv4_address\fR | * ) ] ) [ port ( \fIinteger\fR | * ) ];
+ query\-source\-v6 ( ( \fIipv6_address\fR | * ) | [ address ( \fIipv6_address\fR | * ) ] ) [ port ( \fIinteger\fR | * ) ];
+ use\-queryport\-pool \fIboolean\fR;
+ queryport\-pool\-ports \fIinteger\fR;
+ queryport\-pool\-updateinterval \fIinteger\fR;
+ cleaning\-interval \fIinteger\fR;
+ min\-roots \fIinteger\fR; // not implemented
+ lame\-ttl \fIinteger\fR;
+ max\-ncache\-ttl \fIinteger\fR;
+ max\-cache\-ttl \fIinteger\fR;
+ transfer\-format ( many\-answers | one\-answer );
+ max\-cache\-size \fIsize\fR;
+ max\-acache\-size \fIsize\fR;
+ clients\-per\-query \fInumber\fR;
+ max\-clients\-per\-query \fInumber\fR;
+ check\-names ( master | slave | response )
+ ( fail | warn | ignore );
+ check\-mx ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ cache\-file \fIquoted_string\fR; // test option
+ suppress\-initial\-notify \fIboolean\fR; // not yet implemented
+ preferred\-glue \fIstring\fR;
+ dual\-stack\-servers [ port \fIinteger\fR ] {
+ ( \fIquoted_string\fR [port \fIinteger\fR] |
+ \fIipv4_address\fR [port \fIinteger\fR] |
+ \fIipv6_address\fR [port \fIinteger\fR] ); ...
+ };
+ edns\-udp\-size \fIinteger\fR;
+ max\-udp\-size \fIinteger\fR;
+ root\-delegation\-only [ exclude { \fIquoted_string\fR; ... } ];
+ disable\-algorithms \fIstring\fR { \fIstring\fR; ... };
+ dnssec\-enable \fIboolean\fR;
+ dnssec\-validation \fIboolean\fR;
+ dnssec\-lookaside \fIstring\fR trust\-anchor \fIstring\fR;
+ dnssec\-must\-be\-secure \fIstring\fR \fIboolean\fR;
+ dnssec\-accept\-expired \fIboolean\fR;
+ empty\-server \fIstring\fR;
+ empty\-contact \fIstring\fR;
+ empty\-zones\-enable \fIboolean\fR;
+ disable\-empty\-zone \fIstring\fR;
+ dialup \fIdialuptype\fR;
+ ixfr\-from\-differences \fIixfrdiff\fR;
+ allow\-query { \fIaddress_match_element\fR; ... };
+ allow\-query\-on { \fIaddress_match_element\fR; ... };
+ allow\-query\-cache { \fIaddress_match_element\fR; ... };
+ allow\-query\-cache\-on { \fIaddress_match_element\fR; ... };
+ allow\-transfer { \fIaddress_match_element\fR; ... };
+ allow\-update { \fIaddress_match_element\fR; ... };
+ allow\-update\-forwarding { \fIaddress_match_element\fR; ... };
+ update\-check\-ksk \fIboolean\fR;
+ masterfile\-format ( text | raw );
+ notify \fInotifytype\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-delay \fIseconds\fR;
+ notify\-to\-soa \fIboolean\fR;
+ also\-notify [ port \fIinteger\fR ] { ( \fIipv4_address\fR | \fIipv6_address\fR )
+ [ port \fIinteger\fR ]; ... };
+ allow\-notify { \fIaddress_match_element\fR; ... };
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] {
+ ( \fIipv4_address\fR | \fIipv6_address\fR ) [ port \fIinteger\fR ]; ...
+ };
+ max\-journal\-size \fIsize_no_default\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ min\-refresh\-time \fIinteger\fR;
+ multi\-master \fIboolean\fR;
+ sig\-validity\-interval \fIinteger\fR;
+ sig\-re\-signing\-interval \fIinteger\fR;
+ sig\-signing\-nodes \fIinteger\fR;
+ sig\-signing\-signatures \fIinteger\fR;
+ sig\-signing\-type \fIinteger\fR;
+ transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ use\-alt\-transfer\-source \fIboolean\fR;
+ zone\-statistics \fIboolean\fR;
+ key\-directory \fIquoted_string\fR;
+ try\-tcp\-refresh \fIboolean\fR;
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ zero\-no\-soa\-ttl\-cache \fIboolean\fR;
+ nsec3\-test\-zone \fIboolean\fR; // testing only
+ allow\-v6\-synthesis { \fIaddress_match_element\fR; ... }; // obsolete
+ deallocate\-on\-exit \fIboolean\fR; // obsolete
+ fake\-iquery \fIboolean\fR; // obsolete
+ fetch\-glue \fIboolean\fR; // obsolete
+ has\-old\-clients \fIboolean\fR; // obsolete
+ maintain\-ixfr\-base \fIboolean\fR; // obsolete
+ max\-ixfr\-log\-size \fIsize\fR; // obsolete
+ multiple\-cnames \fIboolean\fR; // obsolete
+ named\-xfer \fIquoted_string\fR; // obsolete
+ serial\-queries \fIinteger\fR; // obsolete
+ treat\-cr\-as\-space \fIboolean\fR; // obsolete
+ use\-id\-pool \fIboolean\fR; // obsolete
+};
+.fi
+.RE
+.SH "VIEW"
+.sp
+.RS 4
+.nf
+view \fIstring\fR \fIoptional_class\fR {
+ match\-clients { \fIaddress_match_element\fR; ... };
+ match\-destinations { \fIaddress_match_element\fR; ... };
+ match\-recursive\-only \fIboolean\fR;
+ key \fIstring\fR {
+ algorithm \fIstring\fR;
+ secret \fIstring\fR;
+ };
+ zone \fIstring\fR \fIoptional_class\fR {
+ ...
+ };
+ server ( \fIipv4_address\fR\fI[/prefixlen]\fR | \fIipv6_address\fR\fI[/prefixlen]\fR ) {
+ ...
+ };
+ trusted\-keys {
+ \fIstring\fR \fIinteger\fR \fIinteger\fR \fIinteger\fR \fIquoted_string\fR; ...
+ };
+ allow\-recursion { \fIaddress_match_element\fR; ... };
+ allow\-recursion\-on { \fIaddress_match_element\fR; ... };
+ sortlist { \fIaddress_match_element\fR; ... };
+ topology { \fIaddress_match_element\fR; ... }; // not implemented
+ auth\-nxdomain \fIboolean\fR; // default changed
+ minimal\-responses \fIboolean\fR;
+ recursion \fIboolean\fR;
+ rrset\-order {
+ [ class \fIstring\fR ] [ type \fIstring\fR ]
+ [ name \fIquoted_string\fR ] \fIstring\fR \fIstring\fR; ...
+ };
+ provide\-ixfr \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ rfc2308\-type1 \fIboolean\fR; // not yet implemented
+ additional\-from\-auth \fIboolean\fR;
+ additional\-from\-cache \fIboolean\fR;
+ query\-source ( ( \fIipv4_address\fR | * ) | [ address ( \fIipv4_address\fR | * ) ] ) [ port ( \fIinteger\fR | * ) ];
+ query\-source\-v6 ( ( \fIipv6_address\fR | * ) | [ address ( \fIipv6_address\fR | * ) ] ) [ port ( \fIinteger\fR | * ) ];
+ use\-queryport\-pool \fIboolean\fR;
+ queryport\-pool\-ports \fIinteger\fR;
+ queryport\-pool\-updateinterval \fIinteger\fR;
+ cleaning\-interval \fIinteger\fR;
+ min\-roots \fIinteger\fR; // not implemented
+ lame\-ttl \fIinteger\fR;
+ max\-ncache\-ttl \fIinteger\fR;
+ max\-cache\-ttl \fIinteger\fR;
+ transfer\-format ( many\-answers | one\-answer );
+ max\-cache\-size \fIsize\fR;
+ max\-acache\-size \fIsize\fR;
+ clients\-per\-query \fInumber\fR;
+ max\-clients\-per\-query \fInumber\fR;
+ check\-names ( master | slave | response )
+ ( fail | warn | ignore );
+ check\-mx ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ cache\-file \fIquoted_string\fR; // test option
+ suppress\-initial\-notify \fIboolean\fR; // not yet implemented
+ preferred\-glue \fIstring\fR;
+ dual\-stack\-servers [ port \fIinteger\fR ] {
+ ( \fIquoted_string\fR [port \fIinteger\fR] |
+ \fIipv4_address\fR [port \fIinteger\fR] |
+ \fIipv6_address\fR [port \fIinteger\fR] ); ...
+ };
+ edns\-udp\-size \fIinteger\fR;
+ max\-udp\-size \fIinteger\fR;
+ root\-delegation\-only [ exclude { \fIquoted_string\fR; ... } ];
+ disable\-algorithms \fIstring\fR { \fIstring\fR; ... };
+ dnssec\-enable \fIboolean\fR;
+ dnssec\-validation \fIboolean\fR;
+ dnssec\-lookaside \fIstring\fR trust\-anchor \fIstring\fR;
+ dnssec\-must\-be\-secure \fIstring\fR \fIboolean\fR;
+ dnssec\-accept\-expired \fIboolean\fR;
+ empty\-server \fIstring\fR;
+ empty\-contact \fIstring\fR;
+ empty\-zones\-enable \fIboolean\fR;
+ disable\-empty\-zone \fIstring\fR;
+ dialup \fIdialuptype\fR;
+ ixfr\-from\-differences \fIixfrdiff\fR;
+ allow\-query { \fIaddress_match_element\fR; ... };
+ allow\-query\-on { \fIaddress_match_element\fR; ... };
+ allow\-query\-cache { \fIaddress_match_element\fR; ... };
+ allow\-query\-cache\-on { \fIaddress_match_element\fR; ... };
+ allow\-transfer { \fIaddress_match_element\fR; ... };
+ allow\-update { \fIaddress_match_element\fR; ... };
+ allow\-update\-forwarding { \fIaddress_match_element\fR; ... };
+ update\-check\-ksk \fIboolean\fR;
+ masterfile\-format ( text | raw );
+ notify \fInotifytype\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-delay \fIseconds\fR;
+ notify\-to\-soa \fIboolean\fR;
+ also\-notify [ port \fIinteger\fR ] { ( \fIipv4_address\fR | \fIipv6_address\fR )
+ [ port \fIinteger\fR ]; ... };
+ allow\-notify { \fIaddress_match_element\fR; ... };
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] {
+ ( \fIipv4_address\fR | \fIipv6_address\fR ) [ port \fIinteger\fR ]; ...
+ };
+ max\-journal\-size \fIsize_no_default\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ min\-refresh\-time \fIinteger\fR;
+ multi\-master \fIboolean\fR;
+ sig\-validity\-interval \fIinteger\fR;
+ transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ use\-alt\-transfer\-source \fIboolean\fR;
+ zone\-statistics \fIboolean\fR;
+ try\-tcp\-refresh \fIboolean\fR;
+ key\-directory \fIquoted_string\fR;
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ zero\-no\-soa\-ttl\-cache \fIboolean\fR;
+ allow\-v6\-synthesis { \fIaddress_match_element\fR; ... }; // obsolete
+ fetch\-glue \fIboolean\fR; // obsolete
+ maintain\-ixfr\-base \fIboolean\fR; // obsolete
+ max\-ixfr\-log\-size \fIsize\fR; // obsolete
+};
+.fi
+.RE
+.SH "ZONE"
+.sp
+.RS 4
+.nf
+zone \fIstring\fR \fIoptional_class\fR {
+ type ( master | slave | stub | hint |
+ forward | delegation\-only );
+ file \fIquoted_string\fR;
+ masters [ port \fIinteger\fR ] {
+ ( \fImasters\fR |
+ \fIipv4_address\fR [port \fIinteger\fR] |
+ \fIipv6_address\fR [ port \fIinteger\fR ] ) [ key \fIstring\fR ]; ...
+ };
+ database \fIstring\fR;
+ delegation\-only \fIboolean\fR;
+ check\-names ( fail | warn | ignore );
+ check\-mx ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ dialup \fIdialuptype\fR;
+ ixfr\-from\-differences \fIboolean\fR;
+ journal \fIquoted_string\fR;
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ allow\-query { \fIaddress_match_element\fR; ... };
+ allow\-query\-on { \fIaddress_match_element\fR; ... };
+ allow\-transfer { \fIaddress_match_element\fR; ... };
+ allow\-update { \fIaddress_match_element\fR; ... };
+ allow\-update\-forwarding { \fIaddress_match_element\fR; ... };
+ update\-policy {
+ ( grant | deny ) \fIstring\fR
+ ( name | subdomain | wildcard | self | selfsub | selfwild |
+ krb5\-self | ms\-self | krb5\-subdomain | ms\-subdomain |
+ tcp\-self | 6to4\-self ) \fIstring\fR
+ \fIrrtypelist\fR; ...
+ };
+ update\-check\-ksk \fIboolean\fR;
+ masterfile\-format ( text | raw );
+ notify \fInotifytype\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ];
+ notify\-delay \fIseconds\fR;
+ notify\-to\-soa \fIboolean\fR;
+ also\-notify [ port \fIinteger\fR ] { ( \fIipv4_address\fR | \fIipv6_address\fR )
+ [ port \fIinteger\fR ]; ... };
+ allow\-notify { \fIaddress_match_element\fR; ... };
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] {
+ ( \fIipv4_address\fR | \fIipv6_address\fR ) [ port \fIinteger\fR ]; ...
+ };
+ max\-journal\-size \fIsize_no_default\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ min\-refresh\-time \fIinteger\fR;
+ multi\-master \fIboolean\fR;
+ sig\-validity\-interval \fIinteger\fR;
+ transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source ( \fIipv4_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * )
+ [ port ( \fIinteger\fR | * ) ];
+ use\-alt\-transfer\-source \fIboolean\fR;
+ zone\-statistics \fIboolean\fR;
+ try\-tcp\-refresh \fIboolean\fR;
+ key\-directory \fIquoted_string\fR;
+ nsec3\-test\-zone \fIboolean\fR; // testing only
+ ixfr\-base \fIquoted_string\fR; // obsolete
+ ixfr\-tmp\-file \fIquoted_string\fR; // obsolete
+ maintain\-ixfr\-base \fIboolean\fR; // obsolete
+ max\-ixfr\-log\-size \fIsize\fR; // obsolete
+ pubkey \fIinteger\fR \fIinteger\fR \fIinteger\fR \fIquoted_string\fR; // obsolete
+};
+.fi
+.RE
+.SH "FILES"
+.PP
+\fI/etc/named.conf\fR
+.SH "SEE ALSO"
+.PP
+\fBnamed\fR(8),
+\fBnamed\-checkconf\fR(8),
+\fBrndc\fR(8),
+BIND 9 Administrator Reference Manual.
+.SH "COPYRIGHT"
+Copyright \(co 2004\-2008 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook
new file mode 100644
index 0000000..a4a8044
--- /dev/null
+++ b/bin/named/named.conf.docbook
@@ -0,0 +1,629 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id: named.conf.docbook,v 1.39 2008/09/24 02:46:21 marka Exp $ -->
+<refentry>
+ <refentryinfo>
+ <date>Aug 13, 2004</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle><filename>named.conf</filename></refentrytitle>
+ <manvolnum>5</manvolnum>
+ <refmiscinfo>BIND9</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname><filename>named.conf</filename></refname>
+ <refpurpose>configuration file for named</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2006</year>
+ <year>2007</year>
+ <year>2008</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>named.conf</command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+ <para><filename>named.conf</filename> is the configuration file
+ for
+ <command>named</command>. Statements are enclosed
+ in braces and terminated with a semi-colon. Clauses in
+ the statements are also semi-colon terminated. The usual
+ comment styles are supported:
+ </para>
+ <para>
+ C style: /* */
+ </para>
+ <para>
+ C++ style: // to end of line
+ </para>
+ <para>
+ Unix style: # to end of line
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>ACL</title>
+ <literallayout>
+acl <replaceable>string</replaceable> { <replaceable>address_match_element</replaceable>; ... };
+
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>KEY</title>
+ <literallayout>
+key <replaceable>domain_name</replaceable> {
+ algorithm <replaceable>string</replaceable>;
+ secret <replaceable>string</replaceable>;
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>MASTERS</title>
+ <literallayout>
+masters <replaceable>string</replaceable> <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv6_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> ) <optional> key <replaceable>string</replaceable> </optional>; ...
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>SERVER</title>
+ <literallayout>
+server ( <replaceable>ipv4_address<optional>/prefixlen</optional></replaceable> | <replaceable>ipv6_address<optional>/prefixlen</optional></replaceable> ) {
+ bogus <replaceable>boolean</replaceable>;
+ edns <replaceable>boolean</replaceable>;
+ edns-udp-size <replaceable>integer</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ provide-ixfr <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ keys <replaceable>server_key</replaceable>;
+ transfers <replaceable>integer</replaceable>;
+ transfer-format ( many-answers | one-answer );
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+
+ support-ixfr <replaceable>boolean</replaceable>; // obsolete
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>TRUSTED-KEYS</title>
+ <literallayout>
+trusted-keys {
+ <replaceable>domain_name</replaceable> <replaceable>flags</replaceable> <replaceable>protocol</replaceable> <replaceable>algorithm</replaceable> <replaceable>key</replaceable>; ...
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>CONTROLS</title>
+ <literallayout>
+controls {
+ inet ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>
+ allow { <replaceable>address_match_element</replaceable>; ... }
+ <optional> keys { <replaceable>string</replaceable>; ... } </optional>;
+ unix <replaceable>unsupported</replaceable>; // not implemented
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>LOGGING</title>
+ <literallayout>
+logging {
+ channel <replaceable>string</replaceable> {
+ file <replaceable>log_file</replaceable>;
+ syslog <replaceable>optional_facility</replaceable>;
+ null;
+ stderr;
+ severity <replaceable>log_severity</replaceable>;
+ print-time <replaceable>boolean</replaceable>;
+ print-severity <replaceable>boolean</replaceable>;
+ print-category <replaceable>boolean</replaceable>;
+ };
+ category <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>LWRES</title>
+ <literallayout>
+lwres {
+ listen-on <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) <optional> port <replaceable>integer</replaceable> </optional>; ...
+ };
+ view <replaceable>string</replaceable> <replaceable>optional_class</replaceable>;
+ search { <replaceable>string</replaceable>; ... };
+ ndots <replaceable>integer</replaceable>;
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+ <literallayout>
+options {
+ avoid-v4-udp-ports { <replaceable>port</replaceable>; ... };
+ avoid-v6-udp-ports { <replaceable>port</replaceable>; ... };
+ blackhole { <replaceable>address_match_element</replaceable>; ... };
+ coresize <replaceable>size</replaceable>;
+ datasize <replaceable>size</replaceable>;
+ directory <replaceable>quoted_string</replaceable>;
+ dump-file <replaceable>quoted_string</replaceable>;
+ files <replaceable>size</replaceable>;
+ heartbeat-interval <replaceable>integer</replaceable>;
+ host-statistics <replaceable>boolean</replaceable>; // not implemented
+ host-statistics-max <replaceable>number</replaceable>; // not implemented
+ hostname ( <replaceable>quoted_string</replaceable> | none );
+ interface-interval <replaceable>integer</replaceable>;
+ listen-on <optional> port <replaceable>integer</replaceable> </optional> { <replaceable>address_match_element</replaceable>; ... };
+ listen-on-v6 <optional> port <replaceable>integer</replaceable> </optional> { <replaceable>address_match_element</replaceable>; ... };
+ match-mapped-addresses <replaceable>boolean</replaceable>;
+ memstatistics-file <replaceable>quoted_string</replaceable>;
+ pid-file ( <replaceable>quoted_string</replaceable> | none );
+ port <replaceable>integer</replaceable>;
+ querylog <replaceable>boolean</replaceable>;
+ recursing-file <replaceable>quoted_string</replaceable>;
+ reserved-sockets <replaceable>integer</replaceable>;
+ random-device <replaceable>quoted_string</replaceable>;
+ recursive-clients <replaceable>integer</replaceable>;
+ serial-query-rate <replaceable>integer</replaceable>;
+ server-id ( <replaceable>quoted_string</replaceable> | none |;
+ stacksize <replaceable>size</replaceable>;
+ statistics-file <replaceable>quoted_string</replaceable>;
+ statistics-interval <replaceable>integer</replaceable>; // not yet implemented
+ tcp-clients <replaceable>integer</replaceable>;
+ tcp-listen-queue <replaceable>integer</replaceable>;
+ tkey-dhkey <replaceable>quoted_string</replaceable> <replaceable>integer</replaceable>;
+ tkey-gssapi-credential <replaceable>quoted_string</replaceable>;
+ tkey-domain <replaceable>quoted_string</replaceable>;
+ transfers-per-ns <replaceable>integer</replaceable>;
+ transfers-in <replaceable>integer</replaceable>;
+ transfers-out <replaceable>integer</replaceable>;
+ use-ixfr <replaceable>boolean</replaceable>;
+ version ( <replaceable>quoted_string</replaceable> | none );
+ allow-recursion { <replaceable>address_match_element</replaceable>; ... };
+ allow-recursion-on { <replaceable>address_match_element</replaceable>; ... };
+ sortlist { <replaceable>address_match_element</replaceable>; ... };
+ topology { <replaceable>address_match_element</replaceable>; ... }; // not implemented
+ auth-nxdomain <replaceable>boolean</replaceable>; // default changed
+ minimal-responses <replaceable>boolean</replaceable>;
+ recursion <replaceable>boolean</replaceable>;
+ rrset-order {
+ <optional> class <replaceable>string</replaceable> </optional> <optional> type <replaceable>string</replaceable> </optional>
+ <optional> name <replaceable>quoted_string</replaceable> </optional> <replaceable>string</replaceable> <replaceable>string</replaceable>; ...
+ };
+ provide-ixfr <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ rfc2308-type1 <replaceable>boolean</replaceable>; // not yet implemented
+ additional-from-auth <replaceable>boolean</replaceable>;
+ additional-from-cache <replaceable>boolean</replaceable>;
+ query-source ( ( <replaceable>ipv4_address</replaceable> | * ) | <optional> address ( <replaceable>ipv4_address</replaceable> | * ) </optional> ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ query-source-v6 ( ( <replaceable>ipv6_address</replaceable> | * ) | <optional> address ( <replaceable>ipv6_address</replaceable> | * ) </optional> ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ use-queryport-pool <replaceable>boolean</replaceable>;
+ queryport-pool-ports <replaceable>integer</replaceable>;
+ queryport-pool-updateinterval <replaceable>integer</replaceable>;
+ cleaning-interval <replaceable>integer</replaceable>;
+ min-roots <replaceable>integer</replaceable>; // not implemented
+ lame-ttl <replaceable>integer</replaceable>;
+ max-ncache-ttl <replaceable>integer</replaceable>;
+ max-cache-ttl <replaceable>integer</replaceable>;
+ transfer-format ( many-answers | one-answer );
+ max-cache-size <replaceable>size</replaceable>;
+ max-acache-size <replaceable>size</replaceable>;
+ clients-per-query <replaceable>number</replaceable>;
+ max-clients-per-query <replaceable>number</replaceable>;
+ check-names ( master | slave | response )
+ ( fail | warn | ignore );
+ check-mx ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx-cname ( fail | warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ cache-file <replaceable>quoted_string</replaceable>; // test option
+ suppress-initial-notify <replaceable>boolean</replaceable>; // not yet implemented
+ preferred-glue <replaceable>string</replaceable>;
+ dual-stack-servers <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>quoted_string</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv4_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv6_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> ); ...
+ };
+ edns-udp-size <replaceable>integer</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ root-delegation-only <optional> exclude { <replaceable>quoted_string</replaceable>; ... } </optional>;
+ disable-algorithms <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
+ dnssec-enable <replaceable>boolean</replaceable>;
+ dnssec-validation <replaceable>boolean</replaceable>;
+ dnssec-lookaside <replaceable>string</replaceable> trust-anchor <replaceable>string</replaceable>;
+ dnssec-must-be-secure <replaceable>string</replaceable> <replaceable>boolean</replaceable>;
+ dnssec-accept-expired <replaceable>boolean</replaceable>;
+
+ empty-server <replaceable>string</replaceable>;
+ empty-contact <replaceable>string</replaceable>;
+ empty-zones-enable <replaceable>boolean</replaceable>;
+ disable-empty-zone <replaceable>string</replaceable>;
+
+ dialup <replaceable>dialuptype</replaceable>;
+ ixfr-from-differences <replaceable>ixfrdiff</replaceable>;
+
+ allow-query { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-on { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache-on { <replaceable>address_match_element</replaceable>; ... };
+ allow-transfer { <replaceable>address_match_element</replaceable>; ... };
+ allow-update { <replaceable>address_match_element</replaceable>; ... };
+ allow-update-forwarding { <replaceable>address_match_element</replaceable>; ... };
+ update-check-ksk <replaceable>boolean</replaceable>;
+
+ masterfile-format ( text | raw );
+ notify <replaceable>notifytype</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-delay <replaceable>seconds</replaceable>;
+ notify-to-soa <replaceable>boolean</replaceable>;
+ also-notify <optional> port <replaceable>integer</replaceable> </optional> { ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> )
+ <optional> port <replaceable>integer</replaceable> </optional>; ... };
+ allow-notify { <replaceable>address_match_element</replaceable>; ... };
+
+ forward ( first | only );
+ forwarders <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) <optional> port <replaceable>integer</replaceable> </optional>; ...
+ };
+
+ max-journal-size <replaceable>size_no_default</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ min-refresh-time <replaceable>integer</replaceable>;
+ multi-master <replaceable>boolean</replaceable>;
+
+ sig-validity-interval <replaceable>integer</replaceable>;
+ sig-re-signing-interval <replaceable>integer</replaceable>;
+ sig-signing-nodes <replaceable>integer</replaceable>;
+ sig-signing-signatures <replaceable>integer</replaceable>;
+ sig-signing-type <replaceable>integer</replaceable>;
+
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+
+ zone-statistics <replaceable>boolean</replaceable>;
+ key-directory <replaceable>quoted_string</replaceable>;
+ try-tcp-refresh <replaceable>boolean</replaceable>;
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+ zero-no-soa-ttl-cache <replaceable>boolean</replaceable>;
+
+ nsec3-test-zone <replaceable>boolean</replaceable>; // testing only
+
+ allow-v6-synthesis { <replaceable>address_match_element</replaceable>; ... }; // obsolete
+ deallocate-on-exit <replaceable>boolean</replaceable>; // obsolete
+ fake-iquery <replaceable>boolean</replaceable>; // obsolete
+ fetch-glue <replaceable>boolean</replaceable>; // obsolete
+ has-old-clients <replaceable>boolean</replaceable>; // obsolete
+ maintain-ixfr-base <replaceable>boolean</replaceable>; // obsolete
+ max-ixfr-log-size <replaceable>size</replaceable>; // obsolete
+ multiple-cnames <replaceable>boolean</replaceable>; // obsolete
+ named-xfer <replaceable>quoted_string</replaceable>; // obsolete
+ serial-queries <replaceable>integer</replaceable>; // obsolete
+ treat-cr-as-space <replaceable>boolean</replaceable>; // obsolete
+ use-id-pool <replaceable>boolean</replaceable>; // obsolete
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>VIEW</title>
+ <literallayout>
+view <replaceable>string</replaceable> <replaceable>optional_class</replaceable> {
+ match-clients { <replaceable>address_match_element</replaceable>; ... };
+ match-destinations { <replaceable>address_match_element</replaceable>; ... };
+ match-recursive-only <replaceable>boolean</replaceable>;
+
+ key <replaceable>string</replaceable> {
+ algorithm <replaceable>string</replaceable>;
+ secret <replaceable>string</replaceable>;
+ };
+
+ zone <replaceable>string</replaceable> <replaceable>optional_class</replaceable> {
+ ...
+ };
+
+ server ( <replaceable>ipv4_address<optional>/prefixlen</optional></replaceable> | <replaceable>ipv6_address<optional>/prefixlen</optional></replaceable> ) {
+ ...
+ };
+
+ trusted-keys {
+ <replaceable>string</replaceable> <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>quoted_string</replaceable>; ...
+ };
+
+ allow-recursion { <replaceable>address_match_element</replaceable>; ... };
+ allow-recursion-on { <replaceable>address_match_element</replaceable>; ... };
+ sortlist { <replaceable>address_match_element</replaceable>; ... };
+ topology { <replaceable>address_match_element</replaceable>; ... }; // not implemented
+ auth-nxdomain <replaceable>boolean</replaceable>; // default changed
+ minimal-responses <replaceable>boolean</replaceable>;
+ recursion <replaceable>boolean</replaceable>;
+ rrset-order {
+ <optional> class <replaceable>string</replaceable> </optional> <optional> type <replaceable>string</replaceable> </optional>
+ <optional> name <replaceable>quoted_string</replaceable> </optional> <replaceable>string</replaceable> <replaceable>string</replaceable>; ...
+ };
+ provide-ixfr <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ rfc2308-type1 <replaceable>boolean</replaceable>; // not yet implemented
+ additional-from-auth <replaceable>boolean</replaceable>;
+ additional-from-cache <replaceable>boolean</replaceable>;
+ query-source ( ( <replaceable>ipv4_address</replaceable> | * ) | <optional> address ( <replaceable>ipv4_address</replaceable> | * ) </optional> ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ query-source-v6 ( ( <replaceable>ipv6_address</replaceable> | * ) | <optional> address ( <replaceable>ipv6_address</replaceable> | * ) </optional> ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ use-queryport-pool <replaceable>boolean</replaceable>;
+ queryport-pool-ports <replaceable>integer</replaceable>;
+ queryport-pool-updateinterval <replaceable>integer</replaceable>;
+ cleaning-interval <replaceable>integer</replaceable>;
+ min-roots <replaceable>integer</replaceable>; // not implemented
+ lame-ttl <replaceable>integer</replaceable>;
+ max-ncache-ttl <replaceable>integer</replaceable>;
+ max-cache-ttl <replaceable>integer</replaceable>;
+ transfer-format ( many-answers | one-answer );
+ max-cache-size <replaceable>size</replaceable>;
+ max-acache-size <replaceable>size</replaceable>;
+ clients-per-query <replaceable>number</replaceable>;
+ max-clients-per-query <replaceable>number</replaceable>;
+ check-names ( master | slave | response )
+ ( fail | warn | ignore );
+ check-mx ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx-cname ( fail | warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ cache-file <replaceable>quoted_string</replaceable>; // test option
+ suppress-initial-notify <replaceable>boolean</replaceable>; // not yet implemented
+ preferred-glue <replaceable>string</replaceable>;
+ dual-stack-servers <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>quoted_string</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv4_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv6_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> ); ...
+ };
+ edns-udp-size <replaceable>integer</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ root-delegation-only <optional> exclude { <replaceable>quoted_string</replaceable>; ... } </optional>;
+ disable-algorithms <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
+ dnssec-enable <replaceable>boolean</replaceable>;
+ dnssec-validation <replaceable>boolean</replaceable>;
+ dnssec-lookaside <replaceable>string</replaceable> trust-anchor <replaceable>string</replaceable>;
+ dnssec-must-be-secure <replaceable>string</replaceable> <replaceable>boolean</replaceable>;
+ dnssec-accept-expired <replaceable>boolean</replaceable>;
+
+ empty-server <replaceable>string</replaceable>;
+ empty-contact <replaceable>string</replaceable>;
+ empty-zones-enable <replaceable>boolean</replaceable>;
+ disable-empty-zone <replaceable>string</replaceable>;
+
+ dialup <replaceable>dialuptype</replaceable>;
+ ixfr-from-differences <replaceable>ixfrdiff</replaceable>;
+
+ allow-query { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-on { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache-on { <replaceable>address_match_element</replaceable>; ... };
+ allow-transfer { <replaceable>address_match_element</replaceable>; ... };
+ allow-update { <replaceable>address_match_element</replaceable>; ... };
+ allow-update-forwarding { <replaceable>address_match_element</replaceable>; ... };
+ update-check-ksk <replaceable>boolean</replaceable>;
+
+ masterfile-format ( text | raw );
+ notify <replaceable>notifytype</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-delay <replaceable>seconds</replaceable>;
+ notify-to-soa <replaceable>boolean</replaceable>;
+ also-notify <optional> port <replaceable>integer</replaceable> </optional> { ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> )
+ <optional> port <replaceable>integer</replaceable> </optional>; ... };
+ allow-notify { <replaceable>address_match_element</replaceable>; ... };
+
+ forward ( first | only );
+ forwarders <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) <optional> port <replaceable>integer</replaceable> </optional>; ...
+ };
+
+ max-journal-size <replaceable>size_no_default</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ min-refresh-time <replaceable>integer</replaceable>;
+ multi-master <replaceable>boolean</replaceable>;
+ sig-validity-interval <replaceable>integer</replaceable>;
+
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+
+ zone-statistics <replaceable>boolean</replaceable>;
+ try-tcp-refresh <replaceable>boolean</replaceable>;
+ key-directory <replaceable>quoted_string</replaceable>;
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+ zero-no-soa-ttl-cache <replaceable>boolean</replaceable>;
+
+ allow-v6-synthesis { <replaceable>address_match_element</replaceable>; ... }; // obsolete
+ fetch-glue <replaceable>boolean</replaceable>; // obsolete
+ maintain-ixfr-base <replaceable>boolean</replaceable>; // obsolete
+ max-ixfr-log-size <replaceable>size</replaceable>; // obsolete
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>ZONE</title>
+ <literallayout>
+zone <replaceable>string</replaceable> <replaceable>optional_class</replaceable> {
+ type ( master | slave | stub | hint |
+ forward | delegation-only );
+ file <replaceable>quoted_string</replaceable>;
+
+ masters <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>masters</replaceable> |
+ <replaceable>ipv4_address</replaceable> <optional>port <replaceable>integer</replaceable></optional> |
+ <replaceable>ipv6_address</replaceable> <optional> port <replaceable>integer</replaceable> </optional> ) <optional> key <replaceable>string</replaceable> </optional>; ...
+ };
+
+ database <replaceable>string</replaceable>;
+ delegation-only <replaceable>boolean</replaceable>;
+ check-names ( fail | warn | ignore );
+ check-mx ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx-cname ( fail | warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ dialup <replaceable>dialuptype</replaceable>;
+ ixfr-from-differences <replaceable>boolean</replaceable>;
+ journal <replaceable>quoted_string</replaceable>;
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+
+ allow-query { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-on { <replaceable>address_match_element</replaceable>; ... };
+ allow-transfer { <replaceable>address_match_element</replaceable>; ... };
+ allow-update { <replaceable>address_match_element</replaceable>; ... };
+ allow-update-forwarding { <replaceable>address_match_element</replaceable>; ... };
+ update-policy {
+ ( grant | deny ) <replaceable>string</replaceable>
+ ( name | subdomain | wildcard | self | selfsub | selfwild |
+ krb5-self | ms-self | krb5-subdomain | ms-subdomain |
+ tcp-self | 6to4-self ) <replaceable>string</replaceable>
+ <replaceable>rrtypelist</replaceable>; ...
+ };
+ update-check-ksk <replaceable>boolean</replaceable>;
+
+ masterfile-format ( text | raw );
+ notify <replaceable>notifytype</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ notify-delay <replaceable>seconds</replaceable>;
+ notify-to-soa <replaceable>boolean</replaceable>;
+ also-notify <optional> port <replaceable>integer</replaceable> </optional> { ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> )
+ <optional> port <replaceable>integer</replaceable> </optional>; ... };
+ allow-notify { <replaceable>address_match_element</replaceable>; ... };
+
+ forward ( first | only );
+ forwarders <optional> port <replaceable>integer</replaceable> </optional> {
+ ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) <optional> port <replaceable>integer</replaceable> </optional>; ...
+ };
+
+ max-journal-size <replaceable>size_no_default</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ min-refresh-time <replaceable>integer</replaceable>;
+ multi-master <replaceable>boolean</replaceable>;
+ sig-validity-interval <replaceable>integer</replaceable>;
+
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * )
+ <optional> port ( <replaceable>integer</replaceable> | * ) </optional>;
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+
+ zone-statistics <replaceable>boolean</replaceable>;
+ try-tcp-refresh <replaceable>boolean</replaceable>;
+ key-directory <replaceable>quoted_string</replaceable>;
+
+ nsec3-test-zone <replaceable>boolean</replaceable>; // testing only
+
+ ixfr-base <replaceable>quoted_string</replaceable>; // obsolete
+ ixfr-tmp-file <replaceable>quoted_string</replaceable>; // obsolete
+ maintain-ixfr-base <replaceable>boolean</replaceable>; // obsolete
+ max-ixfr-log-size <replaceable>size</replaceable>; // obsolete
+ pubkey <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>quoted_string</replaceable>; // obsolete
+};
+</literallayout>
+ </refsect1>
+
+ <refsect1>
+ <title>FILES</title>
+ <para><filename>/etc/named.conf</filename>
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para><citerefentry>
+ <refentrytitle>named</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>named-checkconf</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>rndc</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citetitle>BIND 9 Administrator Reference Manual</citetitle>.
+ </para>
+ </refsect1>
+
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/bin/named/named.conf.html b/bin/named/named.conf.html
new file mode 100644
index 0000000..7bbbd0a
--- /dev/null
+++ b/bin/named/named.conf.html
@@ -0,0 +1,585 @@
+<!--
+ - Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - Permission to use, copy, modify, and distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+<!-- $Id: named.conf.html,v 1.45 2008/09/25 04:45:04 tbox Exp $ -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>named.conf</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.71.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry" lang="en">
+<a name="id2476275"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><code class="filename">named.conf</code> &#8212; configuration file for named</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">named.conf</code> </p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543342"></a><h2>DESCRIPTION</h2>
+<p><code class="filename">named.conf</code> is the configuration file
+ for
+ <span><strong class="command">named</strong></span>. Statements are enclosed
+ in braces and terminated with a semi-colon. Clauses in
+ the statements are also semi-colon terminated. The usual
+ comment styles are supported:
+ </p>
+<p>
+ C style: /* */
+ </p>
+<p>
+ C++ style: // to end of line
+ </p>
+<p>
+ Unix style: # to end of line
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543370"></a><h2>ACL</h2>
+<div class="literallayout"><p><br>
+acl <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543386"></a><h2>KEY</h2>
+<div class="literallayout"><p><br>
+key <em class="replaceable"><code>domain_name</code></em> {<br>
+ algorithm <em class="replaceable"><code>string</code></em>;<br>
+ secret <em class="replaceable"><code>string</code></em>;<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543405"></a><h2>MASTERS</h2>
+<div class="literallayout"><p><br>
+masters <em class="replaceable"><code>string</code></em> [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>masters</code></em> | <em class="replaceable"><code>ipv4_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv6_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] ) [<span class="optional"> key <em class="replaceable"><code>string</code></em> </span>]; ...<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543451"></a><h2>SERVER</h2>
+<div class="literallayout"><p><br>
+server ( <em class="replaceable"><code>ipv4_address[<span class="optional">/prefixlen</span>]</code></em> | <em class="replaceable"><code>ipv6_address[<span class="optional">/prefixlen</span>]</code></em> ) {<br>
+ bogus <em class="replaceable"><code>boolean</code></em>;<br>
+ edns <em class="replaceable"><code>boolean</code></em>;<br>
+ edns-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ keys <em class="replaceable"><code>server_key</code></em>;<br>
+ transfers <em class="replaceable"><code>integer</code></em>;<br>
+ transfer-format ( many-answers | one-answer );<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+<br>
+ support-ixfr <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543520"></a><h2>TRUSTED-KEYS</h2>
+<div class="literallayout"><p><br>
+trusted-keys {<br>
+ <em class="replaceable"><code>domain_name</code></em> <em class="replaceable"><code>flags</code></em> <em class="replaceable"><code>protocol</code></em> <em class="replaceable"><code>algorithm</code></em> <em class="replaceable"><code>key</code></em>; ... <br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543545"></a><h2>CONTROLS</h2>
+<div class="literallayout"><p><br>
+controls {<br>
+ inet ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>]<br>
+ allow { <em class="replaceable"><code>address_match_element</code></em>; ... }<br>
+ [<span class="optional"> keys { <em class="replaceable"><code>string</code></em>; ... } </span>];<br>
+ unix <em class="replaceable"><code>unsupported</code></em>; // not implemented<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543580"></a><h2>LOGGING</h2>
+<div class="literallayout"><p><br>
+logging {<br>
+ channel <em class="replaceable"><code>string</code></em> {<br>
+ file <em class="replaceable"><code>log_file</code></em>;<br>
+ syslog <em class="replaceable"><code>optional_facility</code></em>;<br>
+ null;<br>
+ stderr;<br>
+ severity <em class="replaceable"><code>log_severity</code></em>;<br>
+ print-time <em class="replaceable"><code>boolean</code></em>;<br>
+ print-severity <em class="replaceable"><code>boolean</code></em>;<br>
+ print-category <em class="replaceable"><code>boolean</code></em>;<br>
+ };<br>
+ category <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>; ... };<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543619"></a><h2>LWRES</h2>
+<div class="literallayout"><p><br>
+lwres {<br>
+ listen-on [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ...<br>
+ };<br>
+ view <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>optional_class</code></em>;<br>
+ search { <em class="replaceable"><code>string</code></em>; ... };<br>
+ ndots <em class="replaceable"><code>integer</code></em>;<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543660"></a><h2>OPTIONS</h2>
+<div class="literallayout"><p><br>
+options {<br>
+ avoid-v4-udp-ports { <em class="replaceable"><code>port</code></em>; ... };<br>
+ avoid-v6-udp-ports { <em class="replaceable"><code>port</code></em>; ... };<br>
+ blackhole { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ coresize <em class="replaceable"><code>size</code></em>;<br>
+ datasize <em class="replaceable"><code>size</code></em>;<br>
+ directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ dump-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ files <em class="replaceable"><code>size</code></em>;<br>
+ heartbeat-interval <em class="replaceable"><code>integer</code></em>;<br>
+ host-statistics <em class="replaceable"><code>boolean</code></em>; // not implemented<br>
+ host-statistics-max <em class="replaceable"><code>number</code></em>; // not implemented<br>
+ hostname ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ interface-interval <em class="replaceable"><code>integer</code></em>;<br>
+ listen-on [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ listen-on-v6 [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ match-mapped-addresses <em class="replaceable"><code>boolean</code></em>;<br>
+ memstatistics-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ pid-file ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ port <em class="replaceable"><code>integer</code></em>;<br>
+ querylog <em class="replaceable"><code>boolean</code></em>;<br>
+ recursing-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ reserved-sockets <em class="replaceable"><code>integer</code></em>;<br>
+ random-device <em class="replaceable"><code>quoted_string</code></em>;<br>
+ recursive-clients <em class="replaceable"><code>integer</code></em>;<br>
+ serial-query-rate <em class="replaceable"><code>integer</code></em>;<br>
+ server-id ( <em class="replaceable"><code>quoted_string</code></em> | none |;<br>
+ stacksize <em class="replaceable"><code>size</code></em>;<br>
+ statistics-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ statistics-interval <em class="replaceable"><code>integer</code></em>; // not yet implemented<br>
+ tcp-clients <em class="replaceable"><code>integer</code></em>;<br>
+ tcp-listen-queue <em class="replaceable"><code>integer</code></em>;<br>
+ tkey-dhkey <em class="replaceable"><code>quoted_string</code></em> <em class="replaceable"><code>integer</code></em>;<br>
+ tkey-gssapi-credential <em class="replaceable"><code>quoted_string</code></em>;<br>
+ tkey-domain <em class="replaceable"><code>quoted_string</code></em>;<br>
+ transfers-per-ns <em class="replaceable"><code>integer</code></em>;<br>
+ transfers-in <em class="replaceable"><code>integer</code></em>;<br>
+ transfers-out <em class="replaceable"><code>integer</code></em>;<br>
+ use-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ version ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ allow-recursion { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-recursion-on { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ sortlist { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ topology { <em class="replaceable"><code>address_match_element</code></em>; ... }; // not implemented<br>
+ auth-nxdomain <em class="replaceable"><code>boolean</code></em>; // default changed<br>
+ minimal-responses <em class="replaceable"><code>boolean</code></em>;<br>
+ recursion <em class="replaceable"><code>boolean</code></em>;<br>
+ rrset-order {<br>
+ [<span class="optional"> class <em class="replaceable"><code>string</code></em> </span>] [<span class="optional"> type <em class="replaceable"><code>string</code></em> </span>]<br>
+ [<span class="optional"> name <em class="replaceable"><code>quoted_string</code></em> </span>] <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>string</code></em>; ...<br>
+ };<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ rfc2308-type1 <em class="replaceable"><code>boolean</code></em>; // not yet implemented<br>
+ additional-from-auth <em class="replaceable"><code>boolean</code></em>;<br>
+ additional-from-cache <em class="replaceable"><code>boolean</code></em>;<br>
+ query-source ( ( <em class="replaceable"><code>ipv4_address</code></em> | * ) | [<span class="optional"> address ( <em class="replaceable"><code>ipv4_address</code></em> | * ) </span>] ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ query-source-v6 ( ( <em class="replaceable"><code>ipv6_address</code></em> | * ) | [<span class="optional"> address ( <em class="replaceable"><code>ipv6_address</code></em> | * ) </span>] ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ use-queryport-pool <em class="replaceable"><code>boolean</code></em>;<br>
+ queryport-pool-ports <em class="replaceable"><code>integer</code></em>;<br>
+ queryport-pool-updateinterval <em class="replaceable"><code>integer</code></em>;<br>
+ cleaning-interval <em class="replaceable"><code>integer</code></em>;<br>
+ min-roots <em class="replaceable"><code>integer</code></em>; // not implemented<br>
+ lame-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-ncache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-cache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ transfer-format ( many-answers | one-answer );<br>
+ max-cache-size <em class="replaceable"><code>size</code></em>;<br>
+ max-acache-size <em class="replaceable"><code>size</code></em>;<br>
+ clients-per-query <em class="replaceable"><code>number</code></em>;<br>
+ max-clients-per-query <em class="replaceable"><code>number</code></em>;<br>
+ check-names ( master | slave | response )<br>
+ ( fail | warn | ignore );<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ cache-file <em class="replaceable"><code>quoted_string</code></em>; // test option<br>
+ suppress-initial-notify <em class="replaceable"><code>boolean</code></em>; // not yet implemented<br>
+ preferred-glue <em class="replaceable"><code>string</code></em>;<br>
+ dual-stack-servers [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>quoted_string</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv4_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv6_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] ); ...<br>
+ };<br>
+ edns-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ root-delegation-only [<span class="optional"> exclude { <em class="replaceable"><code>quoted_string</code></em>; ... } </span>];<br>
+ disable-algorithms <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>; ... };<br>
+ dnssec-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-validation <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-lookaside <em class="replaceable"><code>string</code></em> trust-anchor <em class="replaceable"><code>string</code></em>;<br>
+ dnssec-must-be-secure <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-accept-expired <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ empty-server <em class="replaceable"><code>string</code></em>;<br>
+ empty-contact <em class="replaceable"><code>string</code></em>;<br>
+ empty-zones-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ disable-empty-zone <em class="replaceable"><code>string</code></em>;<br>
+<br>
+ dialup <em class="replaceable"><code>dialuptype</code></em>;<br>
+ ixfr-from-differences <em class="replaceable"><code>ixfrdiff</code></em>;<br>
+<br>
+ allow-query { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query-on { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query-cache { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query-cache-on { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-transfer { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update-forwarding { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ masterfile-format ( text | raw );<br>
+ notify <em class="replaceable"><code>notifytype</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-delay <em class="replaceable"><code>seconds</code></em>;<br>
+ notify-to-soa <em class="replaceable"><code>boolean</code></em>;<br>
+ also-notify [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> )<br>
+ [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ... };<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+<br>
+ forward ( first | only );<br>
+ forwarders [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ...<br>
+ };<br>
+<br>
+ max-journal-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ max-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em>;<br>
+ sig-re-signing-interval <em class="replaceable"><code>integer</code></em>;<br>
+ sig-signing-nodes <em class="replaceable"><code>integer</code></em>;<br>
+ sig-signing-signatures <em class="replaceable"><code>integer</code></em>;<br>
+ sig-signing-type <em class="replaceable"><code>integer</code></em>;<br>
+<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ zone-statistics <em class="replaceable"><code>boolean</code></em>;<br>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ try-tcp-refresh <em class="replaceable"><code>boolean</code></em>;<br>
+ zero-no-soa-ttl <em class="replaceable"><code>boolean</code></em>;<br>
+ zero-no-soa-ttl-cache <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ nsec3-test-zone <em class="replaceable"><code>boolean</code></em>;  // testing only<br>
+<br>
+ allow-v6-synthesis { <em class="replaceable"><code>address_match_element</code></em>; ... }; // obsolete<br>
+ deallocate-on-exit <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ fake-iquery <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ fetch-glue <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ has-old-clients <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ maintain-ixfr-base <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ max-ixfr-log-size <em class="replaceable"><code>size</code></em>; // obsolete<br>
+ multiple-cnames <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ named-xfer <em class="replaceable"><code>quoted_string</code></em>; // obsolete<br>
+ serial-queries <em class="replaceable"><code>integer</code></em>; // obsolete<br>
+ treat-cr-as-space <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ use-id-pool <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2544452"></a><h2>VIEW</h2>
+<div class="literallayout"><p><br>
+view <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>optional_class</code></em> {<br>
+ match-clients { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ match-destinations { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ match-recursive-only <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ key <em class="replaceable"><code>string</code></em> {<br>
+ algorithm <em class="replaceable"><code>string</code></em>;<br>
+ secret <em class="replaceable"><code>string</code></em>;<br>
+ };<br>
+<br>
+ zone <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>optional_class</code></em> {<br>
+ ...<br>
+ };<br>
+<br>
+ server ( <em class="replaceable"><code>ipv4_address[<span class="optional">/prefixlen</span>]</code></em> | <em class="replaceable"><code>ipv6_address[<span class="optional">/prefixlen</span>]</code></em> ) {<br>
+ ...<br>
+ };<br>
+<br>
+ trusted-keys {<br>
+ <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>quoted_string</code></em>; ...<br>
+ };<br>
+<br>
+ allow-recursion { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-recursion-on { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ sortlist { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ topology { <em class="replaceable"><code>address_match_element</code></em>; ... }; // not implemented<br>
+ auth-nxdomain <em class="replaceable"><code>boolean</code></em>; // default changed<br>
+ minimal-responses <em class="replaceable"><code>boolean</code></em>;<br>
+ recursion <em class="replaceable"><code>boolean</code></em>;<br>
+ rrset-order {<br>
+ [<span class="optional"> class <em class="replaceable"><code>string</code></em> </span>] [<span class="optional"> type <em class="replaceable"><code>string</code></em> </span>]<br>
+ [<span class="optional"> name <em class="replaceable"><code>quoted_string</code></em> </span>] <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>string</code></em>; ...<br>
+ };<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ rfc2308-type1 <em class="replaceable"><code>boolean</code></em>; // not yet implemented<br>
+ additional-from-auth <em class="replaceable"><code>boolean</code></em>;<br>
+ additional-from-cache <em class="replaceable"><code>boolean</code></em>;<br>
+ query-source ( ( <em class="replaceable"><code>ipv4_address</code></em> | * ) | [<span class="optional"> address ( <em class="replaceable"><code>ipv4_address</code></em> | * ) </span>] ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ query-source-v6 ( ( <em class="replaceable"><code>ipv6_address</code></em> | * ) | [<span class="optional"> address ( <em class="replaceable"><code>ipv6_address</code></em> | * ) </span>] ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ use-queryport-pool <em class="replaceable"><code>boolean</code></em>;<br>
+ queryport-pool-ports <em class="replaceable"><code>integer</code></em>;<br>
+ queryport-pool-updateinterval <em class="replaceable"><code>integer</code></em>;<br>
+ cleaning-interval <em class="replaceable"><code>integer</code></em>;<br>
+ min-roots <em class="replaceable"><code>integer</code></em>; // not implemented<br>
+ lame-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-ncache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-cache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ transfer-format ( many-answers | one-answer );<br>
+ max-cache-size <em class="replaceable"><code>size</code></em>;<br>
+ max-acache-size <em class="replaceable"><code>size</code></em>;<br>
+ clients-per-query <em class="replaceable"><code>number</code></em>;<br>
+ max-clients-per-query <em class="replaceable"><code>number</code></em>;<br>
+ check-names ( master | slave | response )<br>
+ ( fail | warn | ignore );<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ cache-file <em class="replaceable"><code>quoted_string</code></em>; // test option<br>
+ suppress-initial-notify <em class="replaceable"><code>boolean</code></em>; // not yet implemented<br>
+ preferred-glue <em class="replaceable"><code>string</code></em>;<br>
+ dual-stack-servers [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>quoted_string</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv4_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv6_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] ); ...<br>
+ };<br>
+ edns-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ root-delegation-only [<span class="optional"> exclude { <em class="replaceable"><code>quoted_string</code></em>; ... } </span>];<br>
+ disable-algorithms <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>; ... };<br>
+ dnssec-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-validation <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-lookaside <em class="replaceable"><code>string</code></em> trust-anchor <em class="replaceable"><code>string</code></em>;<br>
+ dnssec-must-be-secure <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-accept-expired <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ empty-server <em class="replaceable"><code>string</code></em>;<br>
+ empty-contact <em class="replaceable"><code>string</code></em>;<br>
+ empty-zones-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ disable-empty-zone <em class="replaceable"><code>string</code></em>;<br>
+<br>
+ dialup <em class="replaceable"><code>dialuptype</code></em>;<br>
+ ixfr-from-differences <em class="replaceable"><code>ixfrdiff</code></em>;<br>
+<br>
+ allow-query { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query-on { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query-cache { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query-cache-on { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-transfer { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update-forwarding { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ masterfile-format ( text | raw );<br>
+ notify <em class="replaceable"><code>notifytype</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-delay <em class="replaceable"><code>seconds</code></em>;<br>
+ notify-to-soa <em class="replaceable"><code>boolean</code></em>;<br>
+ also-notify [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> )<br>
+ [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ... };<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+<br>
+ forward ( first | only );<br>
+ forwarders [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ...<br>
+ };<br>
+<br>
+ max-journal-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ max-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em>;<br>
+<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ zone-statistics <em class="replaceable"><code>boolean</code></em>;<br>
+ try-tcp-refresh <em class="replaceable"><code>boolean</code></em>;<br>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ zero-no-soa-ttl <em class="replaceable"><code>boolean</code></em>;<br>
+ zero-no-soa-ttl-cache <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ allow-v6-synthesis { <em class="replaceable"><code>address_match_element</code></em>; ... }; // obsolete<br>
+ fetch-glue <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ maintain-ixfr-base <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ max-ixfr-log-size <em class="replaceable"><code>size</code></em>; // obsolete<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2545113"></a><h2>ZONE</h2>
+<div class="literallayout"><p><br>
+zone <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>optional_class</code></em> {<br>
+ type ( master | slave | stub | hint |<br>
+ forward | delegation-only );<br>
+ file <em class="replaceable"><code>quoted_string</code></em>;<br>
+<br>
+ masters [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>masters</code></em> |<br>
+ <em class="replaceable"><code>ipv4_address</code></em> [<span class="optional">port <em class="replaceable"><code>integer</code></em></span>] |<br>
+ <em class="replaceable"><code>ipv6_address</code></em> [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] ) [<span class="optional"> key <em class="replaceable"><code>string</code></em> </span>]; ...<br>
+ };<br>
+<br>
+ database <em class="replaceable"><code>string</code></em>;<br>
+ delegation-only <em class="replaceable"><code>boolean</code></em>;<br>
+ check-names ( fail | warn | ignore );<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ dialup <em class="replaceable"><code>dialuptype</code></em>;<br>
+ ixfr-from-differences <em class="replaceable"><code>boolean</code></em>;<br>
+ journal <em class="replaceable"><code>quoted_string</code></em>;<br>
+ zero-no-soa-ttl <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ allow-query { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query-on { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-transfer { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-update-forwarding { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ update-policy {<br>
+ ( grant | deny ) <em class="replaceable"><code>string</code></em><br>
+ ( name | subdomain | wildcard | self | selfsub | selfwild |<br>
+                  krb5-self | ms-self | krb5-subdomain | ms-subdomain |<br>
+   tcp-self | 6to4-self ) <em class="replaceable"><code>string</code></em><br>
+ <em class="replaceable"><code>rrtypelist</code></em>; ...<br>
+ };<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ masterfile-format ( text | raw );<br>
+ notify <em class="replaceable"><code>notifytype</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ notify-delay <em class="replaceable"><code>seconds</code></em>;<br>
+ notify-to-soa <em class="replaceable"><code>boolean</code></em>;<br>
+ also-notify [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] { ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> )<br>
+ [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ... };<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+<br>
+ forward ( first | only );<br>
+ forwarders [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>] {<br>
+ ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [<span class="optional"> port <em class="replaceable"><code>integer</code></em> </span>]; ...<br>
+ };<br>
+<br>
+ max-journal-size <em class="replaceable"><code>size_no_default</code></em>;<br>
+ max-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-idle-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em>;<br>
+<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * )<br>
+ [<span class="optional"> port ( <em class="replaceable"><code>integer</code></em> | * ) </span>];<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+<br>
+ zone-statistics <em class="replaceable"><code>boolean</code></em>;<br>
+ try-tcp-refresh <em class="replaceable"><code>boolean</code></em>;<br>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+<br>
+ nsec3-test-zone <em class="replaceable"><code>boolean</code></em>;  // testing only<br>
+<br>
+ ixfr-base <em class="replaceable"><code>quoted_string</code></em>; // obsolete<br>
+ ixfr-tmp-file <em class="replaceable"><code>quoted_string</code></em>; // obsolete<br>
+ maintain-ixfr-base <em class="replaceable"><code>boolean</code></em>; // obsolete<br>
+ max-ixfr-log-size <em class="replaceable"><code>size</code></em>; // obsolete<br>
+ pubkey <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>quoted_string</code></em>; // obsolete<br>
+};<br>
+</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2545410"></a><h2>FILES</h2>
+<p><code class="filename">/etc/named.conf</code>
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2545421"></a><h2>SEE ALSO</h2>
+<p><span class="citerefentry"><span class="refentrytitle">named</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">named-checkconf</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">rndc</span>(8)</span>,
+ <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+ </p>
+</div>
+</div></body>
+</html>
diff --git a/bin/named/named.docbook b/bin/named/named.docbook
new file mode 100644
index 0000000..f47eae1
--- /dev/null
+++ b/bin/named/named.docbook
@@ -0,0 +1,445 @@
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+ [<!ENTITY mdash "&#8212;">]>
+<!--
+ - Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+ -
+ - Permission to use, copy, modify, and/or distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+
+<!-- $Id: named.docbook,v 1.23 2008/11/06 05:30:24 marka Exp $ -->
+<refentry id="man.named">
+ <refentryinfo>
+ <date>June 30, 2000</date>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle><application>named</application></refentrytitle>
+ <manvolnum>8</manvolnum>
+ <refmiscinfo>BIND9</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+ <refname><application>named</application></refname>
+ <refpurpose>Internet domain name server</refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2006</year>
+ <year>2007</year>
+ <year>2008</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ <copyright>
+ <year>2000</year>
+ <year>2001</year>
+ <year>2003</year>
+ <holder>Internet Software Consortium.</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>named</command>
+ <arg><option>-4</option></arg>
+ <arg><option>-6</option></arg>
+ <arg><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
+ <arg><option>-d <replaceable class="parameter">debug-level</replaceable></option></arg>
+ <arg><option>-f</option></arg>
+ <arg><option>-g</option></arg>
+ <arg><option>-m <replaceable class="parameter">flag</replaceable></option></arg>
+ <arg><option>-n <replaceable class="parameter">#cpus</replaceable></option></arg>
+ <arg><option>-p <replaceable class="parameter">port</replaceable></option></arg>
+ <arg><option>-s</option></arg>
+ <arg><option>-S <replaceable class="parameter">#max-socks</replaceable></option></arg>
+ <arg><option>-t <replaceable class="parameter">directory</replaceable></option></arg>
+ <arg><option>-u <replaceable class="parameter">user</replaceable></option></arg>
+ <arg><option>-v</option></arg>
+ <arg><option>-V</option></arg>
+ <arg><option>-x <replaceable class="parameter">cache-file</replaceable></option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>DESCRIPTION</title>
+ <para><command>named</command>
+ is a Domain Name System (DNS) server,
+ part of the BIND 9 distribution from ISC. For more
+ information on the DNS, see RFCs 1033, 1034, and 1035.
+ </para>
+ <para>
+ When invoked without arguments, <command>named</command>
+ will
+ read the default configuration file
+ <filename>/etc/named.conf</filename>, read any initial
+ data, and listen for queries.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>OPTIONS</title>
+
+ <variablelist>
+ <varlistentry>
+ <term>-4</term>
+ <listitem>
+ <para>
+ Use IPv4 only even if the host machine is capable of IPv6.
+ <option>-4</option> and <option>-6</option> are mutually
+ exclusive.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-6</term>
+ <listitem>
+ <para>
+ Use IPv6 only even if the host machine is capable of IPv4.
+ <option>-4</option> and <option>-6</option> are mutually
+ exclusive.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>-c <replaceable class="parameter">config-file</replaceable></term>
+ <listitem>
+ <para>
+ Use <replaceable class="parameter">config-file</replaceable> as the
+ configuration file instead of the default,
+ <filename>/etc/named.conf</filename>. To
+ ensure that reloading the configuration file continues
+ to work after the server has changed its working
+ directory due to to a possible
+ <option>directory</option> option in the configuration
+ file, <replaceable class="parameter">config-file</replaceable> should be
+ an absolute pathname.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-d <replaceable class="parameter">debug-level</replaceable></term>
+ <listitem>
+ <para>
+ Set the daemon's debug level to <replaceable class="parameter">debug-level</replaceable>.
+ Debugging traces from <command>named</command> become
+ more verbose as the debug level increases.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-f</term>
+ <listitem>
+ <para>
+ Run the server in the foreground (i.e. do not daemonize).
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-g</term>
+ <listitem>
+ <para>
+ Run the server in the foreground and force all logging
+ to <filename>stderr</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-m <replaceable class="parameter">flag</replaceable></term>
+ <listitem>
+ <para>
+ Turn on memory usage debugging flags. Possible flags are
+ <replaceable class="parameter">usage</replaceable>,
+ <replaceable class="parameter">trace</replaceable>,
+ <replaceable class="parameter">record</replaceable>,
+ <replaceable class="parameter">size</replaceable>, and
+ <replaceable class="parameter">mctx</replaceable>.
+ These correspond to the ISC_MEM_DEBUGXXXX flags described in
+ <filename>&lt;isc/mem.h&gt;</filename>.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-n <replaceable class="parameter">#cpus</replaceable></term>
+ <listitem>
+ <para>
+ Create <replaceable class="parameter">#cpus</replaceable> worker threads
+ to take advantage of multiple CPUs. If not specified,
+ <command>named</command> will try to determine the
+ number of CPUs present and create one thread per CPU.
+ If it is unable to determine the number of CPUs, a
+ single worker thread will be created.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-p <replaceable class="parameter">port</replaceable></term>
+ <listitem>
+ <para>
+ Listen for queries on port <replaceable class="parameter">port</replaceable>. If not
+ specified, the default is port 53.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-s</term>
+ <listitem>
+ <para>
+ Write memory usage statistics to <filename>stdout</filename> on exit.
+ </para>
+ <note>
+ <para>
+ This option is mainly of interest to BIND 9 developers
+ and may be removed or changed in a future release.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-S <replaceable class="parameter">#max-socks</replaceable></term>
+ <listitem>
+ <para>
+ Allow <command>named</command> to use up to
+ <replaceable class="parameter">#max-socks</replaceable> sockets.
+ </para>
+ <warning>
+ <para>
+ This option should be unnecessary for the vast majority
+ of users.
+ The use of this option could even be harmful because the
+ specified value may exceed the limitation of the
+ underlying system API.
+ It is therefore set only when the default configuration
+ causes exhaustion of file descriptors and the
+ operational environment is known to support the
+ specified number of sockets.
+ Note also that the actual maximum number is normally a little
+ fewer than the specified value because
+ <command>named</command> reserves some file descriptors
+ for its internal use.
+ </para>
+ </warning>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-t <replaceable class="parameter">directory</replaceable></term>
+ <listitem>
+ <para>Chroot
+ to <replaceable class="parameter">directory</replaceable> after
+ processing the command line arguments, but before
+ reading the configuration file.
+ </para>
+ <warning>
+ <para>
+ This option should be used in conjunction with the
+ <option>-u</option> option, as chrooting a process
+ running as root doesn't enhance security on most
+ systems; the way <function>chroot(2)</function> is
+ defined allows a process with root privileges to
+ escape a chroot jail.
+ </para>
+ </warning>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-u <replaceable class="parameter">user</replaceable></term>
+ <listitem>
+ <para>Setuid
+ to <replaceable class="parameter">user</replaceable> after completing
+ privileged operations, such as creating sockets that
+ listen on privileged ports.
+ </para>
+ <note>
+ <para>
+ On Linux, <command>named</command> uses the kernel's
+ capability mechanism to drop all root privileges
+ except the ability to <function>bind(2)</function> to
+ a
+ privileged port and set process resource limits.
+ Unfortunately, this means that the <option>-u</option>
+ option only works when <command>named</command> is
+ run
+ on kernel 2.2.18 or later, or kernel 2.3.99-pre3 or
+ later, since previous kernels did not allow privileges
+ to be retained after <function>setuid(2)</function>.
+ </para>
+ </note>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-v</term>
+ <listitem>
+ <para>
+ Report the version number and exit.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-V</term>
+ <listitem>
+ <para>
+ Report the version number and build options, and exit.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-x <replaceable class="parameter">cache-file</replaceable></term>
+ <listitem>
+ <para>
+ Load data from <replaceable class="parameter">cache-file</replaceable> into the
+ cache of the default view.
+ </para>
+ <warning>
+ <para>
+ This option must not be used. It is only of interest
+ to BIND 9 developers and may be removed or changed in a
+ future release.
+ </para>
+ </warning>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>SIGNALS</title>
+ <para>
+ In routine operation, signals should not be used to control
+ the nameserver; <command>rndc</command> should be used
+ instead.
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>SIGHUP</term>
+ <listitem>
+ <para>
+ Force a reload of the server.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>SIGINT, SIGTERM</term>
+ <listitem>
+ <para>
+ Shut down the server.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+ The result of sending any other signals to the server is undefined.
+ </para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>CONFIGURATION</title>
+ <para>
+ The <command>named</command> configuration file is too complex
+ to describe in detail here. A complete description is provided
+ in the
+ <citetitle>BIND 9 Administrator Reference Manual</citetitle>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>FILES</title>
+
+ <variablelist>
+
+ <varlistentry>
+ <term><filename>/etc/named.conf</filename></term>
+ <listitem>
+ <para>
+ The default configuration file.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><filename>/var/run/named/named.pid</filename></term>
+ <listitem>
+ <para>
+ The default process-id file.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ </refsect1>
+
+ <refsect1>
+ <title>SEE ALSO</title>
+ <para><citetitle>RFC 1033</citetitle>,
+ <citetitle>RFC 1034</citetitle>,
+ <citetitle>RFC 1035</citetitle>,
+ <citerefentry>
+ <refentrytitle>named-checkconf</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>named-checkzone</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>rndc</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>lwresd</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>named.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citetitle>BIND 9 Administrator Reference Manual</citetitle>.
+ </para>
+ </refsect1>
+
+ <refsect1>
+ <title>AUTHOR</title>
+ <para><corpauthor>Internet Systems Consortium</corpauthor>
+ </para>
+ </refsect1>
+
+</refentry><!--
+ - Local variables:
+ - mode: sgml
+ - End:
+-->
diff --git a/bin/named/named.html b/bin/named/named.html
new file mode 100644
index 0000000..23c9a7c
--- /dev/null
+++ b/bin/named/named.html
@@ -0,0 +1,284 @@
+<!--
+ - Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ - Copyright (C) 2000, 2001, 2003 Internet Software Consortium.
+ -
+ - Permission to use, copy, modify, and distribute this software for any
+ - purpose with or without fee is hereby granted, provided that the above
+ - copyright notice and this permission notice appear in all copies.
+ -
+ - THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ - REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ - AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ - INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ - LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ - OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ - PERFORMANCE OF THIS SOFTWARE.
+-->
+<!-- $Id: named.html,v 1.30 2008/11/07 01:11:19 tbox Exp $ -->
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>named</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.71.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry" lang="en">
+<a name="man.named"></a><div class="titlepage"></div>
+<div class="refnamediv">
+<h2>Name</h2>
+<p><span class="application">named</span> &#8212; Internet domain name server</p>
+</div>
+<div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+<div class="cmdsynopsis"><p><code class="command">named</code> [<code class="option">-4</code>] [<code class="option">-6</code>] [<code class="option">-c <em class="replaceable"><code>config-file</code></em></code>] [<code class="option">-d <em class="replaceable"><code>debug-level</code></em></code>] [<code class="option">-f</code>] [<code class="option">-g</code>] [<code class="option">-m <em class="replaceable"><code>flag</code></em></code>] [<code class="option">-n <em class="replaceable"><code>#cpus</code></em></code>] [<code class="option">-p <em class="replaceable"><code>port</code></em></code>] [<code class="option">-s</code>] [<code class="option">-S <em class="replaceable"><code>#max-socks</code></em></code>] [<code class="option">-t <em class="replaceable"><code>directory</code></em></code>] [<code class="option">-u <em class="replaceable"><code>user</code></em></code>] [<code class="option">-v</code>] [<code class="option">-V</code>] [<code class="option">-x <em class="replaceable"><code>cache-file</code></em></code>]</p></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543468"></a><h2>DESCRIPTION</h2>
+<p><span><strong class="command">named</strong></span>
+ is a Domain Name System (DNS) server,
+ part of the BIND 9 distribution from ISC. For more
+ information on the DNS, see RFCs 1033, 1034, and 1035.
+ </p>
+<p>
+ When invoked without arguments, <span><strong class="command">named</strong></span>
+ will
+ read the default configuration file
+ <code class="filename">/etc/named.conf</code>, read any initial
+ data, and listen for queries.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543493"></a><h2>OPTIONS</h2>
+<div class="variablelist"><dl>
+<dt><span class="term">-4</span></dt>
+<dd><p>
+ Use IPv4 only even if the host machine is capable of IPv6.
+ <code class="option">-4</code> and <code class="option">-6</code> are mutually
+ exclusive.
+ </p></dd>
+<dt><span class="term">-6</span></dt>
+<dd><p>
+ Use IPv6 only even if the host machine is capable of IPv4.
+ <code class="option">-4</code> and <code class="option">-6</code> are mutually
+ exclusive.
+ </p></dd>
+<dt><span class="term">-c <em class="replaceable"><code>config-file</code></em></span></dt>
+<dd><p>
+ Use <em class="replaceable"><code>config-file</code></em> as the
+ configuration file instead of the default,
+ <code class="filename">/etc/named.conf</code>. To
+ ensure that reloading the configuration file continues
+ to work after the server has changed its working
+ directory due to to a possible
+ <code class="option">directory</code> option in the configuration
+ file, <em class="replaceable"><code>config-file</code></em> should be
+ an absolute pathname.
+ </p></dd>
+<dt><span class="term">-d <em class="replaceable"><code>debug-level</code></em></span></dt>
+<dd><p>
+ Set the daemon's debug level to <em class="replaceable"><code>debug-level</code></em>.
+ Debugging traces from <span><strong class="command">named</strong></span> become
+ more verbose as the debug level increases.
+ </p></dd>
+<dt><span class="term">-f</span></dt>
+<dd><p>
+ Run the server in the foreground (i.e. do not daemonize).
+ </p></dd>
+<dt><span class="term">-g</span></dt>
+<dd><p>
+ Run the server in the foreground and force all logging
+ to <code class="filename">stderr</code>.
+ </p></dd>
+<dt><span class="term">-m <em class="replaceable"><code>flag</code></em></span></dt>
+<dd><p>
+ Turn on memory usage debugging flags. Possible flags are
+ <em class="replaceable"><code>usage</code></em>,
+ <em class="replaceable"><code>trace</code></em>,
+ <em class="replaceable"><code>record</code></em>,
+ <em class="replaceable"><code>size</code></em>, and
+ <em class="replaceable"><code>mctx</code></em>.
+ These correspond to the ISC_MEM_DEBUGXXXX flags described in
+ <code class="filename">&lt;isc/mem.h&gt;</code>.
+ </p></dd>
+<dt><span class="term">-n <em class="replaceable"><code>#cpus</code></em></span></dt>
+<dd><p>
+ Create <em class="replaceable"><code>#cpus</code></em> worker threads
+ to take advantage of multiple CPUs. If not specified,
+ <span><strong class="command">named</strong></span> will try to determine the
+ number of CPUs present and create one thread per CPU.
+ If it is unable to determine the number of CPUs, a
+ single worker thread will be created.
+ </p></dd>
+<dt><span class="term">-p <em class="replaceable"><code>port</code></em></span></dt>
+<dd><p>
+ Listen for queries on port <em class="replaceable"><code>port</code></em>. If not
+ specified, the default is port 53.
+ </p></dd>
+<dt><span class="term">-s</span></dt>
+<dd>
+<p>
+ Write memory usage statistics to <code class="filename">stdout</code> on exit.
+ </p>
+<div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Note</h3>
+<p>
+ This option is mainly of interest to BIND 9 developers
+ and may be removed or changed in a future release.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-S <em class="replaceable"><code>#max-socks</code></em></span></dt>
+<dd>
+<p>
+ Allow <span><strong class="command">named</strong></span> to use up to
+ <em class="replaceable"><code>#max-socks</code></em> sockets.
+ </p>
+<div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Warning</h3>
+<p>
+ This option should be unnecessary for the vast majority
+ of users.
+ The use of this option could even be harmful because the
+ specified value may exceed the limitation of the
+ underlying system API.
+ It is therefore set only when the default configuration
+ causes exhaustion of file descriptors and the
+ operational environment is known to support the
+ specified number of sockets.
+ Note also that the actual maximum number is normally a little
+ fewer than the specified value because
+ <span><strong class="command">named</strong></span> reserves some file descriptors
+ for its internal use.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-t <em class="replaceable"><code>directory</code></em></span></dt>
+<dd>
+<p>Chroot
+ to <em class="replaceable"><code>directory</code></em> after
+ processing the command line arguments, but before
+ reading the configuration file.
+ </p>
+<div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Warning</h3>
+<p>
+ This option should be used in conjunction with the
+ <code class="option">-u</code> option, as chrooting a process
+ running as root doesn't enhance security on most
+ systems; the way <code class="function">chroot(2)</code> is
+ defined allows a process with root privileges to
+ escape a chroot jail.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-u <em class="replaceable"><code>user</code></em></span></dt>
+<dd>
+<p>Setuid
+ to <em class="replaceable"><code>user</code></em> after completing
+ privileged operations, such as creating sockets that
+ listen on privileged ports.
+ </p>
+<div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Note</h3>
+<p>
+ On Linux, <span><strong class="command">named</strong></span> uses the kernel's
+ capability mechanism to drop all root privileges
+ except the ability to <code class="function">bind(2)</code> to
+ a
+ privileged port and set process resource limits.
+ Unfortunately, this means that the <code class="option">-u</code>
+ option only works when <span><strong class="command">named</strong></span> is
+ run
+ on kernel 2.2.18 or later, or kernel 2.3.99-pre3 or
+ later, since previous kernels did not allow privileges
+ to be retained after <code class="function">setuid(2)</code>.
+ </p>
+</div>
+</dd>
+<dt><span class="term">-v</span></dt>
+<dd><p>
+ Report the version number and exit.
+ </p></dd>
+<dt><span class="term">-V</span></dt>
+<dd><p>
+ Report the version number and build options, and exit.
+ </p></dd>
+<dt><span class="term">-x <em class="replaceable"><code>cache-file</code></em></span></dt>
+<dd>
+<p>
+ Load data from <em class="replaceable"><code>cache-file</code></em> into the
+ cache of the default view.
+ </p>
+<div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;">
+<h3 class="title">Warning</h3>
+<p>
+ This option must not be used. It is only of interest
+ to BIND 9 developers and may be removed or changed in a
+ future release.
+ </p>
+</div>
+</dd>
+</dl></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543928"></a><h2>SIGNALS</h2>
+<p>
+ In routine operation, signals should not be used to control
+ the nameserver; <span><strong class="command">rndc</strong></span> should be used
+ instead.
+ </p>
+<div class="variablelist"><dl>
+<dt><span class="term">SIGHUP</span></dt>
+<dd><p>
+ Force a reload of the server.
+ </p></dd>
+<dt><span class="term">SIGINT, SIGTERM</span></dt>
+<dd><p>
+ Shut down the server.
+ </p></dd>
+</dl></div>
+<p>
+ The result of sending any other signals to the server is undefined.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543976"></a><h2>CONFIGURATION</h2>
+<p>
+ The <span><strong class="command">named</strong></span> configuration file is too complex
+ to describe in detail here. A complete description is provided
+ in the
+ <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2543993"></a><h2>FILES</h2>
+<div class="variablelist"><dl>
+<dt><span class="term"><code class="filename">/etc/named.conf</code></span></dt>
+<dd><p>
+ The default configuration file.
+ </p></dd>
+<dt><span class="term"><code class="filename">/var/run/named/named.pid</code></span></dt>
+<dd><p>
+ The default process-id file.
+ </p></dd>
+</dl></div>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2544033"></a><h2>SEE ALSO</h2>
+<p><em class="citetitle">RFC 1033</em>,
+ <em class="citetitle">RFC 1034</em>,
+ <em class="citetitle">RFC 1035</em>,
+ <span class="citerefentry"><span class="refentrytitle">named-checkconf</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">named-checkzone</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">rndc</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">lwresd</span>(8)</span>,
+ <span class="citerefentry"><span class="refentrytitle">named.conf</span>(5)</span>,
+ <em class="citetitle">BIND 9 Administrator Reference Manual</em>.
+ </p>
+</div>
+<div class="refsect1" lang="en">
+<a name="id2544171"></a><h2>AUTHOR</h2>
+<p><span class="corpauthor">Internet Systems Consortium</span>
+ </p>
+</div>
+</div></body>
+</html>
diff --git a/bin/named/notify.c b/bin/named/notify.c
new file mode 100644
index 0000000..de52b8c
--- /dev/null
+++ b/bin/named/notify.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: notify.c,v 1.37 2007/06/19 23:46:59 tbox Exp $ */
+
+#include <config.h>
+
+#include <isc/log.h>
+#include <isc/print.h>
+
+#include <dns/message.h>
+#include <dns/rdataset.h>
+#include <dns/result.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <named/log.h>
+#include <named/notify.h>
+
+/*! \file
+ * \brief
+ * This module implements notify as in RFC1996.
+ */
+
+static void
+notify_log(ns_client_t *client, int level, const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ ns_client_logv(client, DNS_LOGCATEGORY_NOTIFY, NS_LOGMODULE_NOTIFY,
+ level, fmt, ap);
+ va_end(ap);
+}
+
+static void
+respond(ns_client_t *client, isc_result_t result) {
+ dns_rcode_t rcode;
+ dns_message_t *message;
+ isc_result_t msg_result;
+
+ message = client->message;
+ rcode = dns_result_torcode(result);
+
+ msg_result = dns_message_reply(message, ISC_TRUE);
+ if (msg_result != ISC_R_SUCCESS)
+ msg_result = dns_message_reply(message, ISC_FALSE);
+ if (msg_result != ISC_R_SUCCESS) {
+ ns_client_next(client, msg_result);
+ return;
+ }
+ message->rcode = rcode;
+ if (rcode == dns_rcode_noerror)
+ message->flags |= DNS_MESSAGEFLAG_AA;
+ else
+ message->flags &= ~DNS_MESSAGEFLAG_AA;
+ ns_client_send(client);
+}
+
+void
+ns_notify_start(ns_client_t *client) {
+ dns_message_t *request = client->message;
+ isc_result_t result;
+ dns_name_t *zonename;
+ dns_rdataset_t *zone_rdataset;
+ dns_zone_t *zone = NULL;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char tsigbuf[DNS_NAME_FORMATSIZE + sizeof(": TSIG ''")];
+ dns_tsigkey_t *tsigkey;
+
+ /*
+ * Interpret the question section.
+ */
+ result = dns_message_firstname(request, DNS_SECTION_QUESTION);
+ if (result != ISC_R_SUCCESS) {
+ notify_log(client, ISC_LOG_NOTICE,
+ "notify question section empty");
+ goto formerr;
+ }
+
+ /*
+ * The question section must contain exactly one question.
+ */
+ zonename = NULL;
+ dns_message_currentname(request, DNS_SECTION_QUESTION, &zonename);
+ zone_rdataset = ISC_LIST_HEAD(zonename->list);
+ if (ISC_LIST_NEXT(zone_rdataset, link) != NULL) {
+ notify_log(client, ISC_LOG_NOTICE,
+ "notify question section contains multiple RRs");
+ goto formerr;
+ }
+
+ /* The zone section must have exactly one name. */
+ result = dns_message_nextname(request, DNS_SECTION_ZONE);
+ if (result != ISC_R_NOMORE) {
+ notify_log(client, ISC_LOG_NOTICE,
+ "notify question section contains multiple RRs");
+ goto formerr;
+ }
+
+ /* The one rdataset must be an SOA. */
+ if (zone_rdataset->type != dns_rdatatype_soa) {
+ notify_log(client, ISC_LOG_NOTICE,
+ "notify question section contains no SOA");
+ goto formerr;
+ }
+
+ tsigkey = dns_message_gettsigkey(request);
+ if (tsigkey != NULL) {
+ dns_name_format(&tsigkey->name, namebuf, sizeof(namebuf));
+
+ if (tsigkey->generated) {
+ char cnamebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(tsigkey->creator, cnamebuf,
+ sizeof(cnamebuf));
+ snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s' (%s)",
+ namebuf, cnamebuf);
+ } else {
+ snprintf(tsigbuf, sizeof(tsigbuf), ": TSIG '%s'",
+ namebuf);
+ }
+ } else
+ tsigbuf[0] = '\0';
+ dns_name_format(zonename, namebuf, sizeof(namebuf));
+ result = dns_zt_find(client->view->zonetable, zonename, 0, NULL,
+ &zone);
+ if (result != ISC_R_SUCCESS)
+ goto notauth;
+
+ switch (dns_zone_gettype(zone)) {
+ case dns_zone_master:
+ case dns_zone_slave:
+ case dns_zone_stub: /* Allow dialup passive to work. */
+ notify_log(client, ISC_LOG_INFO,
+ "received notify for zone '%s'%s", namebuf, tsigbuf);
+ respond(client, dns_zone_notifyreceive(zone,
+ ns_client_getsockaddr(client), request));
+ break;
+ default:
+ goto notauth;
+ }
+ dns_zone_detach(&zone);
+ return;
+
+ notauth:
+ notify_log(client, ISC_LOG_NOTICE,
+ "received notify for zone '%s'%s: not authoritative",
+ namebuf, tsigbuf);
+ result = DNS_R_NOTAUTH;
+ goto failure;
+
+ formerr:
+ result = DNS_R_FORMERR;
+
+ failure:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ respond(client, result);
+}
diff --git a/bin/named/query.c b/bin/named/query.c
new file mode 100644
index 0000000..cacd70a
--- /dev/null
+++ b/bin/named/query.c
@@ -0,0 +1,5096 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: query.c,v 1.313 2008/11/03 23:57:22 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <isc/mem.h>
+#include <isc/util.h>
+#include <isc/hex.h>
+
+#include <dns/adb.h>
+#include <dns/byaddr.h>
+#include <dns/db.h>
+#ifdef DLZ
+#include <dns/dlz.h>
+#endif
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/message.h>
+#include <dns/ncache.h>
+#include <dns/nsec3.h>
+#include <dns/order.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/stats.h>
+#include <dns/tkey.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <named/client.h>
+#include <named/log.h>
+#include <named/server.h>
+#include <named/sortlist.h>
+#include <named/xfrout.h>
+
+/*% Partial answer? */
+#define PARTIALANSWER(c) (((c)->query.attributes & \
+ NS_QUERYATTR_PARTIALANSWER) != 0)
+/*% Use Cache? */
+#define USECACHE(c) (((c)->query.attributes & \
+ NS_QUERYATTR_CACHEOK) != 0)
+/*% Recursion OK? */
+#define RECURSIONOK(c) (((c)->query.attributes & \
+ NS_QUERYATTR_RECURSIONOK) != 0)
+/*% Recursing? */
+#define RECURSING(c) (((c)->query.attributes & \
+ NS_QUERYATTR_RECURSING) != 0)
+/*% Cache glue ok? */
+#define CACHEGLUEOK(c) (((c)->query.attributes & \
+ NS_QUERYATTR_CACHEGLUEOK) != 0)
+/*% Want Recursion? */
+#define WANTRECURSION(c) (((c)->query.attributes & \
+ NS_QUERYATTR_WANTRECURSION) != 0)
+/*% Want DNSSEC? */
+#define WANTDNSSEC(c) (((c)->attributes & \
+ NS_CLIENTATTR_WANTDNSSEC) != 0)
+/*% No authority? */
+#define NOAUTHORITY(c) (((c)->query.attributes & \
+ NS_QUERYATTR_NOAUTHORITY) != 0)
+/*% No additional? */
+#define NOADDITIONAL(c) (((c)->query.attributes & \
+ NS_QUERYATTR_NOADDITIONAL) != 0)
+/*% Secure? */
+#define SECURE(c) (((c)->query.attributes & \
+ NS_QUERYATTR_SECURE) != 0)
+
+/*% No QNAME Proof? */
+#define NOQNAME(r) (((r)->attributes & \
+ DNS_RDATASETATTR_NOQNAME) != 0)
+
+#if 0
+#define CTRACE(m) isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_CLIENT, \
+ NS_LOGMODULE_QUERY, \
+ ISC_LOG_DEBUG(3), \
+ "client %p: %s", client, (m))
+#define QTRACE(m) isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_GENERAL, \
+ NS_LOGMODULE_QUERY, \
+ ISC_LOG_DEBUG(3), \
+ "query %p: %s", query, (m))
+#else
+#define CTRACE(m) ((void)m)
+#define QTRACE(m) ((void)m)
+#endif
+
+#define DNS_GETDB_NOEXACT 0x01U
+#define DNS_GETDB_NOLOG 0x02U
+#define DNS_GETDB_PARTIAL 0x04U
+
+typedef struct client_additionalctx {
+ ns_client_t *client;
+ dns_rdataset_t *rdataset;
+} client_additionalctx_t;
+
+static void
+query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
+
+static isc_boolean_t
+validate(ns_client_t *client, dns_db_t *db, dns_name_t *name,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+
+static void
+query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
+ dns_dbversion_t *version, ns_client_t *client,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ dns_name_t *fname, isc_boolean_t exact,
+ dns_name_t *found);
+
+/*%
+ * Increment query statistics counters.
+ */
+static inline void
+inc_stats(ns_client_t *client, dns_statscounter_t counter) {
+ dns_zone_t *zone = client->query.authzone;
+
+ dns_generalstats_increment(ns_g_server->nsstats, counter);
+
+ if (zone != NULL) {
+ dns_stats_t *zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL)
+ dns_generalstats_increment(zonestats, counter);
+ }
+}
+
+static void
+query_send(ns_client_t *client) {
+ dns_statscounter_t counter;
+ if ((client->message->flags & DNS_MESSAGEFLAG_AA) == 0)
+ inc_stats(client, dns_nsstatscounter_nonauthans);
+ else
+ inc_stats(client, dns_nsstatscounter_authans);
+ if (client->message->rcode == dns_rcode_noerror) {
+ if (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER])) {
+ if (client->query.isreferral) {
+ counter = dns_nsstatscounter_referral;
+ } else {
+ counter = dns_nsstatscounter_nxrrset;
+ }
+ } else {
+ counter = dns_nsstatscounter_success;
+ }
+ } else if (client->message->rcode == dns_rcode_nxdomain) {
+ counter = dns_nsstatscounter_nxdomain;
+ } else {
+ /* We end up here in case of YXDOMAIN, and maybe others */
+ counter = dns_nsstatscounter_failure;
+ }
+ inc_stats(client, counter);
+ ns_client_send(client);
+}
+
+static void
+query_error(ns_client_t *client, isc_result_t result) {
+ switch (result) {
+ case DNS_R_SERVFAIL:
+ inc_stats(client, dns_nsstatscounter_servfail);
+ break;
+ case DNS_R_FORMERR:
+ inc_stats(client, dns_nsstatscounter_formerr);
+ break;
+ default:
+ inc_stats(client, dns_nsstatscounter_failure);
+ break;
+ }
+ ns_client_error(client, result);
+}
+
+static void
+query_next(ns_client_t *client, isc_result_t result) {
+ if (result == DNS_R_DUPLICATE)
+ inc_stats(client, dns_nsstatscounter_duplicate);
+ else if (result == DNS_R_DROP)
+ inc_stats(client, dns_nsstatscounter_dropped);
+ else
+ inc_stats(client, dns_nsstatscounter_failure);
+ ns_client_next(client, result);
+}
+
+static inline void
+query_freefreeversions(ns_client_t *client, isc_boolean_t everything) {
+ ns_dbversion_t *dbversion, *dbversion_next;
+ unsigned int i;
+
+ for (dbversion = ISC_LIST_HEAD(client->query.freeversions), i = 0;
+ dbversion != NULL;
+ dbversion = dbversion_next, i++)
+ {
+ dbversion_next = ISC_LIST_NEXT(dbversion, link);
+ /*
+ * If we're not freeing everything, we keep the first three
+ * dbversions structures around.
+ */
+ if (i > 3 || everything) {
+ ISC_LIST_UNLINK(client->query.freeversions, dbversion,
+ link);
+ isc_mem_put(client->mctx, dbversion,
+ sizeof(*dbversion));
+ }
+ }
+}
+
+void
+ns_query_cancel(ns_client_t *client) {
+ LOCK(&client->query.fetchlock);
+ if (client->query.fetch != NULL) {
+ dns_resolver_cancelfetch(client->query.fetch);
+
+ client->query.fetch = NULL;
+ }
+ UNLOCK(&client->query.fetchlock);
+}
+
+static inline void
+query_reset(ns_client_t *client, isc_boolean_t everything) {
+ isc_buffer_t *dbuf, *dbuf_next;
+ ns_dbversion_t *dbversion, *dbversion_next;
+
+ /*%
+ * Reset the query state of a client to its default state.
+ */
+
+ /*
+ * Cancel the fetch if it's running.
+ */
+ ns_query_cancel(client);
+
+ /*
+ * Cleanup any active versions.
+ */
+ for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
+ dbversion != NULL;
+ dbversion = dbversion_next) {
+ dbversion_next = ISC_LIST_NEXT(dbversion, link);
+ dns_db_closeversion(dbversion->db, &dbversion->version,
+ ISC_FALSE);
+ dns_db_detach(&dbversion->db);
+ ISC_LIST_INITANDAPPEND(client->query.freeversions,
+ dbversion, link);
+ }
+ ISC_LIST_INIT(client->query.activeversions);
+
+ if (client->query.authdb != NULL)
+ dns_db_detach(&client->query.authdb);
+ if (client->query.authzone != NULL)
+ dns_zone_detach(&client->query.authzone);
+
+ query_freefreeversions(client, everything);
+
+ for (dbuf = ISC_LIST_HEAD(client->query.namebufs);
+ dbuf != NULL;
+ dbuf = dbuf_next) {
+ dbuf_next = ISC_LIST_NEXT(dbuf, link);
+ if (dbuf_next != NULL || everything) {
+ ISC_LIST_UNLINK(client->query.namebufs, dbuf, link);
+ isc_buffer_free(&dbuf);
+ }
+ }
+
+ if (client->query.restarts > 0) {
+ /*
+ * client->query.qname was dynamically allocated.
+ */
+ dns_message_puttempname(client->message,
+ &client->query.qname);
+ }
+ client->query.qname = NULL;
+ client->query.attributes = (NS_QUERYATTR_RECURSIONOK |
+ NS_QUERYATTR_CACHEOK |
+ NS_QUERYATTR_SECURE);
+ client->query.restarts = 0;
+ client->query.timerset = ISC_FALSE;
+ client->query.origqname = NULL;
+ client->query.qname = NULL;
+ client->query.dboptions = 0;
+ client->query.fetchoptions = 0;
+ client->query.gluedb = NULL;
+ client->query.authdbset = ISC_FALSE;
+ client->query.isreferral = ISC_FALSE;
+}
+
+static void
+query_next_callback(ns_client_t *client) {
+ query_reset(client, ISC_FALSE);
+}
+
+void
+ns_query_free(ns_client_t *client) {
+ query_reset(client, ISC_TRUE);
+}
+
+static inline isc_result_t
+query_newnamebuf(ns_client_t *client) {
+ isc_buffer_t *dbuf;
+ isc_result_t result;
+
+ CTRACE("query_newnamebuf");
+ /*%
+ * Allocate a name buffer.
+ */
+
+ dbuf = NULL;
+ result = isc_buffer_allocate(client->mctx, &dbuf, 1024);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_newnamebuf: isc_buffer_allocate failed: done");
+ return (result);
+ }
+ ISC_LIST_APPEND(client->query.namebufs, dbuf, link);
+
+ CTRACE("query_newnamebuf: done");
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_buffer_t *
+query_getnamebuf(ns_client_t *client) {
+ isc_buffer_t *dbuf;
+ isc_result_t result;
+ isc_region_t r;
+
+ CTRACE("query_getnamebuf");
+ /*%
+ * Return a name buffer with space for a maximal name, allocating
+ * a new one if necessary.
+ */
+
+ if (ISC_LIST_EMPTY(client->query.namebufs)) {
+ result = query_newnamebuf(client);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_getnamebuf: query_newnamebuf failed: done");
+ return (NULL);
+ }
+ }
+
+ dbuf = ISC_LIST_TAIL(client->query.namebufs);
+ INSIST(dbuf != NULL);
+ isc_buffer_availableregion(dbuf, &r);
+ if (r.length < 255) {
+ result = query_newnamebuf(client);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_getnamebuf: query_newnamebuf failed: done");
+ return (NULL);
+
+ }
+ dbuf = ISC_LIST_TAIL(client->query.namebufs);
+ isc_buffer_availableregion(dbuf, &r);
+ INSIST(r.length >= 255);
+ }
+ CTRACE("query_getnamebuf: done");
+ return (dbuf);
+}
+
+static inline void
+query_keepname(ns_client_t *client, dns_name_t *name, isc_buffer_t *dbuf) {
+ isc_region_t r;
+
+ CTRACE("query_keepname");
+ /*%
+ * 'name' is using space in 'dbuf', but 'dbuf' has not yet been
+ * adjusted to take account of that. We do the adjustment.
+ */
+
+ REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) != 0);
+
+ dns_name_toregion(name, &r);
+ isc_buffer_add(dbuf, r.length);
+ dns_name_setbuffer(name, NULL);
+ client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
+}
+
+static inline void
+query_releasename(ns_client_t *client, dns_name_t **namep) {
+ dns_name_t *name = *namep;
+
+ /*%
+ * 'name' is no longer needed. Return it to our pool of temporary
+ * names. If it is using a name buffer, relinquish its exclusive
+ * rights on the buffer.
+ */
+
+ CTRACE("query_releasename");
+ if (dns_name_hasbuffer(name)) {
+ INSIST((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED)
+ != 0);
+ client->query.attributes &= ~NS_QUERYATTR_NAMEBUFUSED;
+ }
+ dns_message_puttempname(client->message, namep);
+ CTRACE("query_releasename: done");
+}
+
+static inline dns_name_t *
+query_newname(ns_client_t *client, isc_buffer_t *dbuf,
+ isc_buffer_t *nbuf)
+{
+ dns_name_t *name;
+ isc_region_t r;
+ isc_result_t result;
+
+ REQUIRE((client->query.attributes & NS_QUERYATTR_NAMEBUFUSED) == 0);
+
+ CTRACE("query_newname");
+ name = NULL;
+ result = dns_message_gettempname(client->message, &name);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_newname: dns_message_gettempname failed: done");
+ return (NULL);
+ }
+ isc_buffer_availableregion(dbuf, &r);
+ isc_buffer_init(nbuf, r.base, r.length);
+ dns_name_init(name, NULL);
+ dns_name_setbuffer(name, nbuf);
+ client->query.attributes |= NS_QUERYATTR_NAMEBUFUSED;
+
+ CTRACE("query_newname: done");
+ return (name);
+}
+
+static inline dns_rdataset_t *
+query_newrdataset(ns_client_t *client) {
+ dns_rdataset_t *rdataset;
+ isc_result_t result;
+
+ CTRACE("query_newrdataset");
+ rdataset = NULL;
+ result = dns_message_gettemprdataset(client->message, &rdataset);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_newrdataset: "
+ "dns_message_gettemprdataset failed: done");
+ return (NULL);
+ }
+ dns_rdataset_init(rdataset);
+
+ CTRACE("query_newrdataset: done");
+ return (rdataset);
+}
+
+static inline void
+query_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
+ dns_rdataset_t *rdataset = *rdatasetp;
+
+ CTRACE("query_putrdataset");
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, rdatasetp);
+ }
+ CTRACE("query_putrdataset: done");
+}
+
+
+static inline isc_result_t
+query_newdbversion(ns_client_t *client, unsigned int n) {
+ unsigned int i;
+ ns_dbversion_t *dbversion;
+
+ for (i = 0; i < n; i++) {
+ dbversion = isc_mem_get(client->mctx, sizeof(*dbversion));
+ if (dbversion != NULL) {
+ dbversion->db = NULL;
+ dbversion->version = NULL;
+ ISC_LIST_INITANDAPPEND(client->query.freeversions,
+ dbversion, link);
+ } else {
+ /*
+ * We only return ISC_R_NOMEMORY if we couldn't
+ * allocate anything.
+ */
+ if (i == 0)
+ return (ISC_R_NOMEMORY);
+ else
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline ns_dbversion_t *
+query_getdbversion(ns_client_t *client) {
+ isc_result_t result;
+ ns_dbversion_t *dbversion;
+
+ if (ISC_LIST_EMPTY(client->query.freeversions)) {
+ result = query_newdbversion(client, 1);
+ if (result != ISC_R_SUCCESS)
+ return (NULL);
+ }
+ dbversion = ISC_LIST_HEAD(client->query.freeversions);
+ INSIST(dbversion != NULL);
+ ISC_LIST_UNLINK(client->query.freeversions, dbversion, link);
+
+ return (dbversion);
+}
+
+isc_result_t
+ns_query_init(ns_client_t *client) {
+ isc_result_t result;
+
+ ISC_LIST_INIT(client->query.namebufs);
+ ISC_LIST_INIT(client->query.activeversions);
+ ISC_LIST_INIT(client->query.freeversions);
+ client->query.restarts = 0;
+ client->query.timerset = ISC_FALSE;
+ client->query.qname = NULL;
+ result = isc_mutex_init(&client->query.fetchlock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ client->query.fetch = NULL;
+ client->query.authdb = NULL;
+ client->query.authzone = NULL;
+ client->query.authdbset = ISC_FALSE;
+ client->query.isreferral = ISC_FALSE;
+ query_reset(client, ISC_FALSE);
+ result = query_newdbversion(client, 3);
+ if (result != ISC_R_SUCCESS) {
+ DESTROYLOCK(&client->query.fetchlock);
+ return (result);
+ }
+ result = query_newnamebuf(client);
+ if (result != ISC_R_SUCCESS)
+ query_freefreeversions(client, ISC_TRUE);
+
+ return (result);
+}
+
+static inline ns_dbversion_t *
+query_findversion(ns_client_t *client, dns_db_t *db,
+ isc_boolean_t *newzonep)
+{
+ ns_dbversion_t *dbversion;
+
+ /*%
+ * We may already have done a query related to this
+ * database. If so, we must be sure to make subsequent
+ * queries from the same version.
+ */
+ for (dbversion = ISC_LIST_HEAD(client->query.activeversions);
+ dbversion != NULL;
+ dbversion = ISC_LIST_NEXT(dbversion, link)) {
+ if (dbversion->db == db)
+ break;
+ }
+
+ if (dbversion == NULL) {
+ /*
+ * This is a new zone for this query. Add it to
+ * the active list.
+ */
+ dbversion = query_getdbversion(client);
+ if (dbversion == NULL)
+ return (NULL);
+ dns_db_attach(db, &dbversion->db);
+ dns_db_currentversion(db, &dbversion->version);
+ dbversion->queryok = ISC_FALSE;
+ ISC_LIST_APPEND(client->query.activeversions,
+ dbversion, link);
+ *newzonep = ISC_TRUE;
+ } else
+ *newzonep = ISC_FALSE;
+
+ return (dbversion);
+}
+
+static inline isc_result_t
+query_validatezonedb(ns_client_t *client, dns_name_t *name,
+ dns_rdatatype_t qtype, unsigned int options,
+ dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t **versionp)
+{
+ isc_result_t result;
+ isc_boolean_t check_acl, new_zone;
+ dns_acl_t *queryacl;
+ ns_dbversion_t *dbversion;
+
+ REQUIRE(zone != NULL);
+ REQUIRE(db != NULL);
+
+ /*
+ * This limits our searching to the zone where the first name
+ * (the query target) was looked for. This prevents following
+ * CNAMES or DNAMES into other zones and prevents returning
+ * additional data from other zones.
+ */
+ if (!client->view->additionalfromauth &&
+ client->query.authdbset &&
+ db != client->query.authdb)
+ goto refuse;
+
+ /*
+ * If the zone has an ACL, we'll check it, otherwise
+ * we use the view's "allow-query" ACL. Each ACL is only checked
+ * once per query.
+ *
+ * Also, get the database version to use.
+ */
+
+ check_acl = ISC_TRUE; /* Keep compiler happy. */
+ queryacl = NULL;
+
+ /*
+ * Get the current version of this database.
+ */
+ dbversion = query_findversion(client, db, &new_zone);
+ if (dbversion == NULL) {
+ result = DNS_R_SERVFAIL;
+ goto fail;
+ }
+ if (new_zone) {
+ check_acl = ISC_TRUE;
+ } else if (!dbversion->queryok) {
+ goto refuse;
+ } else {
+ check_acl = ISC_FALSE;
+ }
+
+ queryacl = dns_zone_getqueryacl(zone);
+ if (queryacl == NULL) {
+ queryacl = client->view->queryacl;
+ if ((client->query.attributes &
+ NS_QUERYATTR_QUERYOKVALID) != 0) {
+ /*
+ * We've evaluated the view's queryacl already. If
+ * NS_QUERYATTR_QUERYOK is set, then the client is
+ * allowed to make queries, otherwise the query should
+ * be refused.
+ */
+ check_acl = ISC_FALSE;
+ if ((client->query.attributes &
+ NS_QUERYATTR_QUERYOK) == 0)
+ goto refuse;
+ } else {
+ /*
+ * We haven't evaluated the view's queryacl yet.
+ */
+ check_acl = ISC_TRUE;
+ }
+ }
+
+ if (check_acl) {
+ isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
+
+ result = ns_client_checkaclsilent(client, NULL, queryacl,
+ ISC_TRUE);
+ if (log) {
+ char msg[NS_CLIENT_ACLMSGSIZE("query")];
+ if (result == ISC_R_SUCCESS) {
+ if (isc_log_wouldlog(ns_g_lctx,
+ ISC_LOG_DEBUG(3)))
+ {
+ ns_client_aclmsg("query", name, qtype,
+ client->view->rdclass,
+ msg, sizeof(msg));
+ ns_client_log(client,
+ DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_DEBUG(3),
+ "%s approved", msg);
+ }
+ } else {
+ ns_client_aclmsg("query", name, qtype,
+ client->view->rdclass,
+ msg, sizeof(msg));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "%s denied", msg);
+ }
+ }
+
+ if (queryacl == client->view->queryacl) {
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We were allowed by the default
+ * "allow-query" ACL. Remember this so we
+ * don't have to check again.
+ */
+ client->query.attributes |=
+ NS_QUERYATTR_QUERYOK;
+ }
+ /*
+ * We've now evaluated the view's query ACL, and
+ * the NS_QUERYATTR_QUERYOK attribute is now valid.
+ */
+ client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
+ }
+
+ if (result != ISC_R_SUCCESS)
+ goto refuse;
+ }
+
+ /* Approved. */
+
+ /*
+ * Remember the result of the ACL check so we
+ * don't have to check again.
+ */
+ dbversion->queryok = ISC_TRUE;
+
+ /* Transfer ownership, if necessary. */
+ if (versionp != NULL)
+ *versionp = dbversion->version;
+
+ return (ISC_R_SUCCESS);
+
+ refuse:
+ return (DNS_R_REFUSED);
+
+ fail:
+ return (result);
+}
+
+static inline isc_result_t
+query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
+ unsigned int options, dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbversion_t **versionp)
+{
+ isc_result_t result;
+ unsigned int ztoptions;
+ dns_zone_t *zone = NULL;
+ dns_db_t *db = NULL;
+ isc_boolean_t partial = ISC_FALSE;
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ /*%
+ * Find a zone database to answer the query.
+ */
+ ztoptions = ((options & DNS_GETDB_NOEXACT) != 0) ?
+ DNS_ZTFIND_NOEXACT : 0;
+
+ result = dns_zt_find(client->view->zonetable, name, ztoptions, NULL,
+ &zone);
+ if (result == DNS_R_PARTIALMATCH)
+ partial = ISC_TRUE;
+ if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
+ result = dns_zone_getdb(zone, &db);
+
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ result = query_validatezonedb(client, name, qtype, options, zone, db,
+ versionp);
+
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ /* Transfer ownership. */
+ *zonep = zone;
+ *dbp = db;
+
+ if (partial && (options & DNS_GETDB_PARTIAL) != 0)
+ return (DNS_R_PARTIALMATCH);
+ return (ISC_R_SUCCESS);
+
+ fail:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ return (result);
+}
+
+static inline isc_result_t
+query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
+ dns_db_t **dbp, unsigned int options)
+{
+ isc_result_t result;
+ isc_boolean_t check_acl;
+ dns_db_t *db = NULL;
+
+ REQUIRE(dbp != NULL && *dbp == NULL);
+
+ /*%
+ * Find a cache database to answer the query.
+ * This may fail with DNS_R_REFUSED if the client
+ * is not allowed to use the cache.
+ */
+
+ if (!USECACHE(client))
+ return (DNS_R_REFUSED);
+ dns_db_attach(client->view->cachedb, &db);
+
+ if ((client->query.attributes &
+ NS_QUERYATTR_QUERYOKVALID) != 0) {
+ /*
+ * We've evaluated the view's queryacl already. If
+ * NS_QUERYATTR_QUERYOK is set, then the client is
+ * allowed to make queries, otherwise the query should
+ * be refused.
+ */
+ check_acl = ISC_FALSE;
+ if ((client->query.attributes &
+ NS_QUERYATTR_QUERYOK) == 0)
+ goto refuse;
+ } else {
+ /*
+ * We haven't evaluated the view's queryacl yet.
+ */
+ check_acl = ISC_TRUE;
+ }
+
+ if (check_acl) {
+ isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
+ char msg[NS_CLIENT_ACLMSGSIZE("query (cache)")];
+
+ result = ns_client_checkaclsilent(client, NULL,
+ client->view->queryacl,
+ ISC_TRUE);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We were allowed by the default
+ * "allow-query" ACL. Remember this so we
+ * don't have to check again.
+ */
+ client->query.attributes |=
+ NS_QUERYATTR_QUERYOK;
+ if (log && isc_log_wouldlog(ns_g_lctx,
+ ISC_LOG_DEBUG(3)))
+ {
+ ns_client_aclmsg("query (cache)", name, qtype,
+ client->view->rdclass,
+ msg, sizeof(msg));
+ ns_client_log(client,
+ DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_DEBUG(3),
+ "%s approved", msg);
+ }
+ } else if (log) {
+ ns_client_aclmsg("query (cache)", name, qtype,
+ client->view->rdclass, msg,
+ sizeof(msg));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "%s denied", msg);
+ }
+ /*
+ * We've now evaluated the view's query ACL, and
+ * the NS_QUERYATTR_QUERYOK attribute is now valid.
+ */
+ client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
+
+ if (result != ISC_R_SUCCESS)
+ goto refuse;
+ }
+
+ /* Approved. */
+
+ /* Transfer ownership. */
+ *dbp = db;
+
+ return (ISC_R_SUCCESS);
+
+ refuse:
+ result = DNS_R_REFUSED;
+
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ return (result);
+}
+
+
+static inline isc_result_t
+query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
+ unsigned int options, dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbversion_t **versionp, isc_boolean_t *is_zonep)
+{
+ isc_result_t result;
+
+#ifdef DLZ
+ isc_result_t tresult;
+ unsigned int namelabels;
+ unsigned int zonelabels;
+ dns_zone_t *zone = NULL;
+ dns_db_t *tdbp;
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ tdbp = NULL;
+
+ /* Calculate how many labels are in name. */
+ namelabels = dns_name_countlabels(name);
+ zonelabels = 0;
+
+ /* Try to find name in bind's standard database. */
+ result = query_getzonedb(client, name, qtype, options, &zone,
+ dbp, versionp);
+
+ /* See how many labels are in the zone's name. */
+ if (result == ISC_R_SUCCESS && zone != NULL)
+ zonelabels = dns_name_countlabels(dns_zone_getorigin(zone));
+ /*
+ * If # zone labels < # name labels, try to find an even better match
+ * Only try if a DLZ driver is loaded for this view
+ */
+ if (zonelabels < namelabels && client->view->dlzdatabase != NULL) {
+ tresult = dns_dlzfindzone(client->view, name,
+ zonelabels, &tdbp);
+ /* If we successful, we found a better match. */
+ if (tresult == ISC_R_SUCCESS) {
+ /*
+ * If the previous search returned a zone, detach it.
+ */
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ /*
+ * If the previous search returned a database,
+ * detach it.
+ */
+ if (*dbp != NULL)
+ dns_db_detach(dbp);
+
+ /*
+ * If the previous search returned a version, clear it.
+ */
+ *versionp = NULL;
+
+ /*
+ * Get our database version.
+ */
+ dns_db_currentversion(tdbp, versionp);
+
+ /*
+ * Be sure to return our database.
+ */
+ *dbp = tdbp;
+
+ /*
+ * We return a null zone, No stats for DLZ zones.
+ */
+ zone = NULL;
+ result = tresult;
+ }
+ }
+#else
+ result = query_getzonedb(client, name, qtype, options,
+ zonep, dbp, versionp);
+#endif
+
+ /* If successfull, Transfer ownership of zone. */
+ if (result == ISC_R_SUCCESS) {
+#ifdef DLZ
+ *zonep = zone;
+#endif
+ /*
+ * If neither attempt above succeeded, return the cache instead
+ */
+ *is_zonep = ISC_TRUE;
+ } else if (result == ISC_R_NOTFOUND) {
+ result = query_getcachedb(client, name, qtype, dbp, options);
+ *is_zonep = ISC_FALSE;
+ }
+ return (result);
+}
+
+static inline isc_boolean_t
+query_isduplicate(ns_client_t *client, dns_name_t *name,
+ dns_rdatatype_t type, dns_name_t **mnamep)
+{
+ dns_section_t section;
+ dns_name_t *mname = NULL;
+ isc_result_t result;
+
+ CTRACE("query_isduplicate");
+
+ for (section = DNS_SECTION_ANSWER;
+ section <= DNS_SECTION_ADDITIONAL;
+ section++) {
+ result = dns_message_findname(client->message, section,
+ name, type, 0, &mname, NULL);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We've already got this RRset in the response.
+ */
+ CTRACE("query_isduplicate: true: done");
+ return (ISC_TRUE);
+ } else if (result == DNS_R_NXRRSET) {
+ /*
+ * The name exists, but the rdataset does not.
+ */
+ if (section == DNS_SECTION_ADDITIONAL)
+ break;
+ } else
+ RUNTIME_CHECK(result == DNS_R_NXDOMAIN);
+ mname = NULL;
+ }
+
+ /*
+ * If the dns_name_t we're looking up is already in the message,
+ * we don't want to trigger the caller's name replacement logic.
+ */
+ if (name == mname)
+ mname = NULL;
+
+ *mnamep = mname;
+
+ CTRACE("query_isduplicate: false: done");
+ return (ISC_FALSE);
+}
+
+static isc_result_t
+query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
+ ns_client_t *client = arg;
+ isc_result_t result, eresult;
+ dns_dbnode_t *node;
+ dns_db_t *db;
+ dns_name_t *fname, *mname;
+ dns_rdataset_t *rdataset, *sigrdataset, *trdataset;
+ isc_buffer_t *dbuf;
+ isc_buffer_t b;
+ dns_dbversion_t *version;
+ isc_boolean_t added_something, need_addname;
+ dns_zone_t *zone;
+ dns_rdatatype_t type;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(qtype != dns_rdatatype_any);
+
+ if (!WANTDNSSEC(client) && dns_rdatatype_isdnssec(qtype))
+ return (ISC_R_SUCCESS);
+
+ CTRACE("query_addadditional");
+
+ /*
+ * Initialization.
+ */
+ eresult = ISC_R_SUCCESS;
+ fname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ trdataset = NULL;
+ db = NULL;
+ version = NULL;
+ node = NULL;
+ added_something = ISC_FALSE;
+ need_addname = ISC_FALSE;
+ zone = NULL;
+
+ /*
+ * We treat type A additional section processing as if it
+ * were "any address type" additional section processing.
+ * To avoid multiple lookups, we do an 'any' database
+ * lookup and iterate over the node.
+ */
+ if (qtype == dns_rdatatype_a)
+ type = dns_rdatatype_any;
+ else
+ type = qtype;
+
+ /*
+ * Get some resources.
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ rdataset = query_newrdataset(client);
+ if (fname == NULL || rdataset == NULL)
+ goto cleanup;
+ if (WANTDNSSEC(client)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+ }
+
+ /*
+ * Look for a zone database that might contain authoritative
+ * additional data.
+ */
+ result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG,
+ &zone, &db, &version);
+ if (result != ISC_R_SUCCESS)
+ goto try_cache;
+
+ CTRACE("query_addadditional: db_find");
+
+ /*
+ * Since we are looking for authoritative data, we do not set
+ * the GLUEOK flag. Glue will be looked for later, but not
+ * necessarily in the same database.
+ */
+ node = NULL;
+ result = dns_db_find(db, name, version, type, client->query.dboptions,
+ client->now, &node, fname, rdataset,
+ sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ if (sigrdataset != NULL && !dns_db_issecure(db) &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ goto found;
+ }
+
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ version = NULL;
+ dns_db_detach(&db);
+
+ /*
+ * No authoritative data was found. The cache is our next best bet.
+ */
+
+ try_cache:
+ result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG);
+ if (result != ISC_R_SUCCESS)
+ /*
+ * Most likely the client isn't allowed to query the cache.
+ */
+ goto try_glue;
+ /*
+ * Attempt to validate glue.
+ */
+ if (sigrdataset == NULL) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+ }
+ result = dns_db_find(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, rdataset,
+ sigrdataset);
+ if (result == DNS_R_GLUE &&
+ validate(client, db, fname, rdataset, sigrdataset))
+ result = ISC_R_SUCCESS;
+ if (!WANTDNSSEC(client))
+ query_putrdataset(client, &sigrdataset);
+ if (result == ISC_R_SUCCESS)
+ goto found;
+
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+
+ try_glue:
+ /*
+ * No cached data was found. Glue is our last chance.
+ * RFC1035 sayeth:
+ *
+ * NS records cause both the usual additional section
+ * processing to locate a type A record, and, when used
+ * in a referral, a special search of the zone in which
+ * they reside for glue information.
+ *
+ * This is the "special search". Note that we must search
+ * the zone where the NS record resides, not the zone it
+ * points to, and that we only do the search in the delegation
+ * case (identified by client->query.gluedb being set).
+ */
+
+ if (client->query.gluedb == NULL)
+ goto cleanup;
+
+ /*
+ * Don't poision caches using the bailiwick protection model.
+ */
+ if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb)))
+ goto cleanup;
+
+ dns_db_attach(client->query.gluedb, &db);
+ result = dns_db_find(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, rdataset,
+ sigrdataset);
+ if (!(result == ISC_R_SUCCESS ||
+ result == DNS_R_ZONECUT ||
+ result == DNS_R_GLUE))
+ goto cleanup;
+
+ found:
+ /*
+ * We have found a potential additional data rdataset, or
+ * at least a node to iterate over.
+ */
+ query_keepname(client, fname, dbuf);
+
+ /*
+ * If we have an rdataset, add it to the additional data
+ * section.
+ */
+ mname = NULL;
+ if (dns_rdataset_isassociated(rdataset) &&
+ !query_isduplicate(client, fname, type, &mname)) {
+ if (mname != NULL) {
+ query_releasename(client, &fname);
+ fname = mname;
+ } else
+ need_addname = ISC_TRUE;
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ trdataset = rdataset;
+ rdataset = NULL;
+ added_something = ISC_TRUE;
+ /*
+ * Note: we only add SIGs if we've added the type they cover,
+ * so we do not need to check if the SIG rdataset is already
+ * in the response.
+ */
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ {
+ ISC_LIST_APPEND(fname->list, sigrdataset, link);
+ sigrdataset = NULL;
+ }
+ }
+
+ if (qtype == dns_rdatatype_a) {
+ /*
+ * We now go looking for A and AAAA records, along with
+ * their signatures.
+ *
+ * XXXRTH This code could be more efficient.
+ */
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ } else {
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ goto addname;
+ }
+ if (sigrdataset != NULL) {
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ } else if (WANTDNSSEC(client)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto addname;
+ }
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_a, 0,
+ client->now, rdataset,
+ sigrdataset);
+ if (result == DNS_R_NCACHENXDOMAIN)
+ goto addname;
+ if (result == DNS_R_NCACHENXRRSET) {
+ dns_rdataset_disassociate(rdataset);
+ /*
+ * Negative cache entries don't have sigrdatasets.
+ */
+ INSIST(sigrdataset == NULL ||
+ ! dns_rdataset_isassociated(sigrdataset));
+ }
+ if (result == ISC_R_SUCCESS) {
+ mname = NULL;
+ if (!query_isduplicate(client, fname,
+ dns_rdatatype_a, &mname)) {
+ if (mname != NULL) {
+ query_releasename(client, &fname);
+ fname = mname;
+ } else
+ need_addname = ISC_TRUE;
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ added_something = ISC_TRUE;
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ {
+ ISC_LIST_APPEND(fname->list,
+ sigrdataset, link);
+ sigrdataset =
+ query_newrdataset(client);
+ }
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ goto addname;
+ if (WANTDNSSEC(client) && sigrdataset == NULL)
+ goto addname;
+ } else {
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ }
+ }
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_aaaa, 0,
+ client->now, rdataset,
+ sigrdataset);
+ if (result == DNS_R_NCACHENXDOMAIN)
+ goto addname;
+ if (result == DNS_R_NCACHENXRRSET) {
+ dns_rdataset_disassociate(rdataset);
+ INSIST(sigrdataset == NULL ||
+ ! dns_rdataset_isassociated(sigrdataset));
+ }
+ if (result == ISC_R_SUCCESS) {
+ mname = NULL;
+ if (!query_isduplicate(client, fname,
+ dns_rdatatype_aaaa, &mname)) {
+ if (mname != NULL) {
+ query_releasename(client, &fname);
+ fname = mname;
+ } else
+ need_addname = ISC_TRUE;
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ added_something = ISC_TRUE;
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ {
+ ISC_LIST_APPEND(fname->list,
+ sigrdataset, link);
+ sigrdataset = NULL;
+ }
+ rdataset = NULL;
+ }
+ }
+ }
+
+ addname:
+ CTRACE("query_addadditional: addname");
+ /*
+ * If we haven't added anything, then we're done.
+ */
+ if (!added_something)
+ goto cleanup;
+
+ /*
+ * We may have added our rdatasets to an existing name, if so, then
+ * need_addname will be ISC_FALSE. Whether we used an existing name
+ * or a new one, we must set fname to NULL to prevent cleanup.
+ */
+ if (need_addname)
+ dns_message_addname(client->message, fname,
+ DNS_SECTION_ADDITIONAL);
+ fname = NULL;
+
+ /*
+ * In a few cases, we want to add additional data for additional
+ * data. It's simpler to just deal with special cases here than
+ * to try to create a general purpose mechanism and allow the
+ * rdata implementations to do it themselves.
+ *
+ * This involves recursion, but the depth is limited. The
+ * most complex case is adding a SRV rdataset, which involves
+ * recursing to add address records, which in turn can cause
+ * recursion to add KEYs.
+ */
+ if (type == dns_rdatatype_srv && trdataset != NULL) {
+ /*
+ * If we're adding SRV records to the additional data
+ * section, it's helpful if we add the SRV additional data
+ * as well.
+ */
+ eresult = dns_rdataset_additionaldata(trdataset,
+ query_addadditional,
+ client);
+ }
+
+ cleanup:
+ CTRACE("query_addadditional: cleanup");
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ CTRACE("query_addadditional: done");
+ return (eresult);
+}
+
+static inline void
+query_discardcache(ns_client_t *client, dns_rdataset_t *rdataset_base,
+ dns_rdatasetadditional_t additionaltype,
+ dns_rdatatype_t type, dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbversion_t **versionp, dns_dbnode_t **nodep,
+ dns_name_t *fname)
+{
+ dns_rdataset_t *rdataset;
+
+ while ((rdataset = ISC_LIST_HEAD(fname->list)) != NULL) {
+ ISC_LIST_UNLINK(fname->list, rdataset, link);
+ query_putrdataset(client, &rdataset);
+ }
+ if (*versionp != NULL)
+ dns_db_closeversion(*dbp, versionp, ISC_FALSE);
+ if (*nodep != NULL)
+ dns_db_detachnode(*dbp, nodep);
+ if (*dbp != NULL)
+ dns_db_detach(dbp);
+ if (*zonep != NULL)
+ dns_zone_detach(zonep);
+ (void)dns_rdataset_putadditional(client->view->acache, rdataset_base,
+ additionaltype, type);
+}
+
+static inline isc_result_t
+query_iscachevalid(dns_zone_t *zone, dns_db_t *db, dns_db_t *db0,
+ dns_dbversion_t *version)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_dbversion_t *version_current = NULL;
+ dns_db_t *db_current = db0;
+
+ if (db_current == NULL) {
+ result = dns_zone_getdb(zone, &db_current);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ dns_db_currentversion(db_current, &version_current);
+ if (db_current != db || version_current != version) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ cleanup:
+ dns_db_closeversion(db_current, &version_current, ISC_FALSE);
+ if (db0 == NULL && db_current != NULL)
+ dns_db_detach(&db_current);
+
+ return (result);
+}
+
+static isc_result_t
+query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
+ client_additionalctx_t *additionalctx = arg;
+ dns_rdataset_t *rdataset_base;
+ ns_client_t *client;
+ isc_result_t result, eresult;
+ dns_dbnode_t *node, *cnode;
+ dns_db_t *db, *cdb;
+ dns_name_t *fname, *mname0, cfname;
+ dns_rdataset_t *rdataset, *sigrdataset;
+ dns_rdataset_t *crdataset, *crdataset_next;
+ isc_buffer_t *dbuf;
+ isc_buffer_t b;
+ dns_dbversion_t *version, *cversion;
+ isc_boolean_t added_something, need_addname, needadditionalcache;
+ isc_boolean_t need_sigrrset;
+ dns_zone_t *zone;
+ dns_rdatatype_t type;
+ dns_rdatasetadditional_t additionaltype;
+
+ if (qtype != dns_rdatatype_a) {
+ /*
+ * This function is optimized for "address" types. For other
+ * types, use a generic routine.
+ * XXX: ideally, this function should be generic enough.
+ */
+ return (query_addadditional(additionalctx->client,
+ name, qtype));
+ }
+
+ /*
+ * Initialization.
+ */
+ rdataset_base = additionalctx->rdataset;
+ client = additionalctx->client;
+ REQUIRE(NS_CLIENT_VALID(client));
+ eresult = ISC_R_SUCCESS;
+ fname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ db = NULL;
+ cdb = NULL;
+ version = NULL;
+ cversion = NULL;
+ node = NULL;
+ cnode = NULL;
+ added_something = ISC_FALSE;
+ need_addname = ISC_FALSE;
+ zone = NULL;
+ needadditionalcache = ISC_FALSE;
+ additionaltype = dns_rdatasetadditional_fromauth;
+ dns_name_init(&cfname, NULL);
+
+ CTRACE("query_addadditional2");
+
+ /*
+ * We treat type A additional section processing as if it
+ * were "any address type" additional section processing.
+ * To avoid multiple lookups, we do an 'any' database
+ * lookup and iterate over the node.
+ * XXXJT: this approach can cause a suboptimal result when the cache
+ * DB only has partial address types and the glue DB has remaining
+ * ones.
+ */
+ type = dns_rdatatype_any;
+
+ /*
+ * Get some resources.
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL)
+ goto cleanup;
+ dns_name_setbuffer(&cfname, &b); /* share the buffer */
+
+ /* Check additional cache */
+ result = dns_rdataset_getadditional(rdataset_base, additionaltype,
+ type, client->view->acache, &zone,
+ &cdb, &cversion, &cnode, &cfname,
+ client->message, client->now);
+ if (result != ISC_R_SUCCESS)
+ goto findauthdb;
+ if (zone == NULL) {
+ CTRACE("query_addadditional2: auth zone not found");
+ goto try_cache;
+ }
+
+ /* Is the cached DB up-to-date? */
+ result = query_iscachevalid(zone, cdb, NULL, cversion);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_addadditional2: old auth additional cache");
+ query_discardcache(client, rdataset_base, additionaltype,
+ type, &zone, &cdb, &cversion, &cnode,
+ &cfname);
+ goto findauthdb;
+ }
+
+ if (cnode == NULL) {
+ /*
+ * We have a negative cache. We don't have to check the zone
+ * ACL, since the result (not using this zone) would be same
+ * regardless of the result.
+ */
+ CTRACE("query_addadditional2: negative auth additional cache");
+ dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+ dns_db_detach(&cdb);
+ dns_zone_detach(&zone);
+ goto try_cache;
+ }
+
+ result = query_validatezonedb(client, name, qtype, DNS_GETDB_NOLOG,
+ zone, cdb, NULL);
+ if (result != ISC_R_SUCCESS) {
+ query_discardcache(client, rdataset_base, additionaltype,
+ type, &zone, &cdb, &cversion, &cnode,
+ &cfname);
+ goto try_cache;
+ }
+
+ /* We've got an active cache. */
+ CTRACE("query_addadditional2: auth additional cache");
+ dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+ db = cdb;
+ node = cnode;
+ dns_name_clone(&cfname, fname);
+ query_keepname(client, fname, dbuf);
+ goto foundcache;
+
+ /*
+ * Look for a zone database that might contain authoritative
+ * additional data.
+ */
+ findauthdb:
+ result = query_getzonedb(client, name, qtype, DNS_GETDB_NOLOG,
+ &zone, &db, &version);
+ if (result != ISC_R_SUCCESS) {
+ /* Cache the negative result */
+ (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+ type, client->view->acache,
+ NULL, NULL, NULL, NULL,
+ NULL);
+ goto try_cache;
+ }
+
+ CTRACE("query_addadditional2: db_find");
+
+ /*
+ * Since we are looking for authoritative data, we do not set
+ * the GLUEOK flag. Glue will be looked for later, but not
+ * necessarily in the same database.
+ */
+ node = NULL;
+ result = dns_db_find(db, name, version, type, client->query.dboptions,
+ client->now, &node, fname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ goto found;
+
+ /* Cache the negative result */
+ (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+ type, client->view->acache, zone, db,
+ version, NULL, fname);
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ version = NULL;
+ dns_db_detach(&db);
+
+ /*
+ * No authoritative data was found. The cache is our next best bet.
+ */
+
+ try_cache:
+ additionaltype = dns_rdatasetadditional_fromcache;
+ result = query_getcachedb(client, name, qtype, &db, DNS_GETDB_NOLOG);
+ if (result != ISC_R_SUCCESS)
+ /*
+ * Most likely the client isn't allowed to query the cache.
+ */
+ goto try_glue;
+
+ result = dns_db_find(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ goto found;
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+
+ try_glue:
+ /*
+ * No cached data was found. Glue is our last chance.
+ * RFC1035 sayeth:
+ *
+ * NS records cause both the usual additional section
+ * processing to locate a type A record, and, when used
+ * in a referral, a special search of the zone in which
+ * they reside for glue information.
+ *
+ * This is the "special search". Note that we must search
+ * the zone where the NS record resides, not the zone it
+ * points to, and that we only do the search in the delegation
+ * case (identified by client->query.gluedb being set).
+ */
+ if (client->query.gluedb == NULL)
+ goto cleanup;
+
+ /*
+ * Don't poision caches using the bailiwick protection model.
+ */
+ if (!dns_name_issubdomain(name, dns_db_origin(client->query.gluedb)))
+ goto cleanup;
+
+ /* Check additional cache */
+ additionaltype = dns_rdatasetadditional_fromglue;
+ result = dns_rdataset_getadditional(rdataset_base, additionaltype,
+ type, client->view->acache, NULL,
+ &cdb, &cversion, &cnode, &cfname,
+ client->message, client->now);
+ if (result != ISC_R_SUCCESS)
+ goto findglue;
+
+ result = query_iscachevalid(zone, cdb, client->query.gluedb, cversion);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_addadditional2: old glue additional cache");
+ query_discardcache(client, rdataset_base, additionaltype,
+ type, &zone, &cdb, &cversion, &cnode,
+ &cfname);
+ goto findglue;
+ }
+
+ if (cnode == NULL) {
+ /* We have a negative cache. */
+ CTRACE("query_addadditional2: negative glue additional cache");
+ dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+ dns_db_detach(&cdb);
+ goto cleanup;
+ }
+
+ /* Cache hit. */
+ CTRACE("query_addadditional2: glue additional cache");
+ dns_db_closeversion(cdb, &cversion, ISC_FALSE);
+ db = cdb;
+ node = cnode;
+ dns_name_clone(&cfname, fname);
+ query_keepname(client, fname, dbuf);
+ goto foundcache;
+
+ findglue:
+ dns_db_attach(client->query.gluedb, &db);
+ result = dns_db_find(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, NULL, NULL);
+ if (!(result == ISC_R_SUCCESS ||
+ result == DNS_R_ZONECUT ||
+ result == DNS_R_GLUE)) {
+ /* cache the negative result */
+ (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+ type, client->view->acache,
+ NULL, db, version, NULL,
+ fname);
+ goto cleanup;
+ }
+
+ found:
+ /*
+ * We have found a DB node to iterate over from a DB.
+ * We are going to look for address RRsets (i.e., A and AAAA) in the DB
+ * node we've just found. We'll then store the complete information
+ * in the additional data cache.
+ */
+ dns_name_clone(fname, &cfname);
+ query_keepname(client, fname, dbuf);
+ needadditionalcache = ISC_TRUE;
+
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ goto cleanup;
+
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+
+ /*
+ * Find A RRset with sig RRset. Even if we don't find a sig RRset
+ * for a client using DNSSEC, we'll continue the process to make a
+ * complete list to be cached. However, we need to cancel the
+ * caching when something unexpected happens, in order to avoid
+ * caching incomplete information.
+ */
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_a, 0,
+ client->now, rdataset, sigrdataset);
+ /*
+ * If we can't promote glue/pending from the cache to secure
+ * then drop it.
+ */
+ if (result == ISC_R_SUCCESS &&
+ additionaltype == dns_rdatasetadditional_fromcache &&
+ (rdataset->trust == dns_trust_pending ||
+ rdataset->trust == dns_trust_glue) &&
+ !validate(client, db, fname, rdataset, sigrdataset)) {
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ result = ISC_R_NOTFOUND;
+ }
+ if (result == DNS_R_NCACHENXDOMAIN)
+ goto setcache;
+ if (result == DNS_R_NCACHENXRRSET) {
+ dns_rdataset_disassociate(rdataset);
+ /*
+ * Negative cache entries don't have sigrdatasets.
+ */
+ INSIST(! dns_rdataset_isassociated(sigrdataset));
+ }
+ if (result == ISC_R_SUCCESS) {
+ /* Remember the result as a cache */
+ ISC_LIST_APPEND(cfname.list, rdataset, link);
+ if (dns_rdataset_isassociated(sigrdataset)) {
+ ISC_LIST_APPEND(cfname.list, sigrdataset, link);
+ sigrdataset = query_newrdataset(client);
+ }
+ rdataset = query_newrdataset(client);
+ if (sigrdataset == NULL || rdataset == NULL) {
+ /* do not cache incomplete information */
+ goto foundcache;
+ }
+ }
+
+ /* Find AAAA RRset with sig RRset */
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_aaaa,
+ 0, client->now, rdataset, sigrdataset);
+ /*
+ * If we can't promote glue/pending from the cache to secure
+ * then drop it.
+ */
+ if (result == ISC_R_SUCCESS &&
+ additionaltype == dns_rdatasetadditional_fromcache &&
+ (rdataset->trust == dns_trust_pending ||
+ rdataset->trust == dns_trust_glue) &&
+ !validate(client, db, fname, rdataset, sigrdataset)) {
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ result = ISC_R_NOTFOUND;
+ }
+ if (result == ISC_R_SUCCESS) {
+ ISC_LIST_APPEND(cfname.list, rdataset, link);
+ rdataset = NULL;
+ if (dns_rdataset_isassociated(sigrdataset)) {
+ ISC_LIST_APPEND(cfname.list, sigrdataset, link);
+ sigrdataset = NULL;
+ }
+ }
+
+ setcache:
+ /*
+ * Set the new result in the cache if required. We do not support
+ * caching additional data from a cache DB.
+ */
+ if (needadditionalcache == ISC_TRUE &&
+ (additionaltype == dns_rdatasetadditional_fromauth ||
+ additionaltype == dns_rdatasetadditional_fromglue)) {
+ (void)dns_rdataset_setadditional(rdataset_base, additionaltype,
+ type, client->view->acache,
+ zone, db, version, node,
+ &cfname);
+ }
+
+ foundcache:
+ need_sigrrset = ISC_FALSE;
+ mname0 = NULL;
+ for (crdataset = ISC_LIST_HEAD(cfname.list);
+ crdataset != NULL;
+ crdataset = crdataset_next) {
+ dns_name_t *mname;
+
+ crdataset_next = ISC_LIST_NEXT(crdataset, link);
+
+ mname = NULL;
+ if (crdataset->type == dns_rdatatype_a ||
+ crdataset->type == dns_rdatatype_aaaa) {
+ if (!query_isduplicate(client, fname, crdataset->type,
+ &mname)) {
+ if (mname != NULL) {
+ /*
+ * A different type of this name is
+ * already stored in the additional
+ * section. We'll reuse the name.
+ * Note that this should happen at most
+ * once. Otherwise, fname->link could
+ * leak below.
+ */
+ INSIST(mname0 == NULL);
+
+ query_releasename(client, &fname);
+ fname = mname;
+ mname0 = mname;
+ } else
+ need_addname = ISC_TRUE;
+ ISC_LIST_UNLINK(cfname.list, crdataset, link);
+ ISC_LIST_APPEND(fname->list, crdataset, link);
+ added_something = ISC_TRUE;
+ need_sigrrset = ISC_TRUE;
+ } else
+ need_sigrrset = ISC_FALSE;
+ } else if (crdataset->type == dns_rdatatype_rrsig &&
+ need_sigrrset && WANTDNSSEC(client)) {
+ ISC_LIST_UNLINK(cfname.list, crdataset, link);
+ ISC_LIST_APPEND(fname->list, crdataset, link);
+ added_something = ISC_TRUE; /* just in case */
+ need_sigrrset = ISC_FALSE;
+ }
+ }
+
+ CTRACE("query_addadditional2: addname");
+
+ /*
+ * If we haven't added anything, then we're done.
+ */
+ if (!added_something)
+ goto cleanup;
+
+ /*
+ * We may have added our rdatasets to an existing name, if so, then
+ * need_addname will be ISC_FALSE. Whether we used an existing name
+ * or a new one, we must set fname to NULL to prevent cleanup.
+ */
+ if (need_addname)
+ dns_message_addname(client->message, fname,
+ DNS_SECTION_ADDITIONAL);
+ fname = NULL;
+
+ cleanup:
+ CTRACE("query_addadditional2: cleanup");
+
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ while ((crdataset = ISC_LIST_HEAD(cfname.list)) != NULL) {
+ ISC_LIST_UNLINK(cfname.list, crdataset, link);
+ query_putrdataset(client, &crdataset);
+ }
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ CTRACE("query_addadditional2: done");
+ return (eresult);
+}
+
+static inline void
+query_addrdataset(ns_client_t *client, dns_name_t *fname,
+ dns_rdataset_t *rdataset)
+{
+ client_additionalctx_t additionalctx;
+
+ /*
+ * Add 'rdataset' and any pertinent additional data to
+ * 'fname', a name in the response message for 'client'.
+ */
+
+ CTRACE("query_addrdataset");
+
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+
+ if (client->view->order != NULL)
+ rdataset->attributes |= dns_order_find(client->view->order,
+ fname, rdataset->type,
+ rdataset->rdclass);
+ rdataset->attributes |= DNS_RDATASETATTR_LOADORDER;
+
+ if (NOADDITIONAL(client))
+ return;
+
+ /*
+ * Add additional data.
+ *
+ * We don't care if dns_rdataset_additionaldata() fails.
+ */
+ additionalctx.client = client;
+ additionalctx.rdataset = rdataset;
+ (void)dns_rdataset_additionaldata(rdataset, query_addadditional2,
+ &additionalctx);
+ CTRACE("query_addrdataset: done");
+}
+
+static void
+query_addrrset(ns_client_t *client, dns_name_t **namep,
+ dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp,
+ isc_buffer_t *dbuf, dns_section_t section)
+{
+ dns_name_t *name, *mname;
+ dns_rdataset_t *rdataset, *mrdataset, *sigrdataset;
+ isc_result_t result;
+
+ /*%
+ * To the current response for 'client', add the answer RRset
+ * '*rdatasetp' and an optional signature set '*sigrdatasetp', with
+ * owner name '*namep', to section 'section', unless they are
+ * already there. Also add any pertinent additional data.
+ *
+ * If 'dbuf' is not NULL, then '*namep' is the name whose data is
+ * stored in 'dbuf'. In this case, query_addrrset() guarantees that
+ * when it returns the name will either have been kept or released.
+ */
+ CTRACE("query_addrrset");
+ name = *namep;
+ rdataset = *rdatasetp;
+ if (sigrdatasetp != NULL)
+ sigrdataset = *sigrdatasetp;
+ else
+ sigrdataset = NULL;
+ mname = NULL;
+ mrdataset = NULL;
+ result = dns_message_findname(client->message, section,
+ name, rdataset->type, rdataset->covers,
+ &mname, &mrdataset);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We've already got an RRset of the given name and type.
+ * There's nothing else to do;
+ */
+ CTRACE("query_addrrset: dns_message_findname succeeded: done");
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ return;
+ } else if (result == DNS_R_NXDOMAIN) {
+ /*
+ * The name doesn't exist.
+ */
+ if (dbuf != NULL)
+ query_keepname(client, name, dbuf);
+ dns_message_addname(client->message, name, section);
+ *namep = NULL;
+ mname = name;
+ } else {
+ RUNTIME_CHECK(result == DNS_R_NXRRSET);
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ }
+
+ if (rdataset->trust != dns_trust_secure &&
+ (section == DNS_SECTION_ANSWER ||
+ section == DNS_SECTION_AUTHORITY))
+ client->query.attributes &= ~NS_QUERYATTR_SECURE;
+ /*
+ * Note: we only add SIGs if we've added the type they cover, so
+ * we do not need to check if the SIG rdataset is already in the
+ * response.
+ */
+ query_addrdataset(client, mname, rdataset);
+ *rdatasetp = NULL;
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset)) {
+ /*
+ * We have a signature. Add it to the response.
+ */
+ ISC_LIST_APPEND(mname->list, sigrdataset, link);
+ *sigrdatasetp = NULL;
+ }
+ CTRACE("query_addrrset: done");
+}
+
+static inline isc_result_t
+query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version,
+ isc_boolean_t zero_ttl)
+{
+ dns_name_t *name;
+ dns_dbnode_t *node;
+ isc_result_t result, eresult;
+ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+ dns_rdataset_t **sigrdatasetp = NULL;
+
+ CTRACE("query_addsoa");
+ /*
+ * Initialization.
+ */
+ eresult = ISC_R_SUCCESS;
+ name = NULL;
+ rdataset = NULL;
+ node = NULL;
+
+ /*
+ * Get resources and make 'name' be the database origin.
+ */
+ result = dns_message_gettempname(client->message, &name);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_name_init(name, NULL);
+ dns_name_clone(dns_db_origin(db), name);
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL) {
+ eresult = DNS_R_SERVFAIL;
+ goto cleanup;
+ }
+ if (WANTDNSSEC(client) && dns_db_issecure(db)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ eresult = DNS_R_SERVFAIL;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Find the SOA.
+ */
+ result = dns_db_getoriginnode(db, &node);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_soa,
+ 0, client->now, rdataset,
+ sigrdataset);
+ } else {
+ dns_fixedname_t foundname;
+ dns_name_t *fname;
+
+ dns_fixedname_init(&foundname);
+ fname = dns_fixedname_name(&foundname);
+
+ result = dns_db_find(db, name, version, dns_rdatatype_soa,
+ client->query.dboptions, 0, &node,
+ fname, rdataset, sigrdataset);
+ }
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * This is bad. We tried to get the SOA RR at the zone top
+ * and it didn't work!
+ */
+ eresult = DNS_R_SERVFAIL;
+ } else {
+ /*
+ * Extract the SOA MINIMUM.
+ */
+ dns_rdata_soa_t soa;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ result = dns_rdataset_first(rdataset);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (zero_ttl) {
+ rdataset->ttl = 0;
+ if (sigrdataset != NULL)
+ sigrdataset->ttl = 0;
+ }
+
+ /*
+ * Add the SOA and its SIG to the response, with the
+ * TTLs adjusted per RFC2308 section 3.
+ */
+ if (rdataset->ttl > soa.minimum)
+ rdataset->ttl = soa.minimum;
+ if (sigrdataset != NULL && sigrdataset->ttl > soa.minimum)
+ sigrdataset->ttl = soa.minimum;
+
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL,
+ DNS_SECTION_AUTHORITY);
+ }
+
+ cleanup:
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (name != NULL)
+ query_releasename(client, &name);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ return (eresult);
+}
+
+static inline isc_result_t
+query_addns(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version) {
+ dns_name_t *name, *fname;
+ dns_dbnode_t *node;
+ isc_result_t result, eresult;
+ dns_fixedname_t foundname;
+ dns_rdataset_t *rdataset = NULL, *sigrdataset = NULL;
+ dns_rdataset_t **sigrdatasetp = NULL;
+
+ CTRACE("query_addns");
+ /*
+ * Initialization.
+ */
+ eresult = ISC_R_SUCCESS;
+ name = NULL;
+ rdataset = NULL;
+ node = NULL;
+ dns_fixedname_init(&foundname);
+ fname = dns_fixedname_name(&foundname);
+
+ /*
+ * Get resources and make 'name' be the database origin.
+ */
+ result = dns_message_gettempname(client->message, &name);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_addns: dns_message_gettempname failed: done");
+ return (result);
+ }
+ dns_name_init(name, NULL);
+ dns_name_clone(dns_db_origin(db), name);
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL) {
+ CTRACE("query_addns: query_newrdataset failed");
+ eresult = DNS_R_SERVFAIL;
+ goto cleanup;
+ }
+ if (WANTDNSSEC(client) && dns_db_issecure(db)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ CTRACE("query_addns: query_newrdataset failed");
+ eresult = DNS_R_SERVFAIL;
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Find the NS rdataset.
+ */
+ result = dns_db_getoriginnode(db, &node);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_ns,
+ 0, client->now, rdataset,
+ sigrdataset);
+ } else {
+ CTRACE("query_addns: calling dns_db_find");
+ result = dns_db_find(db, name, NULL, dns_rdatatype_ns,
+ client->query.dboptions, 0, &node,
+ fname, rdataset, sigrdataset);
+ CTRACE("query_addns: dns_db_find complete");
+ }
+ if (result != ISC_R_SUCCESS) {
+ CTRACE("query_addns: "
+ "dns_db_findrdataset or dns_db_find failed");
+ /*
+ * This is bad. We tried to get the NS rdataset at the zone
+ * top and it didn't work!
+ */
+ eresult = DNS_R_SERVFAIL;
+ } else {
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL,
+ DNS_SECTION_AUTHORITY);
+ }
+
+ cleanup:
+ CTRACE("query_addns: cleanup");
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (name != NULL)
+ query_releasename(client, &name);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ CTRACE("query_addns: done");
+ return (eresult);
+}
+
+static inline isc_result_t
+query_addcnamelike(ns_client_t *client, dns_name_t *qname, dns_name_t *tname,
+ dns_trust_t trust, dns_name_t **anamep, dns_rdatatype_t type)
+{
+ dns_rdataset_t *rdataset;
+ dns_rdatalist_t *rdatalist;
+ dns_rdata_t *rdata;
+ isc_result_t result;
+ isc_region_t r;
+
+ /*
+ * We assume the name data referred to by tname won't go away.
+ */
+
+ REQUIRE(anamep != NULL);
+
+ rdatalist = NULL;
+ result = dns_message_gettemprdatalist(client->message, &rdatalist);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdata = NULL;
+ result = dns_message_gettemprdata(client->message, &rdata);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rdataset = NULL;
+ result = dns_message_gettemprdataset(client->message, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_rdataset_init(rdataset);
+ result = dns_name_dup(qname, client->mctx, *anamep);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttemprdataset(client->message, &rdataset);
+ return (result);
+ }
+
+ rdatalist->type = type;
+ rdatalist->covers = 0;
+ rdatalist->rdclass = client->message->rdclass;
+ rdatalist->ttl = 0;
+
+ dns_name_toregion(tname, &r);
+ rdata->data = r.base;
+ rdata->length = r.length;
+ rdata->rdclass = client->message->rdclass;
+ rdata->type = type;
+
+ ISC_LIST_INIT(rdatalist->rdata);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset)
+ == ISC_R_SUCCESS);
+ rdataset->trust = trust;
+
+ query_addrrset(client, anamep, &rdataset, NULL, NULL,
+ DNS_SECTION_ANSWER);
+
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, &rdataset);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Mark the RRsets as secure. Update the cache (db) to reflect the
+ * change in trust level.
+ */
+static void
+mark_secure(ns_client_t *client, dns_db_t *db, dns_name_t *name,
+ isc_uint32_t ttl, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+
+ rdataset->trust = dns_trust_secure;
+ sigrdataset->trust = dns_trust_secure;
+
+ /*
+ * Save the updated secure state. Ignore failures.
+ */
+ result = dns_db_findnode(db, name, ISC_TRUE, &node);
+ if (result != ISC_R_SUCCESS)
+ return;
+ /*
+ * Bound the validated ttls then minimise.
+ */
+ if (sigrdataset->ttl > ttl)
+ sigrdataset->ttl = ttl;
+ if (rdataset->ttl > ttl)
+ rdataset->ttl = ttl;
+ if (rdataset->ttl > sigrdataset->ttl)
+ rdataset->ttl = sigrdataset->ttl;
+ else
+ sigrdataset->ttl = rdataset->ttl;
+
+ (void)dns_db_addrdataset(db, node, NULL, client->now, rdataset,
+ 0, NULL);
+ (void)dns_db_addrdataset(db, node, NULL, client->now, sigrdataset,
+ 0, NULL);
+ dns_db_detachnode(db, &node);
+}
+
+/*
+ * Find the secure key that corresponds to rrsig.
+ * Note: 'keyrdataset' maintains state between sucessive calls,
+ * there may be multiple keys with the same keyid.
+ * Return ISC_FALSE if we have exhausted all the possible keys.
+ */
+static isc_boolean_t
+get_key(ns_client_t *client, dns_db_t *db, dns_rdata_rrsig_t *rrsig,
+ dns_rdataset_t *keyrdataset, dst_key_t **keyp)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ isc_boolean_t secure = ISC_FALSE;
+
+ if (!dns_rdataset_isassociated(keyrdataset)) {
+ result = dns_db_findnode(db, &rrsig->signer, ISC_FALSE, &node);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+
+ result = dns_db_findrdataset(db, node, NULL,
+ dns_rdatatype_dnskey, 0,
+ client->now, keyrdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+
+ if (keyrdataset->trust != dns_trust_secure)
+ return (ISC_FALSE);
+
+ result = dns_rdataset_first(keyrdataset);
+ } else
+ result = dns_rdataset_next(keyrdataset);
+
+ for ( ; result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(keyrdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_buffer_t b;
+
+ dns_rdataset_current(keyrdataset, &rdata);
+ isc_buffer_init(&b, rdata.data, rdata.length);
+ isc_buffer_add(&b, rdata.length);
+ result = dst_key_fromdns(&rrsig->signer, rdata.rdclass, &b,
+ client->mctx, keyp);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ if (rrsig->algorithm == (dns_secalg_t)dst_key_alg(*keyp) &&
+ rrsig->keyid == (dns_keytag_t)dst_key_id(*keyp) &&
+ dst_key_iszonekey(*keyp)) {
+ secure = ISC_TRUE;
+ break;
+ }
+ dst_key_free(keyp);
+ }
+ return (secure);
+}
+
+static isc_boolean_t
+verify(dst_key_t *key, dns_name_t *name, dns_rdataset_t *rdataset,
+ dns_rdata_t *rdata, isc_mem_t *mctx, isc_boolean_t acceptexpired)
+{
+ isc_result_t result;
+ dns_fixedname_t fixed;
+ isc_boolean_t ignore = ISC_FALSE;
+
+ dns_fixedname_init(&fixed);
+
+again:
+ result = dns_dnssec_verify2(name, rdataset, key, ignore, mctx,
+ rdata, NULL);
+ if (result == DNS_R_SIGEXPIRED && acceptexpired) {
+ ignore = ISC_TRUE;
+ goto again;
+ }
+ if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD)
+ return (ISC_TRUE);
+ return (ISC_FALSE);
+}
+
+/*
+ * Validate the rdataset if possible with available records.
+ */
+static isc_boolean_t
+validate(ns_client_t *client, dns_db_t *db, dns_name_t *name,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_rrsig_t rrsig;
+ dst_key_t *key = NULL;
+ dns_rdataset_t keyrdataset;
+
+ if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset))
+ return (ISC_FALSE);
+
+ for (result = dns_rdataset_first(sigrdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(sigrdataset)) {
+
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(sigrdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_FALSE);
+ if (!dns_resolver_algorithm_supported(client->view->resolver,
+ name, rrsig.algorithm))
+ continue;
+ if (!dns_name_issubdomain(name, &rrsig.signer))
+ continue;
+ dns_rdataset_init(&keyrdataset);
+ do {
+ if (!get_key(client, db, &rrsig, &keyrdataset, &key))
+ break;
+ if (verify(key, name, rdataset, &rdata, client->mctx,
+ client->view->acceptexpired)) {
+ dst_key_free(&key);
+ dns_rdataset_disassociate(&keyrdataset);
+ mark_secure(client, db, name,
+ rrsig.originalttl,
+ rdataset, sigrdataset);
+ return (ISC_TRUE);
+ }
+ dst_key_free(&key);
+ } while (1);
+ if (dns_rdataset_isassociated(&keyrdataset))
+ dns_rdataset_disassociate(&keyrdataset);
+ }
+ return (ISC_FALSE);
+}
+
+static void
+query_addbestns(ns_client_t *client) {
+ dns_db_t *db, *zdb;
+ dns_dbnode_t *node;
+ dns_name_t *fname, *zfname;
+ dns_rdataset_t *rdataset, *sigrdataset, *zrdataset, *zsigrdataset;
+ isc_boolean_t is_zone, use_zone;
+ isc_buffer_t *dbuf;
+ isc_result_t result;
+ dns_dbversion_t *version;
+ dns_zone_t *zone;
+ isc_buffer_t b;
+
+ CTRACE("query_addbestns");
+ fname = NULL;
+ zfname = NULL;
+ rdataset = NULL;
+ zrdataset = NULL;
+ sigrdataset = NULL;
+ zsigrdataset = NULL;
+ node = NULL;
+ db = NULL;
+ zdb = NULL;
+ version = NULL;
+ zone = NULL;
+ is_zone = ISC_FALSE;
+ use_zone = ISC_FALSE;
+
+ /*
+ * Find the right database.
+ */
+ result = query_getdb(client, client->query.qname, dns_rdatatype_ns, 0,
+ &zone, &db, &version, &is_zone);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ db_find:
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ rdataset = query_newrdataset(client);
+ if (fname == NULL || rdataset == NULL)
+ goto cleanup;
+ /*
+ * Get the RRSIGs if the client requested them or if we may
+ * need to validate answers from the cache.
+ */
+ if (WANTDNSSEC(client) || !is_zone) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+ }
+
+ /*
+ * Now look for the zonecut.
+ */
+ if (is_zone) {
+ result = dns_db_find(db, client->query.qname, version,
+ dns_rdatatype_ns, client->query.dboptions,
+ client->now, &node, fname,
+ rdataset, sigrdataset);
+ if (result != DNS_R_DELEGATION)
+ goto cleanup;
+ if (USECACHE(client)) {
+ query_keepname(client, fname, dbuf);
+ zdb = db;
+ zfname = fname;
+ fname = NULL;
+ zrdataset = rdataset;
+ rdataset = NULL;
+ zsigrdataset = sigrdataset;
+ sigrdataset = NULL;
+ dns_db_detachnode(db, &node);
+ version = NULL;
+ db = NULL;
+ dns_db_attach(client->view->cachedb, &db);
+ is_zone = ISC_FALSE;
+ goto db_find;
+ }
+ } else {
+ result = dns_db_findzonecut(db, client->query.qname,
+ client->query.dboptions,
+ client->now, &node, fname,
+ rdataset, sigrdataset);
+ if (result == ISC_R_SUCCESS) {
+ if (zfname != NULL &&
+ !dns_name_issubdomain(fname, zfname)) {
+ /*
+ * We found a zonecut in the cache, but our
+ * zone delegation is better.
+ */
+ use_zone = ISC_TRUE;
+ }
+ } else if (result == ISC_R_NOTFOUND && zfname != NULL) {
+ /*
+ * We didn't find anything in the cache, but we
+ * have a zone delegation, so use it.
+ */
+ use_zone = ISC_TRUE;
+ } else
+ goto cleanup;
+ }
+
+ if (use_zone) {
+ query_releasename(client, &fname);
+ fname = zfname;
+ zfname = NULL;
+ /*
+ * We've already done query_keepname() on
+ * zfname, so we must set dbuf to NULL to
+ * prevent query_addrrset() from trying to
+ * call query_keepname() again.
+ */
+ dbuf = NULL;
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ rdataset = zrdataset;
+ zrdataset = NULL;
+ sigrdataset = zsigrdataset;
+ zsigrdataset = NULL;
+ }
+
+ /*
+ * Attempt to validate RRsets that are pending or that are glue.
+ */
+ if ((rdataset->trust == dns_trust_pending ||
+ (sigrdataset != NULL && sigrdataset->trust == dns_trust_pending))
+ && !validate(client, db, fname, rdataset, sigrdataset) &&
+ (client->query.dboptions & DNS_DBFIND_PENDINGOK) == 0)
+ goto cleanup;
+
+ if ((rdataset->trust == dns_trust_glue ||
+ (sigrdataset != NULL && sigrdataset->trust == dns_trust_glue)) &&
+ !validate(client, db, fname, rdataset, sigrdataset) &&
+ SECURE(client) && WANTDNSSEC(client))
+ goto cleanup;
+
+ /*
+ * If the client doesn't want DNSSEC we can discard the sigrdataset
+ * now.
+ */
+ if (!WANTDNSSEC(client))
+ query_putrdataset(client, &sigrdataset);
+ query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf,
+ DNS_SECTION_AUTHORITY);
+
+ cleanup:
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (zdb != NULL) {
+ query_putrdataset(client, &zrdataset);
+ if (zsigrdataset != NULL)
+ query_putrdataset(client, &zsigrdataset);
+ if (zfname != NULL)
+ query_releasename(client, &zfname);
+ dns_db_detach(&zdb);
+ }
+}
+
+static void
+fixrdataset(ns_client_t *client, dns_rdataset_t **rdataset) {
+ if (*rdataset == NULL)
+ *rdataset = query_newrdataset(client);
+ else if (dns_rdataset_isassociated(*rdataset))
+ dns_rdataset_disassociate(*rdataset);
+}
+
+static void
+fixfname(ns_client_t *client, dns_name_t **fname, isc_buffer_t **dbuf,
+ isc_buffer_t *nbuf)
+{
+ if (*fname == NULL) {
+ *dbuf = query_getnamebuf(client);
+ if (*dbuf == NULL)
+ return;
+ *fname = query_newname(client, *dbuf, nbuf);
+ }
+}
+
+static void
+query_addds(ns_client_t *client, dns_db_t *db, dns_dbnode_t *node,
+ dns_dbversion_t *version, dns_name_t *name)
+{
+ dns_fixedname_t fixed;
+ dns_name_t *fname = NULL;
+ dns_name_t *rname;
+ dns_rdataset_t *rdataset, *sigrdataset;
+ isc_buffer_t *dbuf, b;
+ isc_result_t result;
+ unsigned int count;
+
+ CTRACE("query_addds");
+ rname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+
+ /*
+ * We'll need some resources...
+ */
+ rdataset = query_newrdataset(client);
+ sigrdataset = query_newrdataset(client);
+ if (rdataset == NULL || sigrdataset == NULL)
+ goto cleanup;
+
+ /*
+ * Look for the DS record, which may or may not be present.
+ */
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_ds, 0,
+ client->now, rdataset, sigrdataset);
+ /*
+ * If we didn't find it, look for an NSEC.
+ */
+ if (result == ISC_R_NOTFOUND)
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_nsec, 0, client->now,
+ rdataset, sigrdataset);
+ if (result != ISC_R_SUCCESS && result != ISC_R_NOTFOUND)
+ goto addnsec3;
+ if (!dns_rdataset_isassociated(rdataset) ||
+ !dns_rdataset_isassociated(sigrdataset))
+ goto addnsec3;
+
+ /*
+ * We've already added the NS record, so if the name's not there,
+ * we have other problems. Use this name rather than calling
+ * query_addrrset().
+ */
+ result = dns_message_firstname(client->message, DNS_SECTION_AUTHORITY);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ rname = NULL;
+ dns_message_currentname(client->message, DNS_SECTION_AUTHORITY,
+ &rname);
+ result = dns_message_findtype(rname, dns_rdatatype_ns, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ ISC_LIST_APPEND(rname->list, rdataset, link);
+ ISC_LIST_APPEND(rname->list, sigrdataset, link);
+ rdataset = NULL;
+ sigrdataset = NULL;
+ return;
+
+ addnsec3:
+ if (dns_db_iscache(db))
+ goto cleanup;
+ /*
+ * Add the NSEC3 which proves the DS does not exist.
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ dns_fixedname_init(&fixed);
+ query_findclosestnsec3(name, db, version, client, rdataset,
+ sigrdataset, fname, ISC_TRUE,
+ dns_fixedname_name(&fixed));
+ if (!dns_rdataset_isassociated(rdataset))
+ goto cleanup;
+ query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf,
+ DNS_SECTION_AUTHORITY);
+ /*
+ * Did we find the closest provable encloser instead?
+ * If so add the nearest to the closest provable encloser.
+ */
+ if (!dns_name_equal(name, dns_fixedname_name(&fixed))) {
+ count = dns_name_countlabels(dns_fixedname_name(&fixed)) + 1;
+ dns_name_getlabelsequence(name,
+ dns_name_countlabels(name) - count,
+ count, dns_fixedname_name(&fixed));
+ fixfname(client, &fname, &dbuf, &b);
+ fixrdataset(client, &rdataset);
+ fixrdataset(client, &sigrdataset);
+ if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
+ goto cleanup;
+ query_findclosestnsec3(dns_fixedname_name(&fixed), db, version,
+ client, rdataset, sigrdataset, fname,
+ ISC_FALSE, NULL);
+ if (!dns_rdataset_isassociated(rdataset))
+ goto cleanup;
+ query_addrrset(client, &fname, &rdataset, &sigrdataset, dbuf,
+ DNS_SECTION_AUTHORITY);
+ }
+
+ cleanup:
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+}
+
+static void
+query_addwildcardproof(ns_client_t *client, dns_db_t *db,
+ dns_dbversion_t *version, dns_name_t *name,
+ isc_boolean_t ispositive)
+{
+ isc_buffer_t *dbuf, b;
+ dns_name_t *fname;
+ dns_rdataset_t *rdataset, *sigrdataset;
+ dns_fixedname_t wfixed;
+ dns_name_t *wname;
+ dns_dbnode_t *node;
+ unsigned int options;
+ unsigned int olabels, nlabels, labels;
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec_t nsec;
+ isc_boolean_t have_wname;
+ int order;
+ dns_fixedname_t cfixed;
+ dns_name_t *cname;
+
+ CTRACE("query_addwildcardproof");
+ fname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ node = NULL;
+
+ /*
+ * Get the NOQNAME proof then if !ispositve
+ * get the NOWILDCARD proof.
+ *
+ * DNS_DBFIND_NOWILD finds the NSEC records that covers the
+ * name ignoring any wildcard. From the owner and next names
+ * of this record you can compute which wildcard (if it exists)
+ * will match by finding the longest common suffix of the
+ * owner name and next names with the qname and prefixing that
+ * with the wildcard label.
+ *
+ * e.g.
+ * Given:
+ * example SOA
+ * example NSEC b.example
+ * b.example A
+ * b.example NSEC a.d.example
+ * a.d.example A
+ * a.d.example NSEC g.f.example
+ * g.f.example A
+ * g.f.example NSEC z.i.example
+ * z.i.example A
+ * z.i.example NSEC example
+ *
+ * QNAME:
+ * a.example -> example NSEC b.example
+ * owner common example
+ * next common example
+ * wild *.example
+ * d.b.example -> b.example NSEC a.d.example
+ * owner common b.example
+ * next common example
+ * wild *.b.example
+ * a.f.example -> a.d.example NSEC g.f.example
+ * owner common example
+ * next common f.example
+ * wild *.f.example
+ * j.example -> z.i.example NSEC example
+ * owner common example
+ * next common example
+ * wild *.f.example
+ */
+ options = client->query.dboptions | DNS_DBFIND_NOWILD;
+ dns_fixedname_init(&wfixed);
+ wname = dns_fixedname_name(&wfixed);
+ again:
+ have_wname = ISC_FALSE;
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ rdataset = query_newrdataset(client);
+ sigrdataset = query_newrdataset(client);
+ if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
+ goto cleanup;
+
+ result = dns_db_find(db, name, version, dns_rdatatype_nsec, options,
+ 0, &node, fname, rdataset, sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ if (!dns_rdataset_isassociated(rdataset)) {
+ /*
+ * No NSEC proof available, return NSEC3 proofs instead.
+ */
+ dns_fixedname_init(&cfixed);
+ cname = dns_fixedname_name(&cfixed);
+ /*
+ * Find the closest encloser.
+ */
+ dns_name_copy(name, cname, NULL);
+ while (result == DNS_R_NXDOMAIN) {
+ labels = dns_name_countlabels(cname) - 1;
+ dns_name_split(cname, labels, NULL, cname);
+ result = dns_db_find(db, cname, version,
+ dns_rdatatype_nsec,
+ options, 0, NULL, fname,
+ NULL, NULL);
+ }
+ /*
+ * Add closest (provable) encloser NSEC3.
+ */
+ query_findclosestnsec3(cname, db, NULL, client, rdataset,
+ sigrdataset, fname, ISC_TRUE, cname);
+ if (!dns_rdataset_isassociated(rdataset))
+ goto cleanup;
+ query_addrrset(client, &fname, &rdataset, &sigrdataset,
+ dbuf, DNS_SECTION_AUTHORITY);
+
+ /*
+ * Replace resources which were consumed by query_addrrset.
+ */
+ if (fname == NULL) {
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ }
+
+ if (rdataset == NULL)
+ rdataset = query_newrdataset(client);
+ else if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+
+ if (sigrdataset == NULL)
+ sigrdataset = query_newrdataset(client);
+ else if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+
+ if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
+ goto cleanup;
+ /*
+ * Add no qname proof.
+ */
+ labels = dns_name_countlabels(cname) + 1;
+ if (dns_name_countlabels(name) == labels)
+ dns_name_copy(name, wname, NULL);
+ else
+ dns_name_split(name, labels, NULL, wname);
+
+ query_findclosestnsec3(wname, db, NULL, client, rdataset,
+ sigrdataset, fname, ISC_FALSE, NULL);
+ if (!dns_rdataset_isassociated(rdataset))
+ goto cleanup;
+ query_addrrset(client, &fname, &rdataset, &sigrdataset,
+ dbuf, DNS_SECTION_AUTHORITY);
+
+ if (ispositive)
+ goto cleanup;
+
+ /*
+ * Replace resources which were consumed by query_addrrset.
+ */
+ if (fname == NULL) {
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ }
+
+ if (rdataset == NULL)
+ rdataset = query_newrdataset(client);
+ else if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+
+ if (sigrdataset == NULL)
+ sigrdataset = query_newrdataset(client);
+ else if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+
+ if (fname == NULL || rdataset == NULL || sigrdataset == NULL)
+ goto cleanup;
+ /*
+ * Add the no wildcard proof.
+ */
+ result = dns_name_concatenate(dns_wildcardname,
+ cname, wname, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ query_findclosestnsec3(wname, db, NULL, client, rdataset,
+ sigrdataset, fname, ISC_FALSE, NULL);
+ if (!dns_rdataset_isassociated(rdataset))
+ goto cleanup;
+ query_addrrset(client, &fname, &rdataset, &sigrdataset,
+ dbuf, DNS_SECTION_AUTHORITY);
+
+ goto cleanup;
+ } else if (result == DNS_R_NXDOMAIN) {
+ if (!ispositive)
+ result = dns_rdataset_first(rdataset);
+ if (result == ISC_R_SUCCESS) {
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &nsec, NULL);
+ }
+ if (result == ISC_R_SUCCESS) {
+ (void)dns_name_fullcompare(name, fname, &order,
+ &olabels);
+ (void)dns_name_fullcompare(name, &nsec.next, &order,
+ &nlabels);
+ /*
+ * Check for a pathological condition created when
+ * serving some malformed signed zones and bail out.
+ */
+ if (dns_name_countlabels(name) == nlabels)
+ goto cleanup;
+
+ if (olabels > nlabels)
+ dns_name_split(name, olabels, NULL, wname);
+ else
+ dns_name_split(name, nlabels, NULL, wname);
+ result = dns_name_concatenate(dns_wildcardname,
+ wname, wname, NULL);
+ if (result == ISC_R_SUCCESS)
+ have_wname = ISC_TRUE;
+ dns_rdata_freestruct(&nsec);
+ }
+ query_addrrset(client, &fname, &rdataset, &sigrdataset,
+ dbuf, DNS_SECTION_AUTHORITY);
+ }
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (have_wname) {
+ ispositive = ISC_TRUE; /* prevent loop */
+ if (!dns_name_equal(name, wname)) {
+ name = wname;
+ goto again;
+ }
+ }
+ cleanup:
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+}
+
+static void
+query_addnxrrsetnsec(ns_client_t *client, dns_db_t *db,
+ dns_dbversion_t *version, dns_name_t **namep,
+ dns_rdataset_t **rdatasetp, dns_rdataset_t **sigrdatasetp)
+{
+ dns_name_t *name;
+ dns_rdataset_t *sigrdataset;
+ dns_rdata_t sigrdata;
+ dns_rdata_rrsig_t sig;
+ unsigned int labels;
+ isc_buffer_t *dbuf, b;
+ dns_name_t *fname;
+ isc_result_t result;
+
+ name = *namep;
+ if ((name->attributes & DNS_NAMEATTR_WILDCARD) == 0) {
+ query_addrrset(client, namep, rdatasetp, sigrdatasetp,
+ NULL, DNS_SECTION_AUTHORITY);
+ return;
+ }
+
+ if (sigrdatasetp == NULL)
+ return;
+
+ sigrdataset = *sigrdatasetp;
+ if (sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset))
+ return;
+ result = dns_rdataset_first(sigrdataset);
+ if (result != ISC_R_SUCCESS)
+ return;
+ dns_rdata_init(&sigrdata);
+ dns_rdataset_current(sigrdataset, &sigrdata);
+ result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ labels = dns_name_countlabels(name);
+ if ((unsigned int)sig.labels + 1 >= labels)
+ return;
+
+ /* XXX */
+ query_addwildcardproof(client, db, version, client->query.qname,
+ ISC_TRUE);
+
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ return;
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL)
+ return;
+ dns_name_split(name, sig.labels + 1, NULL, fname);
+ /* This will succeed, since we've stripped labels. */
+ RUNTIME_CHECK(dns_name_concatenate(dns_wildcardname, fname, fname,
+ NULL) == ISC_R_SUCCESS);
+ query_addrrset(client, &fname, rdatasetp, sigrdatasetp,
+ dbuf, DNS_SECTION_AUTHORITY);
+}
+
+static void
+query_resume(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
+ ns_client_t *client;
+ isc_boolean_t fetch_cancelled, client_shuttingdown;
+
+ /*
+ * Resume a query after recursion.
+ */
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+ client = devent->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+ REQUIRE(RECURSING(client));
+
+ LOCK(&client->query.fetchlock);
+ if (client->query.fetch != NULL) {
+ /*
+ * This is the fetch we've been waiting for.
+ */
+ INSIST(devent->fetch == client->query.fetch);
+ client->query.fetch = NULL;
+ fetch_cancelled = ISC_FALSE;
+ /*
+ * Update client->now.
+ */
+ isc_stdtime_get(&client->now);
+ } else {
+ /*
+ * This is a fetch completion event for a cancelled fetch.
+ * Clean up and don't resume the find.
+ */
+ fetch_cancelled = ISC_TRUE;
+ }
+ UNLOCK(&client->query.fetchlock);
+ INSIST(client->query.fetch == NULL);
+
+ client->query.attributes &= ~NS_QUERYATTR_RECURSING;
+ dns_resolver_destroyfetch(&devent->fetch);
+
+ /*
+ * If this client is shutting down, or this transaction
+ * has timed out, do not resume the find.
+ */
+ client_shuttingdown = ns_client_shuttingdown(client);
+ if (fetch_cancelled || client_shuttingdown) {
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ query_putrdataset(client, &devent->rdataset);
+ if (devent->sigrdataset != NULL)
+ query_putrdataset(client, &devent->sigrdataset);
+ isc_event_free(&event);
+ if (fetch_cancelled)
+ query_error(client, DNS_R_SERVFAIL);
+ else
+ query_next(client, ISC_R_CANCELED);
+ /*
+ * This may destroy the client.
+ */
+ ns_client_detach(&client);
+ } else {
+ query_find(client, devent, 0);
+ }
+}
+
+static isc_result_t
+query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
+ dns_rdataset_t *nameservers, isc_boolean_t resuming)
+{
+ isc_result_t result;
+ dns_rdataset_t *rdataset, *sigrdataset;
+ isc_sockaddr_t *peeraddr;
+
+ if (!resuming)
+ inc_stats(client, dns_nsstatscounter_recursion);
+
+ /*
+ * We are about to recurse, which means that this client will
+ * be unavailable for serving new requests for an indeterminate
+ * amount of time. If this client is currently responsible
+ * for handling incoming queries, set up a new client
+ * object to handle them while we are waiting for a
+ * response. There is no need to replace TCP clients
+ * because those have already been replaced when the
+ * connection was accepted (if allowed by the TCP quota).
+ */
+ if (client->recursionquota == NULL) {
+ result = isc_quota_attach(&ns_g_server->recursionquota,
+ &client->recursionquota);
+ if (result == ISC_R_SOFTQUOTA) {
+ static isc_stdtime_t last = 0;
+ isc_stdtime_t now;
+ isc_stdtime_get(&now);
+ if (now != last) {
+ last = now;
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_WARNING,
+ "recursive-clients soft limit "
+ "exceeded, aborting oldest query");
+ }
+ ns_client_killoldestquery(client);
+ result = ISC_R_SUCCESS;
+ } else if (result == ISC_R_QUOTA) {
+ static isc_stdtime_t last = 0;
+ isc_stdtime_t now;
+ isc_stdtime_get(&now);
+ if (now != last) {
+ last = now;
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_WARNING,
+ "no more recursive clients: %s",
+ isc_result_totext(result));
+ }
+ ns_client_killoldestquery(client);
+ }
+ if (result == ISC_R_SUCCESS && !client->mortal &&
+ (client->attributes & NS_CLIENTATTR_TCP) == 0) {
+ result = ns_client_replace(client);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_WARNING,
+ "ns_client_replace() failed: %s",
+ isc_result_totext(result));
+ isc_quota_detach(&client->recursionquota);
+ }
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ ns_client_recursing(client);
+ }
+
+ /*
+ * Invoke the resolver.
+ */
+ REQUIRE(nameservers == NULL || nameservers->type == dns_rdatatype_ns);
+ REQUIRE(client->query.fetch == NULL);
+
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ return (ISC_R_NOMEMORY);
+ if (WANTDNSSEC(client)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ query_putrdataset(client, &rdataset);
+ return (ISC_R_NOMEMORY);
+ }
+ } else
+ sigrdataset = NULL;
+
+ if (client->query.timerset == ISC_FALSE)
+ ns_client_settimeout(client, 60);
+ if ((client->attributes & NS_CLIENTATTR_TCP) == 0)
+ peeraddr = &client->peeraddr;
+ else
+ peeraddr = NULL;
+ result = dns_resolver_createfetch2(client->view->resolver,
+ client->query.qname,
+ qtype, qdomain, nameservers,
+ NULL, peeraddr, client->message->id,
+ client->query.fetchoptions,
+ client->task,
+ query_resume, client,
+ rdataset, sigrdataset,
+ &client->query.fetch);
+
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * Record that we're waiting for an event. A client which
+ * is shutting down will not be destroyed until all the
+ * events have been received.
+ */
+ } else {
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ }
+
+ return (result);
+}
+
+#define MAX_RESTARTS 16
+
+#define QUERY_ERROR(r) \
+do { \
+ eresult = r; \
+ want_restart = ISC_FALSE; \
+} while (0)
+
+/*
+ * Extract a network address from the RDATA of an A or AAAA
+ * record.
+ *
+ * Returns:
+ * ISC_R_SUCCESS
+ * ISC_R_NOTIMPLEMENTED The rdata is not a known address type.
+ */
+static isc_result_t
+rdata_tonetaddr(const dns_rdata_t *rdata, isc_netaddr_t *netaddr) {
+ struct in_addr ina;
+ struct in6_addr in6a;
+
+ switch (rdata->type) {
+ case dns_rdatatype_a:
+ INSIST(rdata->length == 4);
+ memcpy(&ina.s_addr, rdata->data, 4);
+ isc_netaddr_fromin(netaddr, &ina);
+ return (ISC_R_SUCCESS);
+ case dns_rdatatype_aaaa:
+ INSIST(rdata->length == 16);
+ memcpy(in6a.s6_addr, rdata->data, 16);
+ isc_netaddr_fromin6(netaddr, &in6a);
+ return (ISC_R_SUCCESS);
+ default:
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+}
+
+/*
+ * Find the sort order of 'rdata' in the topology-like
+ * ACL forming the second element in a 2-element top-level
+ * sortlist statement.
+ */
+static int
+query_sortlist_order_2element(const dns_rdata_t *rdata, const void *arg) {
+ isc_netaddr_t netaddr;
+
+ if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS)
+ return (INT_MAX);
+ return (ns_sortlist_addrorder2(&netaddr, arg));
+}
+
+/*
+ * Find the sort order of 'rdata' in the matching element
+ * of a 1-element top-level sortlist statement.
+ */
+static int
+query_sortlist_order_1element(const dns_rdata_t *rdata, const void *arg) {
+ isc_netaddr_t netaddr;
+
+ if (rdata_tonetaddr(rdata, &netaddr) != ISC_R_SUCCESS)
+ return (INT_MAX);
+ return (ns_sortlist_addrorder1(&netaddr, arg));
+}
+
+/*
+ * Find the sortlist statement that applies to 'client' and set up
+ * the sortlist info in in client->message appropriately.
+ */
+static void
+setup_query_sortlist(ns_client_t *client) {
+ isc_netaddr_t netaddr;
+ dns_rdatasetorderfunc_t order = NULL;
+ const void *order_arg = NULL;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ switch (ns_sortlist_setup(client->view->sortlist,
+ &netaddr, &order_arg)) {
+ case NS_SORTLISTTYPE_1ELEMENT:
+ order = query_sortlist_order_1element;
+ break;
+ case NS_SORTLISTTYPE_2ELEMENT:
+ order = query_sortlist_order_2element;
+ break;
+ case NS_SORTLISTTYPE_NONE:
+ order = NULL;
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ dns_message_setsortorder(client->message, order, order_arg);
+}
+
+static void
+query_addnoqnameproof(ns_client_t *client, dns_rdataset_t *rdataset) {
+ isc_buffer_t *dbuf, b;
+ dns_name_t *fname;
+ dns_rdataset_t *neg, *negsig;
+ isc_result_t result = ISC_R_NOMEMORY;
+
+ CTRACE("query_addnoqnameproof");
+
+ fname = NULL;
+ neg = NULL;
+ negsig = NULL;
+
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ neg = query_newrdataset(client);
+ negsig = query_newrdataset(client);
+ if (fname == NULL || neg == NULL || negsig == NULL)
+ goto cleanup;
+
+ result = dns_rdataset_getnoqname(rdataset, fname, neg, negsig);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ query_addrrset(client, &fname, &neg, &negsig, dbuf,
+ DNS_SECTION_AUTHORITY);
+
+ if ((rdataset->attributes & DNS_RDATASETATTR_CLOSEST) == 0)
+ goto cleanup;
+
+ if (fname == NULL) {
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL)
+ goto cleanup;
+ fname = query_newname(client, dbuf, &b);
+ }
+ if (neg == NULL)
+ neg = query_newrdataset(client);
+ else if (dns_rdataset_isassociated(neg))
+ dns_rdataset_disassociate(neg);
+ if (negsig == NULL)
+ negsig = query_newrdataset(client);
+ else if (dns_rdataset_isassociated(negsig))
+ dns_rdataset_disassociate(negsig);
+ if (fname == NULL || neg == NULL || negsig == NULL)
+ goto cleanup;
+ result = dns_rdataset_getclosest(rdataset, fname, neg, negsig);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ query_addrrset(client, &fname, &neg, &negsig, dbuf,
+ DNS_SECTION_AUTHORITY);
+
+ cleanup:
+ if (neg != NULL)
+ query_putrdataset(client, &neg);
+ if (negsig != NULL)
+ query_putrdataset(client, &negsig);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+}
+
+static inline void
+answer_in_glue(ns_client_t *client, dns_rdatatype_t qtype) {
+ dns_name_t *name;
+ dns_message_t *msg;
+ dns_section_t section = DNS_SECTION_ADDITIONAL;
+ dns_rdataset_t *rdataset = NULL;
+
+ msg = client->message;
+ for (name = ISC_LIST_HEAD(msg->sections[section]);
+ name != NULL;
+ name = ISC_LIST_NEXT(name, link))
+ if (dns_name_equal(name, client->query.qname)) {
+ for (rdataset = ISC_LIST_HEAD(name->list);
+ rdataset != NULL;
+ rdataset = ISC_LIST_NEXT(rdataset, link))
+ if (rdataset->type == qtype)
+ break;
+ break;
+ }
+ if (rdataset != NULL) {
+ ISC_LIST_UNLINK(msg->sections[section], name, link);
+ ISC_LIST_PREPEND(msg->sections[section], name, link);
+ ISC_LIST_UNLINK(name->list, rdataset, link);
+ ISC_LIST_PREPEND(name->list, rdataset, link);
+ rdataset->attributes |= DNS_RDATASETATTR_REQUIREDGLUE;
+ }
+}
+
+#define NS_NAME_INIT(A,B) \
+ { \
+ DNS_NAME_MAGIC, \
+ A, sizeof(A), sizeof(B), \
+ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, \
+ B, NULL, { (void *)-1, (void *)-1}, \
+ {NULL, NULL} \
+ }
+
+static unsigned char inaddr10_offsets[] = { 0, 3, 11, 16 };
+static unsigned char inaddr172_offsets[] = { 0, 3, 7, 15, 20 };
+static unsigned char inaddr192_offsets[] = { 0, 4, 8, 16, 21 };
+
+static unsigned char inaddr10[] = "\00210\007IN-ADDR\004ARPA";
+
+static unsigned char inaddr16172[] = "\00216\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr17172[] = "\00217\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr18172[] = "\00218\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr19172[] = "\00219\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr20172[] = "\00220\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr21172[] = "\00221\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr22172[] = "\00222\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr23172[] = "\00223\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr24172[] = "\00224\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr25172[] = "\00225\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr26172[] = "\00226\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr27172[] = "\00227\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr28172[] = "\00228\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr29172[] = "\00229\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr30172[] = "\00230\003172\007IN-ADDR\004ARPA";
+static unsigned char inaddr31172[] = "\00231\003172\007IN-ADDR\004ARPA";
+
+static unsigned char inaddr168192[] = "\003168\003192\007IN-ADDR\004ARPA";
+
+static dns_name_t rfc1918names[] = {
+ NS_NAME_INIT(inaddr10, inaddr10_offsets),
+ NS_NAME_INIT(inaddr16172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr17172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr18172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr19172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr20172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr21172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr22172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr23172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr24172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr25172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr26172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr27172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr28172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr29172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr30172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr31172, inaddr172_offsets),
+ NS_NAME_INIT(inaddr168192, inaddr192_offsets)
+};
+
+
+static unsigned char prisoner_data[] = "\010prisoner\004iana\003org";
+static unsigned char hostmaster_data[] = "\012hostmaster\014root-servers\003org";
+
+static unsigned char prisoner_offsets[] = { 0, 9, 14, 18 };
+static unsigned char hostmaster_offsets[] = { 0, 11, 24, 28 };
+
+static dns_name_t prisoner = NS_NAME_INIT(prisoner_data, prisoner_offsets);
+static dns_name_t hostmaster = NS_NAME_INIT(hostmaster_data, hostmaster_offsets);
+
+static void
+warn_rfc1918(ns_client_t *client, dns_name_t *fname, dns_rdataset_t *rdataset) {
+ unsigned int i;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_soa_t soa;
+ dns_rdataset_t found;
+ isc_result_t result;
+
+ for (i = 0; i < (sizeof(rfc1918names)/sizeof(*rfc1918names)); i++) {
+ if (dns_name_issubdomain(fname, &rfc1918names[i])) {
+ dns_rdataset_init(&found);
+ result = dns_ncache_getrdataset(rdataset,
+ &rfc1918names[i],
+ dns_rdatatype_soa,
+ &found);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ result = dns_rdataset_first(&found);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(&found, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ if (result != ISC_R_SUCCESS)
+ return;
+ if (dns_name_equal(&soa.origin, &prisoner) &&
+ dns_name_equal(&soa.contact, &hostmaster)) {
+ char buf[DNS_NAME_FORMATSIZE];
+ dns_name_format(fname, buf, sizeof(buf));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_WARNING,
+ "RFC 1918 response from "
+ "Internet for %s", buf);
+ }
+ dns_rdataset_disassociate(&found);
+ return;
+ }
+ }
+}
+
+static void
+query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
+ dns_dbversion_t *version, ns_client_t *client,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset,
+ dns_name_t *fname, isc_boolean_t exact,
+ dns_name_t *found)
+{
+ unsigned char salt[256];
+ size_t salt_length = sizeof(salt);
+ isc_uint16_t iterations;
+ isc_result_t result;
+ unsigned int dboptions;
+ dns_fixedname_t fixed;
+ dns_hash_t hash;
+ dns_name_t name;
+ int order;
+ unsigned int count;
+ dns_rdata_nsec3_t nsec3;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_boolean_t optout;
+
+ salt_length = sizeof(salt);
+ result = dns_db_getnsec3parameters(db, version, &hash, NULL,
+ &iterations, salt, &salt_length);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ dns_name_init(&name, NULL);
+ dns_name_clone(qname, &name);
+
+ /*
+ * Map unknown algorithm to known value.
+ */
+ if (hash == DNS_NSEC3_UNKNOWNALG)
+ hash = 1;
+
+ again:
+ dns_fixedname_init(&fixed);
+ result = dns_nsec3_hashname(&fixed, NULL, NULL, &name,
+ dns_db_origin(db), hash,
+ iterations, salt, salt_length);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ dboptions = client->query.dboptions | DNS_DBFIND_FORCENSEC3;
+ result = dns_db_find(db, dns_fixedname_name(&fixed), version,
+ dns_rdatatype_nsec3, dboptions, client->now,
+ NULL, fname, rdataset, sigrdataset);
+
+ if (result == DNS_R_NXDOMAIN) {
+ if (!dns_rdataset_isassociated(rdataset)) {
+ return;
+ }
+ result = dns_rdataset_first(rdataset);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, &rdata);
+ dns_rdata_tostruct(&rdata, &nsec3, NULL);
+ dns_rdata_reset(&rdata);
+ optout = ISC_TF((nsec3.flags & DNS_NSEC3FLAG_OPTOUT) != 0);
+ if (found != NULL && optout &&
+ dns_name_fullcompare(&name, dns_db_origin(db), &order,
+ &count) == dns_namereln_subdomain) {
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ count = dns_name_countlabels(&name) - 1;
+ dns_name_getlabelsequence(&name, 1, count, &name);
+ ns_client_log(client, DNS_LOGCATEGORY_DNSSEC,
+ NS_LOGMODULE_QUERY, ISC_LOG_DEBUG(3),
+ "looking for closest provable encloser");
+ goto again;
+ }
+ if (exact)
+ ns_client_log(client, DNS_LOGCATEGORY_DNSSEC,
+ NS_LOGMODULE_QUERY, ISC_LOG_WARNING,
+ "expected a exact match NSEC3, got "
+ "a covering record");
+
+ } else if (result != ISC_R_SUCCESS) {
+ return;
+ } else if (!exact)
+ ns_client_log(client, DNS_LOGCATEGORY_DNSSEC,
+ NS_LOGMODULE_QUERY, ISC_LOG_WARNING,
+ "expected covering NSEC3, got an exact match");
+ if (found != NULL)
+ dns_name_copy(&name, found, NULL);
+ return;
+}
+
+/*
+ * Do the bulk of query processing for the current query of 'client'.
+ * If 'event' is non-NULL, we are returning from recursion and 'qtype'
+ * is ignored. Otherwise, 'qtype' is the query type.
+ */
+static void
+query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
+{
+ dns_db_t *db, *zdb;
+ dns_dbnode_t *node;
+ dns_rdatatype_t type;
+ dns_name_t *fname, *zfname, *tname, *prefix;
+ dns_rdataset_t *rdataset, *trdataset;
+ dns_rdataset_t *sigrdataset, *zrdataset, *zsigrdataset;
+ dns_rdataset_t **sigrdatasetp;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatasetiter_t *rdsiter;
+ isc_boolean_t want_restart, authoritative, is_zone, need_wildcardproof;
+ unsigned int n, nlabels;
+ dns_namereln_t namereln;
+ int order;
+ isc_buffer_t *dbuf;
+ isc_buffer_t b;
+ isc_result_t result, eresult;
+ dns_fixedname_t fixed;
+ dns_fixedname_t wildcardname;
+ dns_dbversion_t *version, *zversion;
+ dns_zone_t *zone;
+ dns_rdata_cname_t cname;
+ dns_rdata_dname_t dname;
+ unsigned int options;
+ isc_boolean_t empty_wild;
+ dns_rdataset_t *noqname;
+ isc_boolean_t resuming;
+
+ CTRACE("query_find");
+
+ /*
+ * One-time initialization.
+ *
+ * It's especially important to initialize anything that the cleanup
+ * code might cleanup.
+ */
+
+ eresult = ISC_R_SUCCESS;
+ fname = NULL;
+ zfname = NULL;
+ rdataset = NULL;
+ zrdataset = NULL;
+ sigrdataset = NULL;
+ zsigrdataset = NULL;
+ zversion = NULL;
+ node = NULL;
+ db = NULL;
+ zdb = NULL;
+ version = NULL;
+ zone = NULL;
+ need_wildcardproof = ISC_FALSE;
+ empty_wild = ISC_FALSE;
+ options = 0;
+ resuming = ISC_FALSE;
+ is_zone = ISC_FALSE;
+
+ if (event != NULL) {
+ /*
+ * We're returning from recursion. Restore the query context
+ * and resume.
+ */
+
+ want_restart = ISC_FALSE;
+ authoritative = ISC_FALSE;
+
+ qtype = event->qtype;
+ if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig)
+ type = dns_rdatatype_any;
+ else
+ type = qtype;
+ db = event->db;
+ node = event->node;
+ rdataset = event->rdataset;
+ sigrdataset = event->sigrdataset;
+
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ tname = dns_fixedname_name(&event->foundname);
+ result = dns_name_copy(tname, fname, NULL);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+
+ result = event->result;
+ resuming = ISC_TRUE;
+
+ goto resume;
+ }
+
+ /*
+ * Not returning from recursion.
+ */
+
+ /*
+ * If it's a SIG query, we'll iterate the node.
+ */
+ if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig)
+ type = dns_rdatatype_any;
+ else
+ type = qtype;
+
+ restart:
+ CTRACE("query_find: restart");
+ want_restart = ISC_FALSE;
+ authoritative = ISC_FALSE;
+ version = NULL;
+ need_wildcardproof = ISC_FALSE;
+
+ if (client->view->checknames &&
+ !dns_rdata_checkowner(client->query.qname,
+ client->message->rdclass,
+ qtype, ISC_FALSE)) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typename[DNS_RDATATYPE_FORMATSIZE];
+ char classname[DNS_RDATACLASS_FORMATSIZE];
+
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(qtype, typename, sizeof(typename));
+ dns_rdataclass_format(client->message->rdclass, classname,
+ sizeof(classname));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY, ISC_LOG_ERROR,
+ "check-names failure %s/%s/%s", namebuf,
+ typename, classname);
+ QUERY_ERROR(DNS_R_REFUSED);
+ goto cleanup;
+ }
+
+ /*
+ * First we must find the right database.
+ */
+ options &= DNS_GETDB_NOLOG; /* Preserve DNS_GETDB_NOLOG. */
+ if (dns_rdatatype_atparent(qtype) &&
+ !dns_name_equal(client->query.qname, dns_rootname))
+ options |= DNS_GETDB_NOEXACT;
+ result = query_getdb(client, client->query.qname, qtype, options,
+ &zone, &db, &version, &is_zone);
+ if ((result != ISC_R_SUCCESS || !is_zone) && !RECURSIONOK(client) &&
+ (options & DNS_GETDB_NOEXACT) != 0 && qtype == dns_rdatatype_ds) {
+ /*
+ * Look to see if we are authoritative for the
+ * child zone if the query type is DS.
+ */
+ dns_db_t *tdb = NULL;
+ dns_zone_t *tzone = NULL;
+ dns_dbversion_t *tversion = NULL;
+ isc_result_t tresult;
+
+ tresult = query_getzonedb(client, client->query.qname, qtype,
+ DNS_GETDB_PARTIAL, &tzone, &tdb,
+ &tversion);
+ if (tresult == ISC_R_SUCCESS) {
+ options &= ~DNS_GETDB_NOEXACT;
+ query_putrdataset(client, &rdataset);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ version = tversion;
+ db = tdb;
+ zone = tzone;
+ is_zone = ISC_TRUE;
+ result = ISC_R_SUCCESS;
+ } else {
+ if (tdb != NULL)
+ dns_db_detach(&tdb);
+ if (tzone != NULL)
+ dns_zone_detach(&tzone);
+ }
+ }
+ if (result != ISC_R_SUCCESS) {
+ if (result == DNS_R_REFUSED) {
+ if (WANTRECURSION(client)) {
+ inc_stats(client,
+ dns_nsstatscounter_recurserej);
+ } else
+ inc_stats(client, dns_nsstatscounter_authrej);
+ if (!PARTIALANSWER(client))
+ QUERY_ERROR(DNS_R_REFUSED);
+ } else
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+
+ if (is_zone)
+ authoritative = ISC_TRUE;
+
+ if (event == NULL && client->query.restarts == 0) {
+ if (is_zone) {
+#ifdef DLZ
+ if (zone != NULL) {
+ /*
+ * if is_zone = true, zone = NULL then this is
+ * a DLZ zone. Don't attempt to attach zone.
+ */
+#endif
+ dns_zone_attach(zone, &client->query.authzone);
+#ifdef DLZ
+ }
+#endif
+ dns_db_attach(db, &client->query.authdb);
+ }
+ client->query.authdbset = ISC_TRUE;
+ }
+
+ db_find:
+ CTRACE("query_find: db_find");
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ rdataset = query_newrdataset(client);
+ if (fname == NULL || rdataset == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ if (WANTDNSSEC(client) && (!is_zone || dns_db_issecure(db))) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Now look for an answer in the database.
+ */
+ result = dns_db_find(db, client->query.qname, version, type,
+ client->query.dboptions, client->now,
+ &node, fname, rdataset, sigrdataset);
+
+ resume:
+ CTRACE("query_find: resume");
+ switch (result) {
+ case ISC_R_SUCCESS:
+ /*
+ * This case is handled in the main line below.
+ */
+ break;
+ case DNS_R_GLUE:
+ case DNS_R_ZONECUT:
+ /*
+ * These cases are handled in the main line below.
+ */
+ INSIST(is_zone);
+ authoritative = ISC_FALSE;
+ break;
+ case ISC_R_NOTFOUND:
+ /*
+ * The cache doesn't even have the root NS. Get them from
+ * the hints DB.
+ */
+ INSIST(!is_zone);
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ if (client->view->hints == NULL) {
+ /* We have no hints. */
+ result = ISC_R_FAILURE;
+ } else {
+ dns_db_attach(client->view->hints, &db);
+ result = dns_db_find(db, dns_rootname,
+ NULL, dns_rdatatype_ns,
+ 0, client->now, &node, fname,
+ rdataset, sigrdataset);
+ }
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * Nonsensical root hints may require cleanup.
+ */
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ /*
+ * We don't have any root server hints, but
+ * we may have working forwarders, so try to
+ * recurse anyway.
+ */
+ if (RECURSIONOK(client)) {
+ result = query_recurse(client, qtype,
+ NULL, NULL, resuming);
+ if (result == ISC_R_SUCCESS)
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ else if (result == DNS_R_DUPLICATE ||
+ result == DNS_R_DROP) {
+ /* Duplicate query. */
+ QUERY_ERROR(result);
+ } else {
+ /* Unable to recurse. */
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ }
+ goto cleanup;
+ } else {
+ /* Unable to give root server referral. */
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+ /*
+ * XXXRTH We should trigger root server priming here.
+ */
+ /* FALLTHROUGH */
+ case DNS_R_DELEGATION:
+ authoritative = ISC_FALSE;
+ if (is_zone) {
+ /*
+ * Look to see if we are authoritative for the
+ * child zone if the query type is DS.
+ */
+ if (!RECURSIONOK(client) &&
+ (options & DNS_GETDB_NOEXACT) != 0 &&
+ qtype == dns_rdatatype_ds) {
+ dns_db_t *tdb = NULL;
+ dns_zone_t *tzone = NULL;
+ dns_dbversion_t *tversion = NULL;
+ result = query_getzonedb(client,
+ client->query.qname,
+ qtype,
+ DNS_GETDB_PARTIAL,
+ &tzone, &tdb,
+ &tversion);
+ if (result == ISC_R_SUCCESS) {
+ options &= ~DNS_GETDB_NOEXACT;
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client,
+ &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client,
+ &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ version = tversion;
+ db = tdb;
+ zone = tzone;
+ authoritative = ISC_TRUE;
+ goto db_find;
+ }
+ if (tdb != NULL)
+ dns_db_detach(&tdb);
+ if (tzone != NULL)
+ dns_zone_detach(&tzone);
+ }
+ /*
+ * We're authoritative for an ancestor of QNAME.
+ */
+ if (!USECACHE(client) || !RECURSIONOK(client)) {
+ dns_fixedname_t fixed;
+
+ dns_fixedname_init(&fixed);
+ dns_name_copy(fname,
+ dns_fixedname_name(&fixed), NULL);
+
+ /*
+ * If we don't have a cache, this is the best
+ * answer.
+ *
+ * If the client is making a nonrecursive
+ * query we always give out the authoritative
+ * delegation. This way even if we get
+ * junk in our cache, we won't fail in our
+ * role as the delegating authority if another
+ * nameserver asks us about a delegated
+ * subzone.
+ *
+ * We enable the retrieval of glue for this
+ * database by setting client->query.gluedb.
+ */
+ client->query.gluedb = db;
+ client->query.isreferral = ISC_TRUE;
+ /*
+ * We must ensure NOADDITIONAL is off,
+ * because the generation of
+ * additional data is required in
+ * delegations.
+ */
+ client->query.attributes &=
+ ~NS_QUERYATTR_NOADDITIONAL;
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ query_addrrset(client, &fname,
+ &rdataset, sigrdatasetp,
+ dbuf, DNS_SECTION_AUTHORITY);
+ client->query.gluedb = NULL;
+ if (WANTDNSSEC(client))
+ query_addds(client, db, node, version,
+ dns_fixedname_name(&fixed));
+ } else {
+ /*
+ * We might have a better answer or delegation
+ * in the cache. We'll remember the current
+ * values of fname, rdataset, and sigrdataset.
+ * We'll then go looking for QNAME in the
+ * cache. If we find something better, we'll
+ * use it instead.
+ */
+ query_keepname(client, fname, dbuf);
+ zdb = db;
+ zfname = fname;
+ fname = NULL;
+ zrdataset = rdataset;
+ rdataset = NULL;
+ zsigrdataset = sigrdataset;
+ sigrdataset = NULL;
+ dns_db_detachnode(db, &node);
+ zversion = version;
+ version = NULL;
+ db = NULL;
+ dns_db_attach(client->view->cachedb, &db);
+ is_zone = ISC_FALSE;
+ goto db_find;
+ }
+ } else {
+ if (zfname != NULL &&
+ !dns_name_issubdomain(fname, zfname)) {
+ /*
+ * We've already got a delegation from
+ * authoritative data, and it is better
+ * than what we found in the cache. Use
+ * it instead of the cache delegation.
+ */
+ query_releasename(client, &fname);
+ fname = zfname;
+ zfname = NULL;
+ /*
+ * We've already done query_keepname() on
+ * zfname, so we must set dbuf to NULL to
+ * prevent query_addrrset() from trying to
+ * call query_keepname() again.
+ */
+ dbuf = NULL;
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client,
+ &sigrdataset);
+ rdataset = zrdataset;
+ zrdataset = NULL;
+ sigrdataset = zsigrdataset;
+ zsigrdataset = NULL;
+ version = zversion;
+ zversion = NULL;
+ /*
+ * We don't clean up zdb here because we
+ * may still need it. It will get cleaned
+ * up by the main cleanup code.
+ */
+ }
+
+ if (RECURSIONOK(client)) {
+ /*
+ * Recurse!
+ */
+ if (dns_rdatatype_atparent(type))
+ result = query_recurse(client, qtype,
+ NULL, NULL,
+ resuming);
+ else
+ result = query_recurse(client, qtype,
+ fname, rdataset,
+ resuming);
+ if (result == ISC_R_SUCCESS)
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ else if (result == DNS_R_DUPLICATE ||
+ result == DNS_R_DROP)
+ QUERY_ERROR(result);
+ else
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ } else {
+ dns_fixedname_t fixed;
+
+ dns_fixedname_init(&fixed);
+ dns_name_copy(fname,
+ dns_fixedname_name(&fixed), NULL);
+ /*
+ * This is the best answer.
+ */
+ client->query.attributes |=
+ NS_QUERYATTR_CACHEGLUEOK;
+ client->query.gluedb = zdb;
+ client->query.isreferral = ISC_TRUE;
+ /*
+ * We must ensure NOADDITIONAL is off,
+ * because the generation of
+ * additional data is required in
+ * delegations.
+ */
+ client->query.attributes &=
+ ~NS_QUERYATTR_NOADDITIONAL;
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ query_addrrset(client, &fname,
+ &rdataset, sigrdatasetp,
+ dbuf, DNS_SECTION_AUTHORITY);
+ client->query.gluedb = NULL;
+ client->query.attributes &=
+ ~NS_QUERYATTR_CACHEGLUEOK;
+ if (WANTDNSSEC(client))
+ query_addds(client, db, node, version,
+ dns_fixedname_name(&fixed));
+ }
+ }
+ goto cleanup;
+ case DNS_R_EMPTYNAME:
+ result = DNS_R_NXRRSET;
+ /* FALLTHROUGH */
+ case DNS_R_NXRRSET:
+ INSIST(is_zone);
+ /*
+ * Look for a NSEC3 record if we don't have a NSEC record.
+ */
+ if (!dns_rdataset_isassociated(rdataset) &&
+ WANTDNSSEC(client)) {
+ if ((fname->attributes & DNS_NAMEATTR_WILDCARD) == 0) {
+ dns_name_t *found;
+ dns_name_t *qname;
+
+ dns_fixedname_init(&fixed);
+ found = dns_fixedname_name(&fixed);
+ qname = client->query.qname;
+
+ query_findclosestnsec3(qname, db, version,
+ client, rdataset,
+ sigrdataset, fname,
+ ISC_TRUE, found);
+ /*
+ * Did we find the closest provable encloser
+ * instead? If so add the nearest to the
+ * closest provable encloser.
+ */
+ if (found &&
+ dns_rdataset_isassociated(rdataset) &&
+ !dns_name_equal(qname, found))
+ {
+ unsigned int count;
+ unsigned int skip;
+
+ /*
+ * Add the closest provable encloser.
+ */
+ query_addrrset(client, &fname,
+ &rdataset, &sigrdataset,
+ dbuf,
+ DNS_SECTION_AUTHORITY);
+
+ count = dns_name_countlabels(found)
+ + 1;
+ skip = dns_name_countlabels(qname) -
+ count;
+ dns_name_getlabelsequence(qname, skip,
+ count,
+ found);
+
+ fixfname(client, &fname, &dbuf, &b);
+ fixrdataset(client, &rdataset);
+ fixrdataset(client, &sigrdataset);
+ if (fname == NULL ||
+ rdataset == NULL ||
+ sigrdataset == NULL) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ /*
+ * 'nearest' doesn't exist so
+ * 'exist' is set to ISC_FALSE.
+ */
+ query_findclosestnsec3(found, db,
+ version,
+ client,
+ rdataset,
+ sigrdataset,
+ fname,
+ ISC_FALSE,
+ NULL);
+ }
+ } else {
+ query_releasename(client, &fname);
+ query_addwildcardproof(client, db, version,
+ client->query.qname,
+ ISC_FALSE);
+ }
+ }
+ if (dns_rdataset_isassociated(rdataset)) {
+ /*
+ * If we've got a NSEC record, we need to save the
+ * name now because we're going call query_addsoa()
+ * below, and it needs to use the name buffer.
+ */
+ query_keepname(client, fname, dbuf);
+ } else if (fname != NULL) {
+ /*
+ * We're not going to use fname, and need to release
+ * our hold on the name buffer so query_addsoa()
+ * may use it.
+ */
+ query_releasename(client, &fname);
+ }
+ /*
+ * Add SOA.
+ */
+ result = query_addsoa(client, db, version, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(result);
+ goto cleanup;
+ }
+ /*
+ * Add NSEC record if we found one.
+ */
+ if (WANTDNSSEC(client)) {
+ if (dns_rdataset_isassociated(rdataset))
+ query_addnxrrsetnsec(client, db, version,
+ &fname, &rdataset,
+ &sigrdataset);
+ }
+ goto cleanup;
+
+ case DNS_R_EMPTYWILD:
+ empty_wild = ISC_TRUE;
+ /* FALLTHROUGH */
+
+ case DNS_R_NXDOMAIN:
+ INSIST(is_zone);
+ if (dns_rdataset_isassociated(rdataset)) {
+ /*
+ * If we've got a NSEC record, we need to save the
+ * name now because we're going call query_addsoa()
+ * below, and it needs to use the name buffer.
+ */
+ query_keepname(client, fname, dbuf);
+ } else if (fname != NULL) {
+ /*
+ * We're not going to use fname, and need to release
+ * our hold on the name buffer so query_addsoa()
+ * may use it.
+ */
+ query_releasename(client, &fname);
+ }
+ /*
+ * Add SOA. If the query was for a SOA record force the
+ * ttl to zero so that it is possible for clients to find
+ * the containing zone of an arbitrary name with a stub
+ * resolver and not have it cached.
+ */
+ if (qtype == dns_rdatatype_soa &&
+#ifdef DLZ
+ zone != NULL &&
+#endif
+ dns_zone_getzeronosoattl(zone))
+ result = query_addsoa(client, db, version, ISC_TRUE);
+ else
+ result = query_addsoa(client, db, version, ISC_FALSE);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(result);
+ goto cleanup;
+ }
+
+ if (WANTDNSSEC(client)) {
+ /*
+ * Add NSEC record if we found one.
+ */
+ if (dns_rdataset_isassociated(rdataset))
+ query_addrrset(client, &fname, &rdataset,
+ &sigrdataset,
+ NULL, DNS_SECTION_AUTHORITY);
+ query_addwildcardproof(client, db, version,
+ client->query.qname, ISC_FALSE);
+ }
+
+ /*
+ * Set message rcode.
+ */
+ if (empty_wild)
+ client->message->rcode = dns_rcode_noerror;
+ else
+ client->message->rcode = dns_rcode_nxdomain;
+ goto cleanup;
+
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ INSIST(!is_zone);
+ authoritative = ISC_FALSE;
+ /*
+ * Set message rcode, if required.
+ */
+ if (result == DNS_R_NCACHENXDOMAIN)
+ client->message->rcode = dns_rcode_nxdomain;
+ /*
+ * Look for RFC 1918 leakage from Internet.
+ */
+ if (result == DNS_R_NCACHENXDOMAIN &&
+ qtype == dns_rdatatype_ptr &&
+ client->message->rdclass == dns_rdataclass_in &&
+ dns_name_countlabels(fname) == 7)
+ warn_rfc1918(client, fname, rdataset);
+ /*
+ * We don't call query_addrrset() because we don't need any
+ * of its extra features (and things would probably break!).
+ */
+ query_keepname(client, fname, dbuf);
+ dns_message_addname(client->message, fname,
+ DNS_SECTION_AUTHORITY);
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ fname = NULL;
+ rdataset = NULL;
+ goto cleanup;
+
+ case DNS_R_CNAME:
+ /*
+ * Keep a copy of the rdataset. We have to do this because
+ * query_addrrset may clear 'rdataset' (to prevent the
+ * cleanup code from cleaning it up).
+ */
+ trdataset = rdataset;
+ /*
+ * Add the CNAME to the answer section.
+ */
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ if (WANTDNSSEC(client) &&
+ (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0)
+ {
+ dns_fixedname_init(&wildcardname);
+ dns_name_copy(fname, dns_fixedname_name(&wildcardname),
+ NULL);
+ need_wildcardproof = ISC_TRUE;
+ }
+ if (NOQNAME(rdataset) && WANTDNSSEC(client))
+ noqname = rdataset;
+ else
+ noqname = NULL;
+ query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf,
+ DNS_SECTION_ANSWER);
+ if (noqname != NULL)
+ query_addnoqnameproof(client, noqname);
+ /*
+ * We set the PARTIALANSWER attribute so that if anything goes
+ * wrong later on, we'll return what we've got so far.
+ */
+ client->query.attributes |= NS_QUERYATTR_PARTIALANSWER;
+ /*
+ * Reset qname to be the target name of the CNAME and restart
+ * the query.
+ */
+ tname = NULL;
+ result = dns_message_gettempname(client->message, &tname);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_rdataset_first(trdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ dns_rdataset_current(trdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &cname, NULL);
+ dns_rdata_reset(&rdata);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ dns_name_init(tname, NULL);
+ result = dns_name_dup(&cname.cname, client->mctx, tname);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ dns_rdata_freestruct(&cname);
+ goto cleanup;
+ }
+ dns_rdata_freestruct(&cname);
+ ns_client_qnamereplace(client, tname);
+ want_restart = ISC_TRUE;
+ if (!WANTRECURSION(client))
+ options |= DNS_GETDB_NOLOG;
+ goto addauth;
+ case DNS_R_DNAME:
+ /*
+ * Compare the current qname to the found name. We need
+ * to know how many labels and bits are in common because
+ * we're going to have to split qname later on.
+ */
+ namereln = dns_name_fullcompare(client->query.qname, fname,
+ &order, &nlabels);
+ INSIST(namereln == dns_namereln_subdomain);
+ /*
+ * Keep a copy of the rdataset. We have to do this because
+ * query_addrrset may clear 'rdataset' (to prevent the
+ * cleanup code from cleaning it up).
+ */
+ trdataset = rdataset;
+ /*
+ * Add the DNAME to the answer section.
+ */
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ if (WANTDNSSEC(client) &&
+ (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0)
+ {
+ dns_fixedname_init(&wildcardname);
+ dns_name_copy(fname, dns_fixedname_name(&wildcardname),
+ NULL);
+ need_wildcardproof = ISC_TRUE;
+ }
+ query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf,
+ DNS_SECTION_ANSWER);
+ /*
+ * We set the PARTIALANSWER attribute so that if anything goes
+ * wrong later on, we'll return what we've got so far.
+ */
+ client->query.attributes |= NS_QUERYATTR_PARTIALANSWER;
+ /*
+ * Get the target name of the DNAME.
+ */
+ tname = NULL;
+ result = dns_message_gettempname(client->message, &tname);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_rdataset_first(trdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ dns_rdataset_current(trdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &dname, NULL);
+ dns_rdata_reset(&rdata);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ dns_name_init(tname, NULL);
+ dns_name_clone(&dname.dname, tname);
+ dns_rdata_freestruct(&dname);
+ /*
+ * Construct the new qname.
+ */
+ dns_fixedname_init(&fixed);
+ prefix = dns_fixedname_name(&fixed);
+ dns_name_split(client->query.qname, nlabels, prefix, NULL);
+ INSIST(fname == NULL);
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL) {
+ dns_message_puttempname(client->message, &tname);
+ goto cleanup;
+ }
+ result = dns_name_concatenate(prefix, tname, fname, NULL);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &tname);
+ if (result == ISC_R_NOSPACE) {
+ /*
+ * RFC2672, section 4.1, subsection 3c says
+ * we should return YXDOMAIN if the constructed
+ * name would be too long.
+ */
+ client->message->rcode = dns_rcode_yxdomain;
+ }
+ goto cleanup;
+ }
+ query_keepname(client, fname, dbuf);
+ /*
+ * Synthesize a CNAME for this DNAME.
+ *
+ * We want to synthesize a CNAME since if we don't
+ * then older software that doesn't understand DNAME
+ * will not chain like it should.
+ *
+ * We do not try to synthesize a signature because we hope
+ * that security aware servers will understand DNAME. Also,
+ * even if we had an online key, making a signature
+ * on-the-fly is costly, and not really legitimate anyway
+ * since the synthesized CNAME is NOT in the zone.
+ */
+ dns_name_init(tname, NULL);
+ (void)query_addcnamelike(client, client->query.qname, fname,
+ trdataset->trust, &tname,
+ dns_rdatatype_cname);
+ if (tname != NULL)
+ dns_message_puttempname(client->message, &tname);
+ /*
+ * Switch to the new qname and restart.
+ */
+ ns_client_qnamereplace(client, fname);
+ fname = NULL;
+ want_restart = ISC_TRUE;
+ if (!WANTRECURSION(client))
+ options |= DNS_GETDB_NOLOG;
+ goto addauth;
+ default:
+ /*
+ * Something has gone wrong.
+ */
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+
+ if (WANTDNSSEC(client) &&
+ (fname->attributes & DNS_NAMEATTR_WILDCARD) != 0)
+ {
+ dns_fixedname_init(&wildcardname);
+ dns_name_copy(fname, dns_fixedname_name(&wildcardname), NULL);
+ need_wildcardproof = ISC_TRUE;
+ }
+
+ if (type == dns_rdatatype_any) {
+ /*
+ * XXXRTH Need to handle zonecuts with special case
+ * code.
+ */
+ n = 0;
+ rdsiter = NULL;
+ result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
+ if (result != ISC_R_SUCCESS) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ /*
+ * Calling query_addrrset() with a non-NULL dbuf is going
+ * to either keep or release the name. We don't want it to
+ * release fname, since we may have to call query_addrrset()
+ * more than once. That means we have to call query_keepname()
+ * now, and pass a NULL dbuf to query_addrrset().
+ *
+ * If we do a query_addrrset() below, we must set fname to
+ * NULL before leaving this block, otherwise we might try to
+ * cleanup fname even though we're using it!
+ */
+ query_keepname(client, fname, dbuf);
+ tname = fname;
+ result = dns_rdatasetiter_first(rdsiter);
+ while (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_current(rdsiter, rdataset);
+ if (is_zone && qtype == dns_rdatatype_any &&
+ !dns_db_issecure(db) &&
+ dns_rdatatype_isdnssec(rdataset->type)) {
+ /*
+ * The zone is transitioning from insecure
+ * to secure. Hide the dnssec records from
+ * ANY queries.
+ */
+ dns_rdataset_disassociate(rdataset);
+ } else if ((qtype == dns_rdatatype_any ||
+ rdataset->type == qtype) && rdataset->type != 0) {
+ if (NOQNAME(rdataset) && WANTDNSSEC(client))
+ noqname = rdataset;
+ else
+ noqname = NULL;
+ query_addrrset(client,
+ fname != NULL ? &fname : &tname,
+ &rdataset, NULL,
+ NULL, DNS_SECTION_ANSWER);
+ if (noqname != NULL)
+ query_addnoqnameproof(client, noqname);
+ n++;
+ INSIST(tname != NULL);
+ /*
+ * rdataset is non-NULL only in certain
+ * pathological cases involving DNAMEs.
+ */
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ break;
+ } else {
+ /*
+ * We're not interested in this rdataset.
+ */
+ dns_rdataset_disassociate(rdataset);
+ }
+ result = dns_rdatasetiter_next(rdsiter);
+ }
+
+ if (fname != NULL)
+ dns_message_puttempname(client->message, &fname);
+
+ if (n == 0 && is_zone) {
+ /*
+ * We didn't match any rdatasets.
+ */
+ if (qtype == dns_rdatatype_rrsig &&
+ result == ISC_R_NOMORE) {
+ /*
+ * XXXRTH If this is a secure zone and we
+ * didn't find any SIGs, we should generate
+ * an error unless we were searching for
+ * glue. Ugh.
+ */
+ if (!is_zone) {
+ authoritative = ISC_FALSE;
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (RECURSIONOK(client)) {
+ result = query_recurse(client,
+ qtype,
+ NULL,
+ NULL,
+ resuming);
+ if (result == ISC_R_SUCCESS)
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ else
+ QUERY_ERROR(DNS_R_SERVFAIL); }
+ goto addauth;
+ }
+ /*
+ * We were searching for SIG records in
+ * a nonsecure zone. Send a "no error,
+ * no data" response.
+ */
+ /*
+ * Add SOA.
+ */
+ result = query_addsoa(client, db, version,
+ ISC_FALSE);
+ if (result == ISC_R_SUCCESS)
+ result = ISC_R_NOMORE;
+ } else {
+ /*
+ * Something went wrong.
+ */
+ result = DNS_R_SERVFAIL;
+ }
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_NOMORE) {
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ } else {
+ /*
+ * This is the "normal" case -- an ordinary question to which
+ * we know the answer.
+ */
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ if (NOQNAME(rdataset) && WANTDNSSEC(client))
+ noqname = rdataset;
+ else
+ noqname = NULL;
+ /*
+ * BIND 8 priming queries need the additional section.
+ */
+ if (is_zone && qtype == dns_rdatatype_ns &&
+ dns_name_equal(client->query.qname, dns_rootname))
+ client->query.attributes &= ~NS_QUERYATTR_NOADDITIONAL;
+
+ query_addrrset(client, &fname, &rdataset, sigrdatasetp, dbuf,
+ DNS_SECTION_ANSWER);
+ if (noqname != NULL)
+ query_addnoqnameproof(client, noqname);
+ /*
+ * We shouldn't ever fail to add 'rdataset'
+ * because it's already in the answer.
+ */
+ INSIST(rdataset == NULL);
+ }
+
+ addauth:
+ CTRACE("query_find: addauth");
+ /*
+ * Add NS records to the authority section (if we haven't already
+ * added them to the answer section).
+ */
+ if (!want_restart && !NOAUTHORITY(client)) {
+ if (is_zone) {
+ if (!((qtype == dns_rdatatype_ns ||
+ qtype == dns_rdatatype_any) &&
+ dns_name_equal(client->query.qname,
+ dns_db_origin(db))))
+ (void)query_addns(client, db, version);
+ } else if (qtype != dns_rdatatype_ns) {
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ query_addbestns(client);
+ }
+ }
+
+ /*
+ * Add NSEC records to the authority section if they're needed for
+ * DNSSEC wildcard proofs.
+ */
+ if (need_wildcardproof && dns_db_issecure(db))
+ query_addwildcardproof(client, db, version,
+ dns_fixedname_name(&wildcardname),
+ ISC_TRUE);
+ cleanup:
+ CTRACE("query_find: cleanup");
+ /*
+ * General cleanup.
+ */
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ if (fname != NULL)
+ query_releasename(client, &fname);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (zdb != NULL) {
+ query_putrdataset(client, &zrdataset);
+ if (zsigrdataset != NULL)
+ query_putrdataset(client, &zsigrdataset);
+ if (zfname != NULL)
+ query_releasename(client, &zfname);
+ dns_db_detach(&zdb);
+ }
+ if (event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&event));
+
+ /*
+ * AA bit.
+ */
+ if (client->query.restarts == 0 && !authoritative) {
+ /*
+ * We're not authoritative, so we must ensure the AA bit
+ * isn't set.
+ */
+ client->message->flags &= ~DNS_MESSAGEFLAG_AA;
+ }
+
+ /*
+ * Restart the query?
+ */
+ if (want_restart && client->query.restarts < MAX_RESTARTS) {
+ client->query.restarts++;
+ goto restart;
+ }
+
+ if (eresult != ISC_R_SUCCESS &&
+ (!PARTIALANSWER(client) || WANTRECURSION(client))) {
+ if (eresult == DNS_R_DUPLICATE || eresult == DNS_R_DROP) {
+ /*
+ * This was a duplicate query that we are
+ * recursing on. Don't send a response now.
+ * The original query will still cause a response.
+ */
+ query_next(client, eresult);
+ } else {
+ /*
+ * If we don't have any answer to give the client,
+ * or if the client requested recursion and thus wanted
+ * the complete answer, send an error response.
+ */
+ query_error(client, eresult);
+ }
+ ns_client_detach(&client);
+ } else if (!RECURSING(client)) {
+ /*
+ * We are done. Set up sortlist data for the message
+ * rendering code, make a final tweak to the AA bit if the
+ * auth-nxdomain config option says so, then render and
+ * send the response.
+ */
+ setup_query_sortlist(client);
+
+ /*
+ * If this is a referral and the answer to the question
+ * is in the glue sort it to the start of the additional
+ * section.
+ */
+ if (client->message->counts[DNS_SECTION_ANSWER] == 0 &&
+ client->message->rcode == dns_rcode_noerror &&
+ (qtype == dns_rdatatype_a || qtype == dns_rdatatype_aaaa))
+ answer_in_glue(client, qtype);
+
+ if (client->message->rcode == dns_rcode_nxdomain &&
+ client->view->auth_nxdomain == ISC_TRUE)
+ client->message->flags |= DNS_MESSAGEFLAG_AA;
+
+ query_send(client);
+ ns_client_detach(&client);
+ }
+ CTRACE("query_find: done");
+}
+
+static inline void
+log_query(ns_client_t *client, unsigned int flags, unsigned int extflags) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typename[DNS_RDATATYPE_FORMATSIZE];
+ char classname[DNS_RDATACLASS_FORMATSIZE];
+ dns_rdataset_t *rdataset;
+ int level = ISC_LOG_INFO;
+
+ if (! isc_log_wouldlog(ns_g_lctx, level))
+ return;
+
+ rdataset = ISC_LIST_HEAD(client->query.qname->list);
+ INSIST(rdataset != NULL);
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(rdataset->rdclass, classname, sizeof(classname));
+ dns_rdatatype_format(rdataset->type, typename, sizeof(typename));
+
+ ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY,
+ level, "query: %s %s %s %s%s%s%s%s", namebuf, classname,
+ typename, WANTRECURSION(client) ? "+" : "-",
+ (client->signer != NULL) ? "S": "",
+ (client->opt != NULL) ? "E" : "",
+ ((extflags & DNS_MESSAGEEXTFLAG_DO) != 0) ? "D" : "",
+ ((flags & DNS_MESSAGEFLAG_CD) != 0) ? "C" : "");
+}
+
+void
+ns_query_start(ns_client_t *client) {
+ isc_result_t result;
+ dns_message_t *message = client->message;
+ dns_rdataset_t *rdataset;
+ ns_client_t *qclient;
+ dns_rdatatype_t qtype;
+ unsigned int saved_extflags = client->extflags;
+ unsigned int saved_flags = client->message->flags;
+ isc_boolean_t want_ad;
+
+ CTRACE("ns_query_start");
+
+ /*
+ * Test only.
+ */
+ if (ns_g_clienttest && (client->attributes & NS_CLIENTATTR_TCP) == 0)
+ RUNTIME_CHECK(ns_client_replace(client) == ISC_R_SUCCESS);
+
+ /*
+ * Ensure that appropriate cleanups occur.
+ */
+ client->next = query_next_callback;
+
+ /*
+ * Behave as if we don't support DNSSEC if not enabled.
+ */
+ if (!client->view->enablednssec) {
+ message->flags &= ~DNS_MESSAGEFLAG_CD;
+ client->extflags &= ~DNS_MESSAGEEXTFLAG_DO;
+ if (client->opt != NULL)
+ client->opt->ttl &= ~DNS_MESSAGEEXTFLAG_DO;
+ }
+
+ if ((message->flags & DNS_MESSAGEFLAG_RD) != 0)
+ client->query.attributes |= NS_QUERYATTR_WANTRECURSION;
+
+ if ((client->extflags & DNS_MESSAGEEXTFLAG_DO) != 0)
+ client->attributes |= NS_CLIENTATTR_WANTDNSSEC;
+
+ if (client->view->minimalresponses)
+ client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
+ NS_QUERYATTR_NOADDITIONAL);
+
+ if ((client->view->cachedb == NULL)
+ || (!client->view->additionalfromcache)) {
+ /*
+ * We don't have a cache. Turn off cache support and
+ * recursion.
+ */
+ client->query.attributes &=
+ ~(NS_QUERYATTR_RECURSIONOK|NS_QUERYATTR_CACHEOK);
+ } else if ((client->attributes & NS_CLIENTATTR_RA) == 0 ||
+ (message->flags & DNS_MESSAGEFLAG_RD) == 0) {
+ /*
+ * If the client isn't allowed to recurse (due to
+ * "recursion no", the allow-recursion ACL, or the
+ * lack of a resolver in this view), or if it
+ * doesn't want recursion, turn recursion off.
+ */
+ client->query.attributes &= ~NS_QUERYATTR_RECURSIONOK;
+ }
+
+ /*
+ * Get the question name.
+ */
+ result = dns_message_firstname(message, DNS_SECTION_QUESTION);
+ if (result != ISC_R_SUCCESS) {
+ query_error(client, result);
+ return;
+ }
+ dns_message_currentname(message, DNS_SECTION_QUESTION,
+ &client->query.qname);
+ client->query.origqname = client->query.qname;
+ result = dns_message_nextname(message, DNS_SECTION_QUESTION);
+ if (result != ISC_R_NOMORE) {
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * There's more than one QNAME in the question
+ * section.
+ */
+ query_error(client, DNS_R_FORMERR);
+ } else
+ query_error(client, result);
+ return;
+ }
+
+ if (ns_g_server->log_queries)
+ log_query(client, saved_flags, saved_extflags);
+
+ /*
+ * Check for multiple question queries, since edns1 is dead.
+ */
+ if (message->counts[DNS_SECTION_QUESTION] > 1) {
+ query_error(client, DNS_R_FORMERR);
+ return;
+ }
+
+ /*
+ * Check for meta-queries like IXFR and AXFR.
+ */
+ rdataset = ISC_LIST_HEAD(client->query.qname->list);
+ INSIST(rdataset != NULL);
+ qtype = rdataset->type;
+ dns_rdatatypestats_increment(ns_g_server->rcvquerystats, qtype);
+ if (dns_rdatatype_ismeta(qtype)) {
+ switch (qtype) {
+ case dns_rdatatype_any:
+ break; /* Let query_find handle it. */
+ case dns_rdatatype_ixfr:
+ case dns_rdatatype_axfr:
+ ns_xfr_start(client, rdataset->type);
+ return;
+ case dns_rdatatype_maila:
+ case dns_rdatatype_mailb:
+ query_error(client, DNS_R_NOTIMP);
+ return;
+ case dns_rdatatype_tkey:
+ result = dns_tkey_processquery(client->message,
+ ns_g_server->tkeyctx,
+ client->view->dynamickeys);
+ if (result == ISC_R_SUCCESS)
+ query_send(client);
+ else
+ query_error(client, result);
+ return;
+ default: /* TSIG, etc. */
+ query_error(client, DNS_R_FORMERR);
+ return;
+ }
+ }
+
+ /*
+ * Turn on minimal response for DNSKEY queries.
+ */
+ if (qtype == dns_rdatatype_dnskey)
+ client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
+ NS_QUERYATTR_NOADDITIONAL);
+
+ /*
+ * If the client has requested that DNSSEC checking be disabled,
+ * allow lookups to return pending data and instruct the resolver
+ * to return data before validation has completed.
+ *
+ * We don't need to set DNS_DBFIND_PENDINGOK when validation is
+ * disabled as there will be no pending data.
+ */
+ if (message->flags & DNS_MESSAGEFLAG_CD ||
+ qtype == dns_rdatatype_rrsig)
+ {
+ client->query.dboptions |= DNS_DBFIND_PENDINGOK;
+ client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
+ } else if (!client->view->enablevalidation)
+ client->query.fetchoptions |= DNS_FETCHOPT_NOVALIDATE;
+
+ /*
+ * Allow glue NS records to be added to the authority section
+ * if the answer is secure.
+ */
+ if (message->flags & DNS_MESSAGEFLAG_CD)
+ client->query.attributes &= ~NS_QUERYATTR_SECURE;
+
+ /*
+ * Set 'want_ad' if the client has set AD in the query.
+ * This allows AD to be returned on queries without DO set.
+ */
+ if ((message->flags & DNS_MESSAGEFLAG_AD) != 0)
+ want_ad = ISC_TRUE;
+ else
+ want_ad = ISC_FALSE;
+
+ /*
+ * This is an ordinary query.
+ */
+ result = dns_message_reply(message, ISC_TRUE);
+ if (result != ISC_R_SUCCESS) {
+ query_next(client, result);
+ return;
+ }
+
+ /*
+ * Assume authoritative response until it is known to be
+ * otherwise.
+ */
+ message->flags |= DNS_MESSAGEFLAG_AA;
+
+ /*
+ * Set AD. We must clear it if we add non-validated data to a
+ * response.
+ */
+ if (WANTDNSSEC(client) || want_ad)
+ message->flags |= DNS_MESSAGEFLAG_AD;
+
+ qclient = NULL;
+ ns_client_attach(client, &qclient);
+ query_find(qclient, NULL, qtype);
+}
diff --git a/bin/named/server.c b/bin/named/server.c
new file mode 100644
index 0000000..31b2761
--- /dev/null
+++ b/bin/named/server.c
@@ -0,0 +1,5462 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: server.c,v 1.520 2008/11/14 23:47:32 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <isc/app.h>
+#include <isc/base64.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/httpd.h>
+#include <isc/lex.h>
+#include <isc/parseint.h>
+#include <isc/portset.h>
+#include <isc/print.h>
+#include <isc/resource.h>
+#include <isc/socket.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+#include <isc/xml.h>
+
+#include <isccfg/namedconf.h>
+
+#include <bind9/check.h>
+
+#include <dns/acache.h>
+#include <dns/adb.h>
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#ifdef DLZ
+#include <dns/dlz.h>
+#endif
+#include <dns/forward.h>
+#include <dns/journal.h>
+#include <dns/keytable.h>
+#include <dns/lib.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/order.h>
+#include <dns/peer.h>
+#include <dns/portlist.h>
+#include <dns/rbt.h>
+#include <dns/rdataclass.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/resolver.h>
+#include <dns/rootns.h>
+#include <dns/secalg.h>
+#include <dns/stats.h>
+#include <dns/tkey.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <dst/dst.h>
+#include <dst/result.h>
+
+#include <named/client.h>
+#include <named/config.h>
+#include <named/control.h>
+#include <named/interfacemgr.h>
+#include <named/log.h>
+#include <named/logconf.h>
+#include <named/lwresd.h>
+#include <named/main.h>
+#include <named/os.h>
+#include <named/server.h>
+#include <named/statschannel.h>
+#include <named/tkeyconf.h>
+#include <named/tsigconf.h>
+#include <named/zoneconf.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#include <stdlib.h>
+#endif
+
+/*%
+ * Check an operation for failure. Assumes that the function
+ * using it has a 'result' variable and a 'cleanup' label.
+ */
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto cleanup; \
+ } while (0)
+
+#define CHECKM(op, msg) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) { \
+ isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_GENERAL, \
+ NS_LOGMODULE_SERVER, \
+ ISC_LOG_ERROR, \
+ "%s: %s", msg, \
+ isc_result_totext(result)); \
+ goto cleanup; \
+ } \
+ } while (0) \
+
+#define CHECKMF(op, msg, file) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) { \
+ isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_GENERAL, \
+ NS_LOGMODULE_SERVER, \
+ ISC_LOG_ERROR, \
+ "%s '%s': %s", msg, file, \
+ isc_result_totext(result)); \
+ goto cleanup; \
+ } \
+ } while (0) \
+
+#define CHECKFATAL(op, msg) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) \
+ fatal(msg, result); \
+ } while (0) \
+
+struct ns_dispatch {
+ isc_sockaddr_t addr;
+ unsigned int dispatchgen;
+ dns_dispatch_t *dispatch;
+ ISC_LINK(struct ns_dispatch) link;
+};
+
+struct dumpcontext {
+ isc_mem_t *mctx;
+ isc_boolean_t dumpcache;
+ isc_boolean_t dumpzones;
+ FILE *fp;
+ ISC_LIST(struct viewlistentry) viewlist;
+ struct viewlistentry *view;
+ struct zonelistentry *zone;
+ dns_dumpctx_t *mdctx;
+ dns_db_t *db;
+ dns_db_t *cache;
+ isc_task_t *task;
+ dns_dbversion_t *version;
+};
+
+struct viewlistentry {
+ dns_view_t *view;
+ ISC_LINK(struct viewlistentry) link;
+ ISC_LIST(struct zonelistentry) zonelist;
+};
+
+struct zonelistentry {
+ dns_zone_t *zone;
+ ISC_LINK(struct zonelistentry) link;
+};
+
+/*
+ * These zones should not leak onto the Internet.
+ */
+static const struct {
+ const char *zone;
+ isc_boolean_t rfc1918;
+} empty_zones[] = {
+#ifdef notyet
+ /* RFC 1918 */
+ { "10.IN-ADDR.ARPA", ISC_TRUE },
+ { "16.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "17.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "18.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "19.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "20.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "21.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "22.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "23.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "24.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "25.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "26.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "27.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "28.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "29.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "30.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "31.172.IN-ADDR.ARPA", ISC_TRUE },
+ { "168.192.IN-ADDR.ARPA", ISC_TRUE },
+#endif
+
+ /* RFC 3330 */
+ { "0.IN-ADDR.ARPA", ISC_FALSE }, /* THIS NETWORK */
+ { "127.IN-ADDR.ARPA", ISC_FALSE }, /* LOOPBACK */
+ { "254.169.IN-ADDR.ARPA", ISC_FALSE }, /* LINK LOCAL */
+ { "2.0.192.IN-ADDR.ARPA", ISC_FALSE }, /* TEST NET */
+ { "255.255.255.255.IN-ADDR.ARPA", ISC_FALSE }, /* BROADCAST */
+
+ /* Local IPv6 Unicast Addresses */
+ { "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE },
+ { "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA", ISC_FALSE },
+ /* LOCALLY ASSIGNED LOCAL ADDRES S SCOPE */
+ { "D.F.IP6.ARPA", ISC_FALSE },
+ { "8.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */
+ { "9.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */
+ { "A.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */
+ { "B.E.F.IP6.ARPA", ISC_FALSE }, /* LINK LOCAL */
+
+ { NULL, ISC_FALSE }
+};
+
+static void
+fatal(const char *msg, isc_result_t result);
+
+static void
+ns_server_reload(isc_task_t *task, isc_event_t *event);
+
+static isc_result_t
+ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, ns_listenelt_t **target);
+static isc_result_t
+ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, ns_listenlist_t **target);
+
+static isc_result_t
+configure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
+ const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype);
+
+static isc_result_t
+configure_alternates(const cfg_obj_t *config, dns_view_t *view,
+ const cfg_obj_t *alternates);
+
+static isc_result_t
+configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
+ const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *aclconf);
+
+static void
+end_reserved_dispatches(ns_server_t *server, isc_boolean_t all);
+
+/*%
+ * Configure a single view ACL at '*aclp'. Get its configuration by
+ * calling 'getvcacl' (for per-view configuration) and maybe 'getscacl'
+ * (for a global default).
+ */
+static isc_result_t
+configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ const char *aclname, cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, dns_acl_t **aclp)
+{
+ isc_result_t result;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *aclobj = NULL;
+ int i = 0;
+
+ if (*aclp != NULL)
+ dns_acl_detach(aclp);
+ if (vconfig != NULL)
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ if (config != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL)
+ maps[i++] = options;
+ }
+ maps[i] = NULL;
+
+ (void)ns_config_get(maps, aclname, &aclobj);
+ if (aclobj == NULL)
+ /*
+ * No value available. *aclp == NULL.
+ */
+ return (ISC_R_SUCCESS);
+
+ result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
+ actx, mctx, 0, aclp);
+
+ return (result);
+}
+
+
+/*%
+ * Configure a sortlist at '*aclp'. Essentially the same as
+ * configure_view_acl() except it calls cfg_acl_fromconfig with a
+ * nest_level value of 2.
+ */
+static isc_result_t
+configure_view_sortlist(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx, isc_mem_t *mctx,
+ dns_acl_t **aclp)
+{
+ isc_result_t result;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *aclobj = NULL;
+ int i = 0;
+
+ if (*aclp != NULL)
+ dns_acl_detach(aclp);
+ if (vconfig != NULL)
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ if (config != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL)
+ maps[i++] = options;
+ }
+ maps[i] = NULL;
+
+ (void)ns_config_get(maps, "sortlist", &aclobj);
+ if (aclobj == NULL)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Use a nest level of 3 for the "top level" of the sortlist;
+ * this means each entry in the top three levels will be stored
+ * as lists of separate, nested ACLs, rather than merged together
+ * into IP tables as is usually done with ACLs.
+ */
+ result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx,
+ actx, mctx, 3, aclp);
+
+ return (result);
+}
+
+static isc_result_t
+configure_view_dnsseckey(const cfg_obj_t *vconfig, const cfg_obj_t *key,
+ dns_keytable_t *keytable, isc_mem_t *mctx)
+{
+ dns_rdataclass_t viewclass;
+ dns_rdata_dnskey_t keystruct;
+ isc_uint32_t flags, proto, alg;
+ const char *keystr, *keynamestr;
+ unsigned char keydata[4096];
+ isc_buffer_t keydatabuf;
+ unsigned char rrdata[4096];
+ isc_buffer_t rrdatabuf;
+ isc_region_t r;
+ dns_fixedname_t fkeyname;
+ dns_name_t *keyname;
+ isc_buffer_t namebuf;
+ isc_result_t result;
+ dst_key_t *dstkey = 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"));
+ keyname = dns_fixedname_name(&fkeyname);
+ keynamestr = cfg_obj_asstring(cfg_tuple_get(key, "name"));
+
+ if (vconfig == NULL)
+ viewclass = dns_rdataclass_in;
+ else {
+ const cfg_obj_t *classobj = cfg_tuple_get(vconfig, "class");
+ CHECK(ns_config_getclass(classobj, dns_rdataclass_in,
+ &viewclass));
+ }
+ keystruct.common.rdclass = viewclass;
+ keystruct.common.rdtype = dns_rdatatype_dnskey;
+ /*
+ * The key data in keystruct is not dynamically allocated.
+ */
+ keystruct.mctx = NULL;
+
+ ISC_LINK_INIT(&keystruct.common, link);
+
+ if (flags > 0xffff)
+ CHECKM(ISC_R_RANGE, "key flags");
+ if (proto > 0xff)
+ CHECKM(ISC_R_RANGE, "key protocol");
+ if (alg > 0xff)
+ CHECKM(ISC_R_RANGE, "key algorithm");
+ keystruct.flags = (isc_uint16_t)flags;
+ keystruct.protocol = (isc_uint8_t)proto;
+ keystruct.algorithm = (isc_uint8_t)alg;
+
+ isc_buffer_init(&keydatabuf, keydata, sizeof(keydata));
+ isc_buffer_init(&rrdatabuf, rrdata, sizeof(rrdata));
+
+ keystr = cfg_obj_asstring(cfg_tuple_get(key, "key"));
+ CHECK(isc_base64_decodestring(keystr, &keydatabuf));
+ isc_buffer_usedregion(&keydatabuf, &r);
+ keystruct.datalen = r.length;
+ keystruct.data = r.base;
+
+ if ((keystruct.algorithm == DST_ALG_RSASHA1 ||
+ keystruct.algorithm == DST_ALG_RSAMD5) &&
+ r.length > 1 && r.base[0] == 1 && r.base[1] == 3)
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
+ "trusted key '%s' has a weak exponent",
+ keynamestr);
+
+ CHECK(dns_rdata_fromstruct(NULL,
+ keystruct.common.rdclass,
+ keystruct.common.rdtype,
+ &keystruct, &rrdatabuf));
+ dns_fixedname_init(&fkeyname);
+ isc_buffer_init(&namebuf, keynamestr, strlen(keynamestr));
+ isc_buffer_add(&namebuf, strlen(keynamestr));
+ CHECK(dns_name_fromtext(keyname, &namebuf,
+ dns_rootname, ISC_FALSE,
+ NULL));
+ CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf,
+ mctx, &dstkey));
+
+ CHECK(dns_keytable_add(keytable, &dstkey));
+ INSIST(dstkey == NULL);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (result == DST_R_NOCRYPTO) {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
+ "ignoring trusted key for '%s': no crypto support",
+ keynamestr);
+ result = ISC_R_SUCCESS;
+ } else {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
+ "configuring trusted key for '%s': %s",
+ keynamestr, isc_result_totext(result));
+ result = ISC_R_FAILURE;
+ }
+
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+
+ return (result);
+}
+
+/*%
+ * Configure DNSSEC keys for a view. Currently used only for
+ * the security roots.
+ *
+ * The per-view configuration values and the server-global defaults are read
+ * from 'vconfig' and 'config'. The variable to be configured is '*target'.
+ */
+static isc_result_t
+configure_view_dnsseckeys(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ isc_mem_t *mctx, dns_keytable_t **target)
+{
+ isc_result_t result;
+ const cfg_obj_t *keys = NULL;
+ const cfg_obj_t *voptions = NULL;
+ const cfg_listelt_t *element, *element2;
+ const cfg_obj_t *keylist;
+ const cfg_obj_t *key;
+ dns_keytable_t *keytable = NULL;
+
+ CHECK(dns_keytable_create(mctx, &keytable));
+
+ if (vconfig != NULL)
+ voptions = cfg_tuple_get(vconfig, "options");
+
+ keys = NULL;
+ if (voptions != NULL)
+ (void)cfg_map_get(voptions, "trusted-keys", &keys);
+ if (keys == NULL)
+ (void)cfg_map_get(config, "trusted-keys", &keys);
+
+ 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);
+ CHECK(configure_view_dnsseckey(vconfig, key,
+ keytable, mctx));
+ }
+ }
+
+ dns_keytable_detach(target);
+ *target = keytable; /* Transfer ownership. */
+ keytable = NULL;
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+static isc_result_t
+mustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver)
+{
+ const cfg_listelt_t *element;
+ const cfg_obj_t *obj;
+ const char *str;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_boolean_t value;
+ isc_result_t result;
+ isc_buffer_t b;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ for (element = cfg_list_first(mbs);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname,
+ ISC_FALSE, NULL));
+ value = cfg_obj_asboolean(cfg_tuple_get(obj, "value"));
+ CHECK(dns_resolver_setmustbesecure(resolver, name, value));
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+/*%
+ * Get a dispatch appropriate for the resolver of a given view.
+ */
+static isc_result_t
+get_view_querysource_dispatch(const cfg_obj_t **maps,
+ int af, dns_dispatch_t **dispatchp,
+ isc_boolean_t is_firstview)
+{
+ isc_result_t result;
+ dns_dispatch_t *disp;
+ isc_sockaddr_t sa;
+ unsigned int attrs, attrmask;
+ const cfg_obj_t *obj = NULL;
+ unsigned int maxdispatchbuffers;
+
+ /*
+ * Make compiler happy.
+ */
+ result = ISC_R_FAILURE;
+
+ switch (af) {
+ case AF_INET:
+ result = ns_config_get(maps, "query-source", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ break;
+ case AF_INET6:
+ result = ns_config_get(maps, "query-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ sa = *(cfg_obj_assockaddr(obj));
+ INSIST(isc_sockaddr_pf(&sa) == af);
+
+ /*
+ * If we don't support this address family, we're done!
+ */
+ switch (af) {
+ case AF_INET:
+ result = isc_net_probeipv4();
+ break;
+ case AF_INET6:
+ result = isc_net_probeipv6();
+ break;
+ default:
+ INSIST(0);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Try to find a dispatcher that we can share.
+ */
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_UDP;
+ switch (af) {
+ case AF_INET:
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ break;
+ case AF_INET6:
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ break;
+ }
+ if (isc_sockaddr_getport(&sa) == 0) {
+ attrs |= DNS_DISPATCHATTR_EXCLUSIVE;
+ maxdispatchbuffers = 4096;
+ } else {
+ INSIST(obj != NULL);
+ if (is_firstview) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_INFO,
+ "using specific query-source port "
+ "suppresses port randomization and can be "
+ "insecure.");
+ }
+ maxdispatchbuffers = 1000;
+ }
+
+ attrmask = 0;
+ attrmask |= DNS_DISPATCHATTR_UDP;
+ attrmask |= DNS_DISPATCHATTR_TCP;
+ attrmask |= DNS_DISPATCHATTR_IPV4;
+ attrmask |= DNS_DISPATCHATTR_IPV6;
+
+ disp = NULL;
+ result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
+ ns_g_taskmgr, &sa, 4096,
+ maxdispatchbuffers, 32768, 16411, 16433,
+ attrs, attrmask, &disp);
+ if (result != ISC_R_SUCCESS) {
+ isc_sockaddr_t any;
+ char buf[ISC_SOCKADDR_FORMATSIZE];
+
+ switch (af) {
+ case AF_INET:
+ isc_sockaddr_any(&any);
+ break;
+ case AF_INET6:
+ isc_sockaddr_any6(&any);
+ break;
+ }
+ if (isc_sockaddr_equal(&sa, &any))
+ return (ISC_R_SUCCESS);
+ isc_sockaddr_format(&sa, buf, sizeof(buf));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "could not get query source dispatcher (%s)",
+ buf);
+ return (result);
+ }
+
+ *dispatchp = disp;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+configure_order(dns_order_t *order, const cfg_obj_t *ent) {
+ dns_rdataclass_t rdclass;
+ dns_rdatatype_t rdtype;
+ const cfg_obj_t *obj;
+ dns_fixedname_t fixed;
+ unsigned int mode = 0;
+ const char *str;
+ isc_buffer_t b;
+ isc_result_t result;
+ isc_boolean_t addroot;
+
+ result = ns_config_getclass(cfg_tuple_get(ent, "class"),
+ dns_rdataclass_any, &rdclass);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = ns_config_gettype(cfg_tuple_get(ent, "type"),
+ dns_rdatatype_any, &rdtype);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = cfg_tuple_get(ent, "name");
+ if (cfg_obj_isstring(obj))
+ str = cfg_obj_asstring(obj);
+ else
+ str = "*";
+ addroot = ISC_TF(strcmp(str, "*") == 0);
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ dns_fixedname_init(&fixed);
+ result = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
+ dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = cfg_tuple_get(ent, "ordering");
+ INSIST(cfg_obj_isstring(obj));
+ str = cfg_obj_asstring(obj);
+ if (!strcasecmp(str, "fixed"))
+ mode = DNS_RDATASETATTR_FIXEDORDER;
+ else if (!strcasecmp(str, "random"))
+ mode = DNS_RDATASETATTR_RANDOMIZE;
+ else if (!strcasecmp(str, "cyclic"))
+ mode = 0;
+ else
+ INSIST(0);
+
+ /*
+ * "*" should match everything including the root (BIND 8 compat).
+ * As dns_name_matcheswildcard(".", "*.") returns FALSE add a
+ * explicit entry for "." when the name is "*".
+ */
+ if (addroot) {
+ result = dns_order_add(order, dns_rootname,
+ rdtype, rdclass, mode);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ return (dns_order_add(order, dns_fixedname_name(&fixed),
+ rdtype, rdclass, mode));
+}
+
+static isc_result_t
+configure_peer(const cfg_obj_t *cpeer, isc_mem_t *mctx, dns_peer_t **peerp) {
+ isc_netaddr_t na;
+ dns_peer_t *peer;
+ const cfg_obj_t *obj;
+ const char *str;
+ isc_result_t result;
+ unsigned int prefixlen;
+
+ cfg_obj_asnetprefix(cfg_map_getname(cpeer), &na, &prefixlen);
+
+ peer = NULL;
+ result = dns_peer_newprefix(mctx, &na, prefixlen, &peer);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "bogus", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setbogus(peer, cfg_obj_asboolean(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "provide-ixfr", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setprovideixfr(peer, cfg_obj_asboolean(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "request-ixfr", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setrequestixfr(peer, cfg_obj_asboolean(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "request-nsid", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setrequestnsid(peer, cfg_obj_asboolean(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "edns", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setsupportedns(peer, cfg_obj_asboolean(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "edns-udp-size", &obj);
+ if (obj != NULL) {
+ isc_uint32_t udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ CHECK(dns_peer_setudpsize(peer, (isc_uint16_t)udpsize));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "max-udp-size", &obj);
+ if (obj != NULL) {
+ isc_uint32_t udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ CHECK(dns_peer_setmaxudp(peer, (isc_uint16_t)udpsize));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "transfers", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_settransfers(peer, cfg_obj_asuint32(obj)));
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "transfer-format", &obj);
+ if (obj != NULL) {
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "many-answers") == 0)
+ CHECK(dns_peer_settransferformat(peer,
+ dns_many_answers));
+ else if (strcasecmp(str, "one-answer") == 0)
+ CHECK(dns_peer_settransferformat(peer,
+ dns_one_answer));
+ else
+ INSIST(0);
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "keys", &obj);
+ if (obj != NULL) {
+ result = dns_peer_setkeybycharp(peer, cfg_obj_asstring(obj));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ obj = NULL;
+ if (na.family == AF_INET)
+ (void)cfg_map_get(cpeer, "transfer-source", &obj);
+ else
+ (void)cfg_map_get(cpeer, "transfer-source-v6", &obj);
+ if (obj != NULL) {
+ result = dns_peer_settransfersource(peer,
+ cfg_obj_assockaddr(obj));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+ }
+
+ obj = NULL;
+ if (na.family == AF_INET)
+ (void)cfg_map_get(cpeer, "notify-source", &obj);
+ else
+ (void)cfg_map_get(cpeer, "notify-source-v6", &obj);
+ if (obj != NULL) {
+ result = dns_peer_setnotifysource(peer,
+ cfg_obj_assockaddr(obj));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+ }
+
+ obj = NULL;
+ if (na.family == AF_INET)
+ (void)cfg_map_get(cpeer, "query-source", &obj);
+ else
+ (void)cfg_map_get(cpeer, "query-source-v6", &obj);
+ if (obj != NULL) {
+ result = dns_peer_setquerysource(peer,
+ cfg_obj_assockaddr(obj));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+ }
+
+ *peerp = peer;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ dns_peer_detach(&peer);
+ return (result);
+}
+
+static isc_result_t
+disable_algorithms(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
+ isc_result_t result;
+ const cfg_obj_t *algorithms;
+ const cfg_listelt_t *element;
+ const char *str;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL));
+
+ algorithms = cfg_tuple_get(disabled, "algorithms");
+ for (element = cfg_list_first(algorithms);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ isc_textregion_t r;
+ dns_secalg_t alg;
+
+ DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
+ r.length = strlen(r.base);
+
+ result = dns_secalg_fromtext(&alg, &r);
+ if (result != ISC_R_SUCCESS) {
+ isc_uint8_t ui;
+ result = isc_parse_uint8(&ui, r.base, 10);
+ alg = ui;
+ }
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(cfg_listelt_value(element),
+ ns_g_lctx, ISC_LOG_ERROR,
+ "invalid algorithm");
+ CHECK(result);
+ }
+ CHECK(dns_resolver_disable_algorithm(resolver, name, alg));
+ }
+ cleanup:
+ return (result);
+}
+
+static isc_boolean_t
+on_disable_list(const cfg_obj_t *disablelist, dns_name_t *zonename) {
+ const cfg_listelt_t *element;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_result_t result;
+ const cfg_obj_t *value;
+ const char *str;
+ isc_buffer_t b;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+
+ for (element = cfg_list_first(disablelist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ value = cfg_listelt_value(element);
+ str = cfg_obj_asstring(value);
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(name, &b, dns_rootname,
+ ISC_TRUE, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (dns_name_equal(name, zonename))
+ return (ISC_TRUE);
+ }
+ return (ISC_FALSE);
+}
+
+static void
+check_dbtype(dns_zone_t **zonep, unsigned int dbtypec, const char **dbargv,
+ isc_mem_t *mctx)
+{
+ char **argv = NULL;
+ unsigned int i;
+ isc_result_t result;
+
+ result = dns_zone_getdbtype(*zonep, &argv, mctx);
+ if (result != ISC_R_SUCCESS) {
+ dns_zone_detach(zonep);
+ return;
+ }
+
+ /*
+ * Check that all the arguments match.
+ */
+ for (i = 0; i < dbtypec; i++)
+ if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0) {
+ dns_zone_detach(zonep);
+ break;
+ }
+
+ /*
+ * Check that there are not extra arguments.
+ */
+ if (i == dbtypec && argv[i] != NULL)
+ dns_zone_detach(zonep);
+ isc_mem_free(mctx, argv);
+}
+
+static isc_result_t
+setquerystats(dns_zone_t *zone, isc_mem_t *mctx, isc_boolean_t on) {
+ isc_result_t result;
+ dns_stats_t *zoneqrystats;
+
+ zoneqrystats = NULL;
+ if (on) {
+ result = dns_generalstats_create(mctx, &zoneqrystats,
+ dns_nsstatscounter_max);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ dns_zone_setrequeststats(zone, zoneqrystats);
+ if (zoneqrystats != NULL)
+ dns_stats_detach(&zoneqrystats);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Configure 'view' according to 'vconfig', taking defaults from 'config'
+ * where values are missing in 'vconfig'.
+ *
+ * When configuring the default view, 'vconfig' will be NULL and the
+ * global defaults in 'config' used exclusively.
+ */
+static isc_result_t
+configure_view(dns_view_t *view, const cfg_obj_t *config,
+ const cfg_obj_t *vconfig, isc_mem_t *mctx,
+ cfg_aclconfctx_t *actx, isc_boolean_t need_hints)
+{
+ const cfg_obj_t *maps[4];
+ const cfg_obj_t *cfgmaps[3];
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *voptions = NULL;
+ const cfg_obj_t *forwardtype;
+ const cfg_obj_t *forwarders;
+ const cfg_obj_t *alternates;
+ const cfg_obj_t *zonelist;
+#ifdef DLZ
+ const cfg_obj_t *dlz;
+ unsigned int dlzargc;
+ char **dlzargv;
+#endif
+ const cfg_obj_t *disabled;
+ const cfg_obj_t *obj;
+ const cfg_listelt_t *element;
+ in_port_t port;
+ dns_cache_t *cache = NULL;
+ isc_result_t result;
+ isc_uint32_t max_adb_size;
+ isc_uint32_t max_cache_size;
+ isc_uint32_t max_acache_size;
+ isc_uint32_t lame_ttl;
+ dns_tsig_keyring_t *ring;
+ dns_view_t *pview = NULL; /* Production view */
+ isc_mem_t *cmctx;
+ dns_dispatch_t *dispatch4 = NULL;
+ dns_dispatch_t *dispatch6 = NULL;
+ isc_boolean_t reused_cache = ISC_FALSE;
+ int i;
+ const char *str;
+ dns_order_t *order = NULL;
+ isc_uint32_t udpsize;
+ unsigned int resopts = 0;
+ dns_zone_t *zone = NULL;
+ isc_uint32_t max_clients_per_query;
+ const char *sep = ": view ";
+ const char *viewname = view->name;
+ const char *forview = " for view ";
+ isc_boolean_t rfc1918;
+ isc_boolean_t empty_zones_enable;
+ const cfg_obj_t *disablelist = NULL;
+ dns_stats_t *resstats = NULL;
+ dns_stats_t *resquerystats = NULL;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ cmctx = NULL;
+
+ if (config != NULL)
+ (void)cfg_map_get(config, "options", &options);
+
+ i = 0;
+ if (vconfig != NULL) {
+ voptions = cfg_tuple_get(vconfig, "options");
+ maps[i++] = voptions;
+ }
+ if (options != NULL)
+ maps[i++] = options;
+ maps[i++] = ns_g_defaults;
+ maps[i] = NULL;
+
+ i = 0;
+ if (voptions != NULL)
+ cfgmaps[i++] = voptions;
+ if (config != NULL)
+ cfgmaps[i++] = config;
+ cfgmaps[i] = NULL;
+
+ if (!strcmp(viewname, "_default")) {
+ sep = "";
+ viewname = "";
+ forview = "";
+ }
+
+ /*
+ * Set the view's port number for outgoing queries.
+ */
+ CHECKM(ns_config_getport(config, &port), "port");
+ dns_view_setdstport(view, port);
+
+ /*
+ * Create additional cache for this view and zones under the view
+ * if explicitly enabled.
+ * XXX950 default to on.
+ */
+ obj = NULL;
+ (void)ns_config_get(maps, "acache-enable", &obj);
+ if (obj != NULL && cfg_obj_asboolean(obj)) {
+ cmctx = NULL;
+ CHECK(isc_mem_create(0, 0, &cmctx));
+ CHECK(dns_acache_create(&view->acache, cmctx, ns_g_taskmgr,
+ ns_g_timermgr));
+ isc_mem_setname(cmctx, "acache", NULL);
+ isc_mem_detach(&cmctx);
+ }
+ if (view->acache != NULL) {
+ obj = NULL;
+ result = ns_config_get(maps, "acache-cleaning-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_acache_setcleaninginterval(view->acache,
+ cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-acache-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isstring(obj)) {
+ str = cfg_obj_asstring(obj);
+ INSIST(strcasecmp(str, "unlimited") == 0);
+ max_acache_size = ISC_UINT32_MAX;
+ } else {
+ isc_resourcevalue_t value;
+
+ value = cfg_obj_asuint64(obj);
+ if (value > ISC_UINT32_MAX) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "'max-acache-size "
+ "%" ISC_PRINT_QUADFORMAT
+ "d' is too large",
+ value);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ max_acache_size = (isc_uint32_t)value;
+ }
+ dns_acache_setcachesize(view->acache, max_acache_size);
+ }
+
+ /*
+ * Configure the zones.
+ */
+ zonelist = NULL;
+ if (voptions != NULL)
+ (void)cfg_map_get(voptions, "zone", &zonelist);
+ else
+ (void)cfg_map_get(config, "zone", &zonelist);
+ for (element = cfg_list_first(zonelist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *zconfig = cfg_listelt_value(element);
+ CHECK(configure_zone(config, zconfig, vconfig, mctx, view,
+ actx));
+ }
+
+#ifdef DLZ
+ /*
+ * Create Dynamically Loadable Zone driver.
+ */
+ dlz = NULL;
+ if (voptions != NULL)
+ (void)cfg_map_get(voptions, "dlz", &dlz);
+ else
+ (void)cfg_map_get(config, "dlz", &dlz);
+
+ obj = NULL;
+ if (dlz != NULL) {
+ (void)cfg_map_get(cfg_tuple_get(dlz, "options"),
+ "database", &obj);
+ if (obj != NULL) {
+ char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
+ if (s == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_free(mctx, s);
+ goto cleanup;
+ }
+
+ obj = cfg_tuple_get(dlz, "name");
+ result = dns_dlzcreate(mctx, cfg_obj_asstring(obj),
+ dlzargv[0], dlzargc, dlzargv,
+ &view->dlzdatabase);
+ isc_mem_free(mctx, s);
+ isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ }
+#endif
+
+ /*
+ * Configure the view's cache. Try to reuse an existing
+ * cache if possible, otherwise create a new cache.
+ * Note that the ADB is not preserved in either case.
+ * When a matching view is found, the associated statistics are
+ * also retrieved and reused.
+ *
+ * XXX Determining when it is safe to reuse a cache is
+ * tricky. When the view's configuration changes, the cached
+ * data may become invalid because it reflects our old
+ * view of the world. As more view attributes become
+ * configurable, we will have to add code here to check
+ * whether they have changed in ways that could
+ * invalidate the cache.
+ */
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (pview != NULL) {
+ INSIST(pview->cache != NULL);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(3),
+ "reusing existing cache");
+ reused_cache = ISC_TRUE;
+ dns_cache_attach(pview->cache, &cache);
+ dns_view_getresstats(pview, &resstats);
+ dns_view_getresquerystats(pview, &resquerystats);
+ dns_view_detach(&pview);
+ } else {
+ CHECK(isc_mem_create(0, 0, &cmctx));
+ CHECK(dns_cache_create(cmctx, ns_g_taskmgr, ns_g_timermgr,
+ view->rdclass, "rbt", 0, NULL, &cache));
+ isc_mem_setname(cmctx, "cache", NULL);
+ }
+ dns_view_setcache(view, cache);
+
+ /*
+ * cache-file cannot be inherited if views are present, but this
+ * should be caught by the configuration checking stage.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "cache-file", &obj);
+ if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") != 0) {
+ CHECK(dns_cache_setfilename(cache, cfg_obj_asstring(obj)));
+ if (!reused_cache)
+ CHECK(dns_cache_load(cache));
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "cleaning-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_cache_setcleaninginterval(cache, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-cache-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isstring(obj)) {
+ str = cfg_obj_asstring(obj);
+ INSIST(strcasecmp(str, "unlimited") == 0);
+ max_cache_size = ISC_UINT32_MAX;
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > ISC_UINT32_MAX) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "'max-cache-size "
+ "%" ISC_PRINT_QUADFORMAT "d' is too large",
+ value);
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ max_cache_size = (isc_uint32_t)value;
+ }
+ dns_cache_setcachesize(cache, max_cache_size);
+
+ dns_cache_detach(&cache);
+
+ /*
+ * Check-names.
+ */
+ obj = NULL;
+ result = ns_checknames_get(maps, "response", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "fail") == 0) {
+ resopts |= DNS_RESOLVER_CHECKNAMES |
+ DNS_RESOLVER_CHECKNAMESFAIL;
+ view->checknames = ISC_TRUE;
+ } else if (strcasecmp(str, "warn") == 0) {
+ resopts |= DNS_RESOLVER_CHECKNAMES;
+ view->checknames = ISC_FALSE;
+ } else if (strcasecmp(str, "ignore") == 0) {
+ view->checknames = ISC_FALSE;
+ } else
+ INSIST(0);
+
+ /*
+ * Resolver.
+ *
+ * XXXRTH Hardwired number of tasks.
+ */
+ CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4,
+ ISC_TF(ISC_LIST_PREV(view, link)
+ == NULL)));
+ CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6,
+ ISC_TF(ISC_LIST_PREV(view, link)
+ == NULL)));
+ if (dispatch4 == NULL && dispatch6 == NULL) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "unable to obtain neither an IPv4 nor"
+ " an IPv6 dispatch");
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+ CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
+ ns_g_socketmgr, ns_g_timermgr,
+ resopts, ns_g_dispatchmgr,
+ dispatch4, dispatch6));
+
+ if (resstats == NULL) {
+ CHECK(dns_generalstats_create(mctx, &resstats,
+ dns_resstatscounter_max));
+ }
+ dns_view_setresstats(view, resstats);
+ if (resquerystats == NULL)
+ CHECK(dns_rdatatypestats_create(mctx, &resquerystats));
+ dns_view_setresquerystats(view, resquerystats);
+
+ /*
+ * Set the ADB cache size to 1/8th of the max-cache-size.
+ */
+ max_adb_size = 0;
+ if (max_cache_size != 0) {
+ max_adb_size = max_cache_size / 8;
+ if (max_adb_size == 0)
+ max_adb_size = 1; /* Force minimum. */
+ }
+ dns_adb_setadbsize(view->adb, max_adb_size);
+
+ /*
+ * Set resolver's lame-ttl.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "lame-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ lame_ttl = cfg_obj_asuint32(obj);
+ if (lame_ttl > 1800)
+ lame_ttl = 1800;
+ dns_resolver_setlamettl(view->resolver, lame_ttl);
+
+ obj = NULL;
+ result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setzeronosoattl(view->resolver, cfg_obj_asboolean(obj));
+
+ /*
+ * Set the resolver's EDNS UDP size.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "edns-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ dns_resolver_setudpsize(view->resolver, (isc_uint16_t)udpsize);
+
+ /*
+ * Set the maximum UDP response size.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "max-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ view->maxudp = udpsize;
+
+ /*
+ * Set supported DNSSEC algorithms.
+ */
+ dns_resolver_reset_algorithms(view->resolver);
+ disabled = NULL;
+ (void)ns_config_get(maps, "disable-algorithms", &disabled);
+ if (disabled != NULL) {
+ for (element = cfg_list_first(disabled);
+ element != NULL;
+ element = cfg_list_next(element))
+ CHECK(disable_algorithms(cfg_listelt_value(element),
+ view->resolver));
+ }
+
+ /*
+ * A global or view "forwarders" option, if present,
+ * creates an entry for "." in the forwarding table.
+ */
+ forwardtype = NULL;
+ forwarders = NULL;
+ (void)ns_config_get(maps, "forward", &forwardtype);
+ (void)ns_config_get(maps, "forwarders", &forwarders);
+ if (forwarders != NULL)
+ CHECK(configure_forward(config, view, dns_rootname,
+ forwarders, forwardtype));
+
+ /*
+ * Dual Stack Servers.
+ */
+ alternates = NULL;
+ (void)ns_config_get(maps, "dual-stack-servers", &alternates);
+ if (alternates != NULL)
+ CHECK(configure_alternates(config, view, alternates));
+
+ /*
+ * We have default hints for class IN if we need them.
+ */
+ if (view->rdclass == dns_rdataclass_in && view->hints == NULL)
+ dns_view_sethints(view, ns_g_server->in_roothints);
+
+ /*
+ * If we still have no hints, this is a non-IN view with no
+ * "hints zone" configured. Issue a warning, except if this
+ * is a root server. Root servers never need to consult
+ * their hints, so it's no point requiring users to configure
+ * them.
+ */
+ if (view->hints == NULL) {
+ dns_zone_t *rootzone = NULL;
+ (void)dns_view_findzone(view, dns_rootname, &rootzone);
+ if (rootzone != NULL) {
+ dns_zone_detach(&rootzone);
+ need_hints = ISC_FALSE;
+ }
+ if (need_hints)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "no root hints for view '%s'",
+ view->name);
+ }
+
+ /*
+ * Configure the view's TSIG keys.
+ */
+ ring = NULL;
+ CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring));
+ dns_view_setkeyring(view, ring);
+
+ /*
+ * Configure the view's peer list.
+ */
+ {
+ const cfg_obj_t *peers = NULL;
+ const cfg_listelt_t *element;
+ dns_peerlist_t *newpeers = NULL;
+
+ (void)ns_config_get(cfgmaps, "server", &peers);
+ CHECK(dns_peerlist_new(mctx, &newpeers));
+ for (element = cfg_list_first(peers);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *cpeer = cfg_listelt_value(element);
+ dns_peer_t *peer;
+
+ CHECK(configure_peer(cpeer, mctx, &peer));
+ dns_peerlist_addpeer(newpeers, peer);
+ dns_peer_detach(&peer);
+ }
+ dns_peerlist_detach(&view->peers);
+ view->peers = newpeers; /* Transfer ownership. */
+ }
+
+ /*
+ * Configure the views rrset-order.
+ */
+ {
+ const cfg_obj_t *rrsetorder = NULL;
+ const cfg_listelt_t *element;
+
+ (void)ns_config_get(maps, "rrset-order", &rrsetorder);
+ CHECK(dns_order_create(mctx, &order));
+ for (element = cfg_list_first(rrsetorder);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *ent = cfg_listelt_value(element);
+
+ CHECK(configure_order(order, ent));
+ }
+ if (view->order != NULL)
+ dns_order_detach(&view->order);
+ dns_order_attach(order, &view->order);
+ dns_order_detach(&order);
+ }
+ /*
+ * Copy the aclenv object.
+ */
+ dns_aclenv_copy(&view->aclenv, &ns_g_server->aclenv);
+
+ /*
+ * Configure the "match-clients" and "match-destinations" ACL.
+ */
+ CHECK(configure_view_acl(vconfig, config, "match-clients", actx,
+ ns_g_mctx, &view->matchclients));
+ CHECK(configure_view_acl(vconfig, config, "match-destinations", actx,
+ ns_g_mctx, &view->matchdestinations));
+
+ /*
+ * Configure the "match-recursive-only" option.
+ */
+ obj = NULL;
+ (void)ns_config_get(maps, "match-recursive-only", &obj);
+ if (obj != NULL && cfg_obj_asboolean(obj))
+ view->matchrecursiveonly = ISC_TRUE;
+ else
+ view->matchrecursiveonly = ISC_FALSE;
+
+ /*
+ * Configure other configurable data.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "recursion", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->recursion = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "auth-nxdomain", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->auth_nxdomain = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "minimal-responses", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->minimalresponses = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "transfer-format", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "many-answers") == 0)
+ view->transfer_format = dns_many_answers;
+ else if (strcasecmp(str, "one-answer") == 0)
+ view->transfer_format = dns_one_answer;
+ else
+ INSIST(0);
+
+ /*
+ * Set sources where additional data and CNAME/DNAME
+ * targets for authoritative answers may be found.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "additional-from-auth", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->additionalfromauth = cfg_obj_asboolean(obj);
+ if (view->recursion && ! view->additionalfromauth) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
+ "'additional-from-auth no' is only supported "
+ "with 'recursion no'");
+ view->additionalfromauth = ISC_TRUE;
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "additional-from-cache", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->additionalfromcache = cfg_obj_asboolean(obj);
+ if (view->recursion && ! view->additionalfromcache) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
+ "'additional-from-cache no' is only supported "
+ "with 'recursion no'");
+ view->additionalfromcache = ISC_TRUE;
+ }
+
+ /*
+ * Set "allow-query-cache", "allow-query-cache-on",
+ * "allow-recursion", and "allow-recursion-on" acls if
+ * configured in named.conf.
+ */
+ CHECK(configure_view_acl(vconfig, config, "allow-query-cache",
+ actx, ns_g_mctx, &view->queryacl));
+ CHECK(configure_view_acl(vconfig, config, "allow-query-cache-on",
+ actx, ns_g_mctx, &view->queryonacl));
+ if (view->queryonacl == NULL)
+ CHECK(configure_view_acl(NULL, ns_g_config,
+ "allow-query-cache-on", actx,
+ ns_g_mctx, &view->queryonacl));
+ if (strcmp(view->name, "_bind") != 0) {
+ CHECK(configure_view_acl(vconfig, config, "allow-recursion",
+ actx, ns_g_mctx,
+ &view->recursionacl));
+ CHECK(configure_view_acl(vconfig, config, "allow-recursion-on",
+ actx, ns_g_mctx,
+ &view->recursiononacl));
+ }
+
+ /*
+ * "allow-query-cache" inherits from "allow-recursion" if set,
+ * otherwise from "allow-query" if set.
+ * "allow-recursion" inherits from "allow-query-cache" if set,
+ * otherwise from "allow-query" if set.
+ */
+ if (view->queryacl == NULL && view->recursionacl != NULL)
+ dns_acl_attach(view->recursionacl, &view->queryacl);
+ if (view->queryacl == NULL)
+ CHECK(configure_view_acl(vconfig, config, "allow-query",
+ actx, ns_g_mctx, &view->queryacl));
+ if (view->recursionacl == NULL && view->queryacl != NULL)
+ dns_acl_attach(view->queryacl, &view->recursionacl);
+
+ /*
+ * Set default "allow-recursion", "allow-recursion-on" and
+ * "allow-query-cache" acls.
+ */
+ if (view->recursionacl == NULL && view->recursion)
+ CHECK(configure_view_acl(NULL, ns_g_config,
+ "allow-recursion",
+ actx, ns_g_mctx,
+ &view->recursionacl));
+ if (view->recursiononacl == NULL && view->recursion)
+ CHECK(configure_view_acl(NULL, ns_g_config,
+ "allow-recursion-on",
+ actx, ns_g_mctx,
+ &view->recursiononacl));
+ if (view->queryacl == NULL)
+ CHECK(configure_view_acl(NULL, ns_g_config,
+ "allow-query-cache", actx,
+ ns_g_mctx, &view->queryacl));
+
+ /*
+ * Configure sortlist, if set
+ */
+ CHECK(configure_view_sortlist(vconfig, config, actx, ns_g_mctx,
+ &view->sortlist));
+
+ /*
+ * Configure default allow-transfer, allow-notify, allow-update
+ * and allow-update-forwarding ACLs, if set, so they can be
+ * inherited by zones.
+ */
+ if (view->notifyacl == NULL)
+ CHECK(configure_view_acl(NULL, ns_g_config,
+ "allow-notify", actx,
+ ns_g_mctx, &view->notifyacl));
+ if (view->transferacl == NULL)
+ CHECK(configure_view_acl(NULL, ns_g_config,
+ "allow-transfer", actx,
+ ns_g_mctx, &view->transferacl));
+ if (view->updateacl == NULL)
+ CHECK(configure_view_acl(NULL, ns_g_config,
+ "allow-update", actx,
+ ns_g_mctx, &view->updateacl));
+ if (view->upfwdacl == NULL)
+ CHECK(configure_view_acl(NULL, ns_g_config,
+ "allow-update-forwarding", actx,
+ ns_g_mctx, &view->upfwdacl));
+
+ obj = NULL;
+ result = ns_config_get(maps, "request-ixfr", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->requestixfr = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "provide-ixfr", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->provideixfr = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "request-nsid", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->requestnsid = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-clients-per-query", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ max_clients_per_query = cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "clients-per-query", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setclientsperquery(view->resolver,
+ cfg_obj_asuint32(obj),
+ max_clients_per_query);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-enable", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->enablednssec = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-accept-expired", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->acceptexpired = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-validation", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->enablevalidation = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-lookaside", &obj);
+ if (result == ISC_R_SUCCESS) {
+ for (element = cfg_list_first(obj);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const char *str;
+ isc_buffer_t b;
+ dns_name_t *dlv;
+
+ obj = cfg_listelt_value(element);
+#if 0
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ /*
+ * When we support multiple dnssec-lookaside
+ * entries this is how to find the domain to be
+ * checked. XXXMPA
+ */
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ str = cfg_obj_asstring(cfg_tuple_get(obj,
+ "domain"));
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname,
+ ISC_TRUE, NULL));
+#endif
+ str = cfg_obj_asstring(cfg_tuple_get(obj,
+ "trust-anchor"));
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ dlv = dns_fixedname_name(&view->dlv_fixed);
+ CHECK(dns_name_fromtext(dlv, &b, dns_rootname,
+ ISC_TRUE, NULL));
+ view->dlv = dns_fixedname_name(&view->dlv_fixed);
+ }
+ } else
+ view->dlv = NULL;
+
+ /*
+ * For now, there is only one kind of trusted keys, the
+ * "security roots".
+ */
+ CHECK(configure_view_dnsseckeys(vconfig, config, mctx,
+ &view->secroots));
+ dns_resolver_resetmustbesecure(view->resolver);
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-must-be-secure", &obj);
+ if (result == ISC_R_SUCCESS)
+ CHECK(mustbesecure(obj, view->resolver));
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-cache-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->maxcachettl = cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-ncache-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->maxncachettl = cfg_obj_asuint32(obj);
+ if (view->maxncachettl > 7 * 24 * 3600)
+ view->maxncachettl = 7 * 24 * 3600;
+
+ obj = NULL;
+ result = ns_config_get(maps, "preferred-glue", &obj);
+ if (result == ISC_R_SUCCESS) {
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "a") == 0)
+ view->preferred_glue = dns_rdatatype_a;
+ else if (strcasecmp(str, "aaaa") == 0)
+ view->preferred_glue = dns_rdatatype_aaaa;
+ else
+ view->preferred_glue = 0;
+ } else
+ view->preferred_glue = 0;
+
+ obj = NULL;
+ result = ns_config_get(maps, "root-delegation-only", &obj);
+ if (result == ISC_R_SUCCESS) {
+ dns_view_setrootdelonly(view, ISC_TRUE);
+ if (!cfg_obj_isvoid(obj)) {
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+ const char *str;
+ const cfg_obj_t *exclude;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ for (element = cfg_list_first(obj);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ exclude = cfg_listelt_value(element);
+ str = cfg_obj_asstring(exclude);
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname,
+ ISC_FALSE, NULL));
+ CHECK(dns_view_excludedelegationonly(view,
+ name));
+ }
+ }
+ } else
+ dns_view_setrootdelonly(view, ISC_FALSE);
+
+ /*
+ * Setup automatic empty zones. If recursion is off then
+ * they are disabled by default.
+ */
+ obj = NULL;
+ (void)ns_config_get(maps, "empty-zones-enable", &obj);
+ (void)ns_config_get(maps, "disable-empty-zone", &disablelist);
+ if (obj == NULL && disablelist == NULL &&
+ view->rdclass == dns_rdataclass_in) {
+ rfc1918 = ISC_FALSE;
+ empty_zones_enable = view->recursion;
+ } else if (view->rdclass == dns_rdataclass_in) {
+ rfc1918 = ISC_TRUE;
+ if (obj != NULL)
+ empty_zones_enable = cfg_obj_asboolean(obj);
+ else
+ empty_zones_enable = view->recursion;
+ } else {
+ rfc1918 = ISC_FALSE;
+ empty_zones_enable = ISC_FALSE;
+ }
+ if (empty_zones_enable) {
+ const char *empty;
+ int empty_zone = 0;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t buffer;
+ const char *str;
+ char server[DNS_NAME_FORMATSIZE + 1];
+ char contact[DNS_NAME_FORMATSIZE + 1];
+ isc_boolean_t logit;
+ const char *empty_dbtype[4] =
+ { "_builtin", "empty", NULL, NULL };
+ int empty_dbtypec = 4;
+ isc_boolean_t zonestats_on;
+
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+
+ obj = NULL;
+ result = ns_config_get(maps, "empty-server", &obj);
+ if (result == ISC_R_SUCCESS) {
+ str = cfg_obj_asstring(obj);
+ isc_buffer_init(&buffer, str, strlen(str));
+ isc_buffer_add(&buffer, strlen(str));
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
+ ISC_FALSE, NULL));
+ isc_buffer_init(&buffer, server, sizeof(server) - 1);
+ CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
+ server[isc_buffer_usedlength(&buffer)] = 0;
+ empty_dbtype[2] = server;
+ } else
+ empty_dbtype[2] = "@";
+
+ obj = NULL;
+ result = ns_config_get(maps, "empty-contact", &obj);
+ if (result == ISC_R_SUCCESS) {
+ str = cfg_obj_asstring(obj);
+ isc_buffer_init(&buffer, str, strlen(str));
+ isc_buffer_add(&buffer, strlen(str));
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
+ ISC_FALSE, NULL));
+ isc_buffer_init(&buffer, contact, sizeof(contact) - 1);
+ CHECK(dns_name_totext(name, ISC_FALSE, &buffer));
+ contact[isc_buffer_usedlength(&buffer)] = 0;
+ empty_dbtype[3] = contact;
+ } else
+ empty_dbtype[3] = ".";
+
+ obj = NULL;
+ result = ns_config_get(maps, "zone-statistics", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ zonestats_on = cfg_obj_asboolean(obj);
+
+ logit = ISC_TRUE;
+ for (empty = empty_zones[empty_zone].zone;
+ empty != NULL;
+ empty = empty_zones[++empty_zone].zone)
+ {
+ dns_forwarders_t *forwarders = NULL;
+ dns_view_t *pview = NULL;
+
+ isc_buffer_init(&buffer, empty, strlen(empty));
+ isc_buffer_add(&buffer, strlen(empty));
+ /*
+ * Look for zone on drop list.
+ */
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
+ ISC_FALSE, NULL));
+ if (disablelist != NULL &&
+ on_disable_list(disablelist, name))
+ continue;
+
+ /*
+ * This zone already exists.
+ */
+ (void)dns_view_findzone(view, name, &zone);
+ if (zone != NULL) {
+ CHECK(setquerystats(zone, mctx, zonestats_on));
+ dns_zone_detach(&zone);
+ continue;
+ }
+
+ /*
+ * If we would forward this name don't add a
+ * empty zone for it.
+ */
+ result = dns_fwdtable_find(view->fwdtable, name,
+ &forwarders);
+ if (result == ISC_R_SUCCESS &&
+ forwarders->fwdpolicy == dns_fwdpolicy_only)
+ continue;
+
+ if (!rfc1918 && empty_zones[empty_zone].rfc1918) {
+ if (logit) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING,
+ "Warning%s%s: "
+ "'empty-zones-enable/"
+ "disable-empty-zone' "
+ "not set: disabling "
+ "RFC 1918 empty zones",
+ sep, viewname);
+ logit = ISC_FALSE;
+ }
+ continue;
+ }
+
+ /*
+ * See if we can re-use a existing zone.
+ */
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND &&
+ result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (pview != NULL) {
+ (void)dns_view_findzone(pview, name, &zone);
+ dns_view_detach(&pview);
+ if (zone != NULL)
+ check_dbtype(&zone, empty_dbtypec,
+ empty_dbtype, mctx);
+ if (zone != NULL) {
+ dns_zone_setview(zone, view);
+ CHECK(dns_view_addzone(view, zone));
+ CHECK(setquerystats(zone, mctx,
+ zonestats_on));
+ dns_zone_detach(&zone);
+ continue;
+ }
+ }
+
+ CHECK(dns_zone_create(&zone, mctx));
+ CHECK(dns_zone_setorigin(zone, name));
+ dns_zone_setview(zone, view);
+ CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
+ dns_zone_setclass(zone, view->rdclass);
+ dns_zone_settype(zone, dns_zone_master);
+ dns_zone_setstats(zone, ns_g_server->zonestats);
+ CHECK(dns_zone_setdbtype(zone, empty_dbtypec,
+ empty_dbtype));
+ if (view->queryacl != NULL)
+ dns_zone_setqueryacl(zone, view->queryacl);
+ if (view->queryonacl != NULL)
+ dns_zone_setqueryonacl(zone, view->queryonacl);
+ dns_zone_setdialup(zone, dns_dialuptype_no);
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS,
+ ISC_TRUE);
+ CHECK(setquerystats(zone, mctx, zonestats_on));
+ CHECK(dns_view_addzone(view, zone));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "automatic empty zone%s%s: %s",
+ sep, viewname, empty);
+ dns_zone_detach(&zone);
+ }
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (dispatch4 != NULL)
+ dns_dispatch_detach(&dispatch4);
+ if (dispatch6 != NULL)
+ dns_dispatch_detach(&dispatch6);
+ if (resstats != NULL)
+ dns_stats_detach(&resstats);
+ if (resquerystats != NULL)
+ dns_stats_detach(&resquerystats);
+ if (order != NULL)
+ dns_order_detach(&order);
+ if (cmctx != NULL)
+ isc_mem_detach(&cmctx);
+
+ if (cache != NULL)
+ dns_cache_detach(&cache);
+
+ return (result);
+}
+
+static isc_result_t
+configure_hints(dns_view_t *view, const char *filename) {
+ isc_result_t result;
+ dns_db_t *db;
+
+ db = NULL;
+ result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
+ if (result == ISC_R_SUCCESS) {
+ dns_view_sethints(view, db);
+ dns_db_detach(&db);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+configure_alternates(const cfg_obj_t *config, dns_view_t *view,
+ const cfg_obj_t *alternates)
+{
+ const cfg_obj_t *portobj;
+ const cfg_obj_t *addresses;
+ const cfg_listelt_t *element;
+ isc_result_t result = ISC_R_SUCCESS;
+ in_port_t port;
+
+ /*
+ * Determine which port to send requests to.
+ */
+ if (ns_g_lwresdonly && ns_g_port != 0)
+ port = ns_g_port;
+ else
+ CHECKM(ns_config_getport(config, &port), "port");
+
+ if (alternates != NULL) {
+ portobj = cfg_tuple_get(alternates, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t) val;
+ }
+ }
+
+ addresses = NULL;
+ if (alternates != NULL)
+ addresses = cfg_tuple_get(alternates, "addresses");
+
+ for (element = cfg_list_first(addresses);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *alternate = cfg_listelt_value(element);
+ isc_sockaddr_t sa;
+
+ if (!cfg_obj_issockaddr(alternate)) {
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ const char *str = cfg_obj_asstring(cfg_tuple_get(
+ alternate, "name"));
+ isc_buffer_t buffer;
+ in_port_t myport = port;
+
+ isc_buffer_init(&buffer, str, strlen(str));
+ isc_buffer_add(&buffer, strlen(str));
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname,
+ ISC_FALSE, NULL));
+
+ portobj = cfg_tuple_get(alternate, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx,
+ ISC_LOG_ERROR,
+ "port '%u' out of range",
+ val);
+ return (ISC_R_RANGE);
+ }
+ myport = (in_port_t) val;
+ }
+ CHECK(dns_resolver_addalternate(view->resolver, NULL,
+ name, myport));
+ continue;
+ }
+
+ sa = *cfg_obj_assockaddr(alternate);
+ if (isc_sockaddr_getport(&sa) == 0)
+ isc_sockaddr_setport(&sa, port);
+ CHECK(dns_resolver_addalternate(view->resolver, &sa,
+ NULL, 0));
+ }
+
+ cleanup:
+ return (result);
+}
+
+static isc_result_t
+configure_forward(const cfg_obj_t *config, dns_view_t *view, dns_name_t *origin,
+ const cfg_obj_t *forwarders, const cfg_obj_t *forwardtype)
+{
+ const cfg_obj_t *portobj;
+ const cfg_obj_t *faddresses;
+ const cfg_listelt_t *element;
+ dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none;
+ isc_sockaddrlist_t addresses;
+ isc_sockaddr_t *sa;
+ isc_result_t result;
+ in_port_t port;
+
+ ISC_LIST_INIT(addresses);
+
+ /*
+ * Determine which port to send forwarded requests to.
+ */
+ if (ns_g_lwresdonly && ns_g_port != 0)
+ port = ns_g_port;
+ else
+ CHECKM(ns_config_getport(config, &port), "port");
+
+ if (forwarders != NULL) {
+ portobj = cfg_tuple_get(forwarders, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ isc_uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port '%u' out of range", val);
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t) val;
+ }
+ }
+
+ faddresses = NULL;
+ if (forwarders != NULL)
+ faddresses = cfg_tuple_get(forwarders, "addresses");
+
+ for (element = cfg_list_first(faddresses);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *forwarder = cfg_listelt_value(element);
+ sa = isc_mem_get(view->mctx, sizeof(isc_sockaddr_t));
+ if (sa == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ *sa = *cfg_obj_assockaddr(forwarder);
+ if (isc_sockaddr_getport(sa) == 0)
+ isc_sockaddr_setport(sa, port);
+ ISC_LINK_INIT(sa, link);
+ ISC_LIST_APPEND(addresses, sa, link);
+ }
+
+ if (ISC_LIST_EMPTY(addresses)) {
+ if (forwardtype != NULL)
+ cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
+ "no forwarders seen; disabling "
+ "forwarding");
+ fwdpolicy = dns_fwdpolicy_none;
+ } else {
+ if (forwardtype == NULL)
+ fwdpolicy = dns_fwdpolicy_first;
+ else {
+ const char *forwardstr = cfg_obj_asstring(forwardtype);
+ if (strcasecmp(forwardstr, "first") == 0)
+ fwdpolicy = dns_fwdpolicy_first;
+ else if (strcasecmp(forwardstr, "only") == 0)
+ fwdpolicy = dns_fwdpolicy_only;
+ else
+ INSIST(0);
+ }
+ }
+
+ result = dns_fwdtable_add(view->fwdtable, origin, &addresses,
+ fwdpolicy);
+ if (result != ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(origin, namebuf, sizeof(namebuf));
+ cfg_obj_log(forwarders, ns_g_lctx, ISC_LOG_WARNING,
+ "could not set up forwarding for domain '%s': %s",
+ namebuf, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+
+ while (!ISC_LIST_EMPTY(addresses)) {
+ sa = ISC_LIST_HEAD(addresses);
+ ISC_LIST_UNLINK(addresses, sa, link);
+ isc_mem_put(view->mctx, sa, sizeof(isc_sockaddr_t));
+ }
+
+ return (result);
+}
+
+/*
+ * Create a new view and add it to the list.
+ *
+ * If 'vconfig' is NULL, create the default view.
+ *
+ * The view created is attached to '*viewp'.
+ */
+static isc_result_t
+create_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
+ dns_view_t **viewp)
+{
+ isc_result_t result;
+ const char *viewname;
+ dns_rdataclass_t viewclass;
+ dns_view_t *view = NULL;
+
+ if (vconfig != NULL) {
+ const cfg_obj_t *classobj = NULL;
+
+ viewname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
+ classobj = cfg_tuple_get(vconfig, "class");
+ result = ns_config_getclass(classobj, dns_rdataclass_in,
+ &viewclass);
+ } else {
+ viewname = "_default";
+ viewclass = dns_rdataclass_in;
+ }
+ result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
+ if (result == ISC_R_SUCCESS)
+ return (ISC_R_EXISTS);
+ if (result != ISC_R_NOTFOUND)
+ return (result);
+ INSIST(view == NULL);
+
+ result = dns_view_create(ns_g_mctx, viewclass, viewname, &view);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ ISC_LIST_APPEND(*viewlist, view, link);
+ dns_view_attach(view, viewp);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Configure or reconfigure a zone.
+ */
+static isc_result_t
+configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
+ const cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *aclconf)
+{
+ dns_view_t *pview = NULL; /* Production view */
+ dns_zone_t *zone = NULL; /* New or reused zone */
+ dns_zone_t *dupzone = NULL;
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *typeobj = NULL;
+ const cfg_obj_t *forwarders = NULL;
+ const cfg_obj_t *forwardtype = NULL;
+ const cfg_obj_t *only = NULL;
+ isc_result_t result;
+ isc_result_t tresult;
+ isc_buffer_t buffer;
+ dns_fixedname_t fixorigin;
+ dns_name_t *origin;
+ const char *zname;
+ dns_rdataclass_t zclass;
+ const char *ztypestr;
+
+ options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+
+ /*
+ * Get the zone origin as a dns_name_t.
+ */
+ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+ isc_buffer_init(&buffer, zname, strlen(zname));
+ isc_buffer_add(&buffer, strlen(zname));
+ dns_fixedname_init(&fixorigin);
+ CHECK(dns_name_fromtext(dns_fixedname_name(&fixorigin),
+ &buffer, dns_rootname, ISC_FALSE, NULL));
+ origin = dns_fixedname_name(&fixorigin);
+
+ CHECK(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
+ view->rdclass, &zclass));
+ if (zclass != view->rdclass) {
+ const char *vname = NULL;
+ if (vconfig != NULL)
+ vname = cfg_obj_asstring(cfg_tuple_get(vconfig,
+ "name"));
+ else
+ vname = "<default view>";
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': wrong class for view '%s'",
+ zname, vname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ (void)cfg_map_get(zoptions, "type", &typeobj);
+ if (typeobj == NULL) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "zone '%s' 'type' not specified", zname);
+ return (ISC_R_FAILURE);
+ }
+ ztypestr = cfg_obj_asstring(typeobj);
+
+ /*
+ * "hints zones" aren't zones. If we've got one,
+ * configure it and return.
+ */
+ if (strcasecmp(ztypestr, "hint") == 0) {
+ const cfg_obj_t *fileobj = NULL;
+ if (cfg_map_get(zoptions, "file", &fileobj) != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': 'file' not specified",
+ zname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ if (dns_name_equal(origin, dns_rootname)) {
+ const char *hintsfile = cfg_obj_asstring(fileobj);
+
+ result = configure_hints(view, hintsfile);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "could not configure root hints "
+ "from '%s': %s", hintsfile,
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ /*
+ * Hint zones may also refer to delegation only points.
+ */
+ only = NULL;
+ tresult = cfg_map_get(zoptions, "delegation-only",
+ &only);
+ if (tresult == ISC_R_SUCCESS && cfg_obj_asboolean(only))
+ CHECK(dns_view_adddelegationonly(view, origin));
+ } else {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "ignoring non-root hint zone '%s'",
+ zname);
+ result = ISC_R_SUCCESS;
+ }
+ /* Skip ordinary zone processing. */
+ goto cleanup;
+ }
+
+ /*
+ * "forward zones" aren't zones either. Translate this syntax into
+ * the appropriate selective forwarding configuration and return.
+ */
+ if (strcasecmp(ztypestr, "forward") == 0) {
+ forwardtype = NULL;
+ forwarders = NULL;
+
+ (void)cfg_map_get(zoptions, "forward", &forwardtype);
+ (void)cfg_map_get(zoptions, "forwarders", &forwarders);
+ result = configure_forward(config, view, origin, forwarders,
+ forwardtype);
+ goto cleanup;
+ }
+
+ /*
+ * "delegation-only zones" aren't zones either.
+ */
+ if (strcasecmp(ztypestr, "delegation-only") == 0) {
+ result = dns_view_adddelegationonly(view, origin);
+ goto cleanup;
+ }
+
+ /*
+ * Check for duplicates in the new zone table.
+ */
+ result = dns_view_findzone(view, origin, &dupzone);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We already have this zone!
+ */
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "zone '%s' already exists", zname);
+ dns_zone_detach(&dupzone);
+ result = ISC_R_EXISTS;
+ goto cleanup;
+ }
+ INSIST(dupzone == NULL);
+
+ /*
+ * See if we can reuse an existing zone. This is
+ * only possible if all of these are true:
+ * - The zone's view exists
+ * - A zone with the right name exists in the view
+ * - The zone is compatible with the config
+ * options (e.g., an existing master zone cannot
+ * be reused if the options specify a slave zone)
+ */
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (pview != NULL)
+ result = dns_view_findzone(pview, origin, &zone);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (zone != NULL && !ns_zone_reusable(zone, zconfig))
+ dns_zone_detach(&zone);
+
+ if (zone != NULL) {
+ /*
+ * We found a reusable zone. Make it use the
+ * new view.
+ */
+ dns_zone_setview(zone, view);
+ if (view->acache != NULL)
+ dns_zone_setacache(zone, view->acache);
+ } else {
+ /*
+ * We cannot reuse an existing zone, we have
+ * to create a new one.
+ */
+ CHECK(dns_zone_create(&zone, mctx));
+ CHECK(dns_zone_setorigin(zone, origin));
+ dns_zone_setview(zone, view);
+ if (view->acache != NULL)
+ dns_zone_setacache(zone, view->acache);
+ CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
+ dns_zone_setstats(zone, ns_g_server->zonestats);
+ }
+
+ /*
+ * If the zone contains a 'forwarders' statement, configure
+ * selective forwarding.
+ */
+ forwarders = NULL;
+ if (cfg_map_get(zoptions, "forwarders", &forwarders) == ISC_R_SUCCESS)
+ {
+ forwardtype = NULL;
+ (void)cfg_map_get(zoptions, "forward", &forwardtype);
+ CHECK(configure_forward(config, view, origin, forwarders,
+ forwardtype));
+ }
+
+ /*
+ * Stub and forward zones may also refer to delegation only points.
+ */
+ only = NULL;
+ if (cfg_map_get(zoptions, "delegation-only", &only) == ISC_R_SUCCESS)
+ {
+ if (cfg_obj_asboolean(only))
+ CHECK(dns_view_adddelegationonly(view, origin));
+ }
+
+ /*
+ * Configure the zone.
+ */
+ CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone));
+
+ /*
+ * Add the zone to its view in the new view list.
+ */
+ CHECK(dns_view_addzone(view, zone));
+
+ cleanup:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (pview != NULL)
+ dns_view_detach(&pview);
+
+ return (result);
+}
+
+/*
+ * Configure a single server quota.
+ */
+static void
+configure_server_quota(const cfg_obj_t **maps, const char *name,
+ isc_quota_t *quota)
+{
+ const cfg_obj_t *obj = NULL;
+ isc_result_t result;
+
+ result = ns_config_get(maps, name, &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ isc_quota_max(quota, cfg_obj_asuint32(obj));
+}
+
+/*
+ * This function is called as soon as the 'directory' statement has been
+ * parsed. This can be extended to support other options if necessary.
+ */
+static isc_result_t
+directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
+ isc_result_t result;
+ const char *directory;
+
+ REQUIRE(strcasecmp("directory", clausename) == 0);
+
+ UNUSED(arg);
+ UNUSED(clausename);
+
+ /*
+ * Change directory.
+ */
+ directory = cfg_obj_asstring(obj);
+
+ if (! isc_file_ischdiridempotent(directory))
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_WARNING,
+ "option 'directory' contains relative path '%s'",
+ directory);
+
+ result = isc_dir_chdir(directory);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "change directory to '%s' failed: %s",
+ directory, isc_result_totext(result));
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+scan_interfaces(ns_server_t *server, isc_boolean_t verbose) {
+ isc_boolean_t match_mapped = server->aclenv.match_mapped;
+
+ ns_interfacemgr_scan(server->interfacemgr, verbose);
+ /*
+ * Update the "localhost" and "localnets" ACLs to match the
+ * current set of network interfaces.
+ */
+ dns_aclenv_copy(&server->aclenv,
+ ns_interfacemgr_getaclenv(server->interfacemgr));
+
+ server->aclenv.match_mapped = match_mapped;
+}
+
+static isc_result_t
+add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
+ isc_boolean_t wcardport_ok)
+{
+ ns_listenelt_t *lelt = NULL;
+ dns_acl_t *src_acl = NULL;
+ isc_result_t result;
+ isc_sockaddr_t any_sa6;
+ isc_netaddr_t netaddr;
+
+ REQUIRE(isc_sockaddr_pf(addr) == AF_INET6);
+
+ isc_sockaddr_any6(&any_sa6);
+ if (!isc_sockaddr_equal(&any_sa6, addr) &&
+ (wcardport_ok || isc_sockaddr_getport(addr) != 0)) {
+ isc_netaddr_fromin6(&netaddr, &addr->type.sin6.sin6_addr);
+
+ result = dns_acl_create(mctx, 0, &src_acl);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_iptable_addprefix(src_acl->iptable,
+ &netaddr, 128, ISC_TRUE);
+ if (result != ISC_R_SUCCESS)
+ goto clean;
+
+ result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr),
+ src_acl, &lelt);
+ if (result != ISC_R_SUCCESS)
+ goto clean;
+ ISC_LIST_APPEND(list->elts, lelt, link);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ clean:
+ INSIST(lelt == NULL);
+ dns_acl_detach(&src_acl);
+
+ return (result);
+}
+
+/*
+ * Make a list of xxx-source addresses and call ns_interfacemgr_adjust()
+ * to update the listening interfaces accordingly.
+ * We currently only consider IPv6, because this only affects IPv6 wildcard
+ * sockets.
+ */
+static void
+adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) {
+ isc_result_t result;
+ ns_listenlist_t *list = NULL;
+ dns_view_t *view;
+ dns_zone_t *zone, *next;
+ isc_sockaddr_t addr, *addrp;
+
+ result = ns_listenlist_create(mctx, &list);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ dns_dispatch_t *dispatch6;
+
+ dispatch6 = dns_resolver_dispatchv6(view->resolver);
+ if (dispatch6 == NULL)
+ continue;
+ result = dns_dispatch_getlocaladdress(dispatch6, &addr);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ /*
+ * We always add non-wildcard address regardless of whether
+ * the port is 'any' (the fourth arg is TRUE): if the port is
+ * specific, we need to add it since it may conflict with a
+ * listening interface; if it's zero, we'll dynamically open
+ * query ports, and some of them may override an existing
+ * wildcard IPv6 port.
+ */
+ result = add_listenelt(mctx, list, &addr, ISC_TRUE);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+
+ zone = NULL;
+ for (result = dns_zone_first(server->zonemgr, &zone);
+ result == ISC_R_SUCCESS;
+ next = NULL, result = dns_zone_next(zone, &next), zone = next) {
+ dns_view_t *zoneview;
+
+ /*
+ * At this point the zone list may contain a stale zone
+ * just removed from the configuration. To see the validity,
+ * check if the corresponding view is in our current view list.
+ * There may also be old zones that are still in the process
+ * of shutting down and have detached from their old view
+ * (zoneview == NULL).
+ */
+ zoneview = dns_zone_getview(zone);
+ if (zoneview == NULL)
+ continue;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL && view != zoneview;
+ view = ISC_LIST_NEXT(view, link))
+ ;
+ if (view == NULL)
+ continue;
+
+ addrp = dns_zone_getnotifysrc6(zone);
+ result = add_listenelt(mctx, list, addrp, ISC_FALSE);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ addrp = dns_zone_getxfrsource6(zone);
+ result = add_listenelt(mctx, list, addrp, ISC_FALSE);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+
+ ns_interfacemgr_adjust(server->interfacemgr, list, ISC_TRUE);
+
+ clean:
+ ns_listenlist_detach(&list);
+ return;
+
+ fail:
+ /*
+ * Even when we failed the procedure, most of other interfaces
+ * should work correctly. We therefore just warn it.
+ */
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "could not adjust the listen-on list; "
+ "some interfaces may not work");
+ goto clean;
+}
+
+/*
+ * This event callback is invoked to do periodic network
+ * interface scanning.
+ */
+static void
+interface_timer_tick(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ ns_server_t *server = (ns_server_t *) event->ev_arg;
+ INSIST(task == server->task);
+ UNUSED(task);
+ isc_event_free(&event);
+ /*
+ * XXX should scan interfaces unlocked and get exclusive access
+ * only to replace ACLs.
+ */
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ scan_interfaces(server, ISC_FALSE);
+ isc_task_endexclusive(server->task);
+}
+
+static void
+heartbeat_timer_tick(isc_task_t *task, isc_event_t *event) {
+ ns_server_t *server = (ns_server_t *) event->ev_arg;
+ dns_view_t *view;
+
+ UNUSED(task);
+ isc_event_free(&event);
+ view = ISC_LIST_HEAD(server->viewlist);
+ while (view != NULL) {
+ dns_view_dialup(view);
+ view = ISC_LIST_NEXT(view, link);
+ }
+}
+
+static void
+pps_timer_tick(isc_task_t *task, isc_event_t *event) {
+ static unsigned int oldrequests = 0;
+ unsigned int requests = ns_client_requests;
+
+ UNUSED(task);
+ isc_event_free(&event);
+
+ /*
+ * Don't worry about wrapping as the overflow result will be right.
+ */
+ dns_pps = (requests - oldrequests) / 1200;
+ oldrequests = requests;
+}
+
+/*
+ * Replace the current value of '*field', a dynamically allocated
+ * string or NULL, with a dynamically allocated copy of the
+ * null-terminated string pointed to by 'value', or NULL.
+ */
+static isc_result_t
+setstring(ns_server_t *server, char **field, const char *value) {
+ char *copy;
+
+ if (value != NULL) {
+ copy = isc_mem_strdup(server->mctx, value);
+ if (copy == NULL)
+ return (ISC_R_NOMEMORY);
+ } else {
+ copy = NULL;
+ }
+
+ if (*field != NULL)
+ isc_mem_free(server->mctx, *field);
+
+ *field = copy;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Replace the current value of '*field', a dynamically allocated
+ * string or NULL, with another dynamically allocated string
+ * or NULL if whether 'obj' is a string or void value, respectively.
+ */
+static isc_result_t
+setoptstring(ns_server_t *server, char **field, const cfg_obj_t *obj) {
+ if (cfg_obj_isvoid(obj))
+ return (setstring(server, field, NULL));
+ else
+ return (setstring(server, field, cfg_obj_asstring(obj)));
+}
+
+static void
+set_limit(const cfg_obj_t **maps, const char *configname,
+ const char *description, isc_resource_t resourceid,
+ isc_resourcevalue_t defaultvalue)
+{
+ const cfg_obj_t *obj = NULL;
+ const char *resource;
+ isc_resourcevalue_t value;
+ isc_result_t result;
+
+ if (ns_config_get(maps, configname, &obj) != ISC_R_SUCCESS)
+ return;
+
+ if (cfg_obj_isstring(obj)) {
+ resource = cfg_obj_asstring(obj);
+ if (strcasecmp(resource, "unlimited") == 0)
+ value = ISC_RESOURCE_UNLIMITED;
+ else {
+ INSIST(strcasecmp(resource, "default") == 0);
+ value = defaultvalue;
+ }
+ } else
+ value = cfg_obj_asuint64(obj);
+
+ result = isc_resource_setlimit(resourceid, value);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ result == ISC_R_SUCCESS ?
+ ISC_LOG_DEBUG(3) : ISC_LOG_WARNING,
+ "set maximum %s to %" ISC_PRINT_QUADFORMAT "d: %s",
+ description, value, isc_result_totext(result));
+}
+
+#define SETLIMIT(cfgvar, resource, description) \
+ set_limit(maps, cfgvar, description, isc_resource_ ## resource, \
+ ns_g_init ## resource)
+
+static void
+set_limits(const cfg_obj_t **maps) {
+ SETLIMIT("stacksize", stacksize, "stack size");
+ SETLIMIT("datasize", datasize, "data size");
+ SETLIMIT("coresize", coresize, "core size");
+ SETLIMIT("files", openfiles, "open files");
+}
+
+static void
+portset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports,
+ isc_boolean_t positive)
+{
+ const cfg_listelt_t *element;
+
+ for (element = cfg_list_first(ports);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ const cfg_obj_t *obj = cfg_listelt_value(element);
+
+ if (cfg_obj_isuint32(obj)) {
+ in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
+
+ if (positive)
+ isc_portset_add(portset, port);
+ else
+ isc_portset_remove(portset, port);
+ } else {
+ const cfg_obj_t *obj_loport, *obj_hiport;
+ in_port_t loport, hiport;
+
+ obj_loport = cfg_tuple_get(obj, "loport");
+ loport = (in_port_t)cfg_obj_asuint32(obj_loport);
+ obj_hiport = cfg_tuple_get(obj, "hiport");
+ hiport = (in_port_t)cfg_obj_asuint32(obj_hiport);
+
+ if (positive)
+ isc_portset_addrange(portset, loport, hiport);
+ else {
+ isc_portset_removerange(portset, loport,
+ hiport);
+ }
+ }
+ }
+}
+
+static isc_result_t
+removed(dns_zone_t *zone, void *uap) {
+ const char *type;
+
+ if (dns_zone_getview(zone) != uap)
+ return (ISC_R_SUCCESS);
+
+ switch (dns_zone_gettype(zone)) {
+ case dns_zone_master:
+ type = "master";
+ break;
+ case dns_zone_slave:
+ type = "slave";
+ break;
+ case dns_zone_stub:
+ type = "stub";
+ break;
+ default:
+ type = "other";
+ break;
+ }
+ dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_configuration(const char *filename, ns_server_t *server,
+ isc_boolean_t first_time)
+{
+ cfg_aclconfctx_t aclconfctx;
+ cfg_obj_t *config;
+ cfg_parser_t *parser = NULL;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *builtin_views;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *obj;
+ const cfg_obj_t *options;
+ const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports;
+ const cfg_obj_t *views;
+ dns_view_t *view = NULL;
+ dns_view_t *view_next;
+ dns_viewlist_t tmpviewlist;
+ dns_viewlist_t viewlist;
+ in_port_t listen_port, udpport_low, udpport_high;
+ int i;
+ isc_interval_t interval;
+ isc_portset_t *v4portset = NULL;
+ isc_portset_t *v6portset = NULL;
+ isc_resourcevalue_t nfiles;
+ isc_result_t result;
+ isc_uint32_t heartbeat_interval;
+ isc_uint32_t interface_interval;
+ isc_uint32_t reserved;
+ isc_uint32_t udpsize;
+ unsigned int maxsocks;
+
+ cfg_aclconfctx_init(&aclconfctx);
+ ISC_LIST_INIT(viewlist);
+
+ /* Ensure exclusive access to configuration data. */
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * Parse the global default pseudo-config file.
+ */
+ if (first_time) {
+ CHECK(ns_config_parsedefaults(ns_g_parser, &ns_g_config));
+ RUNTIME_CHECK(cfg_map_get(ns_g_config, "options",
+ &ns_g_defaults) ==
+ ISC_R_SUCCESS);
+ }
+
+ /*
+ * Parse the configuration file using the new config code.
+ */
+ result = ISC_R_FAILURE;
+ config = NULL;
+
+ /*
+ * Unless this is lwresd with the -C option, parse the config file.
+ */
+ if (!(ns_g_lwresdonly && lwresd_g_useresolvconf)) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "loading configuration from '%s'",
+ filename);
+ CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &parser));
+ cfg_parser_setcallback(parser, directory_callback, NULL);
+ result = cfg_parse_file(parser, filename, &cfg_type_namedconf,
+ &config);
+ }
+
+ /*
+ * If this is lwresd with the -C option, or lwresd with no -C or -c
+ * option where the above parsing failed, parse resolv.conf.
+ */
+ if (ns_g_lwresdonly &&
+ (lwresd_g_useresolvconf ||
+ (!ns_g_conffileset && result == ISC_R_FILENOTFOUND)))
+ {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "loading configuration from '%s'",
+ lwresd_g_resolvconffile);
+ if (parser != NULL)
+ cfg_parser_destroy(&parser);
+ CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &parser));
+ result = ns_lwresd_parseeresolvconf(ns_g_mctx, parser,
+ &config);
+ }
+ CHECK(result);
+
+ /*
+ * Check the validity of the configuration.
+ */
+ CHECK(bind9_check_namedconf(config, ns_g_lctx, ns_g_mctx));
+
+ /*
+ * Fill in the maps array, used for resolving defaults.
+ */
+ i = 0;
+ options = NULL;
+ result = cfg_map_get(config, "options", &options);
+ if (result == ISC_R_SUCCESS)
+ maps[i++] = options;
+ maps[i++] = ns_g_defaults;
+ maps[i++] = NULL;
+
+ /*
+ * Set process limits, which (usually) needs to be done as root.
+ */
+ set_limits(maps);
+
+ /*
+ * Check if max number of open sockets that the system allows is
+ * sufficiently large. Failing this condition is not necessarily fatal,
+ * but may cause subsequent runtime failures for a busy recursive
+ * server.
+ */
+ result = isc_socketmgr_getmaxsockets(ns_g_socketmgr, &maxsocks);
+ if (result != ISC_R_SUCCESS)
+ maxsocks = 0;
+ result = isc_resource_getcurlimit(isc_resource_openfiles, &nfiles);
+ if (result == ISC_R_SUCCESS && (isc_resourcevalue_t)maxsocks > nfiles) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "max open files (%" ISC_PRINT_QUADFORMAT "u)"
+ " is smaller than max sockets (%u)",
+ nfiles, maxsocks);
+ }
+
+ /*
+ * Set the number of socket reserved for TCP, stdio etc.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "reserved-sockets", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ reserved = cfg_obj_asuint32(obj);
+ if (maxsocks != 0) {
+ if (maxsocks < 128U) /* Prevent underflow. */
+ reserved = 0;
+ else if (reserved > maxsocks - 128U) /* Minimum UDP space. */
+ reserved = maxsocks - 128;
+ }
+ /* Minimum TCP/stdio space. */
+ if (reserved < 128U)
+ reserved = 128;
+ if (reserved + 128U > maxsocks && maxsocks != 0) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "less than 128 UDP sockets available after "
+ "applying 'reserved-sockets' and 'maxsockets'");
+ }
+ isc__socketmgr_setreserved(ns_g_socketmgr, reserved);
+
+ /*
+ * Configure various server options.
+ */
+ configure_server_quota(maps, "transfers-out", &server->xfroutquota);
+ configure_server_quota(maps, "tcp-clients", &server->tcpquota);
+ configure_server_quota(maps, "recursive-clients",
+ &server->recursionquota);
+ if (server->recursionquota.max > 1000)
+ isc_quota_soft(&server->recursionquota,
+ server->recursionquota.max - 100);
+ else
+ isc_quota_soft(&server->recursionquota, 0);
+
+ CHECK(configure_view_acl(NULL, config, "blackhole", &aclconfctx,
+ ns_g_mctx, &server->blackholeacl));
+ if (server->blackholeacl != NULL)
+ dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
+ server->blackholeacl);
+
+ obj = NULL;
+ result = ns_config_get(maps, "match-mapped-addresses", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ server->aclenv.match_mapped = cfg_obj_asboolean(obj);
+
+ CHECKM(ns_statschannels_configure(ns_g_server, config, &aclconfctx),
+ "configuring statistics server(s)");
+
+ /*
+ * Configure sets of UDP query source ports.
+ */
+ CHECKM(isc_portset_create(ns_g_mctx, &v4portset),
+ "creating UDP port set");
+ CHECKM(isc_portset_create(ns_g_mctx, &v6portset),
+ "creating UDP port set");
+
+ usev4ports = NULL;
+ usev6ports = NULL;
+ avoidv4ports = NULL;
+ avoidv6ports = NULL;
+
+ (void)ns_config_get(maps, "use-v4-udp-ports", &usev4ports);
+ if (usev4ports != NULL)
+ portset_fromconf(v4portset, usev4ports, ISC_TRUE);
+ else {
+ CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low,
+ &udpport_high),
+ "get the default UDP/IPv4 port range");
+ if (udpport_low == udpport_high)
+ isc_portset_add(v4portset, udpport_low);
+ else {
+ isc_portset_addrange(v4portset, udpport_low,
+ udpport_high);
+ }
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "using default UDP/IPv4 port range: [%d, %d]",
+ udpport_low, udpport_high);
+ }
+ (void)ns_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports);
+ if (avoidv4ports != NULL)
+ portset_fromconf(v4portset, avoidv4ports, ISC_FALSE);
+
+ (void)ns_config_get(maps, "use-v6-udp-ports", &usev6ports);
+ if (usev6ports != NULL)
+ portset_fromconf(v6portset, usev6ports, ISC_TRUE);
+ else {
+ CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low,
+ &udpport_high),
+ "get the default UDP/IPv6 port range");
+ if (udpport_low == udpport_high)
+ isc_portset_add(v6portset, udpport_low);
+ else {
+ isc_portset_addrange(v6portset, udpport_low,
+ udpport_high);
+ }
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "using default UDP/IPv6 port range: [%d, %d]",
+ udpport_low, udpport_high);
+ }
+ (void)ns_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports);
+ if (avoidv6ports != NULL)
+ portset_fromconf(v6portset, avoidv6ports, ISC_FALSE);
+
+ dns_dispatchmgr_setavailports(ns_g_dispatchmgr, v4portset, v6portset);
+
+ /*
+ * Set the EDNS UDP size when we don't match a view.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "edns-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ ns_g_udpsize = (isc_uint16_t)udpsize;
+
+ /*
+ * Configure the zone manager.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "transfers-in", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_settransfersin(server->zonemgr, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "transfers-per-ns", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_settransfersperns(server->zonemgr, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "serial-query-rate", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_setserialqueryrate(server->zonemgr, cfg_obj_asuint32(obj));
+
+ /*
+ * Determine which port to use for listening for incoming connections.
+ */
+ if (ns_g_port != 0)
+ listen_port = ns_g_port;
+ else
+ CHECKM(ns_config_getport(config, &listen_port), "port");
+
+ /*
+ * Find the listen queue depth.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "tcp-listen-queue", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ ns_g_listen = cfg_obj_asuint32(obj);
+ if (ns_g_listen < 3)
+ ns_g_listen = 3;
+
+ /*
+ * Configure the interface manager according to the "listen-on"
+ * statement.
+ */
+ {
+ const cfg_obj_t *clistenon = NULL;
+ ns_listenlist_t *listenon = NULL;
+
+ clistenon = NULL;
+ /*
+ * Even though listen-on is present in the default
+ * configuration, we can't use it here, since it isn't
+ * used if we're in lwresd mode. This way is easier.
+ */
+ if (options != NULL)
+ (void)cfg_map_get(options, "listen-on", &clistenon);
+ if (clistenon != NULL) {
+ result = ns_listenlist_fromconfig(clistenon,
+ config,
+ &aclconfctx,
+ ns_g_mctx,
+ &listenon);
+ } else if (!ns_g_lwresdonly) {
+ /*
+ * Not specified, use default.
+ */
+ CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
+ ISC_TRUE, &listenon));
+ }
+ if (listenon != NULL) {
+ ns_interfacemgr_setlistenon4(server->interfacemgr,
+ listenon);
+ ns_listenlist_detach(&listenon);
+ }
+ }
+ /*
+ * Ditto for IPv6.
+ */
+ {
+ const cfg_obj_t *clistenon = NULL;
+ ns_listenlist_t *listenon = NULL;
+
+ if (options != NULL)
+ (void)cfg_map_get(options, "listen-on-v6", &clistenon);
+ if (clistenon != NULL) {
+ result = ns_listenlist_fromconfig(clistenon,
+ config,
+ &aclconfctx,
+ ns_g_mctx,
+ &listenon);
+ } else if (!ns_g_lwresdonly) {
+ isc_boolean_t enable;
+ /*
+ * Not specified, use default.
+ */
+ enable = ISC_TF(isc_net_probeipv4() != ISC_R_SUCCESS);
+ CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
+ enable, &listenon));
+ }
+ if (listenon != NULL) {
+ ns_interfacemgr_setlistenon6(server->interfacemgr,
+ listenon);
+ ns_listenlist_detach(&listenon);
+ }
+ }
+
+ /*
+ * Rescan the interface list to pick up changes in the
+ * listen-on option. It's important that we do this before we try
+ * to configure the query source, since the dispatcher we use might
+ * be shared with an interface.
+ */
+ scan_interfaces(server, ISC_TRUE);
+
+ /*
+ * Arrange for further interface scanning to occur periodically
+ * as specified by the "interface-interval" option.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "interface-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ interface_interval = cfg_obj_asuint32(obj) * 60;
+ if (interface_interval == 0) {
+ CHECK(isc_timer_reset(server->interface_timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE));
+ } else if (server->interface_interval != interface_interval) {
+ isc_interval_set(&interval, interface_interval, 0);
+ CHECK(isc_timer_reset(server->interface_timer,
+ isc_timertype_ticker,
+ NULL, &interval, ISC_FALSE));
+ }
+ server->interface_interval = interface_interval;
+
+ /*
+ * Configure the dialup heartbeat timer.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "heartbeat-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ heartbeat_interval = cfg_obj_asuint32(obj) * 60;
+ if (heartbeat_interval == 0) {
+ CHECK(isc_timer_reset(server->heartbeat_timer,
+ isc_timertype_inactive,
+ NULL, NULL, ISC_TRUE));
+ } else if (server->heartbeat_interval != heartbeat_interval) {
+ isc_interval_set(&interval, heartbeat_interval, 0);
+ CHECK(isc_timer_reset(server->heartbeat_timer,
+ isc_timertype_ticker,
+ NULL, &interval, ISC_FALSE));
+ }
+ server->heartbeat_interval = heartbeat_interval;
+
+ isc_interval_set(&interval, 1200, 0);
+ CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL,
+ &interval, ISC_FALSE));
+
+ /*
+ * Configure and freeze all explicit views. Explicit
+ * views that have zones were already created at parsing
+ * time, but views with no zones must be created here.
+ */
+ views = NULL;
+ (void)cfg_map_get(config, "view", &views);
+ for (element = cfg_list_first(views);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *vconfig = cfg_listelt_value(element);
+ view = NULL;
+
+ CHECK(create_view(vconfig, &viewlist, &view));
+ INSIST(view != NULL);
+ CHECK(configure_view(view, config, vconfig,
+ ns_g_mctx, &aclconfctx, ISC_TRUE));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Make sure we have a default view if and only if there
+ * were no explicit views.
+ */
+ if (views == NULL) {
+ /*
+ * No explicit views; there ought to be a default view.
+ * There may already be one created as a side effect
+ * of zone statements, or we may have to create one.
+ * In either case, we need to configure and freeze it.
+ */
+ CHECK(create_view(NULL, &viewlist, &view));
+ CHECK(configure_view(view, config, NULL, ns_g_mctx,
+ &aclconfctx, ISC_TRUE));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Create (or recreate) the built-in views. Currently
+ * there is only one, the _bind view.
+ */
+ builtin_views = NULL;
+ RUNTIME_CHECK(cfg_map_get(ns_g_config, "view",
+ &builtin_views) == ISC_R_SUCCESS);
+ for (element = cfg_list_first(builtin_views);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *vconfig = cfg_listelt_value(element);
+ CHECK(create_view(vconfig, &viewlist, &view));
+ CHECK(configure_view(view, config, vconfig, ns_g_mctx,
+ &aclconfctx, ISC_FALSE));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ view = NULL;
+ }
+
+ /*
+ * Swap our new view list with the production one.
+ */
+ tmpviewlist = server->viewlist;
+ server->viewlist = viewlist;
+ viewlist = tmpviewlist;
+
+ /*
+ * Load the TKEY information from the configuration.
+ */
+ if (options != NULL) {
+ dns_tkeyctx_t *t = NULL;
+ CHECKM(ns_tkeyctx_fromconfig(options, ns_g_mctx, ns_g_entropy,
+ &t),
+ "configuring TKEY");
+ if (server->tkeyctx != NULL)
+ dns_tkeyctx_destroy(&server->tkeyctx);
+ server->tkeyctx = t;
+ }
+
+ /*
+ * Bind the control port(s).
+ */
+ CHECKM(ns_controls_configure(ns_g_server->controls, config,
+ &aclconfctx),
+ "binding control channel(s)");
+
+ /*
+ * Bind the lwresd port(s).
+ */
+ CHECKM(ns_lwresd_configure(ns_g_mctx, config),
+ "binding lightweight resolver ports");
+
+ /*
+ * Open the source of entropy.
+ */
+ if (first_time) {
+ obj = NULL;
+ result = ns_config_get(maps, "random-device", &obj);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "no source of entropy found");
+ } else {
+ const char *randomdev = cfg_obj_asstring(obj);
+ result = isc_entropy_createfilesource(ns_g_entropy,
+ randomdev);
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO,
+ "could not open entropy source "
+ "%s: %s",
+ randomdev,
+ isc_result_totext(result));
+#ifdef PATH_RANDOMDEV
+ if (ns_g_fallbackentropy != NULL) {
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO,
+ "using pre-chroot entropy source "
+ "%s",
+ PATH_RANDOMDEV);
+ isc_entropy_detach(&ns_g_entropy);
+ isc_entropy_attach(ns_g_fallbackentropy,
+ &ns_g_entropy);
+ }
+ isc_entropy_detach(&ns_g_fallbackentropy);
+ }
+#endif
+ }
+ }
+
+ /*
+ * Relinquish root privileges.
+ */
+ if (first_time)
+ ns_os_changeuser();
+
+ /*
+ * Check that the working directory is writable.
+ */
+ if (access(".", W_OK) != 0) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "the working directory is not writable");
+ }
+
+ /*
+ * Configure the logging system.
+ *
+ * Do this after changing UID to make sure that any log
+ * files specified in named.conf get created by the
+ * unprivileged user, not root.
+ */
+ if (ns_g_logstderr) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "ignoring config file logging "
+ "statement due to -g option");
+ } else {
+ const cfg_obj_t *logobj = NULL;
+ isc_logconfig_t *logc = NULL;
+
+ CHECKM(isc_logconfig_create(ns_g_lctx, &logc),
+ "creating new logging configuration");
+
+ logobj = NULL;
+ (void)cfg_map_get(config, "logging", &logobj);
+ if (logobj != NULL) {
+ CHECKM(ns_log_configure(logc, logobj),
+ "configuring logging");
+ } else {
+ CHECKM(ns_log_setdefaultchannels(logc),
+ "setting up default logging channels");
+ CHECKM(ns_log_setunmatchedcategory(logc),
+ "setting up default 'category unmatched'");
+ CHECKM(ns_log_setdefaultcategory(logc),
+ "setting up default 'category default'");
+ }
+
+ result = isc_logconfig_use(ns_g_lctx, logc);
+ if (result != ISC_R_SUCCESS) {
+ isc_logconfig_destroy(&logc);
+ CHECKM(result, "installing logging configuration");
+ }
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "now using logging configuration from "
+ "config file");
+ }
+
+ /*
+ * Set the default value of the query logging flag depending
+ * whether a "queries" category has been defined. This is
+ * a disgusting hack, but we need to do this for BIND 8
+ * compatibility.
+ */
+ if (first_time) {
+ const cfg_obj_t *logobj = NULL;
+ const cfg_obj_t *categories = NULL;
+
+ obj = NULL;
+ if (ns_config_get(maps, "querylog", &obj) == ISC_R_SUCCESS) {
+ server->log_queries = cfg_obj_asboolean(obj);
+ } else {
+
+ (void)cfg_map_get(config, "logging", &logobj);
+ if (logobj != NULL)
+ (void)cfg_map_get(logobj, "category",
+ &categories);
+ if (categories != NULL) {
+ const cfg_listelt_t *element;
+ for (element = cfg_list_first(categories);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *catobj;
+ const char *str;
+
+ obj = cfg_listelt_value(element);
+ catobj = cfg_tuple_get(obj, "name");
+ str = cfg_obj_asstring(catobj);
+ if (strcasecmp(str, "queries") == 0)
+ server->log_queries = ISC_TRUE;
+ }
+ }
+ }
+ }
+
+ obj = NULL;
+ if (ns_config_get(maps, "pid-file", &obj) == ISC_R_SUCCESS)
+ if (cfg_obj_isvoid(obj))
+ ns_os_writepidfile(NULL, first_time);
+ else
+ ns_os_writepidfile(cfg_obj_asstring(obj), first_time);
+ else if (ns_g_lwresdonly)
+ ns_os_writepidfile(lwresd_g_defaultpidfile, first_time);
+ else
+ ns_os_writepidfile(ns_g_defaultpidfile, first_time);
+
+ obj = NULL;
+ if (options != NULL &&
+ cfg_map_get(options, "memstatistics", &obj) == ISC_R_SUCCESS)
+ ns_g_memstatistics = cfg_obj_asboolean(obj);
+ else
+ ns_g_memstatistics =
+ ISC_TF((isc_mem_debugging & ISC_MEM_DEBUGRECORD) != 0);
+
+ obj = NULL;
+ if (ns_config_get(maps, "memstatistics-file", &obj) == ISC_R_SUCCESS)
+ ns_main_setmemstats(cfg_obj_asstring(obj));
+ else if (ns_g_memstatistics)
+ ns_main_setmemstats("named.memstats");
+ else
+ ns_main_setmemstats(NULL);
+
+ obj = NULL;
+ result = ns_config_get(maps, "statistics-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->statsfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = ns_config_get(maps, "dump-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->dumpfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = ns_config_get(maps, "recursing-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->recfile, cfg_obj_asstring(obj)),
+ "strdup");
+
+ obj = NULL;
+ result = ns_config_get(maps, "version", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECKM(setoptstring(server, &server->version, obj), "strdup");
+ server->version_set = ISC_TRUE;
+ } else {
+ server->version_set = ISC_FALSE;
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "hostname", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECKM(setoptstring(server, &server->hostname, obj), "strdup");
+ server->hostname_set = ISC_TRUE;
+ } else {
+ server->hostname_set = ISC_FALSE;
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "server-id", &obj);
+ server->server_usehostname = ISC_FALSE;
+ if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
+ /* The parser translates "hostname" to ISC_TRUE */
+ server->server_usehostname = cfg_obj_asboolean(obj);
+ result = setstring(server, &server->server_id, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ } else if (result == ISC_R_SUCCESS) {
+ /* Found a quoted string */
+ CHECKM(setoptstring(server, &server->server_id, obj), "strdup");
+ } else {
+ result = setstring(server, &server->server_id, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "flush-zones-on-shutdown", &obj);
+ if (result == ISC_R_SUCCESS) {
+ server->flushonshutdown = cfg_obj_asboolean(obj);
+ } else {
+ server->flushonshutdown = ISC_FALSE;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (v4portset != NULL)
+ isc_portset_destroy(ns_g_mctx, &v4portset);
+
+ if (v6portset != NULL)
+ isc_portset_destroy(ns_g_mctx, &v6portset);
+
+ cfg_aclconfctx_destroy(&aclconfctx);
+
+ if (parser != NULL) {
+ if (config != NULL)
+ cfg_obj_destroy(parser, &config);
+ cfg_parser_destroy(&parser);
+ }
+
+ if (view != NULL)
+ dns_view_detach(&view);
+
+ /*
+ * This cleans up either the old production view list
+ * or our temporary list depending on whether they
+ * were swapped above or not.
+ */
+ for (view = ISC_LIST_HEAD(viewlist);
+ view != NULL;
+ view = view_next) {
+ view_next = ISC_LIST_NEXT(view, link);
+ ISC_LIST_UNLINK(viewlist, view, link);
+ if (result == ISC_R_SUCCESS &&
+ strcmp(view->name, "_bind") != 0)
+ (void)dns_zt_apply(view->zonetable, ISC_FALSE,
+ removed, view);
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Adjust the listening interfaces in accordance with the source
+ * addresses specified in views and zones.
+ */
+ if (isc_net_probeipv6() == ISC_R_SUCCESS)
+ adjust_interfaces(server, ns_g_mctx);
+
+ /* Relinquish exclusive access to configuration data. */
+ isc_task_endexclusive(server->task);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_DEBUG(1), "load_configuration: %s",
+ isc_result_totext(result));
+
+ return (result);
+}
+
+static isc_result_t
+load_zones(ns_server_t *server, isc_boolean_t stop) {
+ isc_result_t result;
+ dns_view_t *view;
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * Load zone data from disk.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ CHECK(dns_view_load(view, stop));
+ }
+
+ /*
+ * Force zone maintenance. Do this after loading
+ * so that we know when we need to force AXFR of
+ * slave zones whose master files are missing.
+ */
+ CHECK(dns_zonemgr_forcemaint(server->zonemgr));
+ cleanup:
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+static isc_result_t
+load_new_zones(ns_server_t *server, isc_boolean_t stop) {
+ isc_result_t result;
+ dns_view_t *view;
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * Load zone data from disk.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ CHECK(dns_view_loadnew(view, stop));
+ }
+ /*
+ * Force zone maintenance. Do this after loading
+ * so that we know when we need to force AXFR of
+ * slave zones whose master files are missing.
+ */
+ dns_zonemgr_resumexfrs(server->zonemgr);
+ cleanup:
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+static void
+run_server(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ ns_server_t *server = (ns_server_t *)event->ev_arg;
+
+ INSIST(task == server->task);
+
+ isc_event_free(&event);
+
+ CHECKFATAL(dns_dispatchmgr_create(ns_g_mctx, ns_g_entropy,
+ &ns_g_dispatchmgr),
+ "creating dispatch manager");
+
+ dns_dispatchmgr_setstats(ns_g_dispatchmgr, server->resolverstats);
+
+ CHECKFATAL(ns_interfacemgr_create(ns_g_mctx, ns_g_taskmgr,
+ ns_g_socketmgr, ns_g_dispatchmgr,
+ &server->interfacemgr),
+ "creating interface manager");
+
+ CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task,
+ interface_timer_tick,
+ server, &server->interface_timer),
+ "creating interface timer");
+
+ CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task,
+ heartbeat_timer_tick,
+ server, &server->heartbeat_timer),
+ "creating heartbeat timer");
+
+ CHECKFATAL(isc_timer_create(ns_g_timermgr, isc_timertype_inactive,
+ NULL, NULL, server->task, pps_timer_tick,
+ server, &server->pps_timer),
+ "creating pps timer");
+
+ CHECKFATAL(cfg_parser_create(ns_g_mctx, NULL, &ns_g_parser),
+ "creating default configuration parser");
+
+ if (ns_g_lwresdonly)
+ CHECKFATAL(load_configuration(lwresd_g_conffile, server,
+ ISC_TRUE),
+ "loading configuration");
+ else
+ CHECKFATAL(load_configuration(ns_g_conffile, server, ISC_TRUE),
+ "loading configuration");
+
+ isc_hash_init();
+
+ CHECKFATAL(load_zones(server, ISC_FALSE), "loading zones");
+
+ ns_os_started();
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_NOTICE, "running");
+}
+
+void
+ns_server_flushonshutdown(ns_server_t *server, isc_boolean_t flush) {
+
+ REQUIRE(NS_SERVER_VALID(server));
+
+ server->flushonshutdown = flush;
+}
+
+static void
+shutdown_server(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ dns_view_t *view, *view_next;
+ ns_server_t *server = (ns_server_t *)event->ev_arg;
+ isc_boolean_t flush = server->flushonshutdown;
+
+ UNUSED(task);
+ INSIST(task == server->task);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "shutting down%s",
+ flush ? ": flushing changes" : "");
+
+ ns_statschannels_shutdown(server);
+ ns_controls_shutdown(server->controls);
+ end_reserved_dispatches(server, ISC_TRUE);
+
+ cfg_obj_destroy(ns_g_parser, &ns_g_config);
+ cfg_parser_destroy(&ns_g_parser);
+
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = view_next) {
+ view_next = ISC_LIST_NEXT(view, link);
+ ISC_LIST_UNLINK(server->viewlist, view, link);
+ if (flush)
+ dns_view_flushanddetach(&view);
+ else
+ dns_view_detach(&view);
+ }
+
+ isc_timer_detach(&server->interface_timer);
+ isc_timer_detach(&server->heartbeat_timer);
+ isc_timer_detach(&server->pps_timer);
+
+ ns_interfacemgr_shutdown(server->interfacemgr);
+ ns_interfacemgr_detach(&server->interfacemgr);
+
+ dns_dispatchmgr_destroy(&ns_g_dispatchmgr);
+
+ dns_zonemgr_shutdown(server->zonemgr);
+
+ if (server->blackholeacl != NULL)
+ dns_acl_detach(&server->blackholeacl);
+
+ dns_db_detach(&server->in_roothints);
+
+ isc_task_endexclusive(server->task);
+
+ isc_task_detach(&server->task);
+
+ isc_event_free(&event);
+}
+
+void
+ns_server_create(isc_mem_t *mctx, ns_server_t **serverp) {
+ isc_result_t result;
+
+ ns_server_t *server = isc_mem_get(mctx, sizeof(*server));
+ if (server == NULL)
+ fatal("allocating server object", ISC_R_NOMEMORY);
+
+ server->mctx = mctx;
+ server->task = NULL;
+
+ /* Initialize configuration data with default values. */
+
+ result = isc_quota_init(&server->xfroutquota, 10);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = isc_quota_init(&server->tcpquota, 10);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = isc_quota_init(&server->recursionquota, 100);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ result = dns_aclenv_init(mctx, &server->aclenv);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /* Initialize server data structures. */
+ server->zonemgr = NULL;
+ server->interfacemgr = NULL;
+ ISC_LIST_INIT(server->viewlist);
+ server->in_roothints = NULL;
+ server->blackholeacl = NULL;
+
+ CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
+ &server->in_roothints),
+ "setting up root hints");
+
+ CHECKFATAL(isc_mutex_init(&server->reload_event_lock),
+ "initializing reload event lock");
+ server->reload_event =
+ isc_event_allocate(ns_g_mctx, server,
+ NS_EVENT_RELOAD,
+ ns_server_reload,
+ server,
+ sizeof(isc_event_t));
+ CHECKFATAL(server->reload_event == NULL ?
+ ISC_R_NOMEMORY : ISC_R_SUCCESS,
+ "allocating reload event");
+
+ CHECKFATAL(dst_lib_init(ns_g_mctx, ns_g_entropy, ISC_ENTROPY_GOODONLY),
+ "initializing DST");
+
+ server->tkeyctx = NULL;
+ CHECKFATAL(dns_tkeyctx_create(ns_g_mctx, ns_g_entropy,
+ &server->tkeyctx),
+ "creating TKEY context");
+
+ /*
+ * Setup the server task, which is responsible for coordinating
+ * startup and shutdown of the server.
+ */
+ CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task),
+ "creating server task");
+ isc_task_setname(server->task, "server", server);
+ CHECKFATAL(isc_task_onshutdown(server->task, shutdown_server, server),
+ "isc_task_onshutdown");
+ CHECKFATAL(isc_app_onrun(ns_g_mctx, server->task, run_server, server),
+ "isc_app_onrun");
+
+ server->interface_timer = NULL;
+ server->heartbeat_timer = NULL;
+ server->pps_timer = NULL;
+
+ server->interface_interval = 0;
+ server->heartbeat_interval = 0;
+
+ CHECKFATAL(dns_zonemgr_create(ns_g_mctx, ns_g_taskmgr, ns_g_timermgr,
+ ns_g_socketmgr, &server->zonemgr),
+ "dns_zonemgr_create");
+
+ server->statsfile = isc_mem_strdup(server->mctx, "named.stats");
+ CHECKFATAL(server->statsfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
+ "isc_mem_strdup");
+ server->nsstats = NULL;
+ server->rcvquerystats = NULL;
+ server->opcodestats = NULL;
+ server->zonestats = NULL;
+ server->resolverstats = NULL;
+
+ server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db");
+ CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
+ "isc_mem_strdup");
+
+ server->recfile = isc_mem_strdup(server->mctx, "named.recursing");
+ CHECKFATAL(server->recfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
+ "isc_mem_strdup");
+
+ server->hostname_set = ISC_FALSE;
+ server->hostname = NULL;
+ server->version_set = ISC_FALSE;
+ server->version = NULL;
+ server->server_usehostname = ISC_FALSE;
+ server->server_id = NULL;
+
+ CHECKFATAL(dns_generalstats_create(ns_g_mctx, &server->nsstats,
+ dns_nsstatscounter_max),
+ "dns_stats_create (server)");
+
+ CHECKFATAL(dns_rdatatypestats_create(ns_g_mctx,
+ &server->rcvquerystats),
+ "dns_stats_create (rcvquery)");
+
+ CHECKFATAL(dns_opcodestats_create(ns_g_mctx, &server->opcodestats),
+ "dns_stats_create (opcode)");
+
+ CHECKFATAL(dns_generalstats_create(ns_g_mctx, &server->zonestats,
+ dns_zonestatscounter_max),
+ "dns_stats_create (zone)");
+
+ CHECKFATAL(dns_generalstats_create(ns_g_mctx, &server->resolverstats,
+ dns_resstatscounter_max),
+ "dns_stats_create (resolver)");
+
+ server->flushonshutdown = ISC_FALSE;
+ server->log_queries = ISC_FALSE;
+
+ server->controls = NULL;
+ CHECKFATAL(ns_controls_create(server, &server->controls),
+ "ns_controls_create");
+ server->dispatchgen = 0;
+ ISC_LIST_INIT(server->dispatches);
+
+ ISC_LIST_INIT(server->statschannels);
+
+ server->magic = NS_SERVER_MAGIC;
+ *serverp = server;
+}
+
+void
+ns_server_destroy(ns_server_t **serverp) {
+ ns_server_t *server = *serverp;
+ REQUIRE(NS_SERVER_VALID(server));
+
+ ns_controls_destroy(&server->controls);
+
+ dns_stats_detach(&server->nsstats);
+ dns_stats_detach(&server->rcvquerystats);
+ dns_stats_detach(&server->opcodestats);
+ dns_stats_detach(&server->zonestats);
+ dns_stats_detach(&server->resolverstats);
+
+ isc_mem_free(server->mctx, server->statsfile);
+ isc_mem_free(server->mctx, server->dumpfile);
+ isc_mem_free(server->mctx, server->recfile);
+
+ if (server->version != NULL)
+ isc_mem_free(server->mctx, server->version);
+ if (server->hostname != NULL)
+ isc_mem_free(server->mctx, server->hostname);
+ if (server->server_id != NULL)
+ isc_mem_free(server->mctx, server->server_id);
+
+ dns_zonemgr_detach(&server->zonemgr);
+
+ if (server->tkeyctx != NULL)
+ dns_tkeyctx_destroy(&server->tkeyctx);
+
+ dst_lib_destroy();
+
+ isc_event_free(&server->reload_event);
+
+ INSIST(ISC_LIST_EMPTY(server->viewlist));
+
+ dns_aclenv_destroy(&server->aclenv);
+
+ isc_quota_destroy(&server->recursionquota);
+ isc_quota_destroy(&server->tcpquota);
+ isc_quota_destroy(&server->xfroutquota);
+
+ server->magic = 0;
+ isc_mem_put(server->mctx, server, sizeof(*server));
+ *serverp = NULL;
+}
+
+static void
+fatal(const char *msg, isc_result_t result) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_CRITICAL, "%s: %s", msg,
+ isc_result_totext(result));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_CRITICAL, "exiting (due to fatal error)");
+ exit(1);
+}
+
+static void
+start_reserved_dispatches(ns_server_t *server) {
+
+ REQUIRE(NS_SERVER_VALID(server));
+
+ server->dispatchgen++;
+}
+
+static void
+end_reserved_dispatches(ns_server_t *server, isc_boolean_t all) {
+ ns_dispatch_t *dispatch, *nextdispatch;
+
+ REQUIRE(NS_SERVER_VALID(server));
+
+ for (dispatch = ISC_LIST_HEAD(server->dispatches);
+ dispatch != NULL;
+ dispatch = nextdispatch) {
+ nextdispatch = ISC_LIST_NEXT(dispatch, link);
+ if (!all && server->dispatchgen == dispatch-> dispatchgen)
+ continue;
+ ISC_LIST_UNLINK(server->dispatches, dispatch, link);
+ dns_dispatch_detach(&dispatch->dispatch);
+ isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
+ }
+}
+
+void
+ns_add_reserved_dispatch(ns_server_t *server, const isc_sockaddr_t *addr) {
+ ns_dispatch_t *dispatch;
+ in_port_t port;
+ char addrbuf[ISC_SOCKADDR_FORMATSIZE];
+ isc_result_t result;
+ unsigned int attrs, attrmask;
+
+ REQUIRE(NS_SERVER_VALID(server));
+
+ port = isc_sockaddr_getport(addr);
+ if (port == 0 || port >= 1024)
+ return;
+
+ for (dispatch = ISC_LIST_HEAD(server->dispatches);
+ dispatch != NULL;
+ dispatch = ISC_LIST_NEXT(dispatch, link)) {
+ if (isc_sockaddr_equal(&dispatch->addr, addr))
+ break;
+ }
+ if (dispatch != NULL) {
+ dispatch->dispatchgen = server->dispatchgen;
+ return;
+ }
+
+ dispatch = isc_mem_get(server->mctx, sizeof(*dispatch));
+ if (dispatch == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ dispatch->addr = *addr;
+ dispatch->dispatchgen = server->dispatchgen;
+ dispatch->dispatch = NULL;
+
+ attrs = 0;
+ attrs |= DNS_DISPATCHATTR_UDP;
+ switch (isc_sockaddr_pf(addr)) {
+ case AF_INET:
+ attrs |= DNS_DISPATCHATTR_IPV4;
+ break;
+ case AF_INET6:
+ attrs |= DNS_DISPATCHATTR_IPV6;
+ break;
+ default:
+ result = ISC_R_NOTIMPLEMENTED;
+ goto cleanup;
+ }
+ attrmask = 0;
+ attrmask |= DNS_DISPATCHATTR_UDP;
+ attrmask |= DNS_DISPATCHATTR_TCP;
+ attrmask |= DNS_DISPATCHATTR_IPV4;
+ attrmask |= DNS_DISPATCHATTR_IPV6;
+
+ result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
+ ns_g_taskmgr, &dispatch->addr, 4096,
+ 1000, 32768, 16411, 16433,
+ attrs, attrmask, &dispatch->dispatch);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ ISC_LIST_INITANDPREPEND(server->dispatches, dispatch, link);
+
+ return;
+
+ cleanup:
+ if (dispatch != NULL)
+ isc_mem_put(server->mctx, dispatch, sizeof(*dispatch));
+ isc_sockaddr_format(addr, addrbuf, sizeof(addrbuf));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "unable to create dispatch for reserved port %s: %s",
+ addrbuf, isc_result_totext(result));
+}
+
+
+static isc_result_t
+loadconfig(ns_server_t *server) {
+ isc_result_t result;
+ start_reserved_dispatches(server);
+ result = load_configuration(ns_g_lwresdonly ?
+ lwresd_g_conffile : ns_g_conffile,
+ server, ISC_FALSE);
+ if (result == ISC_R_SUCCESS) {
+ end_reserved_dispatches(server, ISC_FALSE);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "reloading configuration succeeded");
+ } else {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "reloading configuration failed: %s",
+ isc_result_totext(result));
+ }
+ return (result);
+}
+
+static isc_result_t
+reload(ns_server_t *server) {
+ isc_result_t result;
+ CHECK(loadconfig(server));
+
+ result = load_zones(server, ISC_FALSE);
+ if (result == ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "reloading zones succeeded");
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "reloading zones failed: %s",
+ isc_result_totext(result));
+
+ cleanup:
+ return (result);
+}
+
+static void
+reconfig(ns_server_t *server) {
+ isc_result_t result;
+ CHECK(loadconfig(server));
+
+ result = load_new_zones(server, ISC_FALSE);
+ if (result == ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "any newly configured zones are now loaded");
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "loading new zones failed: %s",
+ isc_result_totext(result));
+
+ cleanup: ;
+}
+
+/*
+ * Handle a reload event (from SIGHUP).
+ */
+static void
+ns_server_reload(isc_task_t *task, isc_event_t *event) {
+ ns_server_t *server = (ns_server_t *)event->ev_arg;
+
+ INSIST(task = server->task);
+ UNUSED(task);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "received SIGHUP signal to reload zones");
+ (void)reload(server);
+
+ LOCK(&server->reload_event_lock);
+ INSIST(server->reload_event == NULL);
+ server->reload_event = event;
+ UNLOCK(&server->reload_event_lock);
+}
+
+void
+ns_server_reloadwanted(ns_server_t *server) {
+ LOCK(&server->reload_event_lock);
+ if (server->reload_event != NULL)
+ isc_task_send(server->task, &server->reload_event);
+ UNLOCK(&server->reload_event_lock);
+}
+
+static char *
+next_token(char **stringp, const char *delim) {
+ char *res;
+
+ do {
+ res = strsep(stringp, delim);
+ if (res == NULL)
+ break;
+ } while (*res == '\0');
+ return (res);
+}
+
+/*
+ * Find the zone specified in the control channel command 'args',
+ * if any. If a zone is specified, point '*zonep' at it, otherwise
+ * set '*zonep' to NULL.
+ */
+static isc_result_t
+zone_from_args(ns_server_t *server, char *args, dns_zone_t **zonep) {
+ char *input, *ptr;
+ const char *zonetxt;
+ char *classtxt;
+ const char *viewtxt = NULL;
+ dns_fixedname_t name;
+ isc_result_t result;
+ isc_buffer_t buf;
+ dns_view_t *view = NULL;
+ dns_rdataclass_t rdclass;
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ input = args;
+
+ /* Skip the command name. */
+ ptr = next_token(&input, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Look for the zone name. */
+ zonetxt = next_token(&input, " \t");
+ if (zonetxt == NULL)
+ return (ISC_R_SUCCESS);
+
+ /* Look for the optional class name. */
+ classtxt = next_token(&input, " \t");
+ if (classtxt != NULL) {
+ /* Look for the optional view name. */
+ viewtxt = next_token(&input, " \t");
+ }
+
+ isc_buffer_init(&buf, zonetxt, strlen(zonetxt));
+ isc_buffer_add(&buf, strlen(zonetxt));
+ dns_fixedname_init(&name);
+ result = dns_name_fromtext(dns_fixedname_name(&name),
+ &buf, dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto fail1;
+
+ if (classtxt != NULL) {
+ isc_textregion_t r;
+ r.base = classtxt;
+ r.length = strlen(classtxt);
+ result = dns_rdataclass_fromtext(&rdclass, &r);
+ if (result != ISC_R_SUCCESS)
+ goto fail1;
+ } else
+ rdclass = dns_rdataclass_in;
+
+ if (viewtxt == NULL) {
+ result = dns_viewlist_findzone(&server->viewlist,
+ dns_fixedname_name(&name),
+ ISC_TF(classtxt == NULL),
+ rdclass, zonep);
+ } else {
+ result = dns_viewlist_find(&server->viewlist, viewtxt,
+ rdclass, &view);
+ if (result != ISC_R_SUCCESS)
+ goto fail1;
+
+ result = dns_zt_find(view->zonetable, dns_fixedname_name(&name),
+ 0, NULL, zonep);
+ dns_view_detach(&view);
+ }
+
+ /* Partial match? */
+ if (result != ISC_R_SUCCESS && *zonep != NULL)
+ dns_zone_detach(zonep);
+ fail1:
+ return (result);
+}
+
+/*
+ * Act on a "retransfer" command from the command channel.
+ */
+isc_result_t
+ns_server_retransfercommand(ns_server_t *server, char *args) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_zonetype_t type;
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_slave || type == dns_zone_stub)
+ dns_zone_forcereload(zone);
+ else
+ result = ISC_R_NOTFOUND;
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+/*
+ * Act on a "reload" command from the command channel.
+ */
+isc_result_t
+ns_server_reloadcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_zonetype_t type;
+ const char *msg = NULL;
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL) {
+ result = reload(server);
+ if (result == ISC_R_SUCCESS)
+ msg = "server reload successful";
+ } else {
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_slave || type == dns_zone_stub) {
+ dns_zone_refresh(zone);
+ dns_zone_detach(&zone);
+ msg = "zone refresh queued";
+ } else {
+ result = dns_zone_load(zone);
+ dns_zone_detach(&zone);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ msg = "zone reload successful";
+ break;
+ case DNS_R_CONTINUE:
+ msg = "zone reload queued";
+ result = ISC_R_SUCCESS;
+ break;
+ case DNS_R_UPTODATE:
+ msg = "zone reload up-to-date";
+ result = ISC_R_SUCCESS;
+ break;
+ default:
+ /* failure message will be generated by rndc */
+ break;
+ }
+ }
+ }
+ if (msg != NULL && strlen(msg) < isc_buffer_availablelength(text))
+ isc_buffer_putmem(text, (const unsigned char *)msg,
+ strlen(msg) + 1);
+ return (result);
+}
+
+/*
+ * Act on a "reconfig" command from the command channel.
+ */
+isc_result_t
+ns_server_reconfigcommand(ns_server_t *server, char *args) {
+ UNUSED(args);
+
+ reconfig(server);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Act on a "notify" command from the command channel.
+ */
+isc_result_t
+ns_server_notifycommand(ns_server_t *server, char *args, isc_buffer_t *text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ const unsigned char msg[] = "zone notify queued";
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ dns_zone_notify(zone);
+ dns_zone_detach(&zone);
+ if (sizeof(msg) <= isc_buffer_availablelength(text))
+ isc_buffer_putmem(text, msg, sizeof(msg));
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Act on a "refresh" command from the command channel.
+ */
+isc_result_t
+ns_server_refreshcommand(ns_server_t *server, char *args, isc_buffer_t *text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ const unsigned char msg1[] = "zone refresh queued";
+ const unsigned char msg2[] = "not a slave or stub zone";
+ dns_zonetype_t type;
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_slave || type == dns_zone_stub) {
+ dns_zone_refresh(zone);
+ dns_zone_detach(&zone);
+ if (sizeof(msg1) <= isc_buffer_availablelength(text))
+ isc_buffer_putmem(text, msg1, sizeof(msg1));
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_zone_detach(&zone);
+ if (sizeof(msg2) <= isc_buffer_availablelength(text))
+ isc_buffer_putmem(text, msg2, sizeof(msg2));
+ return (ISC_R_FAILURE);
+}
+
+isc_result_t
+ns_server_togglequerylog(ns_server_t *server) {
+ server->log_queries = server->log_queries ? ISC_FALSE : ISC_TRUE;
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "query logging is now %s",
+ server->log_queries ? "on" : "off");
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+ns_listenlist_fromconfig(const cfg_obj_t *listenlist, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, ns_listenlist_t **target)
+{
+ isc_result_t result;
+ const cfg_listelt_t *element;
+ ns_listenlist_t *dlist = NULL;
+
+ REQUIRE(target != NULL && *target == NULL);
+
+ result = ns_listenlist_create(mctx, &dlist);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ for (element = cfg_list_first(listenlist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ ns_listenelt_t *delt = NULL;
+ const cfg_obj_t *listener = cfg_listelt_value(element);
+ result = ns_listenelt_fromconfig(listener, config, actx,
+ mctx, &delt);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ISC_LIST_APPEND(dlist->elts, delt, link);
+ }
+ *target = dlist;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ ns_listenlist_detach(&dlist);
+ return (result);
+}
+
+/*
+ * Create a listen list from the corresponding configuration
+ * data structure.
+ */
+static isc_result_t
+ns_listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
+ cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, ns_listenelt_t **target)
+{
+ isc_result_t result;
+ const cfg_obj_t *portobj;
+ in_port_t port;
+ ns_listenelt_t *delt = NULL;
+ REQUIRE(target != NULL && *target == NULL);
+
+ portobj = cfg_tuple_get(listener, "port");
+ if (!cfg_obj_isuint32(portobj)) {
+ if (ns_g_port != 0) {
+ port = ns_g_port;
+ } else {
+ result = ns_config_getport(config, &port);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ } else {
+ if (cfg_obj_asuint32(portobj) >= ISC_UINT16_MAX) {
+ cfg_obj_log(portobj, ns_g_lctx, ISC_LOG_ERROR,
+ "port value '%u' is out of range",
+ cfg_obj_asuint32(portobj));
+ return (ISC_R_RANGE);
+ }
+ port = (in_port_t)cfg_obj_asuint32(portobj);
+ }
+
+ result = ns_listenelt_create(mctx, port, NULL, &delt);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"),
+ config, ns_g_lctx, actx, mctx, 0,
+ &delt->acl);
+ if (result != ISC_R_SUCCESS) {
+ ns_listenelt_destroy(delt);
+ return (result);
+ }
+ *target = delt;
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_server_dumpstats(ns_server_t *server) {
+ isc_result_t result;
+ FILE *fp = NULL;
+
+ CHECKMF(isc_stdio_open(server->statsfile, "a", &fp),
+ "could not open statistics dump file", server->statsfile);
+
+ result = ns_stats_dump(server, fp);
+ CHECK(result);
+
+ cleanup:
+ if (fp != NULL)
+ (void)isc_stdio_close(fp);
+ if (result == ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpstats complete");
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dumpstats failed: %s",
+ dns_result_totext(result));
+ return (result);
+}
+
+static isc_result_t
+add_zone_tolist(dns_zone_t *zone, void *uap) {
+ struct dumpcontext *dctx = uap;
+ struct zonelistentry *zle;
+
+ zle = isc_mem_get(dctx->mctx, sizeof *zle);
+ if (zle == NULL)
+ return (ISC_R_NOMEMORY);
+ zle->zone = NULL;
+ dns_zone_attach(zone, &zle->zone);
+ ISC_LINK_INIT(zle, link);
+ ISC_LIST_APPEND(ISC_LIST_TAIL(dctx->viewlist)->zonelist, zle, link);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+add_view_tolist(struct dumpcontext *dctx, dns_view_t *view) {
+ struct viewlistentry *vle;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ /*
+ * Prevent duplicate views.
+ */
+ for (vle = ISC_LIST_HEAD(dctx->viewlist);
+ vle != NULL;
+ vle = ISC_LIST_NEXT(vle, link))
+ if (vle->view == view)
+ return (ISC_R_SUCCESS);
+
+ vle = isc_mem_get(dctx->mctx, sizeof *vle);
+ if (vle == NULL)
+ return (ISC_R_NOMEMORY);
+ vle->view = NULL;
+ dns_view_attach(view, &vle->view);
+ ISC_LINK_INIT(vle, link);
+ ISC_LIST_INIT(vle->zonelist);
+ ISC_LIST_APPEND(dctx->viewlist, vle, link);
+ if (dctx->dumpzones)
+ result = dns_zt_apply(view->zonetable, ISC_TRUE,
+ add_zone_tolist, dctx);
+ return (result);
+}
+
+static void
+dumpcontext_destroy(struct dumpcontext *dctx) {
+ struct viewlistentry *vle;
+ struct zonelistentry *zle;
+
+ vle = ISC_LIST_HEAD(dctx->viewlist);
+ while (vle != NULL) {
+ ISC_LIST_UNLINK(dctx->viewlist, vle, link);
+ zle = ISC_LIST_HEAD(vle->zonelist);
+ while (zle != NULL) {
+ ISC_LIST_UNLINK(vle->zonelist, zle, link);
+ dns_zone_detach(&zle->zone);
+ isc_mem_put(dctx->mctx, zle, sizeof *zle);
+ zle = ISC_LIST_HEAD(vle->zonelist);
+ }
+ dns_view_detach(&vle->view);
+ isc_mem_put(dctx->mctx, vle, sizeof *vle);
+ vle = ISC_LIST_HEAD(dctx->viewlist);
+ }
+ if (dctx->version != NULL)
+ dns_db_closeversion(dctx->db, &dctx->version, ISC_FALSE);
+ if (dctx->db != NULL)
+ dns_db_detach(&dctx->db);
+ if (dctx->cache != NULL)
+ dns_db_detach(&dctx->cache);
+ if (dctx->task != NULL)
+ isc_task_detach(&dctx->task);
+ if (dctx->fp != NULL)
+ (void)isc_stdio_close(dctx->fp);
+ if (dctx->mdctx != NULL)
+ dns_dumpctx_detach(&dctx->mdctx);
+ isc_mem_put(dctx->mctx, dctx, sizeof *dctx);
+}
+
+static void
+dumpdone(void *arg, isc_result_t result) {
+ struct dumpcontext *dctx = arg;
+ char buf[1024+32];
+ const dns_master_style_t *style;
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (dctx->mdctx != NULL)
+ dns_dumpctx_detach(&dctx->mdctx);
+ if (dctx->view == NULL) {
+ dctx->view = ISC_LIST_HEAD(dctx->viewlist);
+ if (dctx->view == NULL)
+ goto done;
+ INSIST(dctx->zone == NULL);
+ } else
+ goto resume;
+ nextview:
+ fprintf(dctx->fp, ";\n; Start view %s\n;\n", dctx->view->view->name);
+ resume:
+ if (dctx->zone == NULL && dctx->cache == NULL && dctx->dumpcache) {
+ style = &dns_master_style_cache;
+ /* start cache dump */
+ if (dctx->view->view->cachedb != NULL)
+ dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
+ if (dctx->cache != NULL) {
+
+ fprintf(dctx->fp, ";\n; Cache dump of view '%s'\n;\n",
+ dctx->view->view->name);
+ result = dns_master_dumptostreaminc(dctx->mctx,
+ dctx->cache, NULL,
+ style, dctx->fp,
+ dctx->task,
+ dumpdone, dctx,
+ &dctx->mdctx);
+ if (result == DNS_R_CONTINUE)
+ return;
+ if (result == ISC_R_NOTIMPLEMENTED)
+ fprintf(dctx->fp, "; %s\n",
+ dns_result_totext(result));
+ else if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ }
+ if (dctx->cache != NULL) {
+ dns_adb_dump(dctx->view->view->adb, dctx->fp);
+ dns_db_detach(&dctx->cache);
+ }
+ if (dctx->dumpzones) {
+ style = &dns_master_style_full;
+ nextzone:
+ if (dctx->version != NULL)
+ dns_db_closeversion(dctx->db, &dctx->version,
+ ISC_FALSE);
+ if (dctx->db != NULL)
+ dns_db_detach(&dctx->db);
+ if (dctx->zone == NULL)
+ dctx->zone = ISC_LIST_HEAD(dctx->view->zonelist);
+ else
+ dctx->zone = ISC_LIST_NEXT(dctx->zone, link);
+ if (dctx->zone != NULL) {
+ /* start zone dump */
+ dns_zone_name(dctx->zone->zone, buf, sizeof(buf));
+ fprintf(dctx->fp, ";\n; Zone dump of '%s'\n;\n", buf);
+ result = dns_zone_getdb(dctx->zone->zone, &dctx->db);
+ if (result != ISC_R_SUCCESS) {
+ fprintf(dctx->fp, "; %s\n",
+ dns_result_totext(result));
+ goto nextzone;
+ }
+ dns_db_currentversion(dctx->db, &dctx->version);
+ result = dns_master_dumptostreaminc(dctx->mctx,
+ dctx->db,
+ dctx->version,
+ style, dctx->fp,
+ dctx->task,
+ dumpdone, dctx,
+ &dctx->mdctx);
+ if (result == DNS_R_CONTINUE)
+ return;
+ if (result == ISC_R_NOTIMPLEMENTED) {
+ fprintf(dctx->fp, "; %s\n",
+ dns_result_totext(result));
+ result = ISC_R_SUCCESS;
+ goto nextzone;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ }
+ if (dctx->view != NULL)
+ dctx->view = ISC_LIST_NEXT(dctx->view, link);
+ if (dctx->view != NULL)
+ goto nextview;
+ done:
+ fprintf(dctx->fp, "; Dump complete\n");
+ result = isc_stdio_flush(dctx->fp);
+ if (result == ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpdb complete");
+ cleanup:
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dumpdb failed: %s", dns_result_totext(result));
+ dumpcontext_destroy(dctx);
+}
+
+isc_result_t
+ns_server_dumpdb(ns_server_t *server, char *args) {
+ struct dumpcontext *dctx = NULL;
+ dns_view_t *view;
+ isc_result_t result;
+ char *ptr;
+ const char *sep;
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ dctx = isc_mem_get(server->mctx, sizeof(*dctx));
+ if (dctx == NULL)
+ return (ISC_R_NOMEMORY);
+
+ dctx->mctx = server->mctx;
+ dctx->dumpcache = ISC_TRUE;
+ dctx->dumpzones = ISC_FALSE;
+ dctx->fp = NULL;
+ ISC_LIST_INIT(dctx->viewlist);
+ dctx->view = NULL;
+ dctx->zone = NULL;
+ dctx->cache = NULL;
+ dctx->mdctx = NULL;
+ dctx->db = NULL;
+ dctx->cache = NULL;
+ dctx->task = NULL;
+ dctx->version = NULL;
+ isc_task_attach(server->task, &dctx->task);
+
+ CHECKMF(isc_stdio_open(server->dumpfile, "w", &dctx->fp),
+ "could not open dump file", server->dumpfile);
+
+ sep = (args == NULL) ? "" : ": ";
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpdb started%s%s", sep, (args != NULL) ? args : "");
+
+ ptr = next_token(&args, " \t");
+ if (ptr != NULL && strcmp(ptr, "-all") == 0) {
+ dctx->dumpzones = ISC_TRUE;
+ dctx->dumpcache = ISC_TRUE;
+ ptr = next_token(&args, " \t");
+ } else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
+ dctx->dumpzones = ISC_FALSE;
+ dctx->dumpcache = ISC_TRUE;
+ ptr = next_token(&args, " \t");
+ } else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
+ dctx->dumpzones = ISC_TRUE;
+ dctx->dumpcache = ISC_FALSE;
+ ptr = next_token(&args, " \t");
+ }
+
+ nextview:
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (ptr != NULL && strcmp(view->name, ptr) != 0)
+ continue;
+ CHECK(add_view_tolist(dctx, view));
+ }
+ if (ptr != NULL) {
+ ptr = next_token(&args, " \t");
+ if (ptr != NULL)
+ goto nextview;
+ }
+ dumpdone(dctx, ISC_R_SUCCESS);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (dctx != NULL)
+ dumpcontext_destroy(dctx);
+ return (result);
+}
+
+isc_result_t
+ns_server_dumprecursing(ns_server_t *server) {
+ FILE *fp = NULL;
+ isc_result_t result;
+
+ CHECKMF(isc_stdio_open(server->recfile, "w", &fp),
+ "could not open dump file", server->recfile);
+ fprintf(fp,";\n; Recursing Queries\n;\n");
+ ns_interfacemgr_dumprecursing(fp, server->interfacemgr);
+ fprintf(fp, "; Dump complete\n");
+
+ cleanup:
+ if (fp != NULL)
+ result = isc_stdio_close(fp);
+ if (result == ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumprecursing complete");
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dumprecursing failed: %s",
+ dns_result_totext(result));
+ return (result);
+}
+
+isc_result_t
+ns_server_setdebuglevel(ns_server_t *server, char *args) {
+ char *ptr;
+ char *levelstr;
+ char *endp;
+ long newlevel;
+
+ UNUSED(server);
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Look for the new level name. */
+ levelstr = next_token(&args, " \t");
+ if (levelstr == NULL) {
+ if (ns_g_debuglevel < 99)
+ ns_g_debuglevel++;
+ } else {
+ newlevel = strtol(levelstr, &endp, 10);
+ if (*endp != '\0' || newlevel < 0 || newlevel > 99)
+ return (ISC_R_RANGE);
+ ns_g_debuglevel = (unsigned int)newlevel;
+ }
+ isc_log_setdebuglevel(ns_g_lctx, ns_g_debuglevel);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "debug level is now %d", ns_g_debuglevel);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_server_validation(ns_server_t *server, char *args) {
+ char *ptr, *viewname;
+ dns_view_t *view;
+ isc_boolean_t changed = ISC_FALSE;
+ isc_result_t result;
+ isc_boolean_t enable;
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Find out what we are to do. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
+ !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true"))
+ enable = ISC_TRUE;
+ else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
+ !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false"))
+ enable = ISC_FALSE;
+ else
+ return (DNS_R_SYNTAX);
+
+ /* Look for the view name. */
+ viewname = next_token(&args, " \t");
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
+ continue;
+ result = dns_view_flushcache(view);
+ if (result != ISC_R_SUCCESS)
+ goto out;
+ view->enablevalidation = enable;
+ changed = ISC_TRUE;
+ }
+ if (changed)
+ result = ISC_R_SUCCESS;
+ else
+ result = ISC_R_FAILURE;
+ out:
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+ns_server_flushcache(ns_server_t *server, char *args) {
+ char *ptr, *viewname;
+ dns_view_t *view;
+ isc_boolean_t flushed;
+ isc_boolean_t found;
+ isc_result_t result;
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Look for the view name. */
+ viewname = next_token(&args, " \t");
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ flushed = ISC_TRUE;
+ found = ISC_FALSE;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
+ continue;
+ found = ISC_TRUE;
+ result = dns_view_flushcache(view);
+ if (result != ISC_R_SUCCESS) {
+ flushed = ISC_FALSE;
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing cache in view '%s' failed: %s",
+ view->name, isc_result_totext(result));
+ }
+ }
+ if (flushed && found) {
+ if (viewname != NULL)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing cache in view '%s' succeeded",
+ viewname);
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing caches in all views succeeded");
+ result = ISC_R_SUCCESS;
+ } else {
+ if (!found) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing cache in view '%s' failed: "
+ "view not found", viewname);
+ result = ISC_R_NOTFOUND;
+ } else
+ result = ISC_R_FAILURE;
+ }
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+ns_server_flushname(ns_server_t *server, char *args) {
+ char *ptr, *target, *viewname;
+ dns_view_t *view;
+ isc_boolean_t flushed;
+ isc_boolean_t found;
+ isc_result_t result;
+ isc_buffer_t b;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ /* Skip the command name. */
+ ptr = next_token(&args, " \t");
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Find the domain name to flush. */
+ target = next_token(&args, " \t");
+ if (target == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ isc_buffer_init(&b, target, strlen(target));
+ isc_buffer_add(&b, strlen(target));
+ dns_fixedname_init(&fixed);
+ name = dns_fixedname_name(&fixed);
+ result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* Look for the view name. */
+ viewname = next_token(&args, " \t");
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ flushed = ISC_TRUE;
+ found = ISC_FALSE;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewname != NULL && strcasecmp(viewname, view->name) != 0)
+ continue;
+ found = ISC_TRUE;
+ result = dns_view_flushname(view, name);
+ if (result != ISC_R_SUCCESS) {
+ flushed = ISC_FALSE;
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing name '%s' in cache view '%s' "
+ "failed: %s", target, view->name,
+ isc_result_totext(result));
+ }
+ }
+ if (flushed && found) {
+ if (viewname != NULL)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing name '%s' in cache view '%s' "
+ "succeeded", target, viewname);
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing name '%s' in all cache views "
+ "succeeded", target);
+ result = ISC_R_SUCCESS;
+ } else {
+ if (!found)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing name '%s' in cache view '%s' "
+ "failed: view not found", target,
+ viewname);
+ result = ISC_R_FAILURE;
+ }
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+ns_server_status(ns_server_t *server, isc_buffer_t *text) {
+ int zonecount, xferrunning, xferdeferred, soaqueries;
+ unsigned int n;
+ const char *ob = "", *cb = "", *alt = "";
+
+ if (ns_g_server->version_set) {
+ ob = " (";
+ cb = ")";
+ if (ns_g_server->version == NULL)
+ alt = "version.bind/txt/ch disabled";
+ else
+ alt = ns_g_server->version;
+ }
+ zonecount = dns_zonemgr_getcount(server->zonemgr, DNS_ZONESTATE_ANY);
+ xferrunning = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_XFERRUNNING);
+ xferdeferred = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_XFERDEFERRED);
+ soaqueries = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_SOAQUERY);
+
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "version: %s%s%s%s\n"
+#ifdef ISC_PLATFORM_USETHREADS
+ "CPUs found: %u\n"
+ "worker threads: %u\n"
+#endif
+ "number of zones: %u\n"
+ "debug level: %d\n"
+ "xfers running: %u\n"
+ "xfers deferred: %u\n"
+ "soa queries in progress: %u\n"
+ "query logging is %s\n"
+ "recursive clients: %d/%d/%d\n"
+ "tcp clients: %d/%d\n"
+ "server is up and running",
+ ns_g_version, ob, alt, cb,
+#ifdef ISC_PLATFORM_USETHREADS
+ ns_g_cpus_detected, ns_g_cpus,
+#endif
+ zonecount, ns_g_debuglevel, xferrunning, xferdeferred,
+ soaqueries, server->log_queries ? "ON" : "OFF",
+ server->recursionquota.used, server->recursionquota.soft,
+ server->recursionquota.max,
+ server->tcpquota.used, server->tcpquota.max);
+ if (n >= isc_buffer_availablelength(text))
+ return (ISC_R_NOSPACE);
+ isc_buffer_add(text, n);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+delete_keynames(dns_tsig_keyring_t *ring, char *target,
+ unsigned int *foundkeys)
+{
+ char namestr[DNS_NAME_FORMATSIZE];
+ isc_result_t result;
+ dns_rbtnodechain_t chain;
+ dns_name_t foundname;
+ dns_fixedname_t fixedorigin;
+ dns_name_t *origin;
+ dns_rbtnode_t *node;
+ dns_tsigkey_t *tkey;
+
+ dns_name_init(&foundname, NULL);
+ dns_fixedname_init(&fixedorigin);
+ origin = dns_fixedname_name(&fixedorigin);
+
+ again:
+ dns_rbtnodechain_init(&chain, ring->mctx);
+ result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
+ origin);
+ if (result == ISC_R_NOTFOUND) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (result);
+ }
+
+ for (;;) {
+ node = NULL;
+ dns_rbtnodechain_current(&chain, &foundname, origin, &node);
+ tkey = node->data;
+
+ if (tkey != NULL) {
+ if (!tkey->generated)
+ goto nextkey;
+
+ dns_name_format(&tkey->name, namestr, sizeof(namestr));
+ if (strcmp(namestr, target) == 0) {
+ (*foundkeys)++;
+ dns_rbtnodechain_invalidate(&chain);
+ (void)dns_rbt_deletename(ring->keys,
+ &tkey->name,
+ ISC_FALSE);
+ goto again;
+ }
+ }
+
+ nextkey:
+ result = dns_rbtnodechain_next(&chain, &foundname, origin);
+ if (result == ISC_R_NOMORE)
+ break;
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (result);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_server_tsigdelete(ns_server_t *server, char *command, isc_buffer_t *text) {
+ isc_result_t result;
+ unsigned int n;
+ dns_view_t *view;
+ unsigned int foundkeys = 0;
+ char *target;
+ char *viewname;
+
+ (void)next_token(&command, " \t"); /* skip command name */
+ target = next_token(&command, " \t");
+ if (target == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ viewname = next_token(&command, " \t");
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (viewname == NULL || strcmp(view->name, viewname) == 0) {
+ RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_write);
+ result = delete_keynames(view->dynamickeys, target,
+ &foundkeys);
+ RWUNLOCK(&view->dynamickeys->lock,
+ isc_rwlocktype_write);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_endexclusive(server->task);
+ return (result);
+ }
+ }
+ }
+ isc_task_endexclusive(server->task);
+
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "%d tsig keys deleted.\n", foundkeys);
+ if (n >= isc_buffer_availablelength(text)) {
+ isc_task_endexclusive(server->task);
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_add(text, n);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+list_keynames(dns_view_t *view, dns_tsig_keyring_t *ring, isc_buffer_t *text,
+ unsigned int *foundkeys)
+{
+ char namestr[DNS_NAME_FORMATSIZE];
+ char creatorstr[DNS_NAME_FORMATSIZE];
+ isc_result_t result;
+ dns_rbtnodechain_t chain;
+ dns_name_t foundname;
+ dns_fixedname_t fixedorigin;
+ dns_name_t *origin;
+ dns_rbtnode_t *node;
+ dns_tsigkey_t *tkey;
+ unsigned int n;
+ const char *viewname;
+
+ if (view != NULL)
+ viewname = view->name;
+ else
+ viewname = "(global)";
+
+ dns_name_init(&foundname, NULL);
+ dns_fixedname_init(&fixedorigin);
+ origin = dns_fixedname_name(&fixedorigin);
+ dns_rbtnodechain_init(&chain, ring->mctx);
+ result = dns_rbtnodechain_first(&chain, ring->keys, &foundname,
+ origin);
+ if (result == ISC_R_NOTFOUND) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (result);
+ }
+
+ for (;;) {
+ node = NULL;
+ dns_rbtnodechain_current(&chain, &foundname, origin, &node);
+ tkey = node->data;
+
+ if (tkey != NULL) {
+ (*foundkeys)++;
+ dns_name_format(&tkey->name, namestr, sizeof(namestr));
+ if (tkey->generated) {
+ dns_name_format(tkey->creator, creatorstr,
+ sizeof(creatorstr));
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "view \"%s\"; type \"dynamic\"; key \"%s\"; creator \"%s\";\n",
+ viewname, namestr, creatorstr);
+ } else {
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "view \"%s\"; type \"static\"; key \"%s\";\n",
+ viewname, namestr);
+ }
+ if (n >= isc_buffer_availablelength(text)) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_add(text, n);
+ }
+ result = dns_rbtnodechain_next(&chain, &foundname, origin);
+ if (result == ISC_R_NOMORE)
+ break;
+ if (result != ISC_R_SUCCESS && result != DNS_R_NEWORIGIN) {
+ dns_rbtnodechain_invalidate(&chain);
+ return (result);
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_server_tsiglist(ns_server_t *server, isc_buffer_t *text) {
+ isc_result_t result;
+ unsigned int n;
+ dns_view_t *view;
+ unsigned int foundkeys = 0;
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ RWLOCK(&view->statickeys->lock, isc_rwlocktype_read);
+ result = list_keynames(view, view->statickeys, text,
+ &foundkeys);
+ RWUNLOCK(&view->statickeys->lock, isc_rwlocktype_read);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_endexclusive(server->task);
+ return (result);
+ }
+ RWLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
+ result = list_keynames(view, view->dynamickeys, text,
+ &foundkeys);
+ RWUNLOCK(&view->dynamickeys->lock, isc_rwlocktype_read);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_endexclusive(server->task);
+ return (result);
+ }
+ }
+ isc_task_endexclusive(server->task);
+
+ if (foundkeys == 0) {
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "no tsig keys found.\n");
+ if (n >= isc_buffer_availablelength(text)) {
+ isc_task_endexclusive(server->task);
+ return (ISC_R_NOSPACE);
+ }
+ isc_buffer_add(text, n);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Act on a "freeze" or "thaw" command from the command channel.
+ */
+isc_result_t
+ns_server_freeze(ns_server_t *server, isc_boolean_t freeze, char *args) {
+ isc_result_t result, tresult;
+ dns_zone_t *zone = NULL;
+ dns_zonetype_t type;
+ char classstr[DNS_RDATACLASS_FORMATSIZE];
+ char zonename[DNS_NAME_FORMATSIZE];
+ dns_view_t *view;
+ char *journal;
+ const char *vname, *sep;
+ isc_boolean_t frozen;
+
+ result = zone_from_args(server, args, &zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL) {
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ tresult = ISC_R_SUCCESS;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ result = dns_view_freezezones(view, freeze);
+ if (result != ISC_R_SUCCESS &&
+ tresult == ISC_R_SUCCESS)
+ tresult = result;
+ }
+ isc_task_endexclusive(server->task);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s all zones: %s",
+ freeze ? "freezing" : "thawing",
+ isc_result_totext(tresult));
+ return (tresult);
+ }
+ type = dns_zone_gettype(zone);
+ if (type != dns_zone_master) {
+ dns_zone_detach(&zone);
+ return (ISC_R_NOTFOUND);
+ }
+
+ frozen = dns_zone_getupdatedisabled(zone);
+ if (freeze) {
+ if (frozen)
+ result = DNS_R_FROZEN;
+ if (result == ISC_R_SUCCESS)
+ result = dns_zone_flush(zone);
+ if (result == ISC_R_SUCCESS) {
+ journal = dns_zone_getjournal(zone);
+ if (journal != NULL)
+ (void)isc_file_remove(journal);
+ }
+ } else {
+ if (frozen) {
+ result = dns_zone_load(zone);
+ if (result == DNS_R_CONTINUE ||
+ result == DNS_R_UPTODATE)
+ result = ISC_R_SUCCESS;
+ }
+ }
+ if (result == ISC_R_SUCCESS)
+ dns_zone_setupdatedisabled(zone, freeze);
+
+ view = dns_zone_getview(zone);
+ if (strcmp(view->name, "_bind") == 0 ||
+ strcmp(view->name, "_default") == 0)
+ {
+ vname = "";
+ sep = "";
+ } else {
+ vname = view->name;
+ sep = " ";
+ }
+ dns_rdataclass_format(dns_zone_getclass(zone), classstr,
+ sizeof(classstr));
+ dns_name_format(dns_zone_getorigin(zone),
+ zonename, sizeof(zonename));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s zone '%s/%s'%s%s: %s",
+ freeze ? "freezing" : "thawing",
+ zonename, classstr, sep, vname,
+ isc_result_totext(result));
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+#ifdef HAVE_LIBSCF
+/*
+ * This function adds a message for rndc to echo if named
+ * is managed by smf and is also running chroot.
+ */
+isc_result_t
+ns_smf_add_message(isc_buffer_t *text) {
+ unsigned int n;
+
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "use svcadm(1M) to manage named");
+ if (n >= isc_buffer_availablelength(text))
+ return (ISC_R_NOSPACE);
+ isc_buffer_add(text, n);
+ return (ISC_R_SUCCESS);
+}
+#endif /* HAVE_LIBSCF */
diff --git a/bin/named/sortlist.c b/bin/named/sortlist.c
new file mode 100644
index 0000000..daefa07
--- /dev/null
+++ b/bin/named/sortlist.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: sortlist.c,v 1.17 2007/09/14 01:46:05 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/result.h>
+
+#include <named/globals.h>
+#include <named/server.h>
+#include <named/sortlist.h>
+
+ns_sortlisttype_t
+ns_sortlist_setup(dns_acl_t *acl, isc_netaddr_t *clientaddr,
+ const void **argp)
+{
+ unsigned int i;
+
+ if (acl == NULL)
+ goto dont_sort;
+
+ for (i = 0; i < acl->length; i++) {
+ /*
+ * 'e' refers to the current 'top level statement'
+ * in the sortlist (see ARM).
+ */
+ dns_aclelement_t *e = &acl->elements[i];
+ dns_aclelement_t *try_elt;
+ dns_aclelement_t *order_elt = NULL;
+ const dns_aclelement_t *matched_elt = NULL;
+
+ if (e->type == dns_aclelementtype_nestedacl) {
+ dns_acl_t *inner = e->nestedacl;
+
+ if (inner->length == 0)
+ try_elt = e;
+ else if (inner->length > 2)
+ goto dont_sort;
+ else if (inner->elements[0].negative)
+ goto dont_sort;
+ else {
+ try_elt = &inner->elements[0];
+ if (inner->length == 2)
+ order_elt = &inner->elements[1];
+ }
+ } else {
+ /*
+ * BIND 8 allows bare elements at the top level
+ * as an undocumented feature.
+ */
+ try_elt = e;
+ }
+
+ if (dns_aclelement_match(clientaddr, NULL, try_elt,
+ &ns_g_server->aclenv,
+ &matched_elt)) {
+ if (order_elt != NULL) {
+ if (order_elt->type ==
+ dns_aclelementtype_nestedacl) {
+ *argp = order_elt->nestedacl;
+ return (NS_SORTLISTTYPE_2ELEMENT);
+ } else if (order_elt->type ==
+ dns_aclelementtype_localhost &&
+ ns_g_server->aclenv.localhost != NULL) {
+ *argp = ns_g_server->aclenv.localhost;
+ return (NS_SORTLISTTYPE_2ELEMENT);
+ } else if (order_elt->type ==
+ dns_aclelementtype_localnets &&
+ ns_g_server->aclenv.localnets != NULL) {
+ *argp = ns_g_server->aclenv.localnets;
+ return (NS_SORTLISTTYPE_2ELEMENT);
+ } else {
+ /*
+ * BIND 8 allows a bare IP prefix as
+ * the 2nd element of a 2-element
+ * sortlist statement.
+ */
+ *argp = order_elt;
+ return (NS_SORTLISTTYPE_1ELEMENT);
+ }
+ } else {
+ INSIST(matched_elt != NULL);
+ *argp = matched_elt;
+ return (NS_SORTLISTTYPE_1ELEMENT);
+ }
+ }
+ }
+
+ /* No match; don't sort. */
+ dont_sort:
+ *argp = NULL;
+ return (NS_SORTLISTTYPE_NONE);
+}
+
+int
+ns_sortlist_addrorder2(const isc_netaddr_t *addr, const void *arg) {
+ const dns_acl_t *sortacl = (const dns_acl_t *) arg;
+ int match;
+
+ (void)dns_acl_match(addr, NULL, sortacl,
+ &ns_g_server->aclenv,
+ &match, NULL);
+ if (match > 0)
+ return (match);
+ else if (match < 0)
+ return (INT_MAX - (-match));
+ else
+ return (INT_MAX / 2);
+}
+
+int
+ns_sortlist_addrorder1(const isc_netaddr_t *addr, const void *arg) {
+ const dns_aclelement_t *matchelt = (const dns_aclelement_t *) arg;
+ if (dns_aclelement_match(addr, NULL, matchelt,
+ &ns_g_server->aclenv,
+ NULL)) {
+ return (0);
+ } else {
+ return (INT_MAX);
+ }
+}
+
+void
+ns_sortlist_byaddrsetup(dns_acl_t *sortlist_acl, isc_netaddr_t *client_addr,
+ dns_addressorderfunc_t *orderp,
+ const void **argp)
+{
+ ns_sortlisttype_t sortlisttype;
+
+ sortlisttype = ns_sortlist_setup(sortlist_acl, client_addr, argp);
+
+ switch (sortlisttype) {
+ case NS_SORTLISTTYPE_1ELEMENT:
+ *orderp = ns_sortlist_addrorder1;
+ break;
+ case NS_SORTLISTTYPE_2ELEMENT:
+ *orderp = ns_sortlist_addrorder2;
+ break;
+ case NS_SORTLISTTYPE_NONE:
+ *orderp = NULL;
+ break;
+ default:
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "unexpected return from ns_sortlist_setup(): "
+ "%d", sortlisttype);
+ break;
+ }
+}
+
diff --git a/bin/named/statschannel.c b/bin/named/statschannel.c
new file mode 100644
index 0000000..634a50d
--- /dev/null
+++ b/bin/named/statschannel.c
@@ -0,0 +1,1257 @@
+/*
+ * Copyright (C) 2008 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: statschannel.c,v 1.14.64.1 2008/12/18 02:40:35 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/httpd.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/socket.h>
+#include <isc/task.h>
+
+#include <dns/db.h>
+#include <dns/opcode.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/stats.h>
+#include <dns/view.h>
+#include <dns/zt.h>
+
+#include <named/log.h>
+#include <named/server.h>
+#include <named/statschannel.h>
+
+#include "bind9.xsl.h"
+
+struct ns_statschannel {
+ /* Unlocked */
+ isc_httpdmgr_t *httpdmgr;
+ isc_sockaddr_t address;
+ isc_mem_t *mctx;
+
+ /*
+ * Locked by channel lock: can be refererenced and modified by both
+ * the server task and the channel task.
+ */
+ isc_mutex_t lock;
+ dns_acl_t *acl;
+
+ /* Locked by server task */
+ ISC_LINK(struct ns_statschannel) link;
+};
+
+typedef enum { statsformat_file, statsformat_xml } statsformat_t;
+
+typedef struct
+stats_dumparg {
+ statsformat_t type;
+ void *arg; /* type dependent argument */
+ const char *category; /* used for general statistics */
+ const char **desc; /* used for general statistics */
+ int ncounters; /* used for general statistics */
+} stats_dumparg_t;
+
+static isc_once_t once = ISC_ONCE_INIT;
+
+/*%
+ * Statistics descriptions. These could be statistically initialized at
+ * compile time, but we configure them run time in the init_desc() function
+ * below so that they'll be less susceptible to counter name changes.
+ */
+static const char *nsstats_desc[dns_nsstatscounter_max];
+static const char *resstats_desc[dns_resstatscounter_max];
+static const char *zonestats_desc[dns_zonestatscounter_max];
+#ifdef HAVE_LIBXML2
+static const char *nsstats_xmldesc[dns_nsstatscounter_max];
+static const char *resstats_xmldesc[dns_resstatscounter_max];
+static const char *zonestats_xmldesc[dns_zonestatscounter_max];
+#else
+#define nsstats_xmldesc NULL
+#define resstats_xmldesc NULL
+#define zonestats_xmldesc NULL
+#endif /* HAVE_LIBXML2 */
+
+static inline void
+set_desc(int counter, int maxcounter, const char *fdesc, const char **fdescs,
+ const char *xdesc, const char **xdescs)
+{
+ REQUIRE(counter < maxcounter);
+ REQUIRE(fdescs[counter] == NULL);
+#ifdef HAVE_LIBXML2
+ REQUIRE(xdescs[counter] == NULL);
+#endif
+
+ fdescs[counter] = fdesc;
+#ifdef HAVE_LIBXML2
+ xdescs[counter] = xdesc;
+#else
+ UNUSED(xdesc);
+ UNUSED(xdescs);
+#endif
+}
+
+static void
+init_desc(void) {
+ int i;
+
+ /* Initialize name server statistics */
+ memset(nsstats_desc, 0,
+ dns_nsstatscounter_max * sizeof(nsstats_desc[0]));
+#ifdef HAVE_LIBXML2
+ memset(nsstats_xmldesc, 0,
+ dns_nsstatscounter_max * sizeof(nsstats_xmldesc[0]));
+#endif
+ set_desc(dns_nsstatscounter_requestv4, dns_nsstatscounter_max,
+ "IPv4 requests received", nsstats_desc,
+ "Requestv4", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_requestv6, dns_nsstatscounter_max,
+ "IPv6 requests received", nsstats_desc,
+ "Requestv6", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_edns0in, dns_nsstatscounter_max,
+ "requests with EDNS(0) received", nsstats_desc,
+ "ReqEdns0", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_badednsver, dns_nsstatscounter_max,
+ "requests with unsupported EDNS version received",
+ nsstats_desc,
+ "ReqBadEDNSVer", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_tsigin, dns_nsstatscounter_max,
+ "requests with TSIG received", nsstats_desc,
+ "ReqTSIG", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_sig0in, dns_nsstatscounter_max,
+ "requests with SIG(0) received", nsstats_desc,
+ "ReqSIG0", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_invalidsig, dns_nsstatscounter_max,
+ "requests with invalid signature", nsstats_desc,
+ "ReqBadSIG", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_tcp, dns_nsstatscounter_max,
+ "TCP requests received", nsstats_desc,
+ "ReqTCP", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_authrej, dns_nsstatscounter_max,
+ "auth queries rejected", nsstats_desc,
+ "AuthQryRej", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_recurserej, dns_nsstatscounter_max,
+ "recursive queries rejected", nsstats_desc,
+ "RecQryRej", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_xfrrej, dns_nsstatscounter_max,
+ "transfer requests rejected", nsstats_desc,
+ "XfrRej", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_updaterej, dns_nsstatscounter_max,
+ "update requests rejected", nsstats_desc,
+ "UpdateRej", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_response, dns_nsstatscounter_max,
+ "responses sent", nsstats_desc,
+ "Response", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_truncatedresp, dns_nsstatscounter_max,
+ "truncated responses sent", nsstats_desc,
+ "TruncatedResp", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_edns0out, dns_nsstatscounter_max,
+ "responses with EDNS(0) sent", nsstats_desc,
+ "RespEDNS0", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_tsigout, dns_nsstatscounter_max,
+ "responses with TSIG sent", nsstats_desc,
+ "RespTSIG", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_sig0out, dns_nsstatscounter_max,
+ "responses with SIG(0) sent", nsstats_desc,
+ "RespSIG0", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_success, dns_nsstatscounter_max,
+ "queries resulted in successful answer", nsstats_desc,
+ "QrySuccess", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_authans, dns_nsstatscounter_max,
+ "queries resulted in authoritative answer", nsstats_desc,
+ "QryAuthAns", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_nonauthans, dns_nsstatscounter_max,
+ "queries resulted in non authoritative answer", nsstats_desc,
+ "QryNoauthAns", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_referral, dns_nsstatscounter_max,
+ "queries resulted in referral answer", nsstats_desc,
+ "QryReferral", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_nxrrset, dns_nsstatscounter_max,
+ "queries resulted in nxrrset", nsstats_desc,
+ "QryNxrrset", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_servfail, dns_nsstatscounter_max,
+ "queries resulted in SERVFAIL", nsstats_desc,
+ "QrySERVFAIL", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_formerr, dns_nsstatscounter_max,
+ "queries resulted in FORMERR", nsstats_desc,
+ "QryFORMERR", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_nxdomain, dns_nsstatscounter_max,
+ "queries resulted in NXDOMAIN", nsstats_desc,
+ "QryNXDOMAIN", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_recursion, dns_nsstatscounter_max,
+ "queries caused recursion", nsstats_desc,
+ "QryRecursion", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_duplicate, dns_nsstatscounter_max,
+ "duplicate queries received", nsstats_desc,
+ "QryDuplicate", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_dropped, dns_nsstatscounter_max,
+ "queries dropped", nsstats_desc,
+ "QryDropped", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_failure, dns_nsstatscounter_max,
+ "other query failures", nsstats_desc,
+ "QryFailure", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_xfrdone, dns_nsstatscounter_max,
+ "requested transfers completed", nsstats_desc,
+ "XfrReqDone", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_updatereqfwd, dns_nsstatscounter_max,
+ "update requests forwarded", nsstats_desc,
+ "UpdateReqFwd", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_updaterespfwd, dns_nsstatscounter_max,
+ "update responses forwarded", nsstats_desc,
+ "UpdateRespFwd", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_updatefwdfail, dns_nsstatscounter_max,
+ "update forward failed", nsstats_desc,
+ "UpdateFwdFail", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_updatedone, dns_nsstatscounter_max,
+ "updates completed", nsstats_desc,
+ "UpdateDone", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_updatefail, dns_nsstatscounter_max,
+ "updates failed", nsstats_desc,
+ "UpdateFail", nsstats_xmldesc);
+ set_desc(dns_nsstatscounter_updatebadprereq, dns_nsstatscounter_max,
+ "updates rejected due to prerequisite failure", nsstats_desc,
+ "UpdateBadPrereq", nsstats_xmldesc);
+
+ /* Initialize resolver statistics */
+ memset(resstats_desc, 0,
+ dns_resstatscounter_max * sizeof(resstats_desc[0]));
+#ifdef HAVE_LIBXML2
+ memset(resstats_xmldesc, 0,
+ dns_resstatscounter_max * sizeof(resstats_xmldesc[0]));
+#endif
+ set_desc(dns_resstatscounter_queryv4, dns_resstatscounter_max,
+ "IPv4 queries sent", resstats_desc,
+ "Queryv4", resstats_xmldesc);
+ set_desc(dns_resstatscounter_queryv6, dns_resstatscounter_max,
+ "IPv6 queries sent", resstats_desc,
+ "Queryv6", resstats_xmldesc);
+ set_desc(dns_resstatscounter_responsev4, dns_resstatscounter_max,
+ "IPv4 responses received", resstats_desc,
+ "Responsev4", resstats_xmldesc);
+ set_desc(dns_resstatscounter_responsev6, dns_resstatscounter_max,
+ "IPv6 responses received", resstats_desc,
+ "Responsev6", resstats_xmldesc);
+ set_desc(dns_resstatscounter_nxdomain, dns_resstatscounter_max,
+ "NXDOMAIN received", resstats_desc,
+ "NXDOMAIN", resstats_xmldesc);
+ set_desc(dns_resstatscounter_servfail, dns_resstatscounter_max,
+ "SERVFAIL received", resstats_desc,
+ "SERVFAIL", resstats_xmldesc);
+ set_desc(dns_resstatscounter_formerr, dns_resstatscounter_max,
+ "FORMERR received", resstats_desc,
+ "FORMERR", resstats_xmldesc);
+ set_desc(dns_resstatscounter_othererror, dns_resstatscounter_max,
+ "other errors received", resstats_desc,
+ "OtherError", resstats_xmldesc);
+ set_desc(dns_resstatscounter_edns0fail, dns_resstatscounter_max,
+ "EDNS(0) query failures", resstats_desc,
+ "EDNS0Fail", resstats_xmldesc);
+ set_desc(dns_resstatscounter_mismatch, dns_resstatscounter_max,
+ "mismatch responses received", resstats_desc,
+ "Mismatch", resstats_xmldesc);
+ set_desc(dns_resstatscounter_truncated, dns_resstatscounter_max,
+ "truncated responses received", resstats_desc,
+ "Truncated", resstats_xmldesc);
+ set_desc(dns_resstatscounter_lame, dns_resstatscounter_max,
+ "lame delegations received", resstats_desc,
+ "Lame", resstats_xmldesc);
+ set_desc(dns_resstatscounter_retry, dns_resstatscounter_max,
+ "query retries", resstats_desc,
+ "Retry", resstats_xmldesc);
+ set_desc(dns_resstatscounter_gluefetchv4, dns_resstatscounter_max,
+ "IPv4 NS address fetches", resstats_desc,
+ "GlueFetchv4", resstats_xmldesc);
+ set_desc(dns_resstatscounter_gluefetchv6, dns_resstatscounter_max,
+ "IPv6 NS address fetches", resstats_desc,
+ "GlueFetchv6", resstats_xmldesc);
+ set_desc(dns_resstatscounter_gluefetchv4fail, dns_resstatscounter_max,
+ "IPv4 NS address fetch failed", resstats_desc,
+ "GlueFetchv4Fail", resstats_xmldesc);
+ set_desc(dns_resstatscounter_gluefetchv6fail, dns_resstatscounter_max,
+ "IPv6 NS address fetch failed", resstats_desc,
+ "GlueFetchv6Fail", resstats_xmldesc);
+ set_desc(dns_resstatscounter_val, dns_resstatscounter_max,
+ "DNSSEC validation attempted", resstats_desc,
+ "ValAttempt", resstats_xmldesc);
+ set_desc(dns_resstatscounter_valsuccess, dns_resstatscounter_max,
+ "DNSSEC validation succeeded", resstats_desc,
+ "ValOk", resstats_xmldesc);
+ set_desc(dns_resstatscounter_valnegsuccess, dns_resstatscounter_max,
+ "DNSSEC NX validation succeeded", resstats_desc,
+ "ValNegOk", resstats_xmldesc);
+ set_desc(dns_resstatscounter_valfail, dns_resstatscounter_max,
+ "DNSSEC validation failed", resstats_desc,
+ "ValFail", resstats_xmldesc);
+
+ /* Initialize zone statistics */
+ memset(zonestats_desc, 0,
+ dns_zonestatscounter_max * sizeof(zonestats_desc[0]));
+#ifdef HAVE_LIBXML2
+ memset(zonestats_xmldesc, 0,
+ dns_zonestatscounter_max * sizeof(zonestats_xmldesc[0]));
+#endif
+ set_desc(dns_zonestatscounter_notifyoutv4, dns_zonestatscounter_max,
+ "IPv4 notifies sent", zonestats_desc,
+ "NotifyOutv4", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_notifyoutv6, dns_zonestatscounter_max,
+ "IPv6 notifies sent", zonestats_desc,
+ "NotifyOutv6", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_notifyinv4, dns_zonestatscounter_max,
+ "IPv4 notifies received", zonestats_desc,
+ "NotifyInv4", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_notifyinv6, dns_zonestatscounter_max,
+ "IPv6 notifies received", zonestats_desc,
+ "NotifyInv6", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_notifyrej, dns_zonestatscounter_max,
+ "notifies rejected", zonestats_desc,
+ "NotifyRej", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_soaoutv4, dns_zonestatscounter_max,
+ "IPv4 SOA queries sent", zonestats_desc,
+ "SOAOutv4", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_soaoutv6, dns_zonestatscounter_max,
+ "IPv6 SOA queries sent", zonestats_desc,
+ "SOAOutv6", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_axfrreqv4, dns_zonestatscounter_max,
+ "IPv4 AXFR requested", zonestats_desc,
+ "AXFRReqv4", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_axfrreqv6, dns_zonestatscounter_max,
+ "IPv6 AXFR requested", zonestats_desc,
+ "AXFRReqv6", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_ixfrreqv4, dns_zonestatscounter_max,
+ "IPv4 IXFR requested", zonestats_desc,
+ "IXFRReqv4", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_ixfrreqv6, dns_zonestatscounter_max,
+ "IPv6 IXFR requested", zonestats_desc,
+ "IXFRReqv6", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_xfrsuccess, dns_zonestatscounter_max,
+ "transfer requests succeeded", zonestats_desc,
+ "XfrSuccess", zonestats_xmldesc);
+ set_desc(dns_zonestatscounter_xfrfail, dns_zonestatscounter_max,
+ "transfer requests failed", zonestats_desc,
+ "XfrFail", zonestats_xmldesc);
+
+ /* Sanity check */
+ for (i = 0; i < dns_nsstatscounter_max; i++)
+ INSIST(nsstats_desc[i] != NULL);
+ for (i = 0; i < dns_resstatscounter_max; i++)
+ INSIST(resstats_desc[i] != NULL);
+ for (i = 0; i < dns_zonestatscounter_max; i++)
+ INSIST(zonestats_desc[i] != NULL);
+#ifdef HAVE_LIBXML2
+ for (i = 0; i < dns_nsstatscounter_max; i++)
+ INSIST(nsstats_xmldesc[i] != NULL);
+ for (i = 0; i < dns_resstatscounter_max; i++)
+ INSIST(resstats_xmldesc[i] != NULL);
+ for (i = 0; i < dns_zonestatscounter_max; i++)
+ INSIST(zonestats_xmldesc[i] != NULL);
+#endif
+}
+
+/*%
+ * Dump callback functions.
+ */
+static void
+generalstat_dump(dns_statscounter_t counter, isc_uint64_t val, void *arg) {
+ stats_dumparg_t *dumparg = arg;
+ FILE *fp;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+#endif
+
+ REQUIRE(counter < dumparg->ncounters);
+
+ switch (dumparg->type) {
+ case statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n", val,
+ dumparg->desc[counter]);
+ break;
+ case statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+
+ if (dumparg->category != NULL) {
+ xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR
+ dumparg->category);
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR
+ dumparg->desc[counter]);
+ xmlTextWriterEndElement(writer); /* name */
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR
+ "counter");
+ } else {
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR
+ dumparg->desc[counter]);
+ }
+ xmlTextWriterWriteFormatString(writer,
+ "%" ISC_PRINT_QUADFORMAT "u",
+ val);
+ xmlTextWriterEndElement(writer); /* counter */
+ if (dumparg->category != NULL)
+ xmlTextWriterEndElement(writer); /* category */
+#endif
+ break;
+ }
+}
+
+static void
+rdtypestat_dump(dns_rdatastatstype_t type, isc_uint64_t val, void *arg) {
+ char typebuf[64];
+ const char *typestr;
+ stats_dumparg_t *dumparg = arg;
+ FILE *fp;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+#endif
+
+ if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_OTHERTYPE)
+ == 0) {
+ dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
+ sizeof(typebuf));
+ typestr = typebuf;
+ } else
+ typestr = "Others";
+
+ switch (dumparg->type) {
+ case statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n", val, typestr);
+ break;
+ case statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "rdtype");
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR typestr);
+ xmlTextWriterEndElement(writer); /* name */
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter");
+ xmlTextWriterWriteFormatString(writer,
+ "%" ISC_PRINT_QUADFORMAT "u",
+ val);
+ xmlTextWriterEndElement(writer); /* counter */
+
+ xmlTextWriterEndElement(writer); /* rdtype */
+#endif
+ break;
+ }
+}
+
+static void
+rdatasetstats_dump(dns_rdatastatstype_t type, isc_uint64_t val, void *arg) {
+ stats_dumparg_t *dumparg = arg;
+ FILE *fp;
+ char typebuf[64];
+ const char *typestr;
+ isc_boolean_t nxrrset = ISC_FALSE;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+#endif
+
+ if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_NXDOMAIN)
+ != 0) {
+ typestr = "NXDOMAIN";
+ } else if ((DNS_RDATASTATSTYPE_ATTR(type) &
+ DNS_RDATASTATSTYPE_ATTR_OTHERTYPE) != 0) {
+ typestr = "Others";
+ } else {
+ dns_rdatatype_format(DNS_RDATASTATSTYPE_BASE(type), typebuf,
+ sizeof(typebuf));
+ typestr = typebuf;
+ }
+
+ if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_NXRRSET)
+ != 0)
+ nxrrset = ISC_TRUE;
+
+ switch (dumparg->type) {
+ case statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s%s\n", val,
+ nxrrset ? "!" : "", typestr);
+ break;
+ case statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "rrset");
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
+ xmlTextWriterWriteFormatString(writer, "%s%s",
+ nxrrset ? "!" : "", typestr);
+ xmlTextWriterEndElement(writer); /* name */
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter");
+ xmlTextWriterWriteFormatString(writer,
+ "%" ISC_PRINT_QUADFORMAT "u",
+ val);
+ xmlTextWriterEndElement(writer); /* counter */
+
+ xmlTextWriterEndElement(writer); /* rrset */
+#endif
+ break;
+ }
+}
+
+static void
+opcodestat_dump(dns_opcode_t code, isc_uint64_t val, void *arg) {
+ FILE *fp = arg;
+ isc_buffer_t b;
+ char codebuf[64];
+ stats_dumparg_t *dumparg = arg;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+#endif
+
+ isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1);
+ dns_opcode_totext(code, &b);
+ codebuf[isc_buffer_usedlength(&b)] = '\0';
+
+ switch (dumparg->type) {
+ case statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" ISC_PRINT_QUADFORMAT "u %s\n", val, codebuf);
+ break;
+ case statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "opcode");
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR codebuf);
+ xmlTextWriterEndElement(writer); /* name */
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter");
+ xmlTextWriterWriteFormatString(writer,
+ "%" ISC_PRINT_QUADFORMAT "u",
+ val);
+ xmlTextWriterEndElement(writer); /* counter */
+
+ xmlTextWriterEndElement(writer); /* opcode */
+#endif
+ break;
+ }
+}
+
+#ifdef HAVE_LIBXML2
+
+/* XXXMLG below here sucks. */
+
+#define TRY(a) do { result = (a); INSIST(result == ISC_R_SUCCESS); } while(0);
+#define TRY0(a) do { xmlrc = (a); INSIST(xmlrc >= 0); } while(0);
+
+static isc_result_t
+zone_xmlrender(dns_zone_t *zone, void *arg) {
+ char buf[1024 + 32]; /* sufficiently large for zone name and class */
+ dns_rdataclass_t rdclass;
+ isc_uint32_t serial;
+ xmlTextWriterPtr writer = arg;
+ stats_dumparg_t dumparg;
+ dns_stats_t *zonestats;
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "zone");
+
+ dns_zone_name(zone, buf, sizeof(buf));
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR buf);
+ xmlTextWriterEndElement(writer);
+
+ rdclass = dns_zone_getclass(zone);
+ dns_rdataclass_format(rdclass, buf, sizeof(buf));
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "rdataclass");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR buf);
+ xmlTextWriterEndElement(writer);
+
+ serial = dns_zone_getserial(zone);
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial");
+ xmlTextWriterWriteFormatString(writer, "%u", serial);
+ xmlTextWriterEndElement(writer);
+
+ dumparg.type = statsformat_xml;
+ dumparg.arg = writer;
+ dumparg.category = NULL;
+ dumparg.desc = nsstats_xmldesc;
+ dumparg.ncounters = dns_nsstatscounter_max;
+
+ zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL) {
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters");
+ dns_generalstats_dump(zonestats, generalstat_dump,
+ &dumparg, DNS_STATSDUMP_VERBOSE);
+ xmlTextWriterEndElement(writer); /* counters */
+ }
+
+ xmlTextWriterEndElement(writer); /* zone */
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+generatexml(ns_server_t *server, int *buflen, xmlChar **buf) {
+ char boottime[sizeof "yyyy-mm-ddThh:mm:ssZ"];
+ char nowstr[sizeof "yyyy-mm-ddThh:mm:ssZ"];
+ isc_time_t now;
+ xmlTextWriterPtr writer;
+ xmlDocPtr doc;
+ int xmlrc;
+ dns_view_t *view;
+ stats_dumparg_t dumparg;
+ dns_stats_t *cachestats;
+
+ isc_time_now(&now);
+ isc_time_formatISO8601(&ns_g_boottime, boottime, sizeof boottime);
+ isc_time_formatISO8601(&now, nowstr, sizeof nowstr);
+
+ writer = xmlNewTextWriterDoc(&doc, 0);
+ TRY0(xmlTextWriterStartDocument(writer, NULL, "UTF-8", NULL));
+ TRY0(xmlTextWriterWritePI(writer, ISC_XMLCHAR "xml-stylesheet",
+ ISC_XMLCHAR "type=\"text/xsl\" href=\"/bind9.xsl\""));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "isc"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
+ ISC_XMLCHAR "1.0"));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "bind"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "statistics"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
+ ISC_XMLCHAR "2.0"));
+
+ /* Set common fields for statistics dump */
+ dumparg.type = statsformat_xml;
+ dumparg.arg = writer;
+
+ /*
+ * Start by rendering the views we know of here. For each view we
+ * know of, call its rendering function.
+ */
+ view = ISC_LIST_HEAD(server->viewlist);
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "views"));
+ while (view != NULL) {
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "view");
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "name");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR view->name);
+ xmlTextWriterEndElement(writer);
+
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "zones");
+ dns_zt_apply(view->zonetable, ISC_FALSE, zone_xmlrender,
+ writer);
+ xmlTextWriterEndElement(writer);
+
+ if (view->resquerystats != NULL) {
+ dns_rdatatypestats_dump(view->resquerystats,
+ rdtypestat_dump, &dumparg, 0);
+ }
+
+ if (view->resstats != NULL) {
+ dumparg.ncounters = dns_resstatscounter_max;
+ dumparg.category = "resstat";
+ dumparg.desc = resstats_xmldesc;
+ dns_generalstats_dump(view->resstats, generalstat_dump,
+ &dumparg, DNS_STATSDUMP_VERBOSE);
+ }
+
+ cachestats = dns_db_getrrsetstats(view->cachedb);
+ if (cachestats != NULL) {
+ xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "cache");
+ dns_rdatasetstats_dump(cachestats, rdatasetstats_dump,
+ &dumparg, 0);
+ xmlTextWriterEndElement(writer); /* cache */
+ }
+
+ xmlTextWriterEndElement(writer); /* view */
+
+ view = ISC_LIST_NEXT(view, link);
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* views */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "socketmgr"));
+ isc_socketmgr_renderxml(ns_g_socketmgr, writer);
+ TRY0(xmlTextWriterEndElement(writer)); /* socketmgr */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "taskmgr"));
+ isc_taskmgr_renderxml(ns_g_taskmgr, writer);
+ TRY0(xmlTextWriterEndElement(writer)); /* taskmgr */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "server"));
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "boot-time");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR boottime);
+ xmlTextWriterEndElement(writer);
+ xmlTextWriterStartElement(writer, ISC_XMLCHAR "current-time");
+ xmlTextWriterWriteString(writer, ISC_XMLCHAR nowstr);
+ xmlTextWriterEndElement(writer);
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "requests"));
+ dns_opcodestats_dump(server->opcodestats, opcodestat_dump, &dumparg,
+ 0);
+ xmlTextWriterEndElement(writer); /* requests */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "queries-in"));
+ dns_rdatatypestats_dump(server->rcvquerystats, rdtypestat_dump,
+ &dumparg, 0);
+ xmlTextWriterEndElement(writer); /* queries-in */
+
+ dumparg.category = "nsstat";
+ dumparg.desc = nsstats_xmldesc;
+ dumparg.ncounters = dns_nsstatscounter_max;
+ dns_generalstats_dump(server->nsstats, generalstat_dump, &dumparg,
+ DNS_STATSDUMP_VERBOSE);
+
+ dumparg.category = "zonestat";
+ dumparg.desc = zonestats_xmldesc;
+ dumparg.ncounters = dns_zonestatscounter_max;
+ dns_generalstats_dump(server->zonestats, generalstat_dump, &dumparg,
+ DNS_STATSDUMP_VERBOSE);
+
+ /*
+ * Most of the common resolver statistics entries are 0, so we don't
+ * use the verbose dump here.
+ */
+ dumparg.category = "resstat";
+ dumparg.ncounters = dns_resstatscounter_max;
+ dumparg.desc = resstats_xmldesc;
+ dns_generalstats_dump(server->resolverstats, generalstat_dump,
+ &dumparg, 0);
+
+ xmlTextWriterEndElement(writer); /* server */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "memory"));
+ isc_mem_renderxml(writer);
+ TRY0(xmlTextWriterEndElement(writer)); /* memory */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* statistics */
+ TRY0(xmlTextWriterEndElement(writer)); /* bind */
+ TRY0(xmlTextWriterEndElement(writer)); /* isc */
+
+ TRY0(xmlTextWriterEndDocument(writer));
+
+ xmlFreeTextWriter(writer);
+
+ xmlDocDumpFormatMemoryEnc(doc, buf, buflen, "UTF-8", 1);
+ xmlFreeDoc(doc);
+}
+
+static void
+wrap_xmlfree(isc_buffer_t *buffer, void *arg) {
+ UNUSED(arg);
+
+ xmlFree(isc_buffer_base(buffer));
+}
+
+static isc_result_t
+render_index(const char *url, const char *querystring, void *arg,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb,
+ void **freecb_args)
+{
+ unsigned char *msg;
+ int msglen;
+ ns_server_t *server = arg;
+
+ UNUSED(url);
+ UNUSED(querystring);
+
+ generatexml(server, &msglen, &msg);
+
+ *retcode = 200;
+ *retmsg = "OK";
+ *mimetype = "text/xml";
+ isc_buffer_reinit(b, msg, msglen);
+ isc_buffer_add(b, msglen);
+ *freecb = wrap_xmlfree;
+ *freecb_args = NULL;
+
+ return (ISC_R_SUCCESS);
+}
+
+#endif /* HAVE_LIBXML2 */
+
+static isc_result_t
+render_xsl(const char *url, const char *querystring, void *args,
+ unsigned int *retcode, const char **retmsg, const char **mimetype,
+ isc_buffer_t *b, isc_httpdfree_t **freecb,
+ void **freecb_args)
+{
+ UNUSED(url);
+ UNUSED(querystring);
+ UNUSED(args);
+
+ *retcode = 200;
+ *retmsg = "OK";
+ *mimetype = "text/xslt+xml";
+ isc_buffer_reinit(b, xslmsg, strlen(xslmsg));
+ isc_buffer_add(b, strlen(xslmsg));
+ *freecb = NULL;
+ *freecb_args = NULL;
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+shutdown_listener(ns_statschannel_t *listener) {
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ isc_sockaddr_format(&listener->address, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,NS_LOGMODULE_SERVER,
+ ISC_LOG_NOTICE, "stopping statistics channel on %s",
+ socktext);
+
+ isc_httpdmgr_shutdown(&listener->httpdmgr);
+}
+
+static isc_boolean_t
+client_ok(const isc_sockaddr_t *fromaddr, void *arg) {
+ ns_statschannel_t *listener = arg;
+ isc_netaddr_t netaddr;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+ int match;
+
+ REQUIRE(listener != NULL);
+
+ isc_netaddr_fromsockaddr(&netaddr, fromaddr);
+
+ LOCK(&listener->lock);
+ if (dns_acl_match(&netaddr, NULL, listener->acl, &ns_g_server->aclenv,
+ &match, NULL) == ISC_R_SUCCESS && match > 0) {
+ UNLOCK(&listener->lock);
+ return (ISC_TRUE);
+ }
+ UNLOCK(&listener->lock);
+
+ isc_sockaddr_format(fromaddr, socktext, sizeof(socktext));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "rejected statistics connection from %s", socktext);
+
+ return (ISC_FALSE);
+}
+
+static void
+destroy_listener(void *arg) {
+ ns_statschannel_t *listener = arg;
+
+ REQUIRE(listener != NULL);
+ REQUIRE(!ISC_LINK_LINKED(listener, link));
+
+ /* We don't have to acquire the lock here since it's already unlinked */
+ dns_acl_detach(&listener->acl);
+
+ DESTROYLOCK(&listener->lock);
+ isc_mem_putanddetach(&listener->mctx, listener, sizeof(*listener));
+}
+
+static isc_result_t
+add_listener(ns_server_t *server, ns_statschannel_t **listenerp,
+ const cfg_obj_t *listen_params, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext)
+{
+ isc_result_t result;
+ ns_statschannel_t *listener;
+ isc_task_t *task = NULL;
+ isc_socket_t *sock = NULL;
+ const cfg_obj_t *allow;
+ dns_acl_t *new_acl = NULL;
+
+ listener = isc_mem_get(server->mctx, sizeof(*listener));
+ if (listener == NULL)
+ return (ISC_R_NOMEMORY);
+
+ listener->httpdmgr = NULL;
+ listener->address = *addr;
+ listener->acl = NULL;
+ listener->mctx = NULL;
+ ISC_LINK_INIT(listener, link);
+
+ result = isc_mutex_init(&listener->lock);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(server->mctx, listener, sizeof(*listener));
+ return (ISC_R_FAILURE);
+ }
+
+ isc_mem_attach(server->mctx, &listener->mctx);
+
+ allow = cfg_tuple_get(listen_params, "allow");
+ if (allow != NULL && cfg_obj_islist(allow)) {
+ result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
+ aclconfctx, listener->mctx, 0,
+ &new_acl);
+ } else
+ result = dns_acl_any(listener->mctx, &new_acl);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+
+ result = isc_task_create(ns_g_taskmgr, 0, &task);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_task_setname(task, "statchannel", NULL);
+
+ result = isc_socket_create(ns_g_socketmgr, isc_sockaddr_pf(addr),
+ isc_sockettype_tcp, &sock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ isc_socket_setname(sock, "statchannel", NULL);
+
+#ifndef ISC_ALLOW_MAPPED
+ isc_socket_ipv6only(sock, ISC_TRUE);
+#endif
+
+ result = isc_socket_bind(sock, addr, ISC_SOCKET_REUSEADDRESS);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = isc_httpdmgr_create(server->mctx, sock, task, client_ok,
+ destroy_listener, listener, ns_g_timermgr,
+ &listener->httpdmgr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+#ifdef HAVE_LIBXML2
+ isc_httpdmgr_addurl(listener->httpdmgr, "/", render_index, server);
+#endif
+ isc_httpdmgr_addurl(listener->httpdmgr, "/bind9.xsl", render_xsl,
+ server);
+
+ *listenerp = listener;
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_NOTICE,
+ "statistics channel listening on %s", socktext);
+
+cleanup:
+ if (result != ISC_R_SUCCESS) {
+ if (listener->acl != NULL)
+ dns_acl_detach(&listener->acl);
+ DESTROYLOCK(&listener->lock);
+ isc_mem_putanddetach(&listener->mctx, listener,
+ sizeof(*listener));
+ }
+ if (task != NULL)
+ isc_task_detach(&task);
+ if (sock != NULL)
+ isc_socket_detach(&sock);
+
+ return (result);
+}
+
+static void
+update_listener(ns_server_t *server, ns_statschannel_t **listenerp,
+ const cfg_obj_t *listen_params, const cfg_obj_t *config,
+ isc_sockaddr_t *addr, cfg_aclconfctx_t *aclconfctx,
+ const char *socktext)
+{
+ ns_statschannel_t *listener;
+ const cfg_obj_t *allow = NULL;
+ dns_acl_t *new_acl = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (listener = ISC_LIST_HEAD(server->statschannels);
+ listener != NULL;
+ listener = ISC_LIST_NEXT(listener, link))
+ if (isc_sockaddr_equal(addr, &listener->address))
+ break;
+
+ if (listener == NULL) {
+ *listenerp = NULL;
+ return;
+ }
+
+ /*
+ * Now, keep the old access list unless a new one can be made.
+ */
+ allow = cfg_tuple_get(listen_params, "allow");
+ if (allow != NULL && cfg_obj_islist(allow)) {
+ result = cfg_acl_fromconfig(allow, config, ns_g_lctx,
+ aclconfctx, listener->mctx, 0,
+ &new_acl);
+ } else
+ result = dns_acl_any(listener->mctx, &new_acl);
+
+ if (result == ISC_R_SUCCESS) {
+ LOCK(&listener->lock);
+
+ dns_acl_detach(&listener->acl);
+ dns_acl_attach(new_acl, &listener->acl);
+ dns_acl_detach(&new_acl);
+
+ UNLOCK(&listener->lock);
+ } else {
+ cfg_obj_log(listen_params, ns_g_lctx, ISC_LOG_WARNING,
+ "couldn't install new acl for "
+ "statistics channel %s: %s",
+ socktext, isc_result_totext(result));
+ }
+
+ *listenerp = listener;
+}
+
+isc_result_t
+ns_statschannels_configure(ns_server_t *server, const cfg_obj_t *config,
+ cfg_aclconfctx_t *aclconfctx)
+{
+ ns_statschannel_t *listener, *listener_next;
+ ns_statschannellist_t new_listeners;
+ const cfg_obj_t *statschannellist = NULL;
+ const cfg_listelt_t *element, *element2;
+ char socktext[ISC_SOCKADDR_FORMATSIZE];
+
+ RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);
+
+ ISC_LIST_INIT(new_listeners);
+
+ /*
+ * Get the list of named.conf 'statistics-channels' statements.
+ */
+ (void)cfg_map_get(config, "statistics-channels", &statschannellist);
+
+ /*
+ * Run through the new address/port list, noting sockets that are
+ * already being listened on and moving them to the new list.
+ *
+ * Identifying duplicate addr/port combinations is left to either
+ * the underlying config code, or to the bind attempt getting an
+ * address-in-use error.
+ */
+ if (statschannellist != NULL) {
+#ifndef HAVE_LIBXML2
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "statistics-channels specified but not effective "
+ "due to missing XML library");
+#endif
+
+ for (element = cfg_list_first(statschannellist);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ const cfg_obj_t *statschannel;
+ const cfg_obj_t *listenercfg = NULL;
+
+ statschannel = cfg_listelt_value(element);
+ (void)cfg_map_get(statschannel, "inet",
+ &listenercfg);
+ if (listenercfg == NULL)
+ continue;
+
+ for (element2 = cfg_list_first(listenercfg);
+ element2 != NULL;
+ element2 = cfg_list_next(element2)) {
+ const cfg_obj_t *listen_params;
+ const cfg_obj_t *obj;
+ isc_sockaddr_t addr;
+
+ listen_params = cfg_listelt_value(element2);
+
+ obj = cfg_tuple_get(listen_params, "address");
+ addr = *cfg_obj_assockaddr(obj);
+ if (isc_sockaddr_getport(&addr) == 0)
+ isc_sockaddr_setport(&addr, NS_STATSCHANNEL_HTTPPORT);
+
+ isc_sockaddr_format(&addr, socktext,
+ sizeof(socktext));
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_DEBUG(9),
+ "processing statistics "
+ "channel %s",
+ socktext);
+
+ update_listener(server, &listener,
+ listen_params, config, &addr,
+ aclconfctx, socktext);
+
+ if (listener != NULL) {
+ /*
+ * Remove the listener from the old
+ * list, so it won't be shut down.
+ */
+ ISC_LIST_UNLINK(server->statschannels,
+ listener, link);
+ } else {
+ /*
+ * This is a new listener.
+ */
+ isc_result_t r;
+
+ r = add_listener(server, &listener,
+ listen_params, config,
+ &addr, aclconfctx,
+ socktext);
+ if (r != ISC_R_SUCCESS) {
+ cfg_obj_log(listen_params,
+ ns_g_lctx,
+ ISC_LOG_WARNING,
+ "couldn't allocate "
+ "statistics channel"
+ " %s: %s",
+ socktext,
+ isc_result_totext(r));
+ }
+ }
+
+ if (listener != NULL)
+ ISC_LIST_APPEND(new_listeners, listener,
+ link);
+ }
+ }
+ }
+
+ for (listener = ISC_LIST_HEAD(server->statschannels);
+ listener != NULL;
+ listener = listener_next) {
+ listener_next = ISC_LIST_NEXT(listener, link);
+ ISC_LIST_UNLINK(server->statschannels, listener, link);
+ shutdown_listener(listener);
+ }
+
+ ISC_LIST_APPENDLIST(server->statschannels, new_listeners, link);
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_statschannels_shutdown(ns_server_t *server) {
+ ns_statschannel_t *listener;
+
+ while ((listener = ISC_LIST_HEAD(server->statschannels)) != NULL) {
+ ISC_LIST_UNLINK(server->statschannels, listener, link);
+ shutdown_listener(listener);
+ }
+}
+
+isc_result_t
+ns_stats_dump(ns_server_t *server, FILE *fp) {
+ isc_stdtime_t now;
+ isc_result_t result;
+ dns_view_t *view;
+ dns_zone_t *zone, *next;
+ stats_dumparg_t dumparg;
+
+ RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);
+
+ /* Set common fields */
+ dumparg.type = statsformat_file;
+ dumparg.arg = fp;
+ dumparg.category = NULL; /* unused */
+
+ isc_stdtime_get(&now);
+ fprintf(fp, "+++ Statistics Dump +++ (%lu)\n", (unsigned long)now);
+
+ fprintf(fp, "++ Incoming Requests ++\n");
+ dns_opcodestats_dump(server->opcodestats, opcodestat_dump, &dumparg, 0);
+
+ fprintf(fp, "++ Incoming Queries ++\n");
+ dns_rdatatypestats_dump(server->rcvquerystats, rdtypestat_dump,
+ &dumparg, 0);
+
+ fprintf(fp, "++ Outgoing Queries ++\n");
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (view->resquerystats == NULL)
+ continue;
+ if (strcmp(view->name, "_default") == 0)
+ fprintf(fp, "[View: default]\n");
+ else
+ fprintf(fp, "[View: %s]\n", view->name);
+ dns_rdatatypestats_dump(view->resquerystats, rdtypestat_dump,
+ &dumparg, 0);
+ }
+
+ fprintf(fp, "++ Name Server Statistics ++\n");
+ dumparg.desc = nsstats_desc;
+ dumparg.ncounters = dns_nsstatscounter_max;
+ dns_generalstats_dump(server->nsstats, generalstat_dump, &dumparg, 0);
+ fprintf(fp, "++ Zone Maintenance Statistics ++\n");
+ dumparg.desc = zonestats_desc;
+ dumparg.ncounters = dns_zonestatscounter_max;
+ dns_generalstats_dump(server->zonestats, generalstat_dump, &dumparg, 0);
+
+ fprintf(fp, "++ Resolver Statistics ++\n");
+ fprintf(fp, "[Common]\n");
+ dumparg.desc = resstats_desc;
+ dumparg.ncounters = dns_resstatscounter_max;
+ dns_generalstats_dump(server->resolverstats, generalstat_dump, &dumparg,
+ 0);
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (view->resstats == NULL)
+ continue;
+ if (strcmp(view->name, "_default") == 0)
+ fprintf(fp, "[View: default]\n");
+ else
+ fprintf(fp, "[View: %s]\n", view->name);
+ dns_generalstats_dump(view->resstats, generalstat_dump,
+ &dumparg, 0);
+ }
+
+ fprintf(fp, "++ Cache DB RRsets ++\n");
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ dns_stats_t *cachestats;
+
+ cachestats = dns_db_getrrsetstats(view->cachedb);
+ if (cachestats == NULL)
+ continue;
+ if (strcmp(view->name, "_default") == 0)
+ fprintf(fp, "[View: default]\n");
+ else
+ fprintf(fp, "[View: %s]\n", view->name);
+ dns_rdatasetstats_dump(cachestats, rdatasetstats_dump, &dumparg,
+ 0);
+ }
+
+ fprintf(fp, "++ Per Zone Query Statistics ++\n");
+ zone = NULL;
+ for (result = dns_zone_first(server->zonemgr, &zone);
+ result == ISC_R_SUCCESS;
+ next = NULL, result = dns_zone_next(zone, &next), zone = next)
+ {
+ dns_stats_t *zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL) {
+ char zonename[DNS_NAME_FORMATSIZE];
+
+ dns_name_format(dns_zone_getorigin(zone),
+ zonename, sizeof(zonename));
+ view = dns_zone_getview(zone);
+
+ fprintf(fp, "[%s", zonename);
+ if (strcmp(view->name, "_default") != 0)
+ fprintf(fp, " (view: %s)", view->name);
+ fprintf(fp, "]\n");
+
+ dumparg.desc = nsstats_desc;
+ dumparg.ncounters = dns_nsstatscounter_max;
+ dns_generalstats_dump(zonestats, generalstat_dump,
+ &dumparg, 0);
+ }
+ }
+
+ fprintf(fp, "--- Statistics Dump --- (%lu)\n", (unsigned long)now);
+
+ return (ISC_R_SUCCESS); /* this function currently always succeeds */
+}
diff --git a/bin/named/tkeyconf.c b/bin/named/tkeyconf.c
new file mode 100644
index 0000000..82cf573
--- /dev/null
+++ b/bin/named/tkeyconf.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: tkeyconf.c,v 1.29 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/mem.h>
+
+#include <isccfg/cfg.h>
+
+#include <dns/fixedname.h>
+#include <dns/keyvalues.h>
+#include <dns/name.h>
+#include <dns/tkey.h>
+
+#include <dst/gssapi.h>
+
+#include <named/tkeyconf.h>
+
+#define RETERR(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto failure; \
+ } while (0)
+
+#include<named/log.h>
+#define LOG(msg) \
+ isc_log_write(ns_g_lctx, \
+ NS_LOGCATEGORY_GENERAL, \
+ NS_LOGMODULE_SERVER, \
+ ISC_LOG_ERROR, \
+ "%s", msg)
+
+isc_result_t
+ns_tkeyctx_fromconfig(const cfg_obj_t *options, isc_mem_t *mctx,
+ isc_entropy_t *ectx, dns_tkeyctx_t **tctxp)
+{
+ isc_result_t result;
+ dns_tkeyctx_t *tctx = NULL;
+ const char *s;
+ isc_uint32_t n;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_buffer_t b;
+ const cfg_obj_t *obj;
+ int type;
+
+ result = dns_tkeyctx_create(mctx, ectx, &tctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-dhkey", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
+ n = cfg_obj_asuint32(cfg_tuple_get(obj, "keyid"));
+ isc_buffer_init(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname,
+ ISC_FALSE, NULL));
+ type = DST_TYPE_PUBLIC|DST_TYPE_PRIVATE|DST_TYPE_KEY;
+ RETERR(dst_key_fromfile(name, (dns_keytag_t) n, DNS_KEYALG_DH,
+ type, NULL, mctx, &tctx->dhkey));
+ }
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-domain", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(obj);
+ isc_buffer_init(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE,
+ NULL));
+ tctx->domain = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (tctx->domain == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ dns_name_init(tctx->domain, NULL);
+ RETERR(dns_name_dup(name, mctx, tctx->domain));
+ }
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-gssapi-credential", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(obj);
+
+ isc_buffer_init(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ dns_fixedname_init(&fname);
+ name = dns_fixedname_name(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE,
+ NULL));
+ RETERR(dst_gssapi_acquirecred(name, ISC_FALSE,
+ &tctx->gsscred));
+ }
+
+ *tctxp = tctx;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ dns_tkeyctx_destroy(&tctx);
+ return (result);
+}
+
diff --git a/bin/named/tsigconf.c b/bin/named/tsigconf.c
new file mode 100644
index 0000000..b3c6e02
--- /dev/null
+++ b/bin/named/tsigconf.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2004-2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: tsigconf.c,v 1.30 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+
+#include <isccfg/cfg.h>
+
+#include <dns/tsig.h>
+#include <dns/result.h>
+
+#include <named/log.h>
+
+#include <named/config.h>
+#include <named/tsigconf.h>
+
+static isc_result_t
+add_initial_keys(const cfg_obj_t *list, dns_tsig_keyring_t *ring,
+ isc_mem_t *mctx)
+{
+ dns_tsigkey_t *tsigkey = NULL;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *key = NULL;
+ const char *keyid = NULL;
+ unsigned char *secret = NULL;
+ int secretalloc = 0;
+ int secretlen = 0;
+ isc_result_t ret;
+ isc_stdtime_t now;
+ isc_uint16_t bits;
+
+ for (element = cfg_list_first(list);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *algobj = NULL;
+ const cfg_obj_t *secretobj = NULL;
+ dns_name_t keyname;
+ dns_name_t *alg;
+ const char *algstr;
+ char keynamedata[1024];
+ isc_buffer_t keynamesrc, keynamebuf;
+ const char *secretstr;
+ isc_buffer_t secretbuf;
+
+ key = cfg_listelt_value(element);
+ keyid = cfg_obj_asstring(cfg_map_getname(key));
+
+ algobj = NULL;
+ secretobj = NULL;
+ (void)cfg_map_get(key, "algorithm", &algobj);
+ (void)cfg_map_get(key, "secret", &secretobj);
+ INSIST(algobj != NULL && secretobj != NULL);
+
+ /*
+ * Create the key name.
+ */
+ dns_name_init(&keyname, NULL);
+ isc_buffer_init(&keynamesrc, keyid, strlen(keyid));
+ isc_buffer_add(&keynamesrc, strlen(keyid));
+ isc_buffer_init(&keynamebuf, keynamedata, sizeof(keynamedata));
+ ret = dns_name_fromtext(&keyname, &keynamesrc, dns_rootname,
+ ISC_TRUE, &keynamebuf);
+ if (ret != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Create the algorithm.
+ */
+ algstr = cfg_obj_asstring(algobj);
+ if (ns_config_getkeyalgorithm(algstr, &alg, &bits)
+ != ISC_R_SUCCESS) {
+ cfg_obj_log(algobj, ns_g_lctx, ISC_LOG_ERROR,
+ "key '%s': has a unsupported algorithm '%s'",
+ keyid, algstr);
+ ret = DNS_R_BADALG;
+ goto failure;
+ }
+
+ secretstr = cfg_obj_asstring(secretobj);
+ secretalloc = secretlen = strlen(secretstr) * 3 / 4;
+ secret = isc_mem_get(mctx, secretlen);
+ if (secret == NULL) {
+ ret = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ isc_buffer_init(&secretbuf, secret, secretlen);
+ ret = isc_base64_decodestring(secretstr, &secretbuf);
+ if (ret != ISC_R_SUCCESS)
+ goto failure;
+ secretlen = isc_buffer_usedlength(&secretbuf);
+
+ isc_stdtime_get(&now);
+ ret = dns_tsigkey_create(&keyname, alg, secret, secretlen,
+ ISC_FALSE, NULL, now, now,
+ mctx, ring, &tsigkey);
+ isc_mem_put(mctx, secret, secretalloc);
+ secret = NULL;
+ if (ret != ISC_R_SUCCESS)
+ goto failure;
+ /*
+ * Set digest bits.
+ */
+ dst_key_setbits(tsigkey->key, bits);
+ dns_tsigkey_detach(&tsigkey);
+ }
+
+ return (ISC_R_SUCCESS);
+
+ failure:
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
+ "configuring key '%s': %s", keyid,
+ isc_result_totext(ret));
+
+ if (secret != NULL)
+ isc_mem_put(mctx, secret, secretalloc);
+ return (ret);
+}
+
+isc_result_t
+ns_tsigkeyring_fromconfig(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_tsig_keyring_t **ringp)
+{
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *keylist;
+ dns_tsig_keyring_t *ring = NULL;
+ isc_result_t result;
+ int i;
+
+ i = 0;
+ if (config != NULL)
+ maps[i++] = config;
+ if (vconfig != NULL)
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ maps[i] = NULL;
+
+ result = dns_tsigkeyring_create(mctx, &ring);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ for (i = 0; ; i++) {
+ if (maps[i] == NULL)
+ break;
+ keylist = NULL;
+ result = cfg_map_get(maps[i], "key", &keylist);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ result = add_initial_keys(keylist, ring, mctx);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+
+ *ringp = ring;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ dns_tsigkeyring_destroy(&ring);
+ return (result);
+}
diff --git a/bin/named/unix/Makefile.in b/bin/named/unix/Makefile.in
new file mode 100644
index 0000000..5092834
--- /dev/null
+++ b/bin/named/unix/Makefile.in
@@ -0,0 +1,36 @@
+# Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 1999-2001 Internet Software Consortium.
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: Makefile.in,v 1.10 2007/06/19 23:46:59 tbox Exp $
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+@BIND9_MAKE_INCLUDES@
+
+CINCLUDES = -I${srcdir}/include -I${srcdir}/../include \
+ ${DNS_INCLUDES} ${ISC_INCLUDES}
+
+CDEFINES =
+CWARNINGS =
+
+OBJS = os.@O@
+
+SRCS = os.c
+
+TARGETS = ${OBJS}
+
+@BIND9_MAKE_RULES@
diff --git a/bin/named/unix/include/named/os.h b/bin/named/unix/include/named/os.h
new file mode 100644
index 0000000..d03bf75
--- /dev/null
+++ b/bin/named/unix/include/named/os.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: os.h,v 1.29 2008/10/24 01:44:48 tbox Exp $ */
+
+#ifndef NS_OS_H
+#define NS_OS_H 1
+
+/*! \file */
+
+#include <isc/types.h>
+
+void
+ns_os_init(const char *progname);
+
+void
+ns_os_daemonize(void);
+
+void
+ns_os_opendevnull(void);
+
+void
+ns_os_closedevnull(void);
+
+void
+ns_os_chroot(const char *root);
+
+void
+ns_os_inituserinfo(const char *username);
+
+void
+ns_os_changeuser(void);
+
+void
+ns_os_adjustnofile(void);
+
+void
+ns_os_minprivs(void);
+
+void
+ns_os_writepidfile(const char *filename, isc_boolean_t first_time);
+void
+ns_os_shutdown(void);
+
+isc_result_t
+ns_os_gethostname(char *buf, size_t len);
+
+void
+ns_os_shutdownmsg(char *command, isc_buffer_t *text);
+
+void
+ns_os_tzset(void);
+
+void
+ns_os_started(void);
+
+#endif /* NS_OS_H */
diff --git a/bin/named/unix/os.c b/bin/named/unix/os.c
new file mode 100644
index 0000000..d3b2ff3
--- /dev/null
+++ b/bin/named/unix/os.c
@@ -0,0 +1,816 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: os.c,v 1.89 2008/11/14 05:08:48 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+#include <stdarg.h>
+
+#include <sys/types.h> /* dev_t FreeBSD 2.1 */
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h> /* Required for initgroups() on IRIX. */
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <syslog.h>
+#ifdef HAVE_TZSET
+#include <time.h>
+#endif
+#include <unistd.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/print.h>
+#include <isc/resource.h>
+#include <isc/result.h>
+#include <isc/strerror.h>
+#include <isc/string.h>
+
+#include <named/main.h>
+#include <named/os.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#endif
+
+static char *pidfile = NULL;
+static int devnullfd = -1;
+
+#ifndef ISC_FACILITY
+#define ISC_FACILITY LOG_DAEMON
+#endif
+
+/*
+ * If there's no <linux/capability.h>, we don't care about <sys/prctl.h>
+ */
+#ifndef HAVE_LINUX_CAPABILITY_H
+#undef HAVE_SYS_PRCTL_H
+#endif
+
+/*
+ * Linux defines:
+ * (T) HAVE_LINUXTHREADS
+ * (C) HAVE_SYS_CAPABILITY_H (or HAVE_LINUX_CAPABILITY_H)
+ * (P) HAVE_SYS_PRCTL_H
+ * The possible cases are:
+ * none: setuid() normally
+ * T: no setuid()
+ * C: setuid() normally, drop caps (keep CAP_SETUID)
+ * T+C: no setuid(), drop caps (don't keep CAP_SETUID)
+ * T+C+P: setuid() early, drop caps (keep CAP_SETUID)
+ * C+P: setuid() normally, drop caps (keep CAP_SETUID)
+ * P: not possible
+ * T+P: not possible
+ *
+ * if (C)
+ * caps = BIND_SERVICE + CHROOT + SETGID
+ * if ((T && C && P) || !T)
+ * caps += SETUID
+ * endif
+ * capset(caps)
+ * endif
+ * if (T && C && P && -u)
+ * setuid()
+ * else if (T && -u)
+ * fail
+ * --> start threads
+ * if (!T && -u)
+ * setuid()
+ * if (C && (P || !-u))
+ * caps = BIND_SERVICE
+ * capset(caps)
+ * endif
+ *
+ * It will be nice when Linux threads work properly with setuid().
+ */
+
+#ifdef HAVE_LINUXTHREADS
+static pid_t mainpid = 0;
+#endif
+
+static struct passwd *runas_pw = NULL;
+static isc_boolean_t done_setuid = ISC_FALSE;
+static int dfd[2] = { -1, -1 };
+
+#ifdef HAVE_LINUX_CAPABILITY_H
+
+static isc_boolean_t non_root = ISC_FALSE;
+static isc_boolean_t non_root_caps = ISC_FALSE;
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#else
+/*%
+ * We define _LINUX_FS_H to prevent it from being included. We don't need
+ * anything from it, and the files it includes cause warnings with 2.2
+ * kernels, and compilation failures (due to conflicts between <linux/string.h>
+ * and <string.h>) on 2.3 kernels.
+ */
+#define _LINUX_FS_H
+#include <linux/capability.h>
+#include <syscall.h>
+#ifndef SYS_capset
+#ifndef __NR_capset
+#include <asm/unistd.h> /* Slackware 4.0 needs this. */
+#endif /* __NR_capset */
+#define SYS_capset __NR_capset
+#endif /* SYS_capset */
+#endif /* HAVE_SYS_CAPABILITY_H */
+
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h> /* Required for prctl(). */
+
+/*
+ * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
+ * here. This allows setuid() to work on systems running a new enough
+ * kernel but with /usr/include/linux pointing to "standard" kernel
+ * headers.
+ */
+#ifndef PR_SET_KEEPCAPS
+#define PR_SET_KEEPCAPS 8
+#endif
+
+#endif /* HAVE_SYS_PRCTL_H */
+
+#ifdef HAVE_LIBCAP
+#define SETCAPS_FUNC "cap_set_proc "
+#else
+typedef unsigned int cap_t;
+#define SETCAPS_FUNC "syscall(capset) "
+#endif /* HAVE_LIBCAP */
+
+static void
+linux_setcaps(cap_t caps) {
+#ifndef HAVE_LIBCAP
+ struct __user_cap_header_struct caphead;
+ struct __user_cap_data_struct cap;
+#endif
+ char strbuf[ISC_STRERRORSIZE];
+
+ if ((getuid() != 0 && !non_root_caps) || non_root)
+ return;
+#ifndef HAVE_LIBCAP
+ memset(&caphead, 0, sizeof(caphead));
+ caphead.version = _LINUX_CAPABILITY_VERSION;
+ caphead.pid = 0;
+ memset(&cap, 0, sizeof(cap));
+ cap.effective = caps;
+ cap.permitted = caps;
+ cap.inheritable = 0;
+#endif
+#ifdef HAVE_LIBCAP
+ if (cap_set_proc(caps) < 0) {
+#else
+ if (syscall(SYS_capset, &caphead, &cap) < 0) {
+#endif
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal(SETCAPS_FUNC "failed: %s:"
+ " please ensure that the capset kernel"
+ " module is loaded. see insmod(8)",
+ strbuf);
+ }
+}
+
+#ifdef HAVE_LIBCAP
+#define SET_CAP(flag) \
+ do { \
+ capval = (flag); \
+ cap_flag_value_t curval; \
+ err = cap_get_flag(curcaps, capval, CAP_PERMITTED, &curval); \
+ if (err != -1 && curval) { \
+ err = cap_set_flag(caps, CAP_EFFECTIVE, 1, &capval, CAP_SET); \
+ if (err == -1) { \
+ isc__strerror(errno, strbuf, sizeof(strbuf)); \
+ ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
+ } \
+ \
+ err = cap_set_flag(caps, CAP_PERMITTED, 1, &capval, CAP_SET); \
+ if (err == -1) { \
+ isc__strerror(errno, strbuf, sizeof(strbuf)); \
+ ns_main_earlyfatal("cap_set_proc failed: %s", strbuf); \
+ } \
+ } \
+ } while (0)
+#define INIT_CAP \
+ do { \
+ caps = cap_init(); \
+ if (caps == NULL) { \
+ isc__strerror(errno, strbuf, sizeof(strbuf)); \
+ ns_main_earlyfatal("cap_init failed: %s", strbuf); \
+ } \
+ curcaps = cap_get_proc(); \
+ if (curcaps == NULL) { \
+ isc__strerror(errno, strbuf, sizeof(strbuf)); \
+ ns_main_earlyfatal("cap_get_proc failed: %s", strbuf); \
+ } \
+ } while (0)
+#define FREE_CAP \
+ { \
+ cap_free(caps); \
+ cap_free(curcaps); \
+ } while (0)
+#else
+#define SET_CAP(flag) do { caps |= (1 << (flag)); } while (0)
+#define INIT_CAP do { caps = 0; } while (0)
+#endif /* HAVE_LIBCAP */
+
+static void
+linux_initialprivs(void) {
+ cap_t caps;
+#ifdef HAVE_LIBCAP
+ cap_t curcaps;
+ cap_value_t capval;
+ char strbuf[ISC_STRERRORSIZE];
+ int err;
+#endif
+
+ /*%
+ * We don't need most privileges, so we drop them right away.
+ * Later on linux_minprivs() will be called, which will drop our
+ * capabilities to the minimum needed to run the server.
+ */
+ INIT_CAP;
+
+ /*
+ * We need to be able to bind() to privileged ports, notably port 53!
+ */
+ SET_CAP(CAP_NET_BIND_SERVICE);
+
+ /*
+ * We need chroot() initially too.
+ */
+ SET_CAP(CAP_SYS_CHROOT);
+
+#if defined(HAVE_SYS_PRCTL_H) || !defined(HAVE_LINUXTHREADS)
+ /*
+ * We can setuid() only if either the kernel supports keeping
+ * capabilities after setuid() (which we don't know until we've
+ * tried) or we're not using threads. If either of these is
+ * true, we want the setuid capability.
+ */
+ SET_CAP(CAP_SETUID);
+#endif
+
+ /*
+ * Since we call initgroups, we need this.
+ */
+ SET_CAP(CAP_SETGID);
+
+ /*
+ * Without this, we run into problems reading a configuration file
+ * owned by a non-root user and non-world-readable on startup.
+ */
+ SET_CAP(CAP_DAC_READ_SEARCH);
+
+ /*
+ * XXX We might want to add CAP_SYS_RESOURCE, though it's not
+ * clear it would work right given the way linuxthreads work.
+ * XXXDCL But since we need to be able to set the maximum number
+ * of files, the stack size, data size, and core dump size to
+ * support named.conf options, this is now being added to test.
+ */
+ SET_CAP(CAP_SYS_RESOURCE);
+
+ linux_setcaps(caps);
+
+#ifdef HAVE_LIBCAP
+ FREE_CAP;
+#endif
+}
+
+static void
+linux_minprivs(void) {
+ cap_t caps;
+#ifdef HAVE_LIBCAP
+ cap_t curcaps;
+ cap_value_t capval;
+ char strbuf[ISC_STRERRORSIZE];
+ int err;
+#endif
+
+ INIT_CAP;
+ /*%
+ * Drop all privileges except the ability to bind() to privileged
+ * ports.
+ *
+ * It's important that we drop CAP_SYS_CHROOT. If we didn't, it
+ * chroot() could be used to escape from the chrooted area.
+ */
+
+ SET_CAP(CAP_NET_BIND_SERVICE);
+
+ /*
+ * XXX We might want to add CAP_SYS_RESOURCE, though it's not
+ * clear it would work right given the way linuxthreads work.
+ * XXXDCL But since we need to be able to set the maximum number
+ * of files, the stack size, data size, and core dump size to
+ * support named.conf options, this is now being added to test.
+ */
+ SET_CAP(CAP_SYS_RESOURCE);
+
+ linux_setcaps(caps);
+
+#ifdef HAVE_LIBCAP
+ FREE_CAP;
+#endif
+}
+
+#ifdef HAVE_SYS_PRCTL_H
+static void
+linux_keepcaps(void) {
+ char strbuf[ISC_STRERRORSIZE];
+ /*%
+ * Ask the kernel to allow us to keep our capabilities after we
+ * setuid().
+ */
+
+ if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
+ if (errno != EINVAL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("prctl() failed: %s", strbuf);
+ }
+ } else {
+ non_root_caps = ISC_TRUE;
+ if (getuid() != 0)
+ non_root = ISC_TRUE;
+ }
+}
+#endif
+
+#endif /* HAVE_LINUX_CAPABILITY_H */
+
+
+static void
+setup_syslog(const char *progname) {
+ int options;
+
+ options = LOG_PID;
+#ifdef LOG_NDELAY
+ options |= LOG_NDELAY;
+#endif
+ openlog(isc_file_basename(progname), options, ISC_FACILITY);
+}
+
+void
+ns_os_init(const char *progname) {
+ setup_syslog(progname);
+#ifdef HAVE_LINUX_CAPABILITY_H
+ linux_initialprivs();
+#endif
+#ifdef HAVE_LINUXTHREADS
+ mainpid = getpid();
+#endif
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, SIG_IGN);
+#endif
+}
+
+void
+ns_os_daemonize(void) {
+ pid_t pid;
+ char strbuf[ISC_STRERRORSIZE];
+
+ if (pipe(dfd) == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("pipe(): %s", strbuf);
+ }
+
+ pid = fork();
+ if (pid == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("fork(): %s", strbuf);
+ }
+ if (pid != 0) {
+ int n;
+ /*
+ * Wait for the child to finish loading for the first time.
+ * This would be so much simpler if fork() worked once we
+ * were multi-threaded.
+ */
+ (void)close(dfd[1]);
+ do {
+ char buf;
+ n = read(dfd[0], &buf, 1);
+ if (n == 1)
+ _exit(0);
+ } while (n == -1 && errno == EINTR);
+ _exit(1);
+ }
+ (void)close(dfd[0]);
+
+ /*
+ * We're the child.
+ */
+
+#ifdef HAVE_LINUXTHREADS
+ mainpid = getpid();
+#endif
+
+ if (setsid() == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("setsid(): %s", strbuf);
+ }
+
+ /*
+ * Try to set stdin, stdout, and stderr to /dev/null, but press
+ * on even if it fails.
+ *
+ * XXXMLG The close() calls here are unneeded on all but NetBSD, but
+ * are harmless to include everywhere. dup2() is supposed to close
+ * the FD if it is in use, but unproven-pthreads-0.16 is broken
+ * and will end up closing the wrong FD. This will be fixed eventually,
+ * and these calls will be removed.
+ */
+ if (devnullfd != -1) {
+ if (devnullfd != STDIN_FILENO) {
+ (void)close(STDIN_FILENO);
+ (void)dup2(devnullfd, STDIN_FILENO);
+ }
+ if (devnullfd != STDOUT_FILENO) {
+ (void)close(STDOUT_FILENO);
+ (void)dup2(devnullfd, STDOUT_FILENO);
+ }
+ if (devnullfd != STDERR_FILENO) {
+ (void)close(STDERR_FILENO);
+ (void)dup2(devnullfd, STDERR_FILENO);
+ }
+ }
+}
+
+void
+ns_os_started(void) {
+ char buf = 0;
+
+ /*
+ * Signal to the parent that we stated successfully.
+ */
+ if (dfd[0] != -1 && dfd[1] != -1) {
+ write(dfd[1], &buf, 1);
+ close(dfd[1]);
+ dfd[0] = dfd[1] = -1;
+ }
+}
+
+void
+ns_os_opendevnull(void) {
+ devnullfd = open("/dev/null", O_RDWR, 0);
+}
+
+void
+ns_os_closedevnull(void) {
+ if (devnullfd != STDIN_FILENO &&
+ devnullfd != STDOUT_FILENO &&
+ devnullfd != STDERR_FILENO) {
+ close(devnullfd);
+ devnullfd = -1;
+ }
+}
+
+static isc_boolean_t
+all_digits(const char *s) {
+ if (*s == '\0')
+ return (ISC_FALSE);
+ while (*s != '\0') {
+ if (!isdigit((*s)&0xff))
+ return (ISC_FALSE);
+ s++;
+ }
+ return (ISC_TRUE);
+}
+
+void
+ns_os_chroot(const char *root) {
+ char strbuf[ISC_STRERRORSIZE];
+#ifdef HAVE_LIBSCF
+ ns_smf_chroot = 0;
+#endif
+ if (root != NULL) {
+ if (chroot(root) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("chroot(): %s", strbuf);
+ }
+ if (chdir("/") < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("chdir(/): %s", strbuf);
+ }
+#ifdef HAVE_LIBSCF
+ /* Set ns_smf_chroot flag on successful chroot. */
+ ns_smf_chroot = 1;
+#endif
+ }
+}
+
+void
+ns_os_inituserinfo(const char *username) {
+ char strbuf[ISC_STRERRORSIZE];
+ if (username == NULL)
+ return;
+
+ if (all_digits(username))
+ runas_pw = getpwuid((uid_t)atoi(username));
+ else
+ runas_pw = getpwnam(username);
+ endpwent();
+
+ if (runas_pw == NULL)
+ ns_main_earlyfatal("user '%s' unknown", username);
+
+ if (getuid() == 0) {
+ if (initgroups(runas_pw->pw_name, runas_pw->pw_gid) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("initgroups(): %s", strbuf);
+ }
+ }
+
+}
+
+void
+ns_os_changeuser(void) {
+ char strbuf[ISC_STRERRORSIZE];
+ if (runas_pw == NULL || done_setuid)
+ return;
+
+ done_setuid = ISC_TRUE;
+
+#ifdef HAVE_LINUXTHREADS
+#ifdef HAVE_LINUX_CAPABILITY_H
+ if (!non_root_caps)
+ ns_main_earlyfatal("-u with Linux threads not supported: "
+ "requires kernel support for "
+ "prctl(PR_SET_KEEPCAPS)");
+#else
+ ns_main_earlyfatal("-u with Linux threads not supported: "
+ "no capabilities support or capabilities "
+ "disabled at build time");
+#endif
+#endif
+
+ if (setgid(runas_pw->pw_gid) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("setgid(): %s", strbuf);
+ }
+
+ if (setuid(runas_pw->pw_uid) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("setuid(): %s", strbuf);
+ }
+
+#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
+ /*
+ * Restore the ability of named to drop core after the setuid()
+ * call has disabled it.
+ */
+ if (prctl(PR_SET_DUMPABLE,1,0,0,0) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("prctl(PR_SET_DUMPABLE) failed: %s",
+ strbuf);
+ }
+#endif
+#if defined(HAVE_LINUX_CAPABILITY_H) && !defined(HAVE_LINUXTHREADS)
+ linux_minprivs();
+#endif
+}
+
+void
+ns_os_adjustnofile() {
+#ifdef HAVE_LINUXTHREADS
+ isc_result_t result;
+ isc_resourcevalue_t newvalue;
+
+ /*
+ * Linux: max number of open files specified by one thread doesn't seem
+ * to apply to other threads on Linux.
+ */
+ newvalue = ISC_RESOURCE_UNLIMITED;
+
+ result = isc_resource_setlimit(isc_resource_openfiles, newvalue);
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlywarning("couldn't adjust limit on open files");
+#endif
+}
+
+void
+ns_os_minprivs(void) {
+#ifdef HAVE_SYS_PRCTL_H
+ linux_keepcaps();
+#endif
+
+#ifdef HAVE_LINUXTHREADS
+ ns_os_changeuser(); /* Call setuid() before threads are started */
+#endif
+
+#if defined(HAVE_LINUX_CAPABILITY_H) && defined(HAVE_LINUXTHREADS)
+ linux_minprivs();
+#endif
+}
+
+static int
+safe_open(const char *filename, isc_boolean_t append) {
+ int fd;
+ struct stat sb;
+
+ if (stat(filename, &sb) == -1) {
+ if (errno != ENOENT)
+ return (-1);
+ } else if ((sb.st_mode & S_IFREG) == 0) {
+ errno = EOPNOTSUPP;
+ return (-1);
+ }
+
+ if (append)
+ fd = open(filename, O_WRONLY|O_CREAT|O_APPEND,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ else {
+ (void)unlink(filename);
+ fd = open(filename, O_WRONLY|O_CREAT|O_EXCL,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ }
+ return (fd);
+}
+
+static void
+cleanup_pidfile(void) {
+ if (pidfile != NULL) {
+ (void)unlink(pidfile);
+ free(pidfile);
+ }
+ pidfile = NULL;
+}
+
+void
+ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
+ int fd;
+ FILE *lockfile;
+ size_t len;
+ pid_t pid;
+ char strbuf[ISC_STRERRORSIZE];
+ void (*report)(const char *, ...);
+ unsigned int mode;
+ char *slash;
+ int n;
+
+ /*
+ * The caller must ensure any required synchronization.
+ */
+
+ report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
+
+ cleanup_pidfile();
+
+ if (filename == NULL)
+ return;
+
+ len = strlen(filename);
+ pidfile = malloc(len + 1);
+ if (pidfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't malloc '%s': %s", filename, strbuf);
+ return;
+ }
+ /* This is safe. */
+ strcpy(pidfile, filename);
+
+ /*
+ * Make the containing directory if it doesn't exist.
+ */
+ slash = strrchr(pidfile, '/');
+ if (slash != NULL && slash != pidfile) {
+ *slash = '\0';
+ mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */
+ mode |= S_IRGRP | S_IXGRP; /* g=rx */
+ mode |= S_IROTH | S_IXOTH; /* o=rx */
+ n = mkdir(pidfile, mode);
+ if (n == -1 && errno != EEXIST) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't mkdir %s': %s", filename,
+ strbuf);
+ free(pidfile);
+ pidfile = NULL;
+ return;
+ }
+ *slash = '/';
+ }
+
+ fd = safe_open(filename, ISC_FALSE);
+ if (fd < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't open pid file '%s': %s", filename, strbuf);
+ free(pidfile);
+ pidfile = NULL;
+ return;
+ }
+ lockfile = fdopen(fd, "w");
+ if (lockfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("could not fdopen() pid file '%s': %s",
+ filename, strbuf);
+ (void)close(fd);
+ cleanup_pidfile();
+ return;
+ }
+#ifdef HAVE_LINUXTHREADS
+ pid = mainpid;
+#else
+ pid = getpid();
+#endif
+ if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
+ (*report)("fprintf() to pid file '%s' failed", filename);
+ (void)fclose(lockfile);
+ cleanup_pidfile();
+ return;
+ }
+ if (fflush(lockfile) == EOF) {
+ (*report)("fflush() to pid file '%s' failed", filename);
+ (void)fclose(lockfile);
+ cleanup_pidfile();
+ return;
+ }
+ (void)fclose(lockfile);
+}
+
+void
+ns_os_shutdown(void) {
+ closelog();
+ cleanup_pidfile();
+}
+
+isc_result_t
+ns_os_gethostname(char *buf, size_t len) {
+ int n;
+
+ n = gethostname(buf, len);
+ return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
+}
+
+static char *
+next_token(char **stringp, const char *delim) {
+ char *res;
+
+ do {
+ res = strsep(stringp, delim);
+ if (res == NULL)
+ break;
+ } while (*res == '\0');
+ return (res);
+}
+
+void
+ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
+ char *input, *ptr;
+ unsigned int n;
+ pid_t pid;
+
+ input = command;
+
+ /* Skip the command name. */
+ ptr = next_token(&input, " \t");
+ if (ptr == NULL)
+ return;
+
+ ptr = next_token(&input, " \t");
+ if (ptr == NULL)
+ return;
+
+ if (strcmp(ptr, "-p") != 0)
+ return;
+
+#ifdef HAVE_LINUXTHREADS
+ pid = mainpid;
+#else
+ pid = getpid();
+#endif
+
+ n = snprintf((char *)isc_buffer_used(text),
+ isc_buffer_availablelength(text),
+ "pid: %ld", (long)pid);
+ /* Only send a message if it is complete. */
+ if (n < isc_buffer_availablelength(text))
+ isc_buffer_add(text, n);
+}
+
+void
+ns_os_tzset(void) {
+#ifdef HAVE_TZSET
+ tzset();
+#endif
+}
diff --git a/bin/named/update.c b/bin/named/update.c
new file mode 100644
index 0000000..4285e2c
--- /dev/null
+++ b/bin/named/update.c
@@ -0,0 +1,4296 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: update.c,v 1.151 2008/11/19 06:21:45 marka Exp $ */
+
+#include <config.h>
+
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/serial.h>
+#include <isc/string.h>
+#include <isc/taskpool.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/diff.h>
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/fixedname.h>
+#include <dns/journal.h>
+#include <dns/keyvalues.h>
+#include <dns/message.h>
+#include <dns/nsec.h>
+#include <dns/nsec3.h>
+#include <dns/rdataclass.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatastruct.h>
+#include <dns/rdatatype.h>
+#include <dns/soa.h>
+#include <dns/ssu.h>
+#include <dns/stats.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <named/client.h>
+#include <named/log.h>
+#include <named/server.h>
+#include <named/update.h>
+
+/*! \file
+ * \brief
+ * This module implements dynamic update as in RFC2136.
+ */
+
+/*
+ * XXX TODO:
+ * - document strict minimality
+ */
+
+/**************************************************************************/
+
+/*%
+ * Log level for tracing dynamic update protocol requests.
+ */
+#define LOGLEVEL_PROTOCOL ISC_LOG_INFO
+
+/*%
+ * Log level for low-level debug tracing.
+ */
+#define LOGLEVEL_DEBUG ISC_LOG_DEBUG(8)
+
+/*%
+ * Check an operation for failure. These macros all assume that
+ * the function using them has a 'result' variable and a 'failure'
+ * label.
+ */
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/*%
+ * Fail unconditionally with result 'code', which must not
+ * be ISC_R_SUCCESS. The reason for failure presumably has
+ * been logged already.
+ *
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+
+#define FAIL(code) \
+ do { \
+ result = (code); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/*%
+ * Fail unconditionally and log as a client error.
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAILC(code, msg) \
+ do { \
+ const char *_what = "failed"; \
+ result = (code); \
+ switch (result) { \
+ case DNS_R_NXDOMAIN: \
+ case DNS_R_YXDOMAIN: \
+ case DNS_R_YXRRSET: \
+ case DNS_R_NXRRSET: \
+ _what = "unsuccessful"; \
+ } \
+ update_log(client, zone, LOGLEVEL_PROTOCOL, \
+ "update %s: %s (%s)", _what, \
+ msg, isc_result_totext(result)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+#define PREREQFAILC(code, msg) \
+ do { \
+ inc_stats(zone, dns_nsstatscounter_updatebadprereq); \
+ FAILC(code, msg); \
+ } while (0)
+
+#define FAILN(code, name, msg) \
+ do { \
+ const char *_what = "failed"; \
+ result = (code); \
+ switch (result) { \
+ case DNS_R_NXDOMAIN: \
+ case DNS_R_YXDOMAIN: \
+ case DNS_R_YXRRSET: \
+ case DNS_R_NXRRSET: \
+ _what = "unsuccessful"; \
+ } \
+ if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \
+ char _nbuf[DNS_NAME_FORMATSIZE]; \
+ dns_name_format(name, _nbuf, sizeof(_nbuf)); \
+ update_log(client, zone, LOGLEVEL_PROTOCOL, \
+ "update %s: %s: %s (%s)", _what, _nbuf, \
+ msg, isc_result_totext(result)); \
+ } \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+#define PREREQFAILN(code, name, msg) \
+ do { \
+ inc_stats(zone, dns_nsstatscounter_updatebadprereq); \
+ FAILN(code, name, msg); \
+ } while (0)
+
+#define FAILNT(code, name, type, msg) \
+ do { \
+ const char *_what = "failed"; \
+ result = (code); \
+ switch (result) { \
+ case DNS_R_NXDOMAIN: \
+ case DNS_R_YXDOMAIN: \
+ case DNS_R_YXRRSET: \
+ case DNS_R_NXRRSET: \
+ _what = "unsuccessful"; \
+ } \
+ if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) { \
+ char _nbuf[DNS_NAME_FORMATSIZE]; \
+ char _tbuf[DNS_RDATATYPE_FORMATSIZE]; \
+ dns_name_format(name, _nbuf, sizeof(_nbuf)); \
+ dns_rdatatype_format(type, _tbuf, sizeof(_tbuf)); \
+ update_log(client, zone, LOGLEVEL_PROTOCOL, \
+ "update %s: %s/%s: %s (%s)", \
+ _what, _nbuf, _tbuf, msg, \
+ isc_result_totext(result)); \
+ } \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+#define PREREQFAILNT(code, name, type, msg) \
+ do { \
+ inc_stats(zone, dns_nsstatscounter_updatebadprereq); \
+ FAILNT(code, name, type, msg); \
+ } while (0)
+
+/*%
+ * Fail unconditionally and log as a server error.
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAILS(code, msg) \
+ do { \
+ result = (code); \
+ update_log(client, zone, LOGLEVEL_PROTOCOL, \
+ "error: %s: %s", \
+ msg, isc_result_totext(result)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/*
+ * Return TRUE if NS_CLIENTATTR_TCP is set in the attibutes other FALSE.
+ */
+#define TCPCLIENT(client) (((client)->attributes & NS_CLIENTATTR_TCP) != 0)
+
+/**************************************************************************/
+
+typedef struct rr rr_t;
+
+struct rr {
+ /* dns_name_t name; */
+ isc_uint32_t ttl;
+ dns_rdata_t rdata;
+};
+
+typedef struct update_event update_event_t;
+
+struct update_event {
+ ISC_EVENT_COMMON(update_event_t);
+ dns_zone_t *zone;
+ isc_result_t result;
+ dns_message_t *answer;
+};
+
+/**************************************************************************/
+/*
+ * Forward declarations.
+ */
+
+static void update_action(isc_task_t *task, isc_event_t *event);
+static void updatedone_action(isc_task_t *task, isc_event_t *event);
+static isc_result_t send_forward_event(ns_client_t *client, dns_zone_t *zone);
+static void forward_done(isc_task_t *task, isc_event_t *event);
+
+/**************************************************************************/
+
+static void
+update_log(ns_client_t *client, dns_zone_t *zone,
+ int level, const char *fmt, ...) ISC_FORMAT_PRINTF(4, 5);
+
+static void
+update_log(ns_client_t *client, dns_zone_t *zone,
+ int level, const char *fmt, ...)
+{
+ va_list ap;
+ char message[4096];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+
+ if (client == NULL || zone == NULL)
+ return;
+
+ if (isc_log_wouldlog(ns_g_lctx, level) == ISC_FALSE)
+ return;
+
+ dns_name_format(dns_zone_getorigin(zone), namebuf,
+ sizeof(namebuf));
+ dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
+ sizeof(classbuf));
+
+ va_start(ap, fmt);
+ vsnprintf(message, sizeof(message), fmt, ap);
+ va_end(ap);
+
+ ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
+ level, "updating zone '%s/%s': %s",
+ namebuf, classbuf, message);
+}
+
+/*%
+ * Increment updated-related statistics counters.
+ */
+static inline void
+inc_stats(dns_zone_t *zone, dns_statscounter_t counter) {
+ dns_generalstats_increment(ns_g_server->nsstats, counter);
+
+ if (zone != NULL) {
+ dns_stats_t *zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL)
+ dns_generalstats_increment(zonestats, counter);
+ }
+}
+
+/*%
+ * Override the default acl logging when checking whether a client
+ * can update the zone or whether we can forward the request to the
+ * master based on IP address.
+ *
+ * 'message' contains the type of operation that is being attempted.
+ * 'slave' indicates if this is a slave zone. If 'acl' is NULL then
+ * log at debug=3.
+ * If the zone has no access controls configured ('acl' == NULL &&
+ * 'has_ssutable == ISC_FALS) log the attempt at info, otherwise
+ * at error.
+ *
+ * If the request was signed log that we received it.
+ */
+static isc_result_t
+checkupdateacl(ns_client_t *client, dns_acl_t *acl, const char *message,
+ dns_name_t *zonename, isc_boolean_t slave,
+ isc_boolean_t has_ssutable)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+ int level = ISC_LOG_ERROR;
+ const char *msg = "denied";
+ isc_result_t result;
+
+ if (slave && acl == NULL) {
+ result = DNS_R_NOTIMP;
+ level = ISC_LOG_DEBUG(3);
+ msg = "disabled";
+ } else {
+ result = ns_client_checkaclsilent(client, NULL, acl, ISC_FALSE);
+ if (result == ISC_R_SUCCESS) {
+ level = ISC_LOG_DEBUG(3);
+ msg = "approved";
+ } else if (acl == NULL && !has_ssutable) {
+ level = ISC_LOG_INFO;
+ }
+ }
+
+ if (client->signer != NULL) {
+ dns_name_format(client->signer, namebuf, sizeof(namebuf));
+ ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
+ NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
+ "signer \"%s\" %s", namebuf, msg);
+ }
+
+ dns_name_format(zonename, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(client->view->rdclass, classbuf,
+ sizeof(classbuf));
+
+ ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
+ NS_LOGMODULE_UPDATE, level, "%s '%s/%s' %s",
+ message, namebuf, classbuf, msg);
+ return (result);
+}
+
+/*%
+ * Update a single RR in version 'ver' of 'db' and log the
+ * update in 'diff'.
+ *
+ * Ensures:
+ * \li '*tuple' == NULL. Either the tuple is freed, or its
+ * ownership has been transferred to the diff.
+ */
+static isc_result_t
+do_one_tuple(dns_difftuple_t **tuple, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ dns_diff_t temp_diff;
+ isc_result_t result;
+
+ /*
+ * Create a singleton diff.
+ */
+ dns_diff_init(diff->mctx, &temp_diff);
+ temp_diff.resign = diff->resign;
+ ISC_LIST_APPEND(temp_diff.tuples, *tuple, link);
+
+ /*
+ * Apply it to the database.
+ */
+ result = dns_diff_apply(&temp_diff, db, ver);
+ ISC_LIST_UNLINK(temp_diff.tuples, *tuple, link);
+ if (result != ISC_R_SUCCESS) {
+ dns_difftuple_free(tuple);
+ return (result);
+ }
+
+ /*
+ * Merge it into the current pending journal entry.
+ */
+ dns_diff_appendminimal(diff, tuple);
+
+ /*
+ * Do not clear temp_diff.
+ */
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Perform the updates in 'updates' in version 'ver' of 'db' and log the
+ * update in 'diff'.
+ *
+ * Ensures:
+ * \li 'updates' is empty.
+ */
+static isc_result_t
+do_diff(dns_diff_t *updates, dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ while (! ISC_LIST_EMPTY(updates->tuples)) {
+ dns_difftuple_t *t = ISC_LIST_HEAD(updates->tuples);
+ ISC_LIST_UNLINK(updates->tuples, t, link);
+ CHECK(do_one_tuple(&t, db, ver, diff));
+ }
+ return (ISC_R_SUCCESS);
+
+ failure:
+ dns_diff_clear(diff);
+ return (result);
+}
+
+static isc_result_t
+update_one_rr(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
+ dns_diffop_t op, dns_name_t *name, dns_ttl_t ttl,
+ dns_rdata_t *rdata)
+{
+ dns_difftuple_t *tuple = NULL;
+ isc_result_t result;
+ result = dns_difftuple_create(diff->mctx, op,
+ name, ttl, rdata, &tuple);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ return (do_one_tuple(&tuple, db, ver, diff));
+}
+
+/**************************************************************************/
+/*
+ * Callback-style iteration over rdatasets and rdatas.
+ *
+ * foreach_rrset() can be used to iterate over the RRsets
+ * of a name and call a callback function with each
+ * one. Similarly, foreach_rr() can be used to iterate
+ * over the individual RRs at name, optionally restricted
+ * to RRs of a given type.
+ *
+ * The callback functions are called "actions" and take
+ * two arguments: a void pointer for passing arbitrary
+ * context information, and a pointer to the current RRset
+ * or RR. By convention, their names end in "_action".
+ */
+
+/*
+ * XXXRTH We might want to make this public somewhere in libdns.
+ */
+
+/*%
+ * Function type for foreach_rrset() iterator actions.
+ */
+typedef isc_result_t rrset_func(void *data, dns_rdataset_t *rrset);
+
+/*%
+ * Function type for foreach_rr() iterator actions.
+ */
+typedef isc_result_t rr_func(void *data, rr_t *rr);
+
+/*%
+ * Internal context struct for foreach_node_rr().
+ */
+typedef struct {
+ rr_func * rr_action;
+ void * rr_action_data;
+} foreach_node_rr_ctx_t;
+
+/*%
+ * Internal helper function for foreach_node_rr().
+ */
+static isc_result_t
+foreach_node_rr_action(void *data, dns_rdataset_t *rdataset) {
+ isc_result_t result;
+ foreach_node_rr_ctx_t *ctx = data;
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset))
+ {
+ rr_t rr = { 0, DNS_RDATA_INIT };
+
+ dns_rdataset_current(rdataset, &rr.rdata);
+ rr.ttl = rdataset->ttl;
+ result = (*ctx->rr_action)(ctx->rr_action_data, &rr);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ if (result != ISC_R_NOMORE)
+ return (result);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * For each rdataset of 'name' in 'ver' of 'db', call 'action'
+ * with the rdataset and 'action_data' as arguments. If the name
+ * does not exist, do nothing.
+ *
+ * If 'action' returns an error, abort iteration and return the error.
+ */
+static isc_result_t
+foreach_rrset(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ rrset_func *action, void *action_data)
+{
+ isc_result_t result;
+ dns_dbnode_t *node;
+ dns_rdatasetiter_t *iter;
+
+ node = NULL;
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ iter = NULL;
+ result = dns_db_allrdatasets(db, node, ver,
+ (isc_stdtime_t) 0, &iter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node;
+
+ for (result = dns_rdatasetiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iter))
+ {
+ dns_rdataset_t rdataset;
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatasetiter_current(iter, &rdataset);
+
+ result = (*action)(action_data, &rdataset);
+
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iterator;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ cleanup_iterator:
+ dns_rdatasetiter_destroy(&iter);
+
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/*%
+ * For each RR of 'name' in 'ver' of 'db', call 'action'
+ * with the RR and 'action_data' as arguments. If the name
+ * does not exist, do nothing.
+ *
+ * If 'action' returns an error, abort iteration
+ * and return the error.
+ */
+static isc_result_t
+foreach_node_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ rr_func *rr_action, void *rr_action_data)
+{
+ foreach_node_rr_ctx_t ctx;
+ ctx.rr_action = rr_action;
+ ctx.rr_action_data = rr_action_data;
+ return (foreach_rrset(db, ver, name,
+ foreach_node_rr_action, &ctx));
+}
+
+
+/*%
+ * For each of the RRs specified by 'db', 'ver', 'name', 'type',
+ * (which can be dns_rdatatype_any to match any type), and 'covers', call
+ * 'action' with the RR and 'action_data' as arguments. If the name
+ * does not exist, or if no RRset of the given type exists at the name,
+ * do nothing.
+ *
+ * If 'action' returns an error, abort iteration and return the error.
+ */
+static isc_result_t
+foreach_rr(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, dns_rdatatype_t covers, rr_func *rr_action,
+ void *rr_action_data)
+{
+
+ isc_result_t result;
+ dns_dbnode_t *node;
+ dns_rdataset_t rdataset;
+
+ if (type == dns_rdatatype_any)
+ return (foreach_node_rr(db, ver, name,
+ rr_action, rr_action_data));
+
+ node = NULL;
+ if (type == dns_rdatatype_nsec3 ||
+ (type == dns_rdatatype_rrsig && covers == dns_rdatatype_nsec3))
+ result = dns_db_findnsec3node(db, name, ISC_FALSE, &node);
+ else
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, ver, type, covers,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ goto cleanup_node;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ rr_t rr = { 0, DNS_RDATA_INIT };
+ dns_rdataset_current(&rdataset, &rr.rdata);
+ rr.ttl = rdataset.ttl;
+ result = (*rr_action)(rr_action_data, &rr);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_rdataset;
+ }
+ if (result != ISC_R_NOMORE)
+ goto cleanup_rdataset;
+ result = ISC_R_SUCCESS;
+
+ cleanup_rdataset:
+ dns_rdataset_disassociate(&rdataset);
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/**************************************************************************/
+/*
+ * Various tests on the database contents (for prerequisites, etc).
+ */
+
+/*%
+ * Function type for predicate functions that compare a database RR 'db_rr'
+ * against an update RR 'update_rr'.
+ */
+typedef isc_boolean_t rr_predicate(dns_rdata_t *update_rr, dns_rdata_t *db_rr);
+
+/*%
+ * Helper function for rrset_exists().
+ */
+static isc_result_t
+rrset_exists_action(void *data, rr_t *rr) {
+ UNUSED(data);
+ UNUSED(rr);
+ return (ISC_R_EXISTS);
+}
+
+/*%
+ * Utility macro for RR existence checking functions.
+ *
+ * If the variable 'result' has the value ISC_R_EXISTS or
+ * ISC_R_SUCCESS, set *exists to ISC_TRUE or ISC_FALSE,
+ * respectively, and return success.
+ *
+ * If 'result' has any other value, there was a failure.
+ * Return the failure result code and do not set *exists.
+ *
+ * This would be more readable as "do { if ... } while(0)",
+ * but that form generates tons of warnings on Solaris 2.6.
+ */
+#define RETURN_EXISTENCE_FLAG \
+ return ((result == ISC_R_EXISTS) ? \
+ (*exists = ISC_TRUE, ISC_R_SUCCESS) : \
+ ((result == ISC_R_SUCCESS) ? \
+ (*exists = ISC_FALSE, ISC_R_SUCCESS) : \
+ result))
+
+/*%
+ * Set '*exists' to true iff an rrset of the given type exists,
+ * to false otherwise.
+ */
+static isc_result_t
+rrset_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, dns_rdatatype_t covers,
+ isc_boolean_t *exists)
+{
+ isc_result_t result;
+ result = foreach_rr(db, ver, name, type, covers,
+ rrset_exists_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*%
+ * Set '*visible' to true if the RRset exists and is part of the
+ * visible zone. Otherwise '*visible' is set to false unless a
+ * error occurs.
+ */
+static isc_result_t
+rrset_visible(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, isc_boolean_t *visible)
+{
+ isc_result_t result;
+ dns_fixedname_t fixed;
+
+ dns_fixedname_init(&fixed);
+ result = dns_db_find(db, name, ver, type, DNS_DBFIND_NOWILD,
+ (isc_stdtime_t) 0, NULL,
+ dns_fixedname_name(&fixed), NULL, NULL);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ *visible = ISC_TRUE;
+ break;
+ /*
+ * Glue, obsured, deleted or replaced records.
+ */
+ case DNS_R_DELEGATION:
+ case DNS_R_DNAME:
+ case DNS_R_CNAME:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NXRRSET:
+ case DNS_R_EMPTYNAME:
+ case DNS_R_COVERINGNSEC:
+ *visible = ISC_FALSE;
+ result = ISC_R_SUCCESS;
+ break;
+ default:
+ break;
+ }
+ return (result);
+}
+
+/*%
+ * Helper function for cname_incompatible_rrset_exists.
+ */
+static isc_result_t
+cname_compatibility_action(void *data, dns_rdataset_t *rrset) {
+ UNUSED(data);
+ if (rrset->type != dns_rdatatype_cname &&
+ ! dns_rdatatype_isdnssec(rrset->type))
+ return (ISC_R_EXISTS);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Check whether there is an rrset incompatible with adding a CNAME RR,
+ * i.e., anything but another CNAME (which can be replaced) or a
+ * DNSSEC RR (which can coexist).
+ *
+ * If such an incompatible rrset exists, set '*exists' to ISC_TRUE.
+ * Otherwise, set it to ISC_FALSE.
+ */
+static isc_result_t
+cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
+ dns_name_t *name, isc_boolean_t *exists) {
+ isc_result_t result;
+ result = foreach_rrset(db, ver, name,
+ cname_compatibility_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*%
+ * Helper function for rr_count().
+ */
+static isc_result_t
+count_rr_action(void *data, rr_t *rr) {
+ int *countp = data;
+ UNUSED(rr);
+ (*countp)++;
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Count the number of RRs of 'type' belonging to 'name' in 'ver' of 'db'.
+ */
+static isc_result_t
+rr_count(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_rdatatype_t type, dns_rdatatype_t covers, int *countp)
+{
+ *countp = 0;
+ return (foreach_rr(db, ver, name, type, covers,
+ count_rr_action, countp));
+}
+
+/*%
+ * Context struct and helper function for name_exists().
+ */
+
+static isc_result_t
+name_exists_action(void *data, dns_rdataset_t *rrset) {
+ UNUSED(data);
+ UNUSED(rrset);
+ return (ISC_R_EXISTS);
+}
+
+/*%
+ * Set '*exists' to true iff the given name exists, to false otherwise.
+ */
+static isc_result_t
+name_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ isc_boolean_t *exists)
+{
+ isc_result_t result;
+ result = foreach_rrset(db, ver, name,
+ name_exists_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*
+ * 'ssu_check_t' is used to pass the arguements to
+ * dns_ssutable_checkrules() to the callback function
+ * ssu_checkrule().
+ */
+typedef struct {
+ /* The ownername of the record to be updated. */
+ dns_name_t *name;
+
+ /* The signature's name if the request was signed. */
+ dns_name_t *signer;
+
+ /* The address of the client if the request was received via TCP. */
+ isc_netaddr_t *tcpaddr;
+
+ /* The ssu table to check against. */
+ dns_ssutable_t *table;
+} ssu_check_t;
+
+static isc_result_t
+ssu_checkrule(void *data, dns_rdataset_t *rrset) {
+ ssu_check_t *ssuinfo = data;
+ isc_boolean_t result;
+
+ /*
+ * If we're deleting all records, it's ok to delete RRSIG and NSEC even
+ * if we're normally not allowed to.
+ */
+ if (rrset->type == dns_rdatatype_rrsig ||
+ rrset->type == dns_rdatatype_nsec)
+ return (ISC_R_SUCCESS);
+ result = dns_ssutable_checkrules(ssuinfo->table, ssuinfo->signer,
+ ssuinfo->name, ssuinfo->tcpaddr,
+ rrset->type);
+ return (result == ISC_TRUE ? ISC_R_SUCCESS : ISC_R_FAILURE);
+}
+
+static isc_boolean_t
+ssu_checkall(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_ssutable_t *ssutable, dns_name_t *signer,
+ isc_netaddr_t *tcpaddr)
+{
+ isc_result_t result;
+ ssu_check_t ssuinfo;
+
+ ssuinfo.name = name;
+ ssuinfo.table = ssutable;
+ ssuinfo.signer = signer;
+ ssuinfo.tcpaddr = tcpaddr;
+ result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo);
+ return (ISC_TF(result == ISC_R_SUCCESS));
+}
+
+/**************************************************************************/
+/*
+ * Checking of "RRset exists (value dependent)" prerequisites.
+ *
+ * In the RFC2136 section 3.2.5, this is the pseudocode involving
+ * a variable called "temp", a mapping of <name, type> tuples to rrsets.
+ *
+ * Here, we represent the "temp" data structure as (non-minimial) "dns_diff_t"
+ * where each typle has op==DNS_DIFFOP_EXISTS.
+ */
+
+
+/*%
+ * Append a tuple asserting the existence of the RR with
+ * 'name' and 'rdata' to 'diff'.
+ */
+static isc_result_t
+temp_append(dns_diff_t *diff, dns_name_t *name, dns_rdata_t *rdata) {
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+
+ REQUIRE(DNS_DIFF_VALID(diff));
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_EXISTS,
+ name, 0, rdata, &tuple));
+ ISC_LIST_APPEND(diff->tuples, tuple, link);
+ failure:
+ return (result);
+}
+
+/*%
+ * Compare two rdatasets represented as sorted lists of tuples.
+ * All list elements must have the same owner name and type.
+ * Return ISC_R_SUCCESS if the rdatasets are equal, rcode(dns_rcode_nxrrset)
+ * if not.
+ */
+static isc_result_t
+temp_check_rrset(dns_difftuple_t *a, dns_difftuple_t *b) {
+ for (;;) {
+ if (a == NULL || b == NULL)
+ break;
+ INSIST(a->op == DNS_DIFFOP_EXISTS &&
+ b->op == DNS_DIFFOP_EXISTS);
+ INSIST(a->rdata.type == b->rdata.type);
+ INSIST(dns_name_equal(&a->name, &b->name));
+ if (dns_rdata_compare(&a->rdata, &b->rdata) != 0)
+ return (DNS_R_NXRRSET);
+ a = ISC_LIST_NEXT(a, link);
+ b = ISC_LIST_NEXT(b, link);
+ }
+ if (a != NULL || b != NULL)
+ return (DNS_R_NXRRSET);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * A comparison function defining the sorting order for the entries
+ * in the "temp" data structure. The major sort key is the owner name,
+ * followed by the type and rdata.
+ */
+static int
+temp_order(const void *av, const void *bv) {
+ dns_difftuple_t const * const *ap = av;
+ dns_difftuple_t const * const *bp = bv;
+ dns_difftuple_t const *a = *ap;
+ dns_difftuple_t const *b = *bp;
+ int r;
+ r = dns_name_compare(&a->name, &b->name);
+ if (r != 0)
+ return (r);
+ r = (b->rdata.type - a->rdata.type);
+ if (r != 0)
+ return (r);
+ r = dns_rdata_compare(&a->rdata, &b->rdata);
+ return (r);
+}
+
+/*%
+ * Check the "RRset exists (value dependent)" prerequisite information
+ * in 'temp' against the contents of the database 'db'.
+ *
+ * Return ISC_R_SUCCESS if the prerequisites are satisfied,
+ * rcode(dns_rcode_nxrrset) if not.
+ *
+ * 'temp' must be pre-sorted.
+ */
+
+static isc_result_t
+temp_check(isc_mem_t *mctx, dns_diff_t *temp, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *tmpname, dns_rdatatype_t *typep)
+{
+ isc_result_t result;
+ dns_name_t *name;
+ dns_dbnode_t *node;
+ dns_difftuple_t *t;
+ dns_diff_t trash;
+
+ dns_diff_init(mctx, &trash);
+
+ /*
+ * For each name and type in the prerequisites,
+ * construct a sorted rdata list of the corresponding
+ * database contents, and compare the lists.
+ */
+ t = ISC_LIST_HEAD(temp->tuples);
+ while (t != NULL) {
+ name = &t->name;
+ (void)dns_name_copy(name, tmpname, NULL);
+ *typep = t->rdata.type;
+
+ /* A new unique name begins here. */
+ node = NULL;
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND) {
+ dns_diff_clear(&trash);
+ return (DNS_R_NXRRSET);
+ }
+ if (result != ISC_R_SUCCESS) {
+ dns_diff_clear(&trash);
+ return (result);
+ }
+
+ /* A new unique type begins here. */
+ while (t != NULL && dns_name_equal(&t->name, name)) {
+ dns_rdatatype_t type, covers;
+ dns_rdataset_t rdataset;
+ dns_diff_t d_rrs; /* Database RRs with
+ this name and type */
+ dns_diff_t u_rrs; /* Update RRs with
+ this name and type */
+
+ *typep = type = t->rdata.type;
+ if (type == dns_rdatatype_rrsig ||
+ type == dns_rdatatype_sig)
+ covers = dns_rdata_covers(&t->rdata);
+ else
+ covers = 0;
+
+ /*
+ * Collect all database RRs for this name and type
+ * onto d_rrs and sort them.
+ */
+ dns_rdataset_init(&rdataset);
+ result = dns_db_findrdataset(db, node, ver, type,
+ covers, (isc_stdtime_t) 0,
+ &rdataset, NULL);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_detachnode(db, &node);
+ dns_diff_clear(&trash);
+ return (DNS_R_NXRRSET);
+ }
+
+ dns_diff_init(mctx, &d_rrs);
+ dns_diff_init(mctx, &u_rrs);
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset))
+ {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &rdata);
+ result = temp_append(&d_rrs, name, &rdata);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ result = dns_diff_sort(&d_rrs, temp_order);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * Collect all update RRs for this name and type
+ * onto u_rrs. No need to sort them here -
+ * they are already sorted.
+ */
+ while (t != NULL &&
+ dns_name_equal(&t->name, name) &&
+ t->rdata.type == type)
+ {
+ dns_difftuple_t *next =
+ ISC_LIST_NEXT(t, link);
+ ISC_LIST_UNLINK(temp->tuples, t, link);
+ ISC_LIST_APPEND(u_rrs.tuples, t, link);
+ t = next;
+ }
+
+ /* Compare the two sorted lists. */
+ result = temp_check_rrset(ISC_LIST_HEAD(u_rrs.tuples),
+ ISC_LIST_HEAD(d_rrs.tuples));
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ /*
+ * We are done with the tuples, but we can't free
+ * them yet because "name" still points into one
+ * of them. Move them on a temporary list.
+ */
+ ISC_LIST_APPENDLIST(trash.tuples, u_rrs.tuples, link);
+ ISC_LIST_APPENDLIST(trash.tuples, d_rrs.tuples, link);
+ dns_rdataset_disassociate(&rdataset);
+
+ continue;
+
+ failure:
+ dns_diff_clear(&d_rrs);
+ dns_diff_clear(&u_rrs);
+ dns_diff_clear(&trash);
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (result);
+ }
+
+ dns_db_detachnode(db, &node);
+ }
+
+ dns_diff_clear(&trash);
+ return (ISC_R_SUCCESS);
+}
+
+/**************************************************************************/
+/*
+ * Conditional deletion of RRs.
+ */
+
+/*%
+ * Context structure for delete_if().
+ */
+
+typedef struct {
+ rr_predicate *predicate;
+ dns_db_t *db;
+ dns_dbversion_t *ver;
+ dns_diff_t *diff;
+ dns_name_t *name;
+ dns_rdata_t *update_rr;
+} conditional_delete_ctx_t;
+
+/*%
+ * Predicate functions for delete_if().
+ */
+
+/*%
+ * Return true iff 'db_rr' is neither a SOA nor an NS RR nor
+ * an RRSIG nor an NSEC3PARAM nor a NSEC.
+ */
+static isc_boolean_t
+type_not_soa_nor_ns_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ return ((db_rr->type != dns_rdatatype_soa &&
+ db_rr->type != dns_rdatatype_ns &&
+ db_rr->type != dns_rdatatype_nsec3param &&
+ db_rr->type != dns_rdatatype_rrsig &&
+ db_rr->type != dns_rdatatype_nsec) ?
+ ISC_TRUE : ISC_FALSE);
+}
+
+/*%
+ * Return true iff 'db_rr' is neither a RRSIG nor a NSEC.
+ */
+static isc_boolean_t
+type_not_dnssec(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ return ((db_rr->type != dns_rdatatype_rrsig &&
+ db_rr->type != dns_rdatatype_nsec) ?
+ ISC_TRUE : ISC_FALSE);
+}
+
+/*%
+ * Return true always.
+ */
+static isc_boolean_t
+true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ UNUSED(db_rr);
+ return (ISC_TRUE);
+}
+
+/*%
+ * Return true if the record is a RRSIG.
+ */
+static isc_boolean_t
+rrsig_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ return ((db_rr->type == dns_rdatatype_rrsig) ?
+ ISC_TRUE : ISC_FALSE);
+}
+
+/*%
+ * Return true iff the two RRs have identical rdata.
+ */
+static isc_boolean_t
+rr_equal_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ /*
+ * XXXRTH This is not a problem, but we should consider creating
+ * dns_rdata_equal() (that used dns_name_equal()), since it
+ * would be faster. Not a priority.
+ */
+ return (dns_rdata_compare(update_rr, db_rr) == 0 ?
+ ISC_TRUE : ISC_FALSE);
+}
+
+/*%
+ * Return true iff 'update_rr' should replace 'db_rr' according
+ * to the special RFC2136 rules for CNAME, SOA, and WKS records.
+ *
+ * RFC2136 does not mention NSEC or DNAME, but multiple NSECs or DNAMEs
+ * make little sense, so we replace those, too.
+ *
+ * Additionally replace RRSIG that have been generated by the same key
+ * for the same type. This simplifies refreshing a offline KSK by not
+ * requiring that the old RRSIG be deleted. It also simpifies key
+ * rollover by only requiring that the new RRSIG be added.
+ */
+static isc_boolean_t
+replaces_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ dns_rdata_rrsig_t updatesig, dbsig;
+ isc_result_t result;
+
+ if (db_rr->type != update_rr->type)
+ return (ISC_FALSE);
+ if (db_rr->type == dns_rdatatype_cname)
+ return (ISC_TRUE);
+ if (db_rr->type == dns_rdatatype_dname)
+ return (ISC_TRUE);
+ if (db_rr->type == dns_rdatatype_soa)
+ return (ISC_TRUE);
+ if (db_rr->type == dns_rdatatype_nsec)
+ return (ISC_TRUE);
+ if (db_rr->type == dns_rdatatype_rrsig) {
+ /*
+ * Replace existing RRSIG with the same keyid,
+ * covered and algorithm.
+ */
+ result = dns_rdata_tostruct(db_rr, &dbsig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = dns_rdata_tostruct(update_rr, &updatesig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (dbsig.keyid == updatesig.keyid &&
+ dbsig.covered == updatesig.covered &&
+ dbsig.algorithm == updatesig.algorithm)
+ return (ISC_TRUE);
+ }
+ if (db_rr->type == dns_rdatatype_wks) {
+ /*
+ * Compare the address and protocol fields only. These
+ * form the first five bytes of the RR data. Do a
+ * raw binary comparison; unpacking the WKS RRs using
+ * dns_rdata_tostruct() might be cleaner in some ways.
+ */
+ INSIST(db_rr->length >= 5 && update_rr->length >= 5);
+ return (memcmp(db_rr->data, update_rr->data, 5) == 0 ?
+ ISC_TRUE : ISC_FALSE);
+ }
+
+ if (db_rr->type == dns_rdatatype_nsec3param) {
+ if (db_rr->length != update_rr->length)
+ return (ISC_FALSE);
+ INSIST(db_rr->length >= 4 && update_rr->length >= 4);
+ /*
+ * Replace records added in this UPDATE request.
+ */
+ if (db_rr->data[0] == update_rr->data[0] &&
+ db_rr->data[1] & DNS_NSEC3FLAG_UPDATE &&
+ update_rr->data[1] & DNS_NSEC3FLAG_UPDATE &&
+ memcmp(db_rr->data+2, update_rr->data+2,
+ update_rr->length - 2) == 0)
+ return (ISC_TRUE);
+ }
+ return (ISC_FALSE);
+}
+
+/*%
+ * Internal helper function for delete_if().
+ */
+static isc_result_t
+delete_if_action(void *data, rr_t *rr) {
+ conditional_delete_ctx_t *ctx = data;
+ if ((*ctx->predicate)(ctx->update_rr, &rr->rdata)) {
+ isc_result_t result;
+ result = update_one_rr(ctx->db, ctx->ver, ctx->diff,
+ DNS_DIFFOP_DEL, ctx->name,
+ rr->ttl, &rr->rdata);
+ return (result);
+ } else {
+ return (ISC_R_SUCCESS);
+ }
+}
+
+/*%
+ * Conditionally delete RRs. Apply 'predicate' to the RRs
+ * specified by 'db', 'ver', 'name', and 'type' (which can
+ * be dns_rdatatype_any to match any type). Delete those
+ * RRs for which the predicate returns true, and log the
+ * deletions in 'diff'.
+ */
+static isc_result_t
+delete_if(rr_predicate *predicate, dns_db_t *db, dns_dbversion_t *ver,
+ dns_name_t *name, dns_rdatatype_t type, dns_rdatatype_t covers,
+ dns_rdata_t *update_rr, dns_diff_t *diff)
+{
+ conditional_delete_ctx_t ctx;
+ ctx.predicate = predicate;
+ ctx.db = db;
+ ctx.ver = ver;
+ ctx.diff = diff;
+ ctx.name = name;
+ ctx.update_rr = update_rr;
+ return (foreach_rr(db, ver, name, type, covers,
+ delete_if_action, &ctx));
+}
+
+/**************************************************************************/
+/*%
+ * Prepare an RR for the addition of the new RR 'ctx->update_rr',
+ * with TTL 'ctx->update_rr_ttl', to its rdataset, by deleting
+ * the RRs if it is replaced by the new RR or has a conflicting TTL.
+ * The necessary changes are appended to ctx->del_diff and ctx->add_diff;
+ * we need to do all deletions before any additions so that we don't run
+ * into transient states with conflicting TTLs.
+ */
+
+typedef struct {
+ dns_db_t *db;
+ dns_dbversion_t *ver;
+ dns_diff_t *diff;
+ dns_name_t *name;
+ dns_rdata_t *update_rr;
+ dns_ttl_t update_rr_ttl;
+ isc_boolean_t ignore_add;
+ dns_diff_t del_diff;
+ dns_diff_t add_diff;
+} add_rr_prepare_ctx_t;
+
+static isc_result_t
+add_rr_prepare_action(void *data, rr_t *rr) {
+ isc_result_t result = ISC_R_SUCCESS;
+ add_rr_prepare_ctx_t *ctx = data;
+ dns_difftuple_t *tuple = NULL;
+ isc_boolean_t equal;
+
+ /*
+ * If the update RR is a "duplicate" of the update RR,
+ * the update should be silently ignored.
+ */
+ equal = ISC_TF(dns_rdata_compare(&rr->rdata, ctx->update_rr) == 0);
+ if (equal && rr->ttl == ctx->update_rr_ttl) {
+ ctx->ignore_add = ISC_TRUE;
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * If this RR is "equal" to the update RR, it should
+ * be deleted before the update RR is added.
+ */
+ if (replaces_p(ctx->update_rr, &rr->rdata)) {
+ CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL,
+ ctx->name, rr->ttl, &rr->rdata,
+ &tuple));
+ dns_diff_append(&ctx->del_diff, &tuple);
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * If this RR differs in TTL from the update RR,
+ * its TTL must be adjusted.
+ */
+ if (rr->ttl != ctx->update_rr_ttl) {
+ CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL,
+ ctx->name, rr->ttl, &rr->rdata,
+ &tuple));
+ dns_diff_append(&ctx->del_diff, &tuple);
+ if (!equal) {
+ CHECK(dns_difftuple_create(ctx->add_diff.mctx,
+ DNS_DIFFOP_ADD, ctx->name,
+ ctx->update_rr_ttl,
+ &rr->rdata, &tuple));
+ dns_diff_append(&ctx->add_diff, &tuple);
+ }
+ }
+ failure:
+ return (result);
+}
+
+/**************************************************************************/
+/*
+ * Miscellaneous subroutines.
+ */
+
+/*%
+ * Extract a single update RR from 'section' of dynamic update message
+ * 'msg', with consistency checking.
+ *
+ * Stores the owner name, rdata, and TTL of the update RR at 'name',
+ * 'rdata', and 'ttl', respectively.
+ */
+static void
+get_current_rr(dns_message_t *msg, dns_section_t section,
+ dns_rdataclass_t zoneclass, dns_name_t **name,
+ dns_rdata_t *rdata, dns_rdatatype_t *covers,
+ dns_ttl_t *ttl, dns_rdataclass_t *update_class)
+{
+ dns_rdataset_t *rdataset;
+ isc_result_t result;
+ dns_message_currentname(msg, section, name);
+ rdataset = ISC_LIST_HEAD((*name)->list);
+ INSIST(rdataset != NULL);
+ INSIST(ISC_LIST_NEXT(rdataset, link) == NULL);
+ *covers = rdataset->covers;
+ *ttl = rdataset->ttl;
+ result = dns_rdataset_first(rdataset);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, rdata);
+ INSIST(dns_rdataset_next(rdataset) == ISC_R_NOMORE);
+ *update_class = rdata->rdclass;
+ rdata->rdclass = zoneclass;
+}
+
+/*%
+ * Increment the SOA serial number of database 'db', version 'ver'.
+ * Replace the SOA record in the database, and log the
+ * change in 'diff'.
+ */
+
+ /*
+ * XXXRTH Failures in this routine will be worth logging, when
+ * we have a logging system. Failure to find the zonename
+ * or the SOA rdataset warrant at least an UNEXPECTED_ERROR().
+ */
+
+static isc_result_t
+increment_soa_serial(dns_db_t *db, dns_dbversion_t *ver,
+ dns_diff_t *diff, isc_mem_t *mctx)
+{
+ dns_difftuple_t *deltuple = NULL;
+ dns_difftuple_t *addtuple = NULL;
+ isc_uint32_t serial;
+ isc_result_t result;
+
+ CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_DEL, &deltuple));
+ CHECK(dns_difftuple_copy(deltuple, &addtuple));
+ addtuple->op = DNS_DIFFOP_ADD;
+
+ serial = dns_soa_getserial(&addtuple->rdata);
+
+ /* RFC1982 */
+ serial = (serial + 1) & 0xFFFFFFFF;
+ if (serial == 0)
+ serial = 1;
+
+ dns_soa_setserial(serial, &addtuple->rdata);
+ CHECK(do_one_tuple(&deltuple, db, ver, diff));
+ CHECK(do_one_tuple(&addtuple, db, ver, diff));
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (addtuple != NULL)
+ dns_difftuple_free(&addtuple);
+ if (deltuple != NULL)
+ dns_difftuple_free(&deltuple);
+ return (result);
+}
+
+/*%
+ * Check that the new SOA record at 'update_rdata' does not
+ * illegally cause the SOA serial number to decrease or stay
+ * unchanged relative to the existing SOA in 'db'.
+ *
+ * Sets '*ok' to ISC_TRUE if the update is legal, ISC_FALSE if not.
+ *
+ * William King points out that RFC2136 is inconsistent about
+ * the case where the serial number stays unchanged:
+ *
+ * section 3.4.2.2 requires a server to ignore a SOA update request
+ * if the serial number on the update SOA is less_than_or_equal to
+ * the zone SOA serial.
+ *
+ * section 3.6 requires a server to ignore a SOA update request if
+ * the serial is less_than the zone SOA serial.
+ *
+ * Paul says 3.4.2.2 is correct.
+ *
+ */
+static isc_result_t
+check_soa_increment(dns_db_t *db, dns_dbversion_t *ver,
+ dns_rdata_t *update_rdata, isc_boolean_t *ok)
+{
+ isc_uint32_t db_serial;
+ isc_uint32_t update_serial;
+ isc_result_t result;
+
+ update_serial = dns_soa_getserial(update_rdata);
+
+ result = dns_db_getsoaserial(db, ver, &db_serial);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ if (DNS_SERIAL_GE(db_serial, update_serial)) {
+ *ok = ISC_FALSE;
+ } else {
+ *ok = ISC_TRUE;
+ }
+
+ return (ISC_R_SUCCESS);
+
+}
+
+/**************************************************************************/
+/*
+ * Incremental updating of NSECs and RRSIGs.
+ */
+
+#define MAXZONEKEYS 32 /*%< Maximum number of zone keys supported. */
+
+/*%
+ * We abuse the dns_diff_t type to represent a set of domain names
+ * affected by the update.
+ */
+static isc_result_t
+namelist_append_name(dns_diff_t *list, dns_name_t *name) {
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+ static dns_rdata_t dummy_rdata = DNS_RDATA_INIT;
+
+ CHECK(dns_difftuple_create(list->mctx, DNS_DIFFOP_EXISTS, name, 0,
+ &dummy_rdata, &tuple));
+ dns_diff_append(list, &tuple);
+ failure:
+ return (result);
+}
+
+static isc_result_t
+namelist_append_subdomain(dns_db_t *db, dns_name_t *name, dns_diff_t *affected)
+{
+ isc_result_t result;
+ dns_fixedname_t fixedname;
+ dns_name_t *child;
+ dns_dbiterator_t *dbit = NULL;
+
+ dns_fixedname_init(&fixedname);
+ child = dns_fixedname_name(&fixedname);
+
+ CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit));
+
+ for (result = dns_dbiterator_seek(dbit, name);
+ result == ISC_R_SUCCESS;
+ result = dns_dbiterator_next(dbit))
+ {
+ dns_dbnode_t *node = NULL;
+ CHECK(dns_dbiterator_current(dbit, &node, child));
+ dns_db_detachnode(db, &node);
+ if (! dns_name_issubdomain(child, name))
+ break;
+ CHECK(namelist_append_name(affected, child));
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ failure:
+ if (dbit != NULL)
+ dns_dbiterator_destroy(&dbit);
+ return (result);
+}
+
+
+
+/*%
+ * Helper function for non_nsec_rrset_exists().
+ */
+static isc_result_t
+is_non_nsec_action(void *data, dns_rdataset_t *rrset) {
+ UNUSED(data);
+ if (!(rrset->type == dns_rdatatype_nsec ||
+ rrset->type == dns_rdatatype_nsec3 ||
+ (rrset->type == dns_rdatatype_rrsig &&
+ (rrset->covers == dns_rdatatype_nsec ||
+ rrset->covers == dns_rdatatype_nsec3))))
+ return (ISC_R_EXISTS);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Check whether there is an rrset other than a NSEC or RRSIG NSEC,
+ * i.e., anything that justifies the continued existence of a name
+ * after a secure update.
+ *
+ * If such an rrset exists, set '*exists' to ISC_TRUE.
+ * Otherwise, set it to ISC_FALSE.
+ */
+static isc_result_t
+non_nsec_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
+ dns_name_t *name, isc_boolean_t *exists)
+{
+ isc_result_t result;
+ result = foreach_rrset(db, ver, name, is_non_nsec_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*%
+ * A comparison function for sorting dns_diff_t:s by name.
+ */
+static int
+name_order(const void *av, const void *bv) {
+ dns_difftuple_t const * const *ap = av;
+ dns_difftuple_t const * const *bp = bv;
+ dns_difftuple_t const *a = *ap;
+ dns_difftuple_t const *b = *bp;
+ return (dns_name_compare(&a->name, &b->name));
+}
+
+static isc_result_t
+uniqify_name_list(dns_diff_t *list) {
+ isc_result_t result;
+ dns_difftuple_t *p, *q;
+
+ CHECK(dns_diff_sort(list, name_order));
+
+ p = ISC_LIST_HEAD(list->tuples);
+ while (p != NULL) {
+ do {
+ q = ISC_LIST_NEXT(p, link);
+ if (q == NULL || ! dns_name_equal(&p->name, &q->name))
+ break;
+ ISC_LIST_UNLINK(list->tuples, q, link);
+ dns_difftuple_free(&q);
+ } while (1);
+ p = ISC_LIST_NEXT(p, link);
+ }
+ failure:
+ return (result);
+}
+
+static isc_result_t
+is_active(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ isc_boolean_t *flag, isc_boolean_t *cut, isc_boolean_t *unsecure)
+{
+ isc_result_t result;
+ dns_fixedname_t foundname;
+ dns_fixedname_init(&foundname);
+ result = dns_db_find(db, name, ver, dns_rdatatype_any,
+ DNS_DBFIND_GLUEOK | DNS_DBFIND_NOWILD,
+ (isc_stdtime_t) 0, NULL,
+ dns_fixedname_name(&foundname),
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS || result == DNS_R_EMPTYNAME) {
+ *flag = ISC_TRUE;
+ *cut = ISC_FALSE;
+ if (unsecure != NULL)
+ *unsecure = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+ } else if (result == DNS_R_ZONECUT) {
+ *flag = ISC_TRUE;
+ *cut = ISC_TRUE;
+ if (unsecure != NULL) {
+ /*
+ * We are at the zonecut. Check to see if there
+ * is a DS RRset.
+ */
+ if (dns_db_find(db, name, ver, dns_rdatatype_ds, 0,
+ (isc_stdtime_t) 0, NULL,
+ dns_fixedname_name(&foundname),
+ NULL, NULL) == DNS_R_NXRRSET)
+ *unsecure = ISC_TRUE;
+ else
+ *unsecure = ISC_FALSE;
+ }
+ return (ISC_R_SUCCESS);
+ } else if (result == DNS_R_GLUE || result == DNS_R_DNAME ||
+ result == DNS_R_DELEGATION || result == DNS_R_NXDOMAIN) {
+ *flag = ISC_FALSE;
+ *cut = ISC_FALSE;
+ if (unsecure != NULL)
+ *unsecure = ISC_FALSE;
+ return (ISC_R_SUCCESS);
+ } else {
+ /*
+ * Silence compiler.
+ */
+ *flag = ISC_FALSE;
+ *cut = ISC_FALSE;
+ if (unsecure != NULL)
+ *unsecure = ISC_FALSE;
+ return (result);
+ }
+}
+
+/*%
+ * Find the next/previous name that has a NSEC record.
+ * In other words, skip empty database nodes and names that
+ * have had their NSECs removed because they are obscured by
+ * a zone cut.
+ */
+static isc_result_t
+next_active(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *oldname, dns_name_t *newname,
+ isc_boolean_t forward)
+{
+ isc_result_t result;
+ dns_dbiterator_t *dbit = NULL;
+ isc_boolean_t has_nsec;
+ unsigned int wraps = 0;
+ isc_boolean_t secure = dns_db_issecure(db);
+
+ CHECK(dns_db_createiterator(db, 0, &dbit));
+
+ CHECK(dns_dbiterator_seek(dbit, oldname));
+ do {
+ dns_dbnode_t *node = NULL;
+
+ if (forward)
+ result = dns_dbiterator_next(dbit);
+ else
+ result = dns_dbiterator_prev(dbit);
+ if (result == ISC_R_NOMORE) {
+ /*
+ * Wrap around.
+ */
+ if (forward)
+ CHECK(dns_dbiterator_first(dbit));
+ else
+ CHECK(dns_dbiterator_last(dbit));
+ wraps++;
+ if (wraps == 2) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "secure zone with no NSECs");
+ result = DNS_R_BADZONE;
+ goto failure;
+ }
+ }
+ CHECK(dns_dbiterator_current(dbit, &node, newname));
+ dns_db_detachnode(db, &node);
+
+ /*
+ * The iterator may hold the tree lock, and
+ * rrset_exists() calls dns_db_findnode() which
+ * may try to reacquire it. To avoid deadlock
+ * we must pause the iterator first.
+ */
+ CHECK(dns_dbiterator_pause(dbit));
+ if (secure) {
+ CHECK(rrset_exists(db, ver, newname,
+ dns_rdatatype_nsec, 0, &has_nsec));
+ } else {
+ dns_fixedname_t ffound;
+ dns_name_t *found;
+ dns_fixedname_init(&ffound);
+ found = dns_fixedname_name(&ffound);
+ result = dns_db_find(db, newname, ver,
+ dns_rdatatype_soa,
+ DNS_DBFIND_NOWILD, 0, NULL, found,
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS ||
+ result == DNS_R_EMPTYNAME ||
+ result == DNS_R_NXRRSET ||
+ result == DNS_R_CNAME ||
+ (result == DNS_R_DELEGATION &&
+ dns_name_equal(newname, found))) {
+ has_nsec = ISC_TRUE;
+ result = ISC_R_SUCCESS;
+ } else if (result != DNS_R_NXDOMAIN)
+ break;
+ }
+ } while (! has_nsec);
+ failure:
+ if (dbit != NULL)
+ dns_dbiterator_destroy(&dbit);
+
+ return (result);
+}
+
+static isc_boolean_t
+has_opt_bit(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node) {
+ isc_result_t result;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ isc_boolean_t has_bit = ISC_FALSE;
+
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_findrdataset(db, node, version, dns_rdatatype_nsec,
+ dns_rdatatype_none, 0, &rdataset, NULL));
+ CHECK(dns_rdataset_first(&rdataset));
+ dns_rdataset_current(&rdataset, &rdata);
+ has_bit = dns_nsec_typepresent(&rdata, dns_rdatatype_opt);
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ return (has_bit);
+}
+
+static void
+set_bit(unsigned char *array, unsigned int index) {
+ unsigned int shift, bit;
+
+ shift = 7 - (index % 8);
+ bit = 1 << shift;
+
+ array[index / 8] |= bit;
+}
+
+/*%
+ * Add a NSEC record for "name", recording the change in "diff".
+ * The existing NSEC is removed.
+ */
+static isc_result_t
+add_nsec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *name, dns_ttl_t nsecttl,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ unsigned char buffer[DNS_NSEC_BUFFERSIZE];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_difftuple_t *tuple = NULL;
+ dns_fixedname_t fixedname;
+ dns_name_t *target;
+
+ dns_fixedname_init(&fixedname);
+ target = dns_fixedname_name(&fixedname);
+
+ /*
+ * Find the successor name, aka NSEC target.
+ */
+ CHECK(next_active(client, zone, db, ver, name, target, ISC_TRUE));
+
+ /*
+ * Create the NSEC RDATA.
+ */
+ CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+ dns_rdata_init(&rdata);
+ CHECK(dns_nsec_buildrdata(db, ver, node, target, buffer, &rdata));
+ /*
+ * Preserve the status of the OPT bit in the origin's NSEC record.
+ */
+ if (dns_name_equal(dns_db_origin(db), name) &&
+ has_opt_bit(db, ver, node))
+ {
+ isc_region_t region;
+ dns_name_t next;
+
+ dns_name_init(&next, NULL);
+ dns_rdata_toregion(&rdata, &region);
+ dns_name_fromregion(&next, &region);
+ isc_region_consume(&region, next.length);
+ INSIST(region.length > (2 + dns_rdatatype_opt / 8) &&
+ region.base[0] == 0 &&
+ region.base[1] > dns_rdatatype_opt / 8);
+ set_bit(region.base + 2, dns_rdatatype_opt);
+ }
+ dns_db_detachnode(db, &node);
+
+ /*
+ * Delete the old NSEC and record the change.
+ */
+ CHECK(delete_if(true_p, db, ver, name, dns_rdatatype_nsec, 0,
+ NULL, diff));
+ /*
+ * Add the new NSEC and record the change.
+ */
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
+ nsecttl, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ INSIST(tuple == NULL);
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*%
+ * Add a placeholder NSEC record for "name", recording the change in "diff".
+ */
+static isc_result_t
+add_placeholder_nsec(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_diff_t *diff)
+{
+ isc_result_t result;
+ dns_difftuple_t *tuple = NULL;
+ isc_region_t r;
+ unsigned char data[1] = { 0 }; /* The root domain, no bits. */
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ r.base = data;
+ r.length = sizeof(data);
+ dns_rdata_fromregion(&rdata, dns_db_class(db), dns_rdatatype_nsec, &r);
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name, 0,
+ &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ failure:
+ return (result);
+}
+
+static isc_result_t
+find_zone_keys(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *ver,
+ isc_mem_t *mctx, unsigned int maxkeys,
+ dst_key_t **keys, unsigned int *nkeys)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ const char *directory = dns_zone_getkeydirectory(zone);
+ CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
+ CHECK(dns_dnssec_findzonekeys2(db, ver, node, dns_db_origin(db),
+ directory, mctx, maxkeys, keys, nkeys));
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_boolean_t
+ksk_sanity(dns_db_t *db, dns_dbversion_t *ver) {
+ isc_boolean_t ret = ISC_FALSE;
+ isc_boolean_t have_ksk = ISC_FALSE, have_nonksk = ISC_FALSE;
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_dnskey_t dnskey;
+
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
+ CHECK(dns_db_findrdataset(db, node, ver, dns_rdatatype_dnskey, 0, 0,
+ &rdataset, NULL));
+ CHECK(dns_rdataset_first(&rdataset));
+ while (result == ISC_R_SUCCESS && (!have_ksk || !have_nonksk)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &dnskey, NULL));
+ if ((dnskey.flags & (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH))
+ == DNS_KEYOWNER_ZONE) {
+ if ((dnskey.flags & DNS_KEYFLAG_KSK) != 0)
+ have_ksk = ISC_TRUE;
+ else
+ have_nonksk = ISC_TRUE;
+ }
+ dns_rdata_reset(&rdata);
+ result = dns_rdataset_next(&rdataset);
+ }
+ if (have_ksk && have_nonksk)
+ ret = ISC_TRUE;
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (ret);
+}
+
+/*%
+ * Add RRSIG records for an RRset, recording the change in "diff".
+ */
+static isc_result_t
+add_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *name, dns_rdatatype_t type,
+ dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys,
+ isc_stdtime_t inception, isc_stdtime_t expire,
+ isc_boolean_t check_ksk)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t sig_rdata = DNS_RDATA_INIT;
+ isc_buffer_t buffer;
+ unsigned char data[1024]; /* XXX */
+ unsigned int i;
+ isc_boolean_t added_sig = ISC_FALSE;
+ isc_mem_t *mctx = client->mctx;
+
+ dns_rdataset_init(&rdataset);
+ isc_buffer_init(&buffer, data, sizeof(data));
+
+ /* Get the rdataset to sign. */
+ if (type == dns_rdatatype_nsec3)
+ CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node));
+ else
+ CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+ CHECK(dns_db_findrdataset(db, node, ver, type, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL));
+ dns_db_detachnode(db, &node);
+
+ for (i = 0; i < nkeys; i++) {
+
+ if (check_ksk && type != dns_rdatatype_dnskey &&
+ (dst_key_flags(keys[i]) & DNS_KEYFLAG_KSK) != 0)
+ continue;
+
+ if (!dst_key_isprivate(keys[i]))
+ continue;
+
+ /* Calculate the signature, creating a RRSIG RDATA. */
+ CHECK(dns_dnssec_sign(name, &rdataset, keys[i],
+ &inception, &expire,
+ mctx, &buffer, &sig_rdata));
+
+ /* Update the database and journal with the RRSIG. */
+ /* XXX inefficient - will cause dataset merging */
+ CHECK(update_one_rr(db, ver, diff, DNS_DIFFOP_ADDRESIGN, name,
+ rdataset.ttl, &sig_rdata));
+ dns_rdata_reset(&sig_rdata);
+ added_sig = ISC_TRUE;
+ }
+ if (!added_sig) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "found no private keys, "
+ "unable to generate any signatures");
+ result = ISC_R_NOTFOUND;
+ }
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+/*
+ * Delete expired RRsigs and any RRsigs we are about to re-sign.
+ * See also zone.c:del_sigs().
+ */
+static isc_result_t
+del_keysigs(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned int i;
+ dns_rdata_rrsig_t rrsig;
+ isc_boolean_t found;
+
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_rrsig,
+ dns_rdatatype_dnskey, (isc_stdtime_t) 0,
+ &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &rrsig, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ found = ISC_FALSE;
+ for (i = 0; i < nkeys; i++) {
+ if (rrsig.keyid == dst_key_id(keys[i])) {
+ found = ISC_TRUE;
+ if (!dst_key_isprivate(keys[i])) {
+ /*
+ * The re-signing code in zone.c
+ * will mark this as offline.
+ * Just skip the record for now.
+ */
+ break;
+ }
+ result = update_one_rr(db, ver, diff,
+ DNS_DIFFOP_DEL, name,
+ rdataset.ttl, &rdata);
+ break;
+ }
+ }
+ /*
+ * If there is not a matching DNSKEY then delete the RRSIG.
+ */
+ if (!found)
+ result = update_one_rr(db, ver, diff, DNS_DIFFOP_DEL,
+ name, rdataset.ttl, &rdata);
+ dns_rdata_reset(&rdata);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_result_t
+add_exposed_sigs(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_name_t *name, isc_boolean_t cut,
+ dns_diff_t *diff, dst_key_t **keys, unsigned int nkeys,
+ isc_stdtime_t inception, isc_stdtime_t expire,
+ isc_boolean_t check_ksk)
+{
+ isc_result_t result;
+ dns_dbnode_t *node;
+ dns_rdatasetiter_t *iter;
+
+ node = NULL;
+ result = dns_db_findnode(db, name, ISC_FALSE, &node);
+ if (result == ISC_R_NOTFOUND)
+ return (ISC_R_SUCCESS);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ iter = NULL;
+ result = dns_db_allrdatasets(db, node, ver,
+ (isc_stdtime_t) 0, &iter);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_node;
+
+ for (result = dns_rdatasetiter_first(iter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(iter))
+ {
+ dns_rdataset_t rdataset;
+ dns_rdatatype_t type;
+ isc_boolean_t flag;
+
+ dns_rdataset_init(&rdataset);
+ dns_rdatasetiter_current(iter, &rdataset);
+ type = rdataset.type;
+ dns_rdataset_disassociate(&rdataset);
+
+ /*
+ * We don't need to sign unsigned NSEC records at the cut
+ * as they are handled elsewhere.
+ */
+ if ((type == dns_rdatatype_rrsig) ||
+ (cut && type != dns_rdatatype_ds))
+ continue;
+ result = rrset_exists(db, ver, name, dns_rdatatype_rrsig,
+ type, &flag);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iterator;
+ if (flag)
+ continue;;
+ result = add_sigs(client, zone, db, ver, name, type, diff,
+ keys, nkeys, inception, expire, check_ksk);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_iterator;
+ }
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ cleanup_iterator:
+ dns_rdatasetiter_destroy(&iter);
+
+ cleanup_node:
+ dns_db_detachnode(db, &node);
+
+ return (result);
+}
+
+/*%
+ * Update RRSIG, NSEC and NSEC3 records affected by an update. The original
+ * update, including the SOA serial update but exluding the RRSIG & NSEC
+ * changes, is in "diff" and has already been applied to "newver" of "db".
+ * The database version prior to the update is "oldver".
+ *
+ * The necessary RRSIG, NSEC and NSEC3 changes will be applied to "newver"
+ * and added (as a minimal diff) to "diff".
+ *
+ * The RRSIGs generated will be valid for 'sigvalidityinterval' seconds.
+ */
+static isc_result_t
+update_signatures(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *oldver, dns_dbversion_t *newver,
+ dns_diff_t *diff, isc_uint32_t sigvalidityinterval,
+ isc_boolean_t *deleted_zsk)
+{
+ isc_result_t result;
+ dns_difftuple_t *t;
+ dns_diff_t diffnames;
+ dns_diff_t affected;
+ dns_diff_t sig_diff;
+ dns_diff_t nsec_diff;
+ dns_diff_t nsec_mindiff;
+ isc_boolean_t flag;
+ dst_key_t *zone_keys[MAXZONEKEYS];
+ unsigned int nkeys = 0;
+ unsigned int i;
+ isc_stdtime_t now, inception, expire;
+ dns_ttl_t nsecttl;
+ dns_rdata_soa_t soa;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ dns_dbnode_t *node = NULL;
+ isc_boolean_t check_ksk;
+ isc_boolean_t unsecure;
+ isc_boolean_t cut;
+
+ dns_diff_init(client->mctx, &diffnames);
+ dns_diff_init(client->mctx, &affected);
+
+ dns_diff_init(client->mctx, &sig_diff);
+ sig_diff.resign = dns_zone_getsigresigninginterval(zone);
+ dns_diff_init(client->mctx, &nsec_diff);
+ dns_diff_init(client->mctx, &nsec_mindiff);
+
+ result = find_zone_keys(zone, db, newver, client->mctx,
+ MAXZONEKEYS, zone_keys, &nkeys);
+ if (result != ISC_R_SUCCESS) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "could not get zone keys for secure dynamic update");
+ goto failure;
+ }
+
+ isc_stdtime_get(&now);
+ inception = now - 3600; /* Allow for some clock skew. */
+ expire = now + sigvalidityinterval;
+
+ /*
+ * Do we look at the KSK flag on the DNSKEY to determining which
+ * keys sign which RRsets? First check the zone option then
+ * check the keys flags to make sure atleast one has a ksk set
+ * and one doesn't.
+ */
+ check_ksk = ISC_TF((dns_zone_getoptions(zone) &
+ DNS_ZONEOPT_UPDATECHECKKSK) != 0);
+ /*
+ * If we are not checking the ZSK flag then all DNSKEY's are
+ * already signing all RRsets so we don't need to trigger special
+ * changes.
+ */
+ if (*deleted_zsk && (!check_ksk || !ksk_sanity(db, oldver)))
+ *deleted_zsk = ISC_FALSE;
+
+ if (check_ksk) {
+ check_ksk = ksk_sanity(db, newver);
+ if (!check_ksk && ksk_sanity(db, oldver))
+ update_log(client, zone, ISC_LOG_WARNING,
+ "disabling update-check-ksk");
+ }
+
+ /*
+ * If we have deleted a ZSK and we we still have some ZSK's
+ * we don't need to convert the KSK's to a ZSK's.
+ */
+ if (*deleted_zsk && check_ksk)
+ *deleted_zsk = ISC_FALSE;
+
+ /*
+ * Get the NSEC/NSEC3 TTL from the SOA MINIMUM field.
+ */
+ CHECK(dns_db_findnode(db, dns_db_origin(db), ISC_FALSE, &node));
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_db_findrdataset(db, node, newver, dns_rdatatype_soa, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL));
+ CHECK(dns_rdataset_first(&rdataset));
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &soa, NULL));
+ nsecttl = soa.minimum;
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+
+ /*
+ * Find all RRsets directly affected by the update, and
+ * update their RRSIGs. Also build a list of names affected
+ * by the update in "diffnames".
+ */
+ CHECK(dns_diff_sort(diff, temp_order));
+
+ t = ISC_LIST_HEAD(diff->tuples);
+ while (t != NULL) {
+ dns_name_t *name = &t->name;
+ /* Now "name" is a new, unique name affected by the update. */
+
+ CHECK(namelist_append_name(&diffnames, name));
+
+ while (t != NULL && dns_name_equal(&t->name, name)) {
+ dns_rdatatype_t type;
+ type = t->rdata.type;
+
+ /*
+ * Now "name" and "type" denote a new unique RRset
+ * affected by the update.
+ */
+
+ /* Don't sign RRSIGs. */
+ if (type == dns_rdatatype_rrsig)
+ goto skip;
+
+ /*
+ * Delete all old RRSIGs covering this type, since they
+ * are all invalid when the signed RRset has changed.
+ * We may not be able to recreate all of them - tough.
+ * Special case changes to the zone's DNSKEY records
+ * to support offline KSKs.
+ */
+ if (type == dns_rdatatype_dnskey)
+ del_keysigs(db, newver, name, &sig_diff,
+ zone_keys, nkeys);
+ else
+ CHECK(delete_if(true_p, db, newver, name,
+ dns_rdatatype_rrsig, type,
+ NULL, &sig_diff));
+
+ /*
+ * If this RRset is still visible after the update,
+ * add a new signature for it.
+ */
+ CHECK(rrset_visible(db, newver, name, type, &flag));
+ if (flag) {
+ CHECK(add_sigs(client, zone, db, newver, name,
+ type, &sig_diff, zone_keys,
+ nkeys, inception, expire,
+ check_ksk));
+ }
+ skip:
+ /* Skip any other updates to the same RRset. */
+ while (t != NULL &&
+ dns_name_equal(&t->name, name) &&
+ t->rdata.type == type)
+ {
+ t = ISC_LIST_NEXT(t, link);
+ }
+ }
+ }
+ update_log(client, zone, ISC_LOG_DEBUG(3), "updated data signatures");
+
+ /* Remove orphaned NSECs and RRSIG NSECs. */
+ for (t = ISC_LIST_HEAD(diffnames.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ CHECK(non_nsec_rrset_exists(db, newver, &t->name, &flag));
+ if (! flag) {
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_any, 0,
+ NULL, &sig_diff));
+ }
+ }
+ update_log(client, zone, ISC_LOG_DEBUG(3),
+ "removed any orphaned NSEC records");
+
+ /*
+ * If we don't have a NSEC record at the origin then we need to
+ * update the NSEC3 records.
+ */
+ CHECK(rrset_exists(db, newver, dns_db_origin(db), dns_rdatatype_nsec,
+ 0, &flag));
+ if (!flag)
+ goto update_nsec3;
+
+ update_log(client, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC chain");
+
+ /*
+ * When a name is created or deleted, its predecessor needs to
+ * have its NSEC updated.
+ */
+ for (t = ISC_LIST_HEAD(diffnames.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ isc_boolean_t existed, exists;
+ dns_fixedname_t fixedname;
+ dns_name_t *prevname;
+
+ dns_fixedname_init(&fixedname);
+ prevname = dns_fixedname_name(&fixedname);
+
+ CHECK(name_exists(db, oldver, &t->name, &existed));
+ CHECK(name_exists(db, newver, &t->name, &exists));
+ if (exists == existed)
+ continue;
+
+ /*
+ * Find the predecessor.
+ * When names become obscured or unobscured in this update
+ * transaction, we may find the wrong predecessor because
+ * the NSECs have not yet been updated to reflect the delegation
+ * change. This should not matter because in this case,
+ * the correct predecessor is either the delegation node or
+ * a newly unobscured node, and those nodes are on the
+ * "affected" list in any case.
+ */
+ CHECK(next_active(client, zone, db, newver,
+ &t->name, prevname, ISC_FALSE));
+ CHECK(namelist_append_name(&affected, prevname));
+ }
+
+ /*
+ * Find names potentially affected by delegation changes
+ * (obscured by adding an NS or DNAME, or unobscured by
+ * removing one).
+ */
+ for (t = ISC_LIST_HEAD(diffnames.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ isc_boolean_t ns_existed, dname_existed;
+ isc_boolean_t ns_exists, dname_exists;
+
+ CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_ns, 0,
+ &ns_existed));
+ CHECK(rrset_exists(db, oldver, &t->name, dns_rdatatype_dname, 0,
+ &dname_existed));
+ CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0,
+ &ns_exists));
+ CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_dname, 0,
+ &dname_exists));
+ if ((ns_exists || dname_exists) == (ns_existed || dname_existed))
+ continue;
+ /*
+ * There was a delegation change. Mark all subdomains
+ * of t->name as potentially needing a NSEC update.
+ */
+ CHECK(namelist_append_subdomain(db, &t->name, &affected));
+ }
+
+ ISC_LIST_APPENDLIST(affected.tuples, diffnames.tuples, link);
+ INSIST(ISC_LIST_EMPTY(diffnames.tuples));
+
+ CHECK(uniqify_name_list(&affected));
+
+ /*
+ * Determine which names should have NSECs, and delete/create
+ * NSECs to make it so. We don't know the final NSEC targets yet,
+ * so we just create placeholder NSECs with arbitrary contents
+ * to indicate that their respective owner names should be part of
+ * the NSEC chain.
+ */
+ for (t = ISC_LIST_HEAD(affected.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ isc_boolean_t exists;
+ dns_name_t *name = &t->name;
+
+ CHECK(name_exists(db, newver, name, &exists));
+ if (! exists)
+ continue;
+ CHECK(is_active(db, newver, name, &flag, &cut, NULL));
+ if (!flag) {
+ /*
+ * This name is obscured. Delete any
+ * existing NSEC record.
+ */
+ CHECK(delete_if(true_p, db, newver, name,
+ dns_rdatatype_nsec, 0,
+ NULL, &nsec_diff));
+ CHECK(delete_if(rrsig_p, db, newver, name,
+ dns_rdatatype_any, 0, NULL, diff));
+ } else {
+ /*
+ * This name is not obscured. It should have a NSEC.
+ */
+ CHECK(rrset_exists(db, newver, name,
+ dns_rdatatype_nsec, 0, &flag));
+ if (! flag)
+ CHECK(add_placeholder_nsec(db, newver, name,
+ diff));
+ CHECK(add_exposed_sigs(client, zone, db, newver, name,
+ cut, diff, zone_keys, nkeys,
+ inception, expire, check_ksk));
+ }
+ }
+
+ /*
+ * Now we know which names are part of the NSEC chain.
+ * Make them all point at their correct targets.
+ */
+ for (t = ISC_LIST_HEAD(affected.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ CHECK(rrset_exists(db, newver, &t->name,
+ dns_rdatatype_nsec, 0, &flag));
+ if (flag) {
+ /*
+ * There is a NSEC, but we don't know if it is correct.
+ * Delete it and create a correct one to be sure.
+ * If the update was unnecessary, the diff minimization
+ * will take care of eliminating it from the journal,
+ * IXFRs, etc.
+ *
+ * The RRSIG bit should always be set in the NSECs
+ * we generate, because they will all get RRSIG NSECs.
+ * (XXX what if the zone keys are missing?).
+ * Because the RRSIG NSECs have not necessarily been
+ * created yet, the correctness of the bit mask relies
+ * on the assumption that NSECs are only created if
+ * there is other data, and if there is other data,
+ * there are other RRSIGs.
+ */
+ CHECK(add_nsec(client, zone, db, newver, &t->name,
+ nsecttl, &nsec_diff));
+ }
+ }
+
+ /*
+ * Minimize the set of NSEC updates so that we don't
+ * have to regenerate the RRSIG NSECs for NSECs that were
+ * replaced with identical ones.
+ */
+ while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(nsec_diff.tuples, t, link);
+ dns_diff_appendminimal(&nsec_mindiff, &t);
+ }
+
+ update_log(client, zone, ISC_LOG_DEBUG(3),
+ "signing rebuilt NSEC chain");
+
+ /* Update RRSIG NSECs. */
+ for (t = ISC_LIST_HEAD(nsec_mindiff.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ if (t->op == DNS_DIFFOP_DEL) {
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_rrsig, dns_rdatatype_nsec,
+ NULL, &sig_diff));
+ } else if (t->op == DNS_DIFFOP_ADD) {
+ CHECK(add_sigs(client, zone, db, newver, &t->name,
+ dns_rdatatype_nsec, &sig_diff,
+ zone_keys, nkeys, inception, expire,
+ check_ksk));
+ } else {
+ INSIST(0);
+ }
+ }
+
+ update_nsec3:
+
+ /* Record our changes for the journal. */
+ while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(sig_diff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+ while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+
+ INSIST(ISC_LIST_EMPTY(sig_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(nsec_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples));
+
+ /*
+ * Check if we have any active NSEC3 chains by looking for a
+ * NSEC3PARAM RRset.
+ */
+ CHECK(rrset_exists(db, newver, dns_db_origin(db),
+ dns_rdatatype_nsec3param, 0, &flag));
+ if (!flag) {
+ update_log(client, zone, ISC_LOG_DEBUG(3),
+ "no NSEC3 chains to rebuild");
+ goto failure;
+ }
+
+ update_log(client, zone, ISC_LOG_DEBUG(3), "rebuilding NSEC3 chains");
+
+ dns_diff_clear(&diffnames);
+ dns_diff_clear(&affected);
+
+ CHECK(dns_diff_sort(diff, temp_order));
+
+ /*
+ * Find names potentially affected by delegation changes
+ * (obscured by adding an NS or DNAME, or unobscured by
+ * removing one).
+ */
+ t = ISC_LIST_HEAD(diff->tuples);
+ while (t != NULL) {
+ dns_name_t *name = &t->name;
+
+ isc_boolean_t ns_existed, dname_existed;
+ isc_boolean_t ns_exists, dname_exists;
+
+ if (t->rdata.type == dns_rdatatype_nsec ||
+ t->rdata.type == dns_rdatatype_rrsig) {
+ t = ISC_LIST_NEXT(t, link);
+ continue;
+ }
+
+ CHECK(namelist_append_name(&affected, name));
+
+ CHECK(rrset_exists(db, oldver, name, dns_rdatatype_ns, 0,
+ &ns_existed));
+ CHECK(rrset_exists(db, oldver, name, dns_rdatatype_dname, 0,
+ &dname_existed));
+ CHECK(rrset_exists(db, newver, name, dns_rdatatype_ns, 0,
+ &ns_exists));
+ CHECK(rrset_exists(db, newver, name, dns_rdatatype_dname, 0,
+ &dname_exists));
+
+ if ((ns_exists || dname_exists) == (ns_existed || dname_existed))
+ goto nextname;
+ /*
+ * There was a delegation change. Mark all subdomains
+ * of t->name as potentially needing a NSEC3 update.
+ */
+ CHECK(namelist_append_subdomain(db, name, &affected));
+
+ nextname:
+ while (t != NULL && dns_name_equal(&t->name, name))
+ t = ISC_LIST_NEXT(t, link);
+ }
+
+ for (t = ISC_LIST_HEAD(affected.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link)) {
+ dns_name_t *name = &t->name;
+
+ unsecure = ISC_FALSE; /* Silence compiler warning. */
+ CHECK(is_active(db, newver, name, &flag, &cut, &unsecure));
+
+ if (!flag) {
+ CHECK(delete_if(rrsig_p, db, newver, name,
+ dns_rdatatype_any, 0, NULL, diff));
+ CHECK(dns_nsec3_delnsec3s(db, newver, name,
+ &nsec_diff));
+ } else {
+ CHECK(add_exposed_sigs(client, zone, db, newver, name,
+ cut, diff, zone_keys, nkeys,
+ inception, expire, check_ksk));
+ CHECK(dns_nsec3_addnsec3s(db, newver, name, nsecttl,
+ unsecure, &nsec_diff));
+ }
+ }
+
+ /*
+ * Minimize the set of NSEC3 updates so that we don't
+ * have to regenerate the RRSIG NSEC3s for NSEC3s that were
+ * replaced with identical ones.
+ */
+ while ((t = ISC_LIST_HEAD(nsec_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(nsec_diff.tuples, t, link);
+ dns_diff_appendminimal(&nsec_mindiff, &t);
+ }
+
+ update_log(client, zone, ISC_LOG_DEBUG(3),
+ "signing rebuilt NSEC3 chain");
+
+ /* Update RRSIG NSEC3s. */
+ for (t = ISC_LIST_HEAD(nsec_mindiff.tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link))
+ {
+ if (t->op == DNS_DIFFOP_DEL) {
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_rrsig,
+ dns_rdatatype_nsec3,
+ NULL, &sig_diff));
+ } else if (t->op == DNS_DIFFOP_ADD) {
+ CHECK(add_sigs(client, zone, db, newver, &t->name,
+ dns_rdatatype_nsec3,
+ &sig_diff, zone_keys, nkeys,
+ inception, expire, check_ksk));
+ } else {
+ INSIST(0);
+ }
+ }
+
+ /* Record our changes for the journal. */
+ while ((t = ISC_LIST_HEAD(sig_diff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(sig_diff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+ while ((t = ISC_LIST_HEAD(nsec_mindiff.tuples)) != NULL) {
+ ISC_LIST_UNLINK(nsec_mindiff.tuples, t, link);
+ dns_diff_appendminimal(diff, &t);
+ }
+
+ INSIST(ISC_LIST_EMPTY(sig_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(nsec_diff.tuples));
+ INSIST(ISC_LIST_EMPTY(nsec_mindiff.tuples));
+
+ failure:
+ dns_diff_clear(&sig_diff);
+ dns_diff_clear(&nsec_diff);
+ dns_diff_clear(&nsec_mindiff);
+
+ dns_diff_clear(&affected);
+ dns_diff_clear(&diffnames);
+
+ for (i = 0; i < nkeys; i++)
+ dst_key_free(&zone_keys[i]);
+
+ return (result);
+}
+
+
+/**************************************************************************/
+/*%
+ * The actual update code in all its glory. We try to follow
+ * the RFC2136 pseudocode as closely as possible.
+ */
+
+static isc_result_t
+send_update_event(ns_client_t *client, dns_zone_t *zone) {
+ isc_result_t result = ISC_R_SUCCESS;
+ update_event_t *event = NULL;
+ isc_task_t *zonetask = NULL;
+ ns_client_t *evclient;
+
+ event = (update_event_t *)
+ isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
+ update_action, NULL, sizeof(*event));
+ if (event == NULL)
+ FAIL(ISC_R_NOMEMORY);
+ event->zone = zone;
+ event->result = ISC_R_SUCCESS;
+
+ evclient = NULL;
+ ns_client_attach(client, &evclient);
+ INSIST(client->nupdates == 0);
+ client->nupdates++;
+ event->ev_arg = evclient;
+
+ dns_zone_gettask(zone, &zonetask);
+ isc_task_send(zonetask, ISC_EVENT_PTR(&event));
+
+ failure:
+ if (event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&event));
+ return (result);
+}
+
+static void
+respond(ns_client_t *client, isc_result_t result) {
+ isc_result_t msg_result;
+
+ msg_result = dns_message_reply(client->message, ISC_TRUE);
+ if (msg_result != ISC_R_SUCCESS)
+ goto msg_failure;
+ client->message->rcode = dns_result_torcode(result);
+
+ ns_client_send(client);
+ return;
+
+ msg_failure:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
+ ISC_LOG_ERROR,
+ "could not create update response message: %s",
+ isc_result_totext(msg_result));
+ ns_client_next(client, msg_result);
+}
+
+void
+ns_update_start(ns_client_t *client, isc_result_t sigresult) {
+ dns_message_t *request = client->message;
+ isc_result_t result;
+ dns_name_t *zonename;
+ dns_rdataset_t *zone_rdataset;
+ dns_zone_t *zone = NULL;
+
+ /*
+ * Interpret the zone section.
+ */
+ result = dns_message_firstname(request, DNS_SECTION_ZONE);
+ if (result != ISC_R_SUCCESS)
+ FAILC(DNS_R_FORMERR, "update zone section empty");
+
+ /*
+ * The zone section must contain exactly one "question", and
+ * it must be of type SOA.
+ */
+ zonename = NULL;
+ dns_message_currentname(request, DNS_SECTION_ZONE, &zonename);
+ zone_rdataset = ISC_LIST_HEAD(zonename->list);
+ if (zone_rdataset->type != dns_rdatatype_soa)
+ FAILC(DNS_R_FORMERR,
+ "update zone section contains non-SOA");
+ if (ISC_LIST_NEXT(zone_rdataset, link) != NULL)
+ FAILC(DNS_R_FORMERR,
+ "update zone section contains multiple RRs");
+
+ /* The zone section must have exactly one name. */
+ result = dns_message_nextname(request, DNS_SECTION_ZONE);
+ if (result != ISC_R_NOMORE)
+ FAILC(DNS_R_FORMERR,
+ "update zone section contains multiple RRs");
+
+ result = dns_zt_find(client->view->zonetable, zonename, 0, NULL,
+ &zone);
+ if (result != ISC_R_SUCCESS)
+ FAILC(DNS_R_NOTAUTH, "not authoritative for update zone");
+
+ switch(dns_zone_gettype(zone)) {
+ case dns_zone_master:
+ /*
+ * We can now fail due to a bad signature as we now know
+ * that we are the master.
+ */
+ if (sigresult != ISC_R_SUCCESS)
+ FAIL(sigresult);
+ CHECK(send_update_event(client, zone));
+ break;
+ case dns_zone_slave:
+ CHECK(checkupdateacl(client, dns_zone_getforwardacl(zone),
+ "update forwarding", zonename, ISC_TRUE,
+ ISC_FALSE));
+ CHECK(send_forward_event(client, zone));
+ break;
+ default:
+ FAILC(DNS_R_NOTAUTH, "not authoritative for update zone");
+ }
+ return;
+
+ failure:
+ if (result == DNS_R_REFUSED) {
+ INSIST(dns_zone_gettype(zone) == dns_zone_slave);
+ inc_stats(zone, dns_nsstatscounter_updaterej);
+ }
+ /*
+ * We failed without having sent an update event to the zone.
+ * We are still in the client task context, so we can
+ * simply give an error response without switching tasks.
+ */
+ respond(client, result);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+}
+
+/*%
+ * DS records are not allowed to exist without corresponding NS records,
+ * RFC 3658, 2.2 Protocol Change,
+ * "DS RRsets MUST NOT appear at non-delegation points or at a zone's apex".
+ */
+
+static isc_result_t
+remove_orphaned_ds(dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) {
+ isc_result_t result;
+ isc_boolean_t ns_exists;
+ dns_difftuple_t *t;
+
+ for (t = ISC_LIST_HEAD(diff->tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link)) {
+ if (!((t->op == DNS_DIFFOP_DEL &&
+ t->rdata.type == dns_rdatatype_ns) ||
+ (t->op == DNS_DIFFOP_ADD &&
+ t->rdata.type == dns_rdatatype_ds)))
+ continue;
+ CHECK(rrset_exists(db, newver, &t->name, dns_rdatatype_ns, 0,
+ &ns_exists));
+ if (ns_exists && !dns_name_equal(&t->name, dns_db_origin(db)))
+ continue;
+ CHECK(delete_if(true_p, db, newver, &t->name,
+ dns_rdatatype_ds, 0, NULL, diff));
+ }
+ return (ISC_R_SUCCESS);
+
+ failure:
+ return (result);
+}
+
+/*
+ * This implements the post load integrity checks for mx records.
+ */
+static isc_result_t
+check_mx(ns_client_t *client, dns_zone_t *zone,
+ dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff)
+{
+ char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")];
+ char ownerbuf[DNS_NAME_FORMATSIZE];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char altbuf[DNS_NAME_FORMATSIZE];
+ dns_difftuple_t *t;
+ dns_fixedname_t fixed;
+ dns_name_t *foundname;
+ dns_rdata_mx_t mx;
+ dns_rdata_t rdata;
+ isc_boolean_t ok = ISC_TRUE;
+ isc_boolean_t isaddress;
+ isc_result_t result;
+ struct in6_addr addr6;
+ struct in_addr addr;
+ unsigned int options;
+
+ dns_fixedname_init(&fixed);
+ foundname = dns_fixedname_name(&fixed);
+ dns_rdata_init(&rdata);
+ options = dns_zone_getoptions(zone);
+
+ for (t = ISC_LIST_HEAD(diff->tuples);
+ t != NULL;
+ t = ISC_LIST_NEXT(t, link)) {
+ if (t->op != DNS_DIFFOP_ADD ||
+ t->rdata.type != dns_rdatatype_mx)
+ continue;
+
+ result = dns_rdata_tostruct(&t->rdata, &mx, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ /*
+ * Check if we will error out if we attempt to reload the
+ * zone.
+ */
+ dns_name_format(&mx.mx, namebuf, sizeof(namebuf));
+ dns_name_format(&t->name, ownerbuf, sizeof(ownerbuf));
+ isaddress = ISC_FALSE;
+ if ((options & DNS_RDATA_CHECKMX) != 0 &&
+ strlcpy(tmp, namebuf, sizeof(tmp)) < sizeof(tmp)) {
+ if (tmp[strlen(tmp) - 1] == '.')
+ tmp[strlen(tmp) - 1] = '\0';
+ if (inet_aton(tmp, &addr) == 1 ||
+ inet_pton(AF_INET6, tmp, &addr6) == 1)
+ isaddress = ISC_TRUE;
+ }
+
+ if (isaddress && (options & DNS_RDATA_CHECKMXFAIL) != 0) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX: '%s': %s",
+ ownerbuf, namebuf,
+ dns_result_totext(DNS_R_MXISADDRESS));
+ ok = ISC_FALSE;
+ } else if (isaddress) {
+ update_log(client, zone, ISC_LOG_WARNING,
+ "%s/MX: warning: '%s': %s",
+ ownerbuf, namebuf,
+ dns_result_totext(DNS_R_MXISADDRESS));
+ }
+
+ /*
+ * Check zone integrity checks.
+ */
+ if ((options & DNS_ZONEOPT_CHECKINTEGRITY) == 0)
+ continue;
+ result = dns_db_find(db, &mx.mx, newver, dns_rdatatype_a,
+ 0, 0, NULL, foundname, NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ continue;
+
+ if (result == DNS_R_NXRRSET) {
+ result = dns_db_find(db, &mx.mx, newver,
+ dns_rdatatype_aaaa,
+ 0, 0, NULL, foundname,
+ NULL, NULL);
+ if (result == ISC_R_SUCCESS)
+ continue;
+ }
+
+ if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX '%s' has no address records "
+ "(A or AAAA)", ownerbuf, namebuf);
+ ok = ISC_FALSE;
+ } else if (result == DNS_R_CNAME) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX '%s' is a CNAME (illegal)",
+ ownerbuf, namebuf);
+ ok = ISC_FALSE;
+ } else if (result == DNS_R_DNAME) {
+ dns_name_format(foundname, altbuf, sizeof altbuf);
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX '%s' is below a DNAME '%s' (illegal)",
+ ownerbuf, namebuf, altbuf);
+ ok = ISC_FALSE;
+ }
+ }
+ return (ok ? ISC_R_SUCCESS : DNS_R_REFUSED);
+}
+
+static isc_result_t
+rr_exists(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *name,
+ const dns_rdata_t *rdata, isc_boolean_t *flag)
+{
+ dns_rdataset_t rdataset;
+ dns_dbnode_t *node = NULL;
+ isc_result_t result;
+
+ dns_rdataset_init(&rdataset);
+ if (rdata->type == dns_rdatatype_nsec3)
+ CHECK(dns_db_findnsec3node(db, name, ISC_FALSE, &node));
+ else
+ CHECK(dns_db_findnode(db, name, ISC_FALSE, &node));
+ result = dns_db_findrdataset(db, node, ver, rdata->type, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ *flag = ISC_FALSE;
+ result = ISC_R_SUCCESS;
+ goto failure;
+ }
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t myrdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &myrdata);
+ if (!dns_rdata_compare(&myrdata, rdata))
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS) {
+ *flag = ISC_TRUE;
+ } else if (result == ISC_R_NOMORE) {
+ *flag = ISC_FALSE;
+ result = ISC_R_SUCCESS;
+ }
+
+ failure:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_result_t
+get_iterations(dns_db_t *db, dns_dbversion_t *ver, unsigned int *iterationsp) {
+ dns_dbnode_t *node = NULL;
+ dns_rdata_nsec3param_t nsec3param;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ unsigned int iterations = 0;
+
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+ 0, (isc_stdtime_t) 0, &rdataset, NULL);
+ dns_db_detachnode(db, &node);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(&rdataset, &rdata);
+ CHECK(dns_rdata_tostruct(&rdata, &nsec3param, NULL));
+ if ((nsec3param.flags & DNS_NSEC3FLAG_REMOVE) != 0)
+ continue;
+ if (nsec3param.iterations > iterations)
+ iterations = nsec3param.iterations;
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+
+ success:
+ *iterationsp = iterations;
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
+
+/*
+ * Prevent the zone entering a inconsistant state where
+ * NSEC only DNSKEYs are present with NSEC3 chains.
+ */
+static isc_result_t
+check_dnssec(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_dbversion_t *ver, dns_diff_t *diff)
+{
+ dns_diff_t temp_diff;
+ dns_diffop_t op;
+ dns_difftuple_t *tuple, *newtuple = NULL, *next;
+ isc_boolean_t flag;
+ isc_result_t result;
+ unsigned int iterations = 0, max;
+
+ dns_diff_init(diff->mctx, &temp_diff);
+
+ CHECK(dns_nsec_nseconly(db, ver, &flag));
+
+ if (flag)
+ CHECK(dns_nsec3_active(db, ver, ISC_FALSE, &flag));
+ if (flag) {
+ update_log(client, zone, ISC_LOG_WARNING,
+ "NSEC only DNSKEYs and NSEC3 chains not allowed");
+ } else {
+ CHECK(get_iterations(db, ver, &iterations));
+ CHECK(dns_nsec3_maxiterations(db, ver, client->mctx, &max));
+ if (iterations > max) {
+ flag = ISC_TRUE;
+ update_log(client, zone, ISC_LOG_WARNING,
+ "too many NSEC3 iterations (%u) for "
+ "weakest DNSKEY (%u)", iterations, max);
+ }
+ }
+ if (flag) {
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = next) {
+ next = ISC_LIST_NEXT(tuple, link);
+ if (tuple->rdata.type != dns_rdatatype_dnskey &&
+ tuple->rdata.type != dns_rdatatype_nsec3param)
+ continue;
+ op = (tuple->op == DNS_DIFFOP_DEL) ?
+ DNS_DIFFOP_ADD : DNS_DIFFOP_DEL;
+ CHECK(dns_difftuple_create(temp_diff.mctx, op,
+ &tuple->name, tuple->ttl,
+ &tuple->rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, &temp_diff));
+ INSIST(newtuple == NULL);
+ }
+ for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_HEAD(temp_diff.tuples)) {
+ ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+ dns_diff_appendminimal(diff, &tuple);
+ }
+ }
+
+
+ failure:
+ dns_diff_clear(&temp_diff);
+ return (result);
+}
+
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+/*
+ * Delay NSEC3PARAM changes as they need to be applied to the whole zone.
+ */
+static isc_result_t
+add_nsec3param_records(ns_client_t *client, dns_zone_t *zone, dns_db_t *db,
+ dns_name_t *name, dns_dbversion_t *ver, dns_diff_t *diff)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_difftuple_t *tuple, *newtuple = NULL, *next;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_diff_t temp_diff;
+ dns_diffop_t op;
+ isc_boolean_t flag;
+
+ update_log(client, zone, ISC_LOG_DEBUG(3),
+ "checking for NSEC3PARAM changes");
+
+ dns_diff_init(diff->mctx, &temp_diff);
+
+ /*
+ * Extract NSEC3PARAM tuples from list.
+ */
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = next) {
+
+ next = ISC_LIST_NEXT(tuple, link);
+
+ if (tuple->rdata.type != dns_rdatatype_nsec3param ||
+ !dns_name_equal(name, &tuple->name))
+ continue;
+ ISC_LIST_UNLINK(diff->tuples, tuple, link);
+ ISC_LIST_APPEND(temp_diff.tuples, tuple, link);
+ }
+
+ for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+ tuple != NULL; tuple = next) {
+
+ if (tuple->op == DNS_DIFFOP_ADD) {
+ next = ISC_LIST_NEXT(tuple, link);
+ while (next != NULL) {
+ unsigned char *next_data = next->rdata.data;
+ unsigned char *tuple_data = tuple->rdata.data;
+ if (next_data[0] != tuple_data[0] ||
+ /* Ignore flags. */
+ next_data[2] != tuple_data[2] ||
+ next_data[3] != tuple_data[3] ||
+ next_data[4] != tuple_data[4] ||
+ !memcmp(&next_data[5], &tuple_data[5],
+ tuple_data[4])) {
+ next = ISC_LIST_NEXT(next, link);
+ continue;
+ }
+ op = (next->op == DNS_DIFFOP_DEL) ?
+ DNS_DIFFOP_ADD : DNS_DIFFOP_DEL;
+ CHECK(dns_difftuple_create(diff->mctx, op,
+ name, next->ttl,
+ &next->rdata,
+ &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ ISC_LIST_UNLINK(temp_diff.tuples, next, link);
+ dns_diff_appendminimal(diff, &next);
+ next = ISC_LIST_NEXT(tuple, link);
+ }
+
+ INSIST(tuple->rdata.data[1] & DNS_NSEC3FLAG_UPDATE);
+
+ /*
+ * See if we already have a CREATE request in progress.
+ */
+ dns_rdata_clone(&tuple->rdata, &rdata);
+ INSIST(rdata.length <= sizeof(buf));
+ memcpy(buf, rdata.data, rdata.length);
+ buf[1] |= DNS_NSEC3FLAG_CREATE;
+ buf[1] &= ~DNS_NSEC3FLAG_UPDATE;
+ rdata.data = buf;
+
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+
+ if (!flag) {
+ CHECK(dns_difftuple_create(diff->mctx,
+ DNS_DIFFOP_ADD,
+ name, tuple->ttl,
+ &rdata,
+ &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ }
+ /*
+ * Remove the temporary add record.
+ */
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
+ name, tuple->ttl,
+ &tuple->rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ next = ISC_LIST_NEXT(tuple, link);
+ ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+ dns_diff_appendminimal(diff, &tuple);
+ dns_rdata_reset(&rdata);
+ } else
+ next = ISC_LIST_NEXT(tuple, link);
+ }
+
+ /*
+ * Reverse any pending changes.
+ */
+ for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+ tuple != NULL; tuple = next) {
+ next = ISC_LIST_NEXT(tuple, link);
+ if ((tuple->rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) != 0) {
+ op = (tuple->op == DNS_DIFFOP_DEL) ?
+ DNS_DIFFOP_ADD : DNS_DIFFOP_DEL;
+ CHECK(dns_difftuple_create(diff->mctx, op, name,
+ tuple->ttl, &tuple->rdata,
+ &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+ dns_diff_appendminimal(diff, &tuple);
+ }
+ }
+
+ /*
+ * Convert deletions into delayed deletions.
+ */
+ for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+ tuple != NULL; tuple = next) {
+ next = ISC_LIST_NEXT(tuple, link);
+ /*
+ * See if we already have a REMOVE request in progress.
+ */
+ dns_rdata_clone(&tuple->rdata, &rdata);
+ INSIST(rdata.length <= sizeof(buf));
+ memcpy(buf, rdata.data, rdata.length);
+ buf[1] |= DNS_NSEC3FLAG_REMOVE;
+ rdata.data = buf;
+
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+
+ if (!flag) {
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ name, tuple->ttl, &rdata,
+ &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ }
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
+ tuple->ttl, &tuple->rdata,
+ &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+ dns_diff_appendminimal(diff, &tuple);
+ dns_rdata_reset(&rdata);
+ }
+
+ result = ISC_R_SUCCESS;
+ failure:
+ dns_diff_clear(&temp_diff);
+ return (result);
+}
+#endif
+
+/*
+ * Add records to cause the delayed signing of the zone by added DNSKEY
+ * to remove the RRSIG records generated by a deleted DNSKEY.
+ */
+static isc_result_t
+add_signing_records(dns_db_t *db, dns_name_t *name, dns_dbversion_t *ver,
+ dns_rdatatype_t privatetype, dns_diff_t *diff)
+{
+ dns_difftuple_t *tuple, *newtuple = NULL;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ isc_boolean_t flag;
+ isc_region_t r;
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_uint16_t keyid;
+ unsigned char buf[5];
+
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ if (tuple->rdata.type != dns_rdatatype_dnskey)
+ continue;
+
+ dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL);
+ if ((dnskey.flags &
+ (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH))
+ != DNS_KEYOWNER_ZONE)
+ continue;
+
+ dns_rdata_toregion(&tuple->rdata, &r);
+ keyid = dst_region_computeid(&r, dnskey.algorithm);
+
+ buf[0] = dnskey.algorithm;
+ buf[1] = (keyid & 0xff00) >> 8;
+ buf[2] = (keyid & 0xff);
+ buf[3] = (tuple->op == DNS_DIFFOP_ADD) ? 0 : 1;
+ buf[4] = 0;
+ rdata.data = buf;
+ rdata.length = sizeof(buf);
+ rdata.type = privatetype;
+ rdata.rdclass = tuple->rdata.rdclass;
+
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+ if (flag)
+ continue;
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ name, 0, &rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ INSIST(newtuple == NULL);
+ /*
+ * Remove any record which says this operation has already
+ * completed.
+ */
+ buf[4] = 1;
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+ if (flag) {
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
+ name, 0, &rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ INSIST(newtuple == NULL);
+ }
+ }
+ failure:
+ return (result);
+}
+
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+/*
+ * Mark all NSEC3 chains for deletion without creating a NSEC chain as
+ * a side effect of deleting the last chain.
+ */
+static isc_result_t
+delete_chains(dns_db_t *db, dns_dbversion_t *ver, dns_name_t *origin,
+ dns_diff_t *diff)
+{
+ dns_dbnode_t *node = NULL;
+ dns_difftuple_t *tuple = NULL;
+ dns_name_t next;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ isc_boolean_t flag;
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+
+ dns_name_init(&next, NULL);
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Cause all NSEC3 chains to be deleted.
+ */
+ result = dns_db_findrdataset(db, node, ver, dns_rdatatype_nsec3param,
+ 0, (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND)
+ goto success;
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ for (result = dns_rdataset_first(&rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&rdataset)) {
+ dns_rdataset_current(&rdataset, &rdata);
+ INSIST(rdata.length <= sizeof(buf));
+ memcpy(buf, rdata.data, rdata.length);
+
+ if (buf[1] == (DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC)) {
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
+ origin, 0, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ INSIST(tuple == NULL);
+
+ buf[1] = DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC;
+ rdata.data = buf;
+
+ CHECK(rr_exists(db, ver, origin, &rdata, &flag));
+
+ if (!flag) {
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ origin, 0, &rdata, &tuple));
+ CHECK(do_one_tuple(&tuple, db, ver, diff));
+ INSIST(tuple == NULL);
+ }
+ dns_rdata_reset(&rdata);
+ }
+ if (result != ISC_R_NOMORE)
+ goto failure;
+ success:
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+#endif
+
+static void
+update_action(isc_task_t *task, isc_event_t *event) {
+ update_event_t *uev = (update_event_t *) event;
+ dns_zone_t *zone = uev->zone;
+ ns_client_t *client = (ns_client_t *)event->ev_arg;
+
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *oldver = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_diff_t diff; /* Pending updates. */
+ dns_diff_t temp; /* Pending RR existence assertions. */
+ isc_boolean_t soa_serial_changed = ISC_FALSE;
+ isc_mem_t *mctx = client->mctx;
+ dns_rdatatype_t covers;
+ dns_message_t *request = client->message;
+ dns_rdataclass_t zoneclass;
+ dns_name_t *zonename;
+ dns_ssutable_t *ssutable = NULL;
+ dns_fixedname_t tmpnamefixed;
+ dns_name_t *tmpname = NULL;
+ unsigned int options;
+ isc_boolean_t deleted_zsk;
+ dns_difftuple_t *tuple;
+ dns_rdata_dnskey_t dnskey;
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+#endif
+#if !defined(ALLOW_SECURE_TO_INSECURE) || !defined(ALLOW_INSECURE_TO_SECURE)
+ isc_boolean_t had_dnskey;
+#endif
+
+ INSIST(event->ev_type == DNS_EVENT_UPDATE);
+
+ dns_diff_init(mctx, &diff);
+ dns_diff_init(mctx, &temp);
+
+ CHECK(dns_zone_getdb(zone, &db));
+ zonename = dns_db_origin(db);
+ zoneclass = dns_db_class(db);
+ dns_zone_getssutable(zone, &ssutable);
+ dns_db_currentversion(db, &oldver);
+ CHECK(dns_db_newversion(db, &ver));
+
+ /*
+ * Check prerequisites.
+ */
+
+ for (result = dns_message_firstname(request, DNS_SECTION_PREREQUISITE);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(request, DNS_SECTION_PREREQUISITE))
+ {
+ dns_name_t *name = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_ttl_t ttl;
+ dns_rdataclass_t update_class;
+ isc_boolean_t flag;
+
+ get_current_rr(request, DNS_SECTION_PREREQUISITE, zoneclass,
+ &name, &rdata, &covers, &ttl, &update_class);
+
+ if (ttl != 0)
+ PREREQFAILC(DNS_R_FORMERR,
+ "prerequisite TTL is not zero");
+
+ if (! dns_name_issubdomain(name, zonename))
+ PREREQFAILN(DNS_R_NOTZONE, name,
+ "prerequisite name is out of zone");
+
+ if (update_class == dns_rdataclass_any) {
+ if (rdata.length != 0)
+ PREREQFAILC(DNS_R_FORMERR,
+ "class ANY prerequisite "
+ "RDATA is not empty");
+ if (rdata.type == dns_rdatatype_any) {
+ CHECK(name_exists(db, ver, name, &flag));
+ if (! flag) {
+ PREREQFAILN(DNS_R_NXDOMAIN, name,
+ "'name in use' "
+ "prerequisite not "
+ "satisfied");
+ }
+ } else {
+ CHECK(rrset_exists(db, ver, name,
+ rdata.type, covers, &flag));
+ if (! flag) {
+ /* RRset does not exist. */
+ PREREQFAILNT(DNS_R_NXRRSET, name, rdata.type,
+ "'rrset exists (value independent)' "
+ "prerequisite not satisfied");
+ }
+ }
+ } else if (update_class == dns_rdataclass_none) {
+ if (rdata.length != 0)
+ PREREQFAILC(DNS_R_FORMERR,
+ "class NONE prerequisite "
+ "RDATA is not empty");
+ if (rdata.type == dns_rdatatype_any) {
+ CHECK(name_exists(db, ver, name, &flag));
+ if (flag) {
+ PREREQFAILN(DNS_R_YXDOMAIN, name,
+ "'name not in use' "
+ "prerequisite not "
+ "satisfied");
+ }
+ } else {
+ CHECK(rrset_exists(db, ver, name,
+ rdata.type, covers, &flag));
+ if (flag) {
+ /* RRset exists. */
+ PREREQFAILNT(DNS_R_YXRRSET, name,
+ rdata.type,
+ "'rrset does not exist' "
+ "prerequisite not "
+ "satisfied");
+ }
+ }
+ } else if (update_class == zoneclass) {
+ /* "temp<rr.name, rr.type> += rr;" */
+ result = temp_append(&temp, name, &rdata);
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "temp entry creation failed: %s",
+ dns_result_totext(result));
+ FAIL(ISC_R_UNEXPECTED);
+ }
+ } else {
+ PREREQFAILC(DNS_R_FORMERR, "malformed prerequisite");
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ FAIL(result);
+
+
+ /*
+ * Perform the final check of the "rrset exists (value dependent)"
+ * prerequisites.
+ */
+ if (ISC_LIST_HEAD(temp.tuples) != NULL) {
+ dns_rdatatype_t type;
+
+ /*
+ * Sort the prerequisite records by owner name,
+ * type, and rdata.
+ */
+ result = dns_diff_sort(&temp, temp_order);
+ if (result != ISC_R_SUCCESS)
+ FAILC(result, "'RRset exists (value dependent)' "
+ "prerequisite not satisfied");
+
+ dns_fixedname_init(&tmpnamefixed);
+ tmpname = dns_fixedname_name(&tmpnamefixed);
+ result = temp_check(mctx, &temp, db, ver, tmpname, &type);
+ if (result != ISC_R_SUCCESS)
+ FAILNT(result, tmpname, type,
+ "'RRset exists (value dependent)' "
+ "prerequisite not satisfied");
+ }
+
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "prerequisites are OK");
+
+ /*
+ * Check Requestor's Permissions. It seems a bit silly to do this
+ * only after prerequisite testing, but that is what RFC2136 says.
+ */
+ result = ISC_R_SUCCESS;
+ if (ssutable == NULL)
+ CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone),
+ "update", zonename, ISC_FALSE, ISC_FALSE));
+ else if (client->signer == NULL && !TCPCLIENT(client))
+ CHECK(checkupdateacl(client, NULL, "update", zonename,
+ ISC_FALSE, ISC_TRUE));
+
+ if (dns_zone_getupdatedisabled(zone))
+ FAILC(DNS_R_REFUSED, "dynamic update temporarily disabled "
+ "because the zone is frozen. Use "
+ "'rndc thaw' to re-enable updates.");
+
+ /*
+ * Perform the Update Section Prescan.
+ */
+
+ for (result = dns_message_firstname(request, DNS_SECTION_UPDATE);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(request, DNS_SECTION_UPDATE))
+ {
+ dns_name_t *name = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_ttl_t ttl;
+ dns_rdataclass_t update_class;
+ get_current_rr(request, DNS_SECTION_UPDATE, zoneclass,
+ &name, &rdata, &covers, &ttl, &update_class);
+
+ if (! dns_name_issubdomain(name, zonename))
+ FAILC(DNS_R_NOTZONE,
+ "update RR is outside zone");
+ if (update_class == zoneclass) {
+ /*
+ * Check for meta-RRs. The RFC2136 pseudocode says
+ * check for ANY|AXFR|MAILA|MAILB, but the text adds
+ * "or any other QUERY metatype"
+ */
+ if (dns_rdatatype_ismeta(rdata.type)) {
+ FAILC(DNS_R_FORMERR,
+ "meta-RR in update");
+ }
+ result = dns_zone_checknames(zone, name, &rdata);
+ if (result != ISC_R_SUCCESS)
+ FAIL(DNS_R_REFUSED);
+ } else if (update_class == dns_rdataclass_any) {
+ if (ttl != 0 || rdata.length != 0 ||
+ (dns_rdatatype_ismeta(rdata.type) &&
+ rdata.type != dns_rdatatype_any))
+ FAILC(DNS_R_FORMERR,
+ "meta-RR in update");
+ } else if (update_class == dns_rdataclass_none) {
+ if (ttl != 0 ||
+ dns_rdatatype_ismeta(rdata.type))
+ FAILC(DNS_R_FORMERR,
+ "meta-RR in update");
+ } else {
+ update_log(client, zone, ISC_LOG_WARNING,
+ "update RR has incorrect class %d",
+ update_class);
+ FAIL(DNS_R_FORMERR);
+ }
+ /*
+ * draft-ietf-dnsind-simple-secure-update-01 says
+ * "Unlike traditional dynamic update, the client
+ * is forbidden from updating NSEC records."
+ */
+ if (dns_db_issecure(db)) {
+ if (rdata.type == dns_rdatatype_nsec3) {
+ FAILC(DNS_R_REFUSED,
+ "explicit NSEC3 updates are not allowed "
+ "in secure zones");
+ } else if (rdata.type == dns_rdatatype_nsec) {
+ FAILC(DNS_R_REFUSED,
+ "explicit NSEC updates are not allowed "
+ "in secure zones");
+ } else if (rdata.type == dns_rdatatype_rrsig &&
+ !dns_name_equal(name, zonename)) {
+ FAILC(DNS_R_REFUSED,
+ "explicit RRSIG updates are currently "
+ "not supported in secure zones except "
+ "at the apex");
+ }
+ }
+
+ if (ssutable != NULL) {
+ isc_netaddr_t *tcpaddr, netaddr;
+ /*
+ * If this is a TCP connection then pass the
+ * address of the client through for tcp-self
+ * and 6to4-self otherwise pass NULL. This
+ * provides weak address based authentication.
+ */
+ if (TCPCLIENT(client)) {
+ isc_netaddr_fromsockaddr(&netaddr,
+ &client->peeraddr);
+ tcpaddr = &netaddr;
+ } else
+ tcpaddr = NULL;
+ if (rdata.type != dns_rdatatype_any) {
+ if (!dns_ssutable_checkrules(ssutable,
+ client->signer,
+ name, tcpaddr,
+ rdata.type))
+ FAILC(DNS_R_REFUSED,
+ "rejected by secure update");
+ } else {
+ if (!ssu_checkall(db, ver, name, ssutable,
+ client->signer, tcpaddr))
+ FAILC(DNS_R_REFUSED,
+ "rejected by secure update");
+ }
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ FAIL(result);
+
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "update section prescan OK");
+
+ /*
+ * Process the Update Section.
+ */
+
+ options = dns_zone_getoptions(zone);
+ for (result = dns_message_firstname(request, DNS_SECTION_UPDATE);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(request, DNS_SECTION_UPDATE))
+ {
+ dns_name_t *name = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_ttl_t ttl;
+ dns_rdataclass_t update_class;
+ isc_boolean_t flag;
+
+ get_current_rr(request, DNS_SECTION_UPDATE, zoneclass,
+ &name, &rdata, &covers, &ttl, &update_class);
+
+ if (update_class == zoneclass) {
+
+ /*
+ * RFC1123 doesn't allow MF and MD in master zones. */
+ if (rdata.type == dns_rdatatype_md ||
+ rdata.type == dns_rdatatype_mf) {
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_rdatatype_format(rdata.type, typebuf,
+ sizeof(typebuf));
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "attempt to add %s ignored",
+ typebuf);
+ continue;
+ }
+ if ((rdata.type == dns_rdatatype_ns ||
+ rdata.type == dns_rdatatype_dname) &&
+ dns_name_iswildcard(name)) {
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_rdatatype_format(rdata.type, typebuf,
+ sizeof(typebuf));
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to add wildcard %s record "
+ "ignored", typebuf);
+ continue;
+ }
+ if (rdata.type == dns_rdatatype_cname) {
+ CHECK(cname_incompatible_rrset_exists(db, ver,
+ name,
+ &flag));
+ if (flag) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to add CNAME "
+ "alongside non-CNAME "
+ "ignored");
+ continue;
+ }
+ } else {
+ CHECK(rrset_exists(db, ver, name,
+ dns_rdatatype_cname, 0,
+ &flag));
+ if (flag &&
+ ! dns_rdatatype_isdnssec(rdata.type))
+ {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to add non-CNAME "
+ "alongside CNAME ignored");
+ continue;
+ }
+ }
+ if (rdata.type == dns_rdatatype_soa) {
+ isc_boolean_t ok;
+ CHECK(rrset_exists(db, ver, name,
+ dns_rdatatype_soa, 0,
+ &flag));
+ if (! flag) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to create 2nd "
+ "SOA ignored");
+ continue;
+ }
+ CHECK(check_soa_increment(db, ver, &rdata,
+ &ok));
+ if (! ok) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "SOA update failed to "
+ "increment serial, "
+ "ignoring it");
+ continue;
+ }
+ soa_serial_changed = ISC_TRUE;
+ }
+
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+ if (rdata.type == dns_rdatatype_nsec3param) {
+ /*
+ * Ignore attempts to add NSEC3PARAM records
+ * with any flags other than OPTOUT.
+ */
+ if ((rdata.data[1] & ~DNS_NSEC3FLAG_OPTOUT) != 0) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to add NSEC3PARAM "
+ "record with non OPTOUT "
+ "flag");
+ continue;
+ }
+
+ /*
+ * Set the NSEC3CHAIN creation flag.
+ */
+ INSIST(rdata.length <= sizeof(buf));
+ memcpy(buf, rdata.data, rdata.length);
+ buf[1] |= DNS_NSEC3FLAG_UPDATE;
+ rdata.data = buf;
+ /*
+ * Force the TTL to zero for NSEC3PARAM records.
+ */
+ ttl = 0;
+ }
+#else
+ if (rdata.type == dns_rdatatype_nsec3param) {
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "attempt to add NSEC3PARAM "
+ "record ignored");
+ continue;
+ };
+#endif
+
+ if ((options & DNS_ZONEOPT_CHECKWILDCARD) != 0 &&
+ dns_name_internalwildcard(name)) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namestr,
+ sizeof(namestr));
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "warning: ownername '%s' contains "
+ "a non-terminal wildcard", namestr);
+ }
+
+ if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char typestr[DNS_RDATATYPE_FORMATSIZE];
+ dns_name_format(name, namestr,
+ sizeof(namestr));
+ dns_rdatatype_format(rdata.type, typestr,
+ sizeof(typestr));
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "adding an RR at '%s' %s",
+ namestr, typestr);
+ }
+
+ /* Prepare the affected RRset for the addition. */
+ {
+ add_rr_prepare_ctx_t ctx;
+ ctx.db = db;
+ ctx.ver = ver;
+ ctx.diff = &diff;
+ ctx.name = name;
+ ctx.update_rr = &rdata;
+ ctx.update_rr_ttl = ttl;
+ ctx.ignore_add = ISC_FALSE;
+ dns_diff_init(mctx, &ctx.del_diff);
+ dns_diff_init(mctx, &ctx.add_diff);
+ CHECK(foreach_rr(db, ver, name, rdata.type,
+ covers, add_rr_prepare_action,
+ &ctx));
+
+ if (ctx.ignore_add) {
+ dns_diff_clear(&ctx.del_diff);
+ dns_diff_clear(&ctx.add_diff);
+ } else {
+ CHECK(do_diff(&ctx.del_diff, db, ver,
+ &diff));
+ CHECK(do_diff(&ctx.add_diff, db, ver,
+ &diff));
+ CHECK(update_one_rr(db, ver, &diff,
+ DNS_DIFFOP_ADD,
+ name, ttl, &rdata));
+ }
+ }
+ } else if (update_class == dns_rdataclass_any) {
+ if (rdata.type == dns_rdatatype_any) {
+ if (isc_log_wouldlog(ns_g_lctx,
+ LOGLEVEL_PROTOCOL))
+ {
+ char namestr[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, namestr,
+ sizeof(namestr));
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "delete all rrsets from "
+ "name '%s'", namestr);
+ }
+ if (dns_name_equal(name, zonename)) {
+ CHECK(delete_if(type_not_soa_nor_ns_p,
+ db, ver, name,
+ dns_rdatatype_any, 0,
+ &rdata, &diff));
+ } else {
+ CHECK(delete_if(type_not_dnssec,
+ db, ver, name,
+ dns_rdatatype_any, 0,
+ &rdata, &diff));
+ }
+#ifndef ALLOW_NSEC3PARAM_UPDATE
+ } else if (rdata.type == dns_rdatatype_nsec3param) {
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "attempt to delete a NSEC3PARAM "
+ "records ignored");
+ continue;
+#endif
+ } else if (dns_name_equal(name, zonename) &&
+ (rdata.type == dns_rdatatype_soa ||
+ rdata.type == dns_rdatatype_ns)) {
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "attempt to delete all SOA "
+ "or NS records ignored");
+ continue;
+ } else {
+ if (isc_log_wouldlog(ns_g_lctx,
+ LOGLEVEL_PROTOCOL))
+ {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char typestr[DNS_RDATATYPE_FORMATSIZE];
+ dns_name_format(name, namestr,
+ sizeof(namestr));
+ dns_rdatatype_format(rdata.type,
+ typestr,
+ sizeof(typestr));
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "deleting rrset at '%s' %s",
+ namestr, typestr);
+ }
+ CHECK(delete_if(true_p, db, ver, name,
+ rdata.type, covers, &rdata,
+ &diff));
+ }
+ } else if (update_class == dns_rdataclass_none) {
+ /*
+ * The (name == zonename) condition appears in
+ * RFC2136 3.4.2.4 but is missing from the pseudocode.
+ */
+ if (dns_name_equal(name, zonename)) {
+ if (rdata.type == dns_rdatatype_soa) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to delete SOA "
+ "ignored");
+ continue;
+ }
+ if (rdata.type == dns_rdatatype_ns) {
+ int count;
+ CHECK(rr_count(db, ver, name,
+ dns_rdatatype_ns,
+ 0, &count));
+ if (count == 1) {
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "attempt to "
+ "delete last "
+ "NS ignored");
+ continue;
+ }
+ }
+ }
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "deleting an RR");
+ CHECK(delete_if(rr_equal_p, db, ver, name,
+ rdata.type, covers, &rdata, &diff));
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ FAIL(result);
+
+ /*
+ * Check that any changes to DNSKEY/NSEC3PARAM records make sense.
+ * If they don't then back out all changes to DNSKEY/NSEC3PARAM
+ * records.
+ */
+ if (! ISC_LIST_EMPTY(diff.tuples))
+ CHECK(check_dnssec(client, zone, db, ver, &diff));
+
+ /*
+ * If any changes were made, increment the SOA serial number,
+ * update RRSIGs and NSECs (if zone is secure), and write the update
+ * to the journal.
+ */
+ if (! ISC_LIST_EMPTY(diff.tuples)) {
+ char *journalfile;
+ dns_journal_t *journal;
+ isc_boolean_t has_dnskey;
+
+ /*
+ * Increment the SOA serial, but only if it was not
+ * changed as a result of an update operation.
+ */
+ if (! soa_serial_changed) {
+ CHECK(increment_soa_serial(db, ver, &diff, mctx));
+ }
+
+ CHECK(check_mx(client, zone, db, ver, &diff));
+
+ CHECK(remove_orphaned_ds(db, ver, &diff));
+
+ CHECK(rrset_exists(db, ver, zonename, dns_rdatatype_dnskey,
+ 0, &has_dnskey));
+
+#if !defined(ALLOW_SECURE_TO_INSECURE) || !defined(ALLOW_INSECURE_TO_SECURE)
+ CHECK(rrset_exists(db, oldver, zonename, dns_rdatatype_dnskey,
+ 0, &had_dnskey));
+
+#ifndef ALLOW_SECURE_TO_INSECURE
+ if (had_dnskey && !has_dnskey) {
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "update rejected: all DNSKEY records "
+ "removed");
+ result = DNS_R_REFUSED;
+ goto failure;
+ }
+#endif
+#ifndef ALLOW_INSECURE_TO_SECURE
+ if (had_dnskey && !has_dnskey) {
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "update rejected: DNSKEY record added");
+ result = DNS_R_REFUSED;
+ goto failure;
+ }
+#endif
+#endif
+
+ CHECK(add_signing_records(db, zonename, ver,
+ dns_zone_getprivatetype(zone),
+ &diff));
+
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+ CHECK(add_nsec3param_records(client, zone, db, zonename,
+ ver, &diff));
+#endif
+
+ if (!has_dnskey) {
+ /*
+ * We are transitioning from secure to insecure.
+ * Cause all NSEC3 chains to be deleted. When the
+ * the last signature for the DNSKEY records are
+ * remove any NSEC chain present will also be removed.
+ */
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+ CHECK(delete_chains(db, ver, zonename, &diff));
+#endif
+ } else if (has_dnskey && dns_db_isdnssec(db)) {
+ isc_uint32_t interval;
+ interval = dns_zone_getsigvalidityinterval(zone);
+ result = update_signatures(client, zone, db, oldver,
+ ver, &diff, interval,
+ &deleted_zsk);
+ if (result != ISC_R_SUCCESS) {
+ update_log(client, zone,
+ ISC_LOG_ERROR,
+ "RRSIG/NSEC/NSEC3 update failed: %s",
+ isc_result_totext(result));
+ goto failure;
+ }
+ }
+
+ journalfile = dns_zone_getjournal(zone);
+ if (journalfile != NULL) {
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "writing journal %s", journalfile);
+
+ journal = NULL;
+ result = dns_journal_open(mctx, journalfile,
+ ISC_TRUE, &journal);
+ if (result != ISC_R_SUCCESS)
+ FAILS(result, "journal open failed");
+
+ result = dns_journal_write_transaction(journal, &diff);
+ if (result != ISC_R_SUCCESS) {
+ dns_journal_destroy(&journal);
+ FAILS(result, "journal write failed");
+ }
+
+ dns_journal_destroy(&journal);
+ }
+
+ /*
+ * XXXRTH Just a note that this committing code will have
+ * to change to handle databases that need two-phase
+ * commit, but this isn't a priority.
+ */
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "committing update transaction");
+
+ dns_db_closeversion(db, &ver, ISC_TRUE);
+
+ /*
+ * Mark the zone as dirty so that it will be written to disk.
+ */
+ dns_zone_markdirty(zone);
+
+ /*
+ * Notify slaves of the change we just made.
+ */
+ dns_zone_notify(zone);
+
+ /*
+ * Cause the zone to be signed with the key that we
+ * have just added or have the corresponding signatures
+ * deleted.
+ *
+ * Note: we are already committed to this course of action.
+ */
+ for (tuple = ISC_LIST_HEAD(diff.tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ isc_region_t r;
+ dns_secalg_t algorithm;
+ isc_uint16_t keyid;
+
+ if (tuple->rdata.type != dns_rdatatype_dnskey)
+ continue;
+
+ dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL);
+ if ((dnskey.flags &
+ (DNS_KEYFLAG_OWNERMASK|DNS_KEYTYPE_NOAUTH))
+ != DNS_KEYOWNER_ZONE)
+ continue;
+
+ dns_rdata_toregion(&tuple->rdata, &r);
+ algorithm = dnskey.algorithm;
+ keyid = dst_region_computeid(&r, algorithm);
+
+ result = dns_zone_signwithkey(zone, algorithm, keyid,
+ ISC_TF(tuple->op == DNS_DIFFOP_DEL));
+ if (result != ISC_R_SUCCESS) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "dns_zone_signwithkey failed: %s",
+ dns_result_totext(result));
+ }
+ }
+
+#ifdef ALLOW_NSEC3PARAM_UPDATE
+ /*
+ * Cause the zone to add/delete NSEC3 chains for the
+ * defered NSEC3PARAM changes.
+ *
+ * Note: we are already committed to this course of action.
+ */
+ for (tuple = ISC_LIST_HEAD(diff.tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ dns_rdata_nsec3param_t nsec3param;
+
+ if (tuple->rdata.type != dns_rdatatype_nsec3param ||
+ tuple->op != DNS_DIFFOP_ADD)
+ continue;
+
+ dns_rdata_tostruct(&tuple->rdata, &nsec3param, NULL);
+ if (nsec3param.flags == 0)
+ continue;
+
+ result = dns_zone_addnsec3chain(zone, &nsec3param);
+ if (result != ISC_R_SUCCESS) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "dns_zone_addnsec3chain failed: %s",
+ dns_result_totext(result));
+ }
+ }
+#endif
+ } else {
+ update_log(client, zone, LOGLEVEL_DEBUG, "redundant request");
+ dns_db_closeversion(db, &ver, ISC_TRUE);
+ }
+ result = ISC_R_SUCCESS;
+ goto common;
+
+ failure:
+ if (result == DNS_R_REFUSED)
+ inc_stats(zone, dns_nsstatscounter_updaterej);
+
+ /*
+ * The reason for failure should have been logged at this point.
+ */
+ if (ver != NULL) {
+ update_log(client, zone, LOGLEVEL_DEBUG,
+ "rolling back");
+ dns_db_closeversion(db, &ver, ISC_FALSE);
+ }
+
+ common:
+ dns_diff_clear(&temp);
+ dns_diff_clear(&diff);
+
+ if (oldver != NULL)
+ dns_db_closeversion(db, &oldver, ISC_FALSE);
+
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ if (ssutable != NULL)
+ dns_ssutable_detach(&ssutable);
+
+ isc_task_detach(&task);
+ uev->result = result;
+ if (zone != NULL)
+ INSIST(uev->zone == zone); /* we use this later */
+ uev->ev_type = DNS_EVENT_UPDATEDONE;
+ uev->ev_action = updatedone_action;
+ isc_task_send(client->task, &event);
+ INSIST(event == NULL);
+}
+
+static void
+updatedone_action(isc_task_t *task, isc_event_t *event) {
+ update_event_t *uev = (update_event_t *) event;
+ ns_client_t *client = (ns_client_t *) event->ev_arg;
+
+ UNUSED(task);
+
+ INSIST(event->ev_type == DNS_EVENT_UPDATEDONE);
+ INSIST(task == client->task);
+
+ INSIST(client->nupdates > 0);
+ switch (uev->result) {
+ case ISC_R_SUCCESS:
+ inc_stats(uev->zone, dns_nsstatscounter_updatedone);
+ break;
+ case DNS_R_REFUSED:
+ inc_stats(uev->zone, dns_nsstatscounter_updaterej);
+ break;
+ default:
+ inc_stats(uev->zone, dns_nsstatscounter_updatefail);
+ break;
+ }
+ if (uev->zone != NULL)
+ dns_zone_detach(&uev->zone);
+ client->nupdates--;
+ respond(client, uev->result);
+ isc_event_free(&event);
+ ns_client_detach(&client);
+}
+
+/*%
+ * Update forwarding support.
+ */
+
+static void
+forward_fail(isc_task_t *task, isc_event_t *event) {
+ ns_client_t *client = (ns_client_t *)event->ev_arg;
+
+ UNUSED(task);
+
+ INSIST(client->nupdates > 0);
+ client->nupdates--;
+ respond(client, DNS_R_SERVFAIL);
+ isc_event_free(&event);
+ ns_client_detach(&client);
+}
+
+
+static void
+forward_callback(void *arg, isc_result_t result, dns_message_t *answer) {
+ update_event_t *uev = arg;
+ ns_client_t *client = uev->ev_arg;
+ dns_zone_t *zone = uev->zone;
+
+ if (result != ISC_R_SUCCESS) {
+ INSIST(answer == NULL);
+ uev->ev_type = DNS_EVENT_UPDATEDONE;
+ uev->ev_action = forward_fail;
+ inc_stats(zone, dns_nsstatscounter_updatefwdfail);
+ } else {
+ uev->ev_type = DNS_EVENT_UPDATEDONE;
+ uev->ev_action = forward_done;
+ uev->answer = answer;
+ inc_stats(zone, dns_nsstatscounter_updaterespfwd);
+ }
+ isc_task_send(client->task, ISC_EVENT_PTR(&uev));
+ dns_zone_detach(&zone);
+}
+
+static void
+forward_done(isc_task_t *task, isc_event_t *event) {
+ update_event_t *uev = (update_event_t *) event;
+ ns_client_t *client = (ns_client_t *)event->ev_arg;
+
+ UNUSED(task);
+
+ INSIST(client->nupdates > 0);
+ client->nupdates--;
+ ns_client_sendraw(client, uev->answer);
+ dns_message_destroy(&uev->answer);
+ isc_event_free(&event);
+ ns_client_detach(&client);
+}
+
+static void
+forward_action(isc_task_t *task, isc_event_t *event) {
+ update_event_t *uev = (update_event_t *) event;
+ dns_zone_t *zone = uev->zone;
+ ns_client_t *client = (ns_client_t *)event->ev_arg;
+ isc_result_t result;
+
+ result = dns_zone_forwardupdate(zone, client->message,
+ forward_callback, event);
+ if (result != ISC_R_SUCCESS) {
+ uev->ev_type = DNS_EVENT_UPDATEDONE;
+ uev->ev_action = forward_fail;
+ isc_task_send(client->task, &event);
+ inc_stats(zone, dns_nsstatscounter_updatefwdfail);
+ dns_zone_detach(&zone);
+ } else
+ inc_stats(zone, dns_nsstatscounter_updatereqfwd);
+ isc_task_detach(&task);
+}
+
+static isc_result_t
+send_forward_event(ns_client_t *client, dns_zone_t *zone) {
+ isc_result_t result = ISC_R_SUCCESS;
+ update_event_t *event = NULL;
+ isc_task_t *zonetask = NULL;
+ ns_client_t *evclient;
+
+ event = (update_event_t *)
+ isc_event_allocate(client->mctx, client, DNS_EVENT_UPDATE,
+ forward_action, NULL, sizeof(*event));
+ if (event == NULL)
+ FAIL(ISC_R_NOMEMORY);
+ event->zone = zone;
+ event->result = ISC_R_SUCCESS;
+
+ evclient = NULL;
+ ns_client_attach(client, &evclient);
+ INSIST(client->nupdates == 0);
+ client->nupdates++;
+ event->ev_arg = evclient;
+
+ dns_zone_gettask(zone, &zonetask);
+ isc_task_send(zonetask, ISC_EVENT_PTR(&event));
+
+ failure:
+ if (event != NULL)
+ isc_event_free(ISC_EVENT_PTR(&event));
+ return (result);
+}
diff --git a/bin/named/win32/include/named/ntservice.h b/bin/named/win32/include/named/ntservice.h
new file mode 100644
index 0000000..0298388
--- /dev/null
+++ b/bin/named/win32/include/named/ntservice.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: ntservice.h,v 1.6 2007/06/19 23:46:59 tbox Exp $ */
+
+#ifndef NTSERVICE_H
+#define NTSERVICE_H
+
+#include <winsvc.h>
+
+#define BIND_DISPLAY_NAME "ISC BIND"
+#define BIND_SERVICE_NAME "named"
+
+void
+ntservice_init();
+void UpdateSCM(DWORD);
+void ServiceControl(DWORD dwCtrlCode);
+void
+ntservice_shutdown();
+BOOL ntservice_isservice();
+#endif
diff --git a/bin/named/win32/include/named/os.h b/bin/named/win32/include/named/os.h
new file mode 100644
index 0000000..c00e973
--- /dev/null
+++ b/bin/named/win32/include/named/os.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2004, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: os.h,v 1.14 2008/10/24 01:44:48 tbox Exp $ */
+
+#ifndef NS_OS_H
+#define NS_OS_H 1
+
+#include <isc/types.h>
+
+void
+ns_os_init(const char *progname);
+
+void
+ns_os_daemonize(void);
+
+void
+ns_os_opendevnull(void);
+
+void
+ns_os_closedevnull(void);
+
+void
+ns_os_chroot(const char *root);
+
+void
+ns_os_inituserinfo(const char *username);
+
+void
+ns_os_changeuser(void);
+
+void
+ns_os_adjustnofile(void);
+
+void
+ns_os_minprivs(void);
+
+void
+ns_os_writepidfile(const char *filename, isc_boolean_t first_time);
+
+void
+ns_os_shutdown(void);
+
+isc_result_t
+ns_os_gethostname(char *buf, size_t len);
+
+void
+ns_os_shutdownmsg(char *command, isc_buffer_t *text);
+
+void
+ns_os_tzset(void);
+
+void
+ns_os_started(void);
+
+#endif /* NS_OS_H */
diff --git a/bin/named/win32/named.dsp b/bin/named/win32/named.dsp
new file mode 100644
index 0000000..c7dec7d
--- /dev/null
+++ b/bin/named/win32/named.dsp
@@ -0,0 +1,327 @@
+# Microsoft Developer Studio Project File - Name="named" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=named - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "named.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "named.mak" CFG="named - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "named - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "named - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "./" /I "../../../" /I "../win32/include" /I "../include" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /I "../../../lib/isc/noatomic/include" /I "../../../lib/dns/win32/include" /I "../../../lib/dns/include" /I "../../../lib/isccc/include" /I "../../../lib/lwres/win32/include" /I "../../../lib/lwres/include" /I "../../../lib/isccfg/include" /I "../../../lib/bind9/include" /D "WIN32" /D "NDEBUG" /D "__STDC__" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 user32.lib advapi32.lib kernel32.lib ws2_32.lib ../../../lib/isc/win32/Release/libisc.lib ../../../lib/dns/win32/Release/libdns.lib ../../../lib/isccc/win32/Release/libisccc.lib ../../../lib/lwres/win32/Release/liblwres.lib ../../../lib/isccfg/win32/Release/libisccfg.lib ../../../lib/bind9/win32/Release/libbind9.lib /nologo /subsystem:console /machine:I386 /out:"../../../Build/Release/named.exe"
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "./" /I "../../../" /I "../win32/include" /I "../include" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /I "../../../lib/isc/noatomic/include" /I "../../../lib/dns/win32/include" /I "../../../lib/dns/include" /I "../../../lib/isccc/include" /I "../../../lib/lwres/win32/include" /I "../../../lib/lwres/include" /I "../../../lib/isccfg/include" /I "../../../lib/bind9/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "i386" /FR /FD /GZ /c
+# SUBTRACT CPP /X /YX
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 user32.lib advapi32.lib kernel32.lib ws2_32.lib ../../../lib/isc/win32/Debug/libisc.lib ../../../lib/dns/win32/Debug/libdns.lib ../../../lib/isccc/win32/Debug/libisccc.lib ../../../lib/lwres/win32/Debug/liblwres.lib ../../../lib/isccfg/win32/Debug/libisccfg.lib ../../../lib/bind9/win32/Debug/libbind9.lib /nologo /subsystem:console /map /debug /machine:I386 /out:"../../../Build/Debug/named.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "named - Win32 Release"
+# Name "named - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=..\builtin.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\client.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\config.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\control.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\controlconf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\interfacemgr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\listenlist.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\log.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\logconf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lwaddr.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lwdclient.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lwderror.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lwdgabn.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lwdgnba.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lwdgrbn.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lwdnoop.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lwresd.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\lwsearch.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\main.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\notify.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ntservice.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\os.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\query.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\server.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\sortlist.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\statschannel.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\tkeyconf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\tsigconf.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\update.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\xfrout.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\zoneconf.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=..\include\named\client.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\config.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\globals.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\interfacemgr.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\listenlist.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\log.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\logconf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\lwaddr.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\lwdclient.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\lwresd.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\lwsearch.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\main.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\notify.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\named\ntservice.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\omapi.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\include\named\os.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\query.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\server.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\sortlist.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\statschannel.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\tkeyconf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\tsigconf.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\update.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\xfrout.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\named\zoneconf.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/bin/named/win32/named.dsw b/bin/named/win32/named.dsw
new file mode 100644
index 0000000..c2913ef
--- /dev/null
+++ b/bin/named/win32/named.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "named"=".\named.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/bin/named/win32/named.mak b/bin/named/win32/named.mak
new file mode 100644
index 0000000..8f006c3
--- /dev/null
+++ b/bin/named/win32/named.mak
@@ -0,0 +1,1174 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on named.dsp
+!IF "$(CFG)" == ""
+CFG=named - Win32 Debug
+!MESSAGE No configuration specified. Defaulting to named - Win32 Debug.
+!ENDIF
+
+!IF "$(CFG)" != "named - Win32 Release" && "$(CFG)" != "named - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "named.mak" CFG="named - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "named - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "named - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "named - Win32 Release"
+_VC_MANIFEST_INC=0
+_VC_MANIFEST_BASENAME=__VC80
+!ELSE
+_VC_MANIFEST_INC=1
+_VC_MANIFEST_BASENAME=__VC80.Debug
+!ENDIF
+
+####################################################
+# Specifying name of temporary resource file used only in incremental builds:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+_VC_MANIFEST_AUTO_RES=$(_VC_MANIFEST_BASENAME).auto.res
+!else
+_VC_MANIFEST_AUTO_RES=
+!endif
+
+####################################################
+# _VC_MANIFEST_EMBED_EXE - command to embed manifest in EXE:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+#MT_SPECIAL_RETURN=1090650113
+#MT_SPECIAL_SWITCH=-notify_resource_update
+MT_SPECIAL_RETURN=0
+MT_SPECIAL_SWITCH=
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
+if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
+rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
+link $** /out:$@ $(LFLAGS)
+
+!else
+
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;1
+
+!endif
+
+####################################################
+# _VC_MANIFEST_EMBED_DLL - command to embed manifest in DLL:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+#MT_SPECIAL_RETURN=1090650113
+#MT_SPECIAL_SWITCH=-notify_resource_update
+MT_SPECIAL_RETURN=0
+MT_SPECIAL_SWITCH=
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
+if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
+rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
+link $** /out:$@ $(LFLAGS)
+
+!else
+
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;2
+
+!endif
+####################################################
+# _VC_MANIFEST_CLEAN - command to clean resources files generated temporarily:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+_VC_MANIFEST_CLEAN=-del $(_VC_MANIFEST_BASENAME).auto.res \
+ $(_VC_MANIFEST_BASENAME).auto.rc \
+ $(_VC_MANIFEST_BASENAME).auto.manifest
+
+!else
+
+_VC_MANIFEST_CLEAN=
+
+!endif
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "..\..\..\Build\Release\named.exe"
+
+!ELSE
+
+ALL : "libisccfg - Win32 Release" "libisccc - Win32 Release" "liblwres - Win32 Release" "libbind9 - Win32 Release" "libisc - Win32 Release" "libdns - Win32 Release" "..\..\..\Build\Release\named.exe"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libdns - Win32 ReleaseCLEAN" "libisc - Win32 ReleaseCLEAN" "libbind9 - Win32 ReleaseCLEAN" "liblwres - Win32 ReleaseCLEAN" "libisccc - Win32 ReleaseCLEAN" "libisccfg - Win32 ReleaseCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\builtin.obj"
+ -@erase "$(INTDIR)\client.obj"
+ -@erase "$(INTDIR)\config.obj"
+ -@erase "$(INTDIR)\control.obj"
+ -@erase "$(INTDIR)\controlconf.obj"
+ -@erase "$(INTDIR)\interfacemgr.obj"
+ -@erase "$(INTDIR)\listenlist.obj"
+ -@erase "$(INTDIR)\log.obj"
+ -@erase "$(INTDIR)\logconf.obj"
+ -@erase "$(INTDIR)\lwaddr.obj"
+ -@erase "$(INTDIR)\lwdclient.obj"
+ -@erase "$(INTDIR)\lwderror.obj"
+ -@erase "$(INTDIR)\lwdgabn.obj"
+ -@erase "$(INTDIR)\lwdgnba.obj"
+ -@erase "$(INTDIR)\lwdgrbn.obj"
+ -@erase "$(INTDIR)\lwdnoop.obj"
+ -@erase "$(INTDIR)\lwresd.obj"
+ -@erase "$(INTDIR)\lwsearch.obj"
+ -@erase "$(INTDIR)\main.obj"
+ -@erase "$(INTDIR)\notify.obj"
+ -@erase "$(INTDIR)\ntservice.obj"
+ -@erase "$(INTDIR)\os.obj"
+ -@erase "$(INTDIR)\query.obj"
+ -@erase "$(INTDIR)\server.obj"
+ -@erase "$(INTDIR)\sortlist.obj"
+ -@erase "$(INTDIR)\statschannel.obj"
+ -@erase "$(INTDIR)\tkeyconf.obj"
+ -@erase "$(INTDIR)\tsigconf.obj"
+ -@erase "$(INTDIR)\update.obj"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(INTDIR)\xfrout.obj"
+ -@erase "$(INTDIR)\zoneconf.obj"
+ -@erase "..\..\..\Build\Release\named.exe"
+ -@$(_VC_MANIFEST_CLEAN)
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "./" /I "../../../" /I "../win32/include" /I "../include" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /I "../../../lib/isc/noatomic/include" /I "../../../lib/dns/win32/include" /I "../../../lib/dns/include" /I "../../../lib/isccc/include" /I "../../../lib/lwres/win32/include" /I "../../../lib/lwres/include" /I "../../../lib/isccfg/include" /I "../../../lib/bind9/include" /D "WIN32" /D "NDEBUG" /D "__STDC__" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\named.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\named.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=user32.lib advapi32.lib kernel32.lib ws2_32.lib ../../../lib/isc/win32/Release/libisc.lib ../../../lib/dns/win32/Release/libdns.lib ../../../lib/isccc/win32/Release/libisccc.lib ../../../lib/lwres/win32/Release/liblwres.lib ../../../lib/isccfg/win32/Release/libisccfg.lib ../../../lib/bind9/win32/Release/libbind9.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\named.pdb" /machine:I386 /out:"../../../Build/Release/named.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\client.obj" \
+ "$(INTDIR)\config.obj" \
+ "$(INTDIR)\control.obj" \
+ "$(INTDIR)\controlconf.obj" \
+ "$(INTDIR)\interfacemgr.obj" \
+ "$(INTDIR)\listenlist.obj" \
+ "$(INTDIR)\log.obj" \
+ "$(INTDIR)\logconf.obj" \
+ "$(INTDIR)\lwaddr.obj" \
+ "$(INTDIR)\lwdclient.obj" \
+ "$(INTDIR)\lwderror.obj" \
+ "$(INTDIR)\lwdgabn.obj" \
+ "$(INTDIR)\lwdgnba.obj" \
+ "$(INTDIR)\lwdgrbn.obj" \
+ "$(INTDIR)\lwdnoop.obj" \
+ "$(INTDIR)\lwresd.obj" \
+ "$(INTDIR)\lwsearch.obj" \
+ "$(INTDIR)\main.obj" \
+ "$(INTDIR)\notify.obj" \
+ "$(INTDIR)\ntservice.obj" \
+ "$(INTDIR)\os.obj" \
+ "$(INTDIR)\query.obj" \
+ "$(INTDIR)\server.obj" \
+ "$(INTDIR)\sortlist.obj" \
+ "$(INTDIR)\statschannel.obj" \
+ "$(INTDIR)\tkeyconf.obj" \
+ "$(INTDIR)\tsigconf.obj" \
+ "$(INTDIR)\update.obj" \
+ "$(INTDIR)\xfrout.obj" \
+ "$(INTDIR)\zoneconf.obj" \
+ "$(INTDIR)\builtin.obj" \
+ "..\..\..\lib\dns\win32\Release\libdns.lib" \
+ "..\..\..\lib\isc\win32\Release\libisc.lib" \
+ "..\..\..\lib\bind9\win32\Release\libbind9.lib" \
+ "..\..\..\lib\lwres\win32\Release\liblwres.lib" \
+ "..\..\..\lib\isccc\win32\Release\libisccc.lib" \
+ "..\..\..\lib\isccfg\win32\Release\libisccfg.lib"
+
+"..\..\..\Build\Release\named.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+ $(_VC_MANIFEST_EMBED_EXE)
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "..\..\..\Build\Debug\named.exe" "$(OUTDIR)\named.bsc"
+
+!ELSE
+
+ALL : "libisccfg - Win32 Debug" "libisccc - Win32 Debug" "liblwres - Win32 Debug" "libbind9 - Win32 Debug" "libisc - Win32 Debug" "libdns - Win32 Debug" "..\..\..\Build\Debug\named.exe" "$(OUTDIR)\named.bsc"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libdns - Win32 DebugCLEAN" "libisc - Win32 DebugCLEAN" "libbind9 - Win32 DebugCLEAN" "liblwres - Win32 DebugCLEAN" "libisccc - Win32 DebugCLEAN" "libisccfg - Win32 DebugCLEAN"
+!ELSE
+CLEAN :
+!ENDIF
+ -@erase "$(INTDIR)\builtin.obj"
+ -@erase "$(INTDIR)\builtin.sbr"
+ -@erase "$(INTDIR)\client.obj"
+ -@erase "$(INTDIR)\client.sbr"
+ -@erase "$(INTDIR)\config.obj"
+ -@erase "$(INTDIR)\config.sbr"
+ -@erase "$(INTDIR)\control.obj"
+ -@erase "$(INTDIR)\control.sbr"
+ -@erase "$(INTDIR)\controlconf.obj"
+ -@erase "$(INTDIR)\controlconf.sbr"
+ -@erase "$(INTDIR)\interfacemgr.obj"
+ -@erase "$(INTDIR)\interfacemgr.sbr"
+ -@erase "$(INTDIR)\listenlist.obj"
+ -@erase "$(INTDIR)\listenlist.sbr"
+ -@erase "$(INTDIR)\log.obj"
+ -@erase "$(INTDIR)\log.sbr"
+ -@erase "$(INTDIR)\logconf.obj"
+ -@erase "$(INTDIR)\logconf.sbr"
+ -@erase "$(INTDIR)\lwaddr.obj"
+ -@erase "$(INTDIR)\lwaddr.sbr"
+ -@erase "$(INTDIR)\lwdclient.obj"
+ -@erase "$(INTDIR)\lwdclient.sbr"
+ -@erase "$(INTDIR)\lwderror.obj"
+ -@erase "$(INTDIR)\lwderror.sbr"
+ -@erase "$(INTDIR)\lwdgabn.obj"
+ -@erase "$(INTDIR)\lwdgabn.sbr"
+ -@erase "$(INTDIR)\lwdgnba.obj"
+ -@erase "$(INTDIR)\lwdgnba.sbr"
+ -@erase "$(INTDIR)\lwdgrbn.obj"
+ -@erase "$(INTDIR)\lwdgrbn.sbr"
+ -@erase "$(INTDIR)\lwdnoop.obj"
+ -@erase "$(INTDIR)\lwdnoop.sbr"
+ -@erase "$(INTDIR)\lwresd.obj"
+ -@erase "$(INTDIR)\lwresd.sbr"
+ -@erase "$(INTDIR)\lwsearch.obj"
+ -@erase "$(INTDIR)\lwsearch.sbr"
+ -@erase "$(INTDIR)\main.obj"
+ -@erase "$(INTDIR)\main.sbr"
+ -@erase "$(INTDIR)\notify.obj"
+ -@erase "$(INTDIR)\notify.sbr"
+ -@erase "$(INTDIR)\ntservice.obj"
+ -@erase "$(INTDIR)\ntservice.sbr"
+ -@erase "$(INTDIR)\os.obj"
+ -@erase "$(INTDIR)\os.sbr"
+ -@erase "$(INTDIR)\query.obj"
+ -@erase "$(INTDIR)\query.sbr"
+ -@erase "$(INTDIR)\server.obj"
+ -@erase "$(INTDIR)\server.sbr"
+ -@erase "$(INTDIR)\sortlist.obj"
+ -@erase "$(INTDIR)\sortlist.sbr"
+ -@erase "$(INTDIR)\statschannel.obj"
+ -@erase "$(INTDIR)\statschannel.sbr"
+ -@erase "$(INTDIR)\tkeyconf.obj"
+ -@erase "$(INTDIR)\tkeyconf.sbr"
+ -@erase "$(INTDIR)\tsigconf.obj"
+ -@erase "$(INTDIR)\tsigconf.sbr"
+ -@erase "$(INTDIR)\update.obj"
+ -@erase "$(INTDIR)\update.sbr"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(INTDIR)\vc60.pdb"
+ -@erase "$(INTDIR)\xfrout.obj"
+ -@erase "$(INTDIR)\xfrout.sbr"
+ -@erase "$(INTDIR)\zoneconf.obj"
+ -@erase "$(INTDIR)\zoneconf.sbr"
+ -@erase "$(OUTDIR)\named.bsc"
+ -@erase "$(OUTDIR)\named.map"
+ -@erase "$(OUTDIR)\named.pdb"
+ -@erase "..\..\..\Build\Debug\named.exe"
+ -@erase "..\..\..\Build\Debug\named.ilk"
+ -@$(_VC_MANIFEST_CLEAN)
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP_PROJ=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "./" /I "../../../" /I "../win32/include" /I "../include" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/include" /I "../../../lib/isc/noatomic/include" /I "../../../lib/dns/win32/include" /I "../../../lib/dns/include" /I "../../../lib/isccc/include" /I "../../../lib/lwres/win32/include" /I "../../../lib/lwres/include" /I "../../../lib/isccfg/include" /I "../../../lib/bind9/include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "i386" /FR"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\named.bsc"
+BSC32_SBRS= \
+ "$(INTDIR)\client.sbr" \
+ "$(INTDIR)\config.sbr" \
+ "$(INTDIR)\control.sbr" \
+ "$(INTDIR)\controlconf.sbr" \
+ "$(INTDIR)\interfacemgr.sbr" \
+ "$(INTDIR)\listenlist.sbr" \
+ "$(INTDIR)\log.sbr" \
+ "$(INTDIR)\logconf.sbr" \
+ "$(INTDIR)\lwaddr.sbr" \
+ "$(INTDIR)\lwdclient.sbr" \
+ "$(INTDIR)\lwderror.sbr" \
+ "$(INTDIR)\lwdgabn.sbr" \
+ "$(INTDIR)\lwdgnba.sbr" \
+ "$(INTDIR)\lwdgrbn.sbr" \
+ "$(INTDIR)\lwdnoop.sbr" \
+ "$(INTDIR)\lwresd.sbr" \
+ "$(INTDIR)\lwsearch.sbr" \
+ "$(INTDIR)\main.sbr" \
+ "$(INTDIR)\notify.sbr" \
+ "$(INTDIR)\ntservice.sbr" \
+ "$(INTDIR)\os.sbr" \
+ "$(INTDIR)\query.sbr" \
+ "$(INTDIR)\server.sbr" \
+ "$(INTDIR)\sortlist.sbr" \
+ "$(INTDIR)\statschannel.sbr" \
+ "$(INTDIR)\tkeyconf.sbr" \
+ "$(INTDIR)\tsigconf.sbr" \
+ "$(INTDIR)\update.sbr" \
+ "$(INTDIR)\xfrout.sbr" \
+ "$(INTDIR)\zoneconf.sbr" \
+ "$(INTDIR)\builtin.sbr"
+
+"$(OUTDIR)\named.bsc" : "$(OUTDIR)" $(BSC32_SBRS)
+ $(BSC32) @<<
+ $(BSC32_FLAGS) $(BSC32_SBRS)
+<<
+
+LINK32=link.exe
+LINK32_FLAGS=user32.lib advapi32.lib kernel32.lib ws2_32.lib ../../../lib/isc/win32/Debug/libisc.lib ../../../lib/dns/win32/Debug/libdns.lib ../../../lib/isccc/win32/Debug/libisccc.lib ../../../lib/lwres/win32/Debug/liblwres.lib ../../../lib/isccfg/win32/Debug/libisccfg.lib ../../../lib/bind9/win32/Debug/libbind9.lib /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\named.pdb" /map:"$(INTDIR)\named.map" /debug /machine:I386 /out:"../../../Build/Debug/named.exe" /pdbtype:sept
+LINK32_OBJS= \
+ "$(INTDIR)\client.obj" \
+ "$(INTDIR)\config.obj" \
+ "$(INTDIR)\control.obj" \
+ "$(INTDIR)\controlconf.obj" \
+ "$(INTDIR)\interfacemgr.obj" \
+ "$(INTDIR)\listenlist.obj" \
+ "$(INTDIR)\log.obj" \
+ "$(INTDIR)\logconf.obj" \
+ "$(INTDIR)\lwaddr.obj" \
+ "$(INTDIR)\lwdclient.obj" \
+ "$(INTDIR)\lwderror.obj" \
+ "$(INTDIR)\lwdgabn.obj" \
+ "$(INTDIR)\lwdgnba.obj" \
+ "$(INTDIR)\lwdgrbn.obj" \
+ "$(INTDIR)\lwdnoop.obj" \
+ "$(INTDIR)\lwresd.obj" \
+ "$(INTDIR)\lwsearch.obj" \
+ "$(INTDIR)\main.obj" \
+ "$(INTDIR)\notify.obj" \
+ "$(INTDIR)\ntservice.obj" \
+ "$(INTDIR)\os.obj" \
+ "$(INTDIR)\query.obj" \
+ "$(INTDIR)\server.obj" \
+ "$(INTDIR)\sortlist.obj" \
+ "$(INTDIR)\statschannel.obj" \
+ "$(INTDIR)\tkeyconf.obj" \
+ "$(INTDIR)\tsigconf.obj" \
+ "$(INTDIR)\update.obj" \
+ "$(INTDIR)\xfrout.obj" \
+ "$(INTDIR)\zoneconf.obj" \
+ "$(INTDIR)\builtin.obj" \
+ "..\..\..\lib\dns\win32\Debug\libdns.lib" \
+ "..\..\..\lib\isc\win32\Debug\libisc.lib" \
+ "..\..\..\lib\bind9\win32\Debug\libbind9.lib" \
+ "..\..\..\lib\lwres\win32\Debug\liblwres.lib" \
+ "..\..\..\lib\isccc\win32\Debug\libisccc.lib" \
+ "..\..\..\lib\isccfg\win32\Debug\libisccfg.lib"
+
+"..\..\..\Build\Debug\named.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+ $(_VC_MANIFEST_EMBED_EXE)
+
+!ENDIF
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("named.dep")
+!INCLUDE "named.dep"
+!ELSE
+!MESSAGE Warning: cannot find "named.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "named - Win32 Release" || "$(CFG)" == "named - Win32 Debug"
+SOURCE=..\builtin.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\builtin.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\builtin.obj" "$(INTDIR)\builtin.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\client.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\client.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\client.obj" "$(INTDIR)\client.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\config.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\config.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\config.obj" "$(INTDIR)\config.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\control.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\control.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\control.obj" "$(INTDIR)\control.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\controlconf.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\controlconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\controlconf.obj" "$(INTDIR)\controlconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\interfacemgr.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\interfacemgr.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\interfacemgr.obj" "$(INTDIR)\interfacemgr.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\listenlist.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\listenlist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\listenlist.obj" "$(INTDIR)\listenlist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\log.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\log.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\log.obj" "$(INTDIR)\log.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\logconf.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\logconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\logconf.obj" "$(INTDIR)\logconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwaddr.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\lwaddr.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\lwaddr.obj" "$(INTDIR)\lwaddr.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdclient.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\lwdclient.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\lwdclient.obj" "$(INTDIR)\lwdclient.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwderror.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\lwderror.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\lwderror.obj" "$(INTDIR)\lwderror.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdgabn.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\lwdgabn.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\lwdgabn.obj" "$(INTDIR)\lwdgabn.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdgnba.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\lwdgnba.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\lwdgnba.obj" "$(INTDIR)\lwdgnba.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdgrbn.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\lwdgrbn.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\lwdgrbn.obj" "$(INTDIR)\lwdgrbn.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdnoop.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\lwdnoop.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\lwdnoop.obj" "$(INTDIR)\lwdnoop.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwresd.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\lwresd.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\lwresd.obj" "$(INTDIR)\lwresd.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwsearch.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\lwsearch.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\lwsearch.obj" "$(INTDIR)\lwsearch.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\main.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\main.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\main.obj" "$(INTDIR)\main.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\notify.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\notify.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\notify.obj" "$(INTDIR)\notify.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\ntservice.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\ntservice.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\ntservice.obj" "$(INTDIR)\ntservice.sbr" : $(SOURCE) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=.\os.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\os.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\os.obj" "$(INTDIR)\os.sbr" : $(SOURCE) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=..\query.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\query.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\query.obj" "$(INTDIR)\query.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\server.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\server.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\server.obj" "$(INTDIR)\server.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\sortlist.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\sortlist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\sortlist.obj" "$(INTDIR)\sortlist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\statschannel.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\statschannel.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\statschannel.obj" "$(INTDIR)\statschannel.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tkeyconf.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\tkeyconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\tkeyconf.obj" "$(INTDIR)\tkeyconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tsigconf.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\tsigconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\tsigconf.obj" "$(INTDIR)\tsigconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\update.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\update.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\update.obj" "$(INTDIR)\update.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\xfrout.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\xfrout.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\xfrout.obj" "$(INTDIR)\xfrout.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\zoneconf.c
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+
+"$(INTDIR)\zoneconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+
+"$(INTDIR)\zoneconf.obj" "$(INTDIR)\zoneconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+"libdns - Win32 Release" :
+ cd "..\..\..\lib\dns\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - Win32 Release"
+ cd "..\..\..\bin\named\win32"
+
+"libdns - Win32 ReleaseCLEAN" :
+ cd "..\..\..\lib\dns\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+"libdns - Win32 Debug" :
+ cd "..\..\..\lib\dns\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - Win32 Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libdns - Win32 DebugCLEAN" :
+ cd "..\..\..\lib\dns\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+"libisc - Win32 Release" :
+ cd "..\..\..\lib\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - Win32 Release"
+ cd "..\..\..\bin\named\win32"
+
+"libisc - Win32 ReleaseCLEAN" :
+ cd "..\..\..\lib\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+"libisc - Win32 Debug" :
+ cd "..\..\..\lib\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - Win32 Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libisc - Win32 DebugCLEAN" :
+ cd "..\..\..\lib\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+"libbind9 - Win32 Release" :
+ cd "..\..\..\lib\bind9\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - Win32 Release"
+ cd "..\..\..\bin\named\win32"
+
+"libbind9 - Win32 ReleaseCLEAN" :
+ cd "..\..\..\lib\bind9\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+"libbind9 - Win32 Debug" :
+ cd "..\..\..\lib\bind9\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - Win32 Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libbind9 - Win32 DebugCLEAN" :
+ cd "..\..\..\lib\bind9\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+"liblwres - Win32 Release" :
+ cd "..\..\..\lib\lwres\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\liblwres.mak" CFG="liblwres - Win32 Release"
+ cd "..\..\..\bin\named\win32"
+
+"liblwres - Win32 ReleaseCLEAN" :
+ cd "..\..\..\lib\lwres\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\liblwres.mak" CFG="liblwres - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+"liblwres - Win32 Debug" :
+ cd "..\..\..\lib\lwres\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\liblwres.mak" CFG="liblwres - Win32 Debug"
+ cd "..\..\..\bin\named\win32"
+
+"liblwres - Win32 DebugCLEAN" :
+ cd "..\..\..\lib\lwres\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\liblwres.mak" CFG="liblwres - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+"libisccc - Win32 Release" :
+ cd "..\..\..\lib\isccc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccc.mak" CFG="libisccc - Win32 Release"
+ cd "..\..\..\bin\named\win32"
+
+"libisccc - Win32 ReleaseCLEAN" :
+ cd "..\..\..\lib\isccc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccc.mak" CFG="libisccc - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+"libisccc - Win32 Debug" :
+ cd "..\..\..\lib\isccc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccc.mak" CFG="libisccc - Win32 Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libisccc - Win32 DebugCLEAN" :
+ cd "..\..\..\lib\isccc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccc.mak" CFG="libisccc - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - Win32 Release"
+
+"libisccfg - Win32 Release" :
+ cd "..\..\..\lib\isccfg\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccfg.mak" CFG="libisccfg - Win32 Release"
+ cd "..\..\..\bin\named\win32"
+
+"libisccfg - Win32 ReleaseCLEAN" :
+ cd "..\..\..\lib\isccfg\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccfg.mak" CFG="libisccfg - Win32 Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - Win32 Debug"
+
+"libisccfg - Win32 Debug" :
+ cd "..\..\..\lib\isccfg\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccfg.mak" CFG="libisccfg - Win32 Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libisccfg - Win32 DebugCLEAN" :
+ cd "..\..\..\lib\isccfg\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccfg.mak" CFG="libisccfg - Win32 Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+
+!ENDIF
+
+####################################################
+# Commands to generate initial empty manifest file and the RC file
+# that references it, and for generating the .res file:
+
+$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_MANIFEST_BASENAME).auto.rc
+
+$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_MANIFEST_BASENAME).auto.manifest
+ type <<$@
+#include <winuser.h>
+1RT_MANIFEST"$(_VC_MANIFEST_BASENAME).auto.manifest"
+<< KEEP
+
+$(_VC_MANIFEST_BASENAME).auto.manifest :
+ type <<$@
+<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
+<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
+</assembly>
+<< KEEP
diff --git a/bin/named/win32/ntservice.c b/bin/named/win32/ntservice.c
new file mode 100644
index 0000000..74570cf
--- /dev/null
+++ b/bin/named/win32/ntservice.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2004, 2006, 2007 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: ntservice.c,v 1.12 2007/06/19 23:46:59 tbox Exp $ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include <isc/app.h>
+#include <isc/log.h>
+
+#include <named/globals.h>
+#include <named/ntservice.h>
+#include <named/main.h>
+#include <named/server.h>
+
+/* Handle to SCM for updating service status */
+static SERVICE_STATUS_HANDLE hServiceStatus = 0;
+static BOOL foreground = FALSE;
+static char ConsoleTitle[128];
+
+/*
+ * Forward declarations
+ */
+void ServiceControl(DWORD dwCtrlCode);
+void GetArgs(int *, char ***, char ***);
+int main(int, char *[], char *[]); /* From ns_main.c */
+
+/*
+ * Here we change the entry point for the executable to bindmain() from main()
+ * This allows us to invoke as a service or from the command line easily.
+ */
+#pragma comment(linker, "/entry:bindmain")
+
+/*
+ * This is the entry point for the executable
+ * We can now call main() explicitly or via StartServiceCtrlDispatcher()
+ * as we need to.
+ */
+int bindmain()
+{
+ int rc,
+ i = 1;
+
+ int argc;
+ char **envp, **argv;
+
+ /*
+ * We changed the entry point function, so we must initialize argv,
+ * etc. ourselves. Ick.
+ */
+ GetArgs(&argc, &argv, &envp);
+
+ /* Command line users should put -f in the options */
+ while (argv[i]) {
+ if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "-g")) {
+ foreground = TRUE;
+ break;
+ }
+ i++;
+ }
+
+ if (foreground) {
+ /* run in console window */
+ exit(main(argc, argv, envp));
+ } else {
+ /* Start up as service */
+ char *SERVICE_NAME = BIND_SERVICE_NAME;
+
+ SERVICE_TABLE_ENTRY dispatchTable[] = {
+ { TEXT(SERVICE_NAME), (LPSERVICE_MAIN_FUNCTION)main },
+ { NULL, NULL }
+ };
+
+ rc = StartServiceCtrlDispatcher(dispatchTable);
+ if (!rc) {
+ fprintf(stderr, "Use -f to run from the command line.\n");
+ exit(GetLastError());
+ }
+ }
+ exit(0);
+}
+
+/*
+ * Initialize the Service by registering it.
+ */
+void
+ntservice_init() {
+ if (!foreground) {
+ /* Register handler with the SCM */
+ hServiceStatus = RegisterServiceCtrlHandler(BIND_SERVICE_NAME,
+ (LPHANDLER_FUNCTION)ServiceControl);
+ if (!hServiceStatus) {
+ ns_main_earlyfatal(
+ "could not register service control handler");
+ UpdateSCM(SERVICE_STOPPED);
+ exit(1);
+ }
+ UpdateSCM(SERVICE_RUNNING);
+ } else {
+ strcpy(ConsoleTitle, "BIND Version ");
+ strcat(ConsoleTitle, VERSION);
+ SetConsoleTitle(ConsoleTitle);
+ }
+}
+
+void
+ntservice_shutdown() {
+ UpdateSCM(SERVICE_STOPPED);
+}
+/*
+ * Routine to check if this is a service or a foreground program
+ */
+BOOL
+ntservice_isservice() {
+ return(!foreground);
+}
+/*
+ * ServiceControl(): Handles requests from the SCM and passes them on
+ * to named.
+ */
+void
+ServiceControl(DWORD dwCtrlCode) {
+ /* Handle the requested control code */
+ switch(dwCtrlCode) {
+ case SERVICE_CONTROL_INTERROGATE:
+ UpdateSCM(0);
+ break;
+
+ case SERVICE_CONTROL_SHUTDOWN:
+ case SERVICE_CONTROL_STOP:
+ ns_server_flushonshutdown(ns_g_server, ISC_TRUE);
+ isc_app_shutdown();
+ UpdateSCM(SERVICE_STOPPED);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * Tell the Service Control Manager the state of the service.
+ */
+void UpdateSCM(DWORD state) {
+ SERVICE_STATUS ss;
+ static DWORD dwState = SERVICE_STOPPED;
+
+ if (hServiceStatus) {
+ if (state)
+ dwState = state;
+
+ memset(&ss, 0, sizeof(SERVICE_STATUS));
+ ss.dwServiceType |= SERVICE_WIN32_OWN_PROCESS;
+ ss.dwCurrentState = dwState;
+ ss.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_SHUTDOWN;
+ ss.dwCheckPoint = 0;
+ ss.dwServiceSpecificExitCode = 0;
+ ss.dwWin32ExitCode = NO_ERROR;
+ ss.dwWaitHint = dwState == SERVICE_STOP_PENDING ? 10000 : 1000;
+
+ if (!SetServiceStatus(hServiceStatus, &ss)) {
+ ss.dwCurrentState = SERVICE_STOPPED;
+ SetServiceStatus(hServiceStatus, &ss);
+ }
+ }
+}
+
+/*
+ * C-runtime stuff used to initialize the app and
+ * get argv, argc, envp.
+ */
+
+typedef struct
+{
+ int newmode;
+} _startupinfo;
+
+_CRTIMP void __cdecl __set_app_type(int);
+_CRTIMP void __cdecl __getmainargs(int *, char ***, char ***, int,
+ _startupinfo *);
+void __cdecl _setargv(void);
+
+#ifdef _M_IX86
+/* Pentium FDIV adjustment */
+extern int _adjust_fdiv;
+extern int * _imp___adjust_fdiv;
+/* Floating point precision */
+extern void _setdefaultprecision();
+#endif
+
+extern int _newmode; /* malloc new() handler mode */
+extern int _dowildcard; /* passed to __getmainargs() */
+
+typedef void (__cdecl *_PVFV)(void);
+extern void __cdecl _initterm(_PVFV *, _PVFV *);
+extern _PVFV *__onexitbegin;
+extern _PVFV *__onexitend;
+extern _CRTIMP char **__initenv;
+
+/*
+ * Do the work that mainCRTStartup() would normally do
+ */
+void GetArgs(int *argc, char ***argv, char ***envp)
+{
+ _startupinfo startinfo;
+
+ /*
+ * Set the app type to Console (check CRT/SRC/INTERNAL.H:
+ * \#define _CONSOLE_APP 1)
+ */
+ __set_app_type(1);
+
+ /* Mark this module as an EXE file */
+ __onexitbegin = __onexitend = (_PVFV *)(-1);
+
+ startinfo.newmode = _newmode;
+ __getmainargs(argc, argv, envp, _dowildcard, &startinfo);
+ __initenv = *envp;
+
+#ifdef _M_IX86
+ _adjust_fdiv = * _imp___adjust_fdiv;
+ _setdefaultprecision();
+#endif
+}
diff --git a/bin/named/win32/os.c b/bin/named/win32/os.c
new file mode 100644
index 0000000..4df4443
--- /dev/null
+++ b/bin/named/win32/os.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2004, 2005, 2007, 2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: os.c,v 1.31 2008/11/17 05:41:10 marka Exp $ */
+
+#include <config.h>
+#include <stdarg.h>
+
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <io.h>
+#include <process.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/strerror.h>
+#include <isc/string.h>
+#include <isc/ntpaths.h>
+#include <isc/util.h>
+#include <isc/win32os.h>
+
+#include <named/main.h>
+#include <named/log.h>
+#include <named/os.h>
+#include <named/globals.h>
+#include <named/ntservice.h>
+
+
+static char *pidfile = NULL;
+static int devnullfd = -1;
+
+static BOOL Initialized = FALSE;
+
+static char *version_error =
+ "named requires Windows 2000 Service Pack 2 or later to run correctly";
+
+void
+ns_paths_init() {
+ if (!Initialized)
+ isc_ntpaths_init();
+
+ ns_g_conffile = isc_ntpaths_get(NAMED_CONF_PATH);
+ lwresd_g_conffile = isc_ntpaths_get(LWRES_CONF_PATH);
+ lwresd_g_resolvconffile = isc_ntpaths_get(RESOLV_CONF_PATH);
+ ns_g_conffile = isc_ntpaths_get(NAMED_CONF_PATH);
+ ns_g_defaultpidfile = isc_ntpaths_get(NAMED_PID_PATH);
+ lwresd_g_defaultpidfile = isc_ntpaths_get(LWRESD_PID_PATH);
+ ns_g_keyfile = isc_ntpaths_get(RNDC_KEY_PATH);
+
+ Initialized = TRUE;
+}
+
+/*
+ * Due to Knowledge base article Q263823 we need to make sure that
+ * Windows 2000 systems have Service Pack 2 or later installed and
+ * warn when it isn't.
+ */
+static void
+version_check(const char *progname) {
+
+ if(isc_win32os_majorversion() < 5)
+ return; /* No problem with Version 4.0 */
+ if(isc_win32os_versioncheck(5, 0, 2, 0) < 0)
+ if (ntservice_isservice())
+ NTReportError(progname, version_error);
+ else
+ fprintf(stderr, "%s\n", version_error);
+}
+
+static void
+setup_syslog(const char *progname) {
+ int options;
+
+ options = LOG_PID;
+#ifdef LOG_NDELAY
+ options |= LOG_NDELAY;
+#endif
+
+ openlog(progname, options, LOG_DAEMON);
+}
+
+void
+ns_os_init(const char *progname) {
+ ns_paths_init();
+ setup_syslog(progname);
+ /*
+ * XXXMPA. We may need to split ntservice_init() in two and
+ * just mark as running in ns_os_started(). If we do that
+ * this is where the first part of ntservice_init() should be
+ * called from.
+ *
+ * XXX970 Remove comment if no problems by 9.7.0.
+ *
+ * ntservice_init();
+ */
+ version_check(progname);
+}
+
+void
+ns_os_daemonize(void) {
+ /*
+ * Try to set stdin, stdout, and stderr to /dev/null, but press
+ * on even if it fails.
+ */
+ if (devnullfd != -1) {
+ if (devnullfd != _fileno(stdin)) {
+ close(_fileno(stdin));
+ (void)_dup2(devnullfd, _fileno(stdin));
+ }
+ if (devnullfd != _fileno(stdout)) {
+ close(_fileno(stdout));
+ (void)_dup2(devnullfd, _fileno(stdout));
+ }
+ if (devnullfd != _fileno(stderr)) {
+ close(_fileno(stderr));
+ (void)_dup2(devnullfd, _fileno(stderr));
+ }
+ }
+}
+
+void
+ns_os_opendevnull(void) {
+ devnullfd = open("NUL", O_RDWR, 0);
+}
+
+void
+ns_os_closedevnull(void) {
+ if (devnullfd != _fileno(stdin) &&
+ devnullfd != _fileno(stdout) &&
+ devnullfd != _fileno(stderr)) {
+ close(devnullfd);
+ devnullfd = -1;
+ }
+}
+
+void
+ns_os_chroot(const char *root) {
+ if (root != NULL)
+ ns_main_earlyfatal("chroot(): isn't supported by Win32 API");
+}
+
+void
+ns_os_inituserinfo(const char *username) {
+}
+
+void
+ns_os_changeuser(void) {
+}
+
+void
+ns_os_adjustnofile(void) {
+}
+
+void
+ns_os_minprivs(void) {
+}
+
+static int
+safe_open(const char *filename, isc_boolean_t append) {
+ int fd;
+ struct stat sb;
+
+ if (stat(filename, &sb) == -1) {
+ if (errno != ENOENT)
+ return (-1);
+ } else if ((sb.st_mode & S_IFREG) == 0)
+ return (-1);
+
+ if (append)
+ fd = open(filename, O_WRONLY|O_CREAT|O_APPEND,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ else {
+ (void)unlink(filename);
+ fd = open(filename, O_WRONLY|O_CREAT|O_EXCL,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ }
+ return (fd);
+}
+
+static void
+cleanup_pidfile(void) {
+ if (pidfile != NULL) {
+ (void)unlink(pidfile);
+ free(pidfile);
+ }
+ pidfile = NULL;
+}
+
+void
+ns_os_writepidfile(const char *filename, isc_boolean_t first_time) {
+ int fd;
+ FILE *lockfile;
+ size_t len;
+ pid_t pid;
+ char strbuf[ISC_STRERRORSIZE];
+ void (*report)(const char *, ...);
+
+ /*
+ * The caller must ensure any required synchronization.
+ */
+
+ report = first_time ? ns_main_earlyfatal : ns_main_earlywarning;
+
+ cleanup_pidfile();
+
+ if (filename == NULL)
+ return;
+ len = strlen(filename);
+ pidfile = malloc(len + 1);
+ if (pidfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't malloc '%s': %s", filename, strbuf);
+ return;
+ }
+ /* This is safe. */
+ strcpy(pidfile, filename);
+
+ fd = safe_open(filename, ISC_FALSE);
+ if (fd < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't open pid file '%s': %s", filename, strbuf);
+ free(pidfile);
+ pidfile = NULL;
+ return;
+ }
+ lockfile = fdopen(fd, "w");
+ if (lockfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("could not fdopen() pid file '%s': %s",
+ filename, strbuf);
+ (void)close(fd);
+ cleanup_pidfile();
+ return;
+ }
+
+ pid = getpid();
+
+ if (fprintf(lockfile, "%ld\n", (long)pid) < 0) {
+ (*report)("fprintf() to pid file '%s' failed", filename);
+ (void)fclose(lockfile);
+ cleanup_pidfile();
+ return;
+ }
+ if (fflush(lockfile) == EOF) {
+ (*report)("fflush() to pid file '%s' failed", filename);
+ (void)fclose(lockfile);
+ cleanup_pidfile();
+ return;
+ }
+ (void)fclose(lockfile);
+}
+
+void
+ns_os_shutdown(void) {
+ closelog();
+ cleanup_pidfile();
+ ntservice_shutdown(); /* This MUST be the last thing done */
+}
+
+isc_result_t
+ns_os_gethostname(char *buf, size_t len) {
+ int n;
+
+ n = gethostname(buf, len);
+ return ((n == 0) ? ISC_R_SUCCESS : ISC_R_FAILURE);
+}
+
+void
+ns_os_shutdownmsg(char *command, isc_buffer_t *text) {
+ UNUSED(command);
+ UNUSED(text);
+}
+
+void
+ns_os_tzset(void) {
+#ifdef HAVE_TZSET
+ tzset();
+#endif
+}
+
+void
+ns_os_started(void) {
+ ntservice_init();
+}
diff --git a/bin/named/xfrout.c b/bin/named/xfrout.c
new file mode 100644
index 0000000..55b19f0
--- /dev/null
+++ b/bin/named/xfrout.c
@@ -0,0 +1,1842 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: xfrout.c,v 1.131 2008/10/29 02:54:20 marka Exp $ */
+
+#include <config.h>
+
+#include <isc/formatcheck.h>
+#include <isc/mem.h>
+#include <isc/timer.h>
+#include <isc/print.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#ifdef DLZ
+#include <dns/dlz.h>
+#endif
+#include <dns/fixedname.h>
+#include <dns/journal.h>
+#include <dns/message.h>
+#include <dns/peer.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/result.h>
+#include <dns/soa.h>
+#include <dns/stats.h>
+#include <dns/timer.h>
+#include <dns/tsig.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+#include <dns/zt.h>
+
+#include <named/client.h>
+#include <named/log.h>
+#include <named/server.h>
+#include <named/xfrout.h>
+
+/*! \file
+ * \brief
+ * Outgoing AXFR and IXFR.
+ */
+
+/*
+ * TODO:
+ * - IXFR over UDP
+ */
+
+#define XFROUT_COMMON_LOGARGS \
+ ns_g_lctx, DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT
+
+#define XFROUT_PROTOCOL_LOGARGS \
+ XFROUT_COMMON_LOGARGS, ISC_LOG_INFO
+
+#define XFROUT_DEBUG_LOGARGS(n) \
+ XFROUT_COMMON_LOGARGS, ISC_LOG_DEBUG(n)
+
+#define XFROUT_RR_LOGARGS \
+ XFROUT_COMMON_LOGARGS, XFROUT_RR_LOGLEVEL
+
+#define XFROUT_RR_LOGLEVEL ISC_LOG_DEBUG(8)
+
+/*%
+ * Fail unconditionally and log as a client error.
+ * The test against ISC_R_SUCCESS is there to keep the Solaris compiler
+ * from complaining about "end-of-loop code not reached".
+ */
+#define FAILC(code, msg) \
+ do { \
+ result = (code); \
+ ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
+ NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
+ "bad zone transfer request: %s (%s)", \
+ msg, isc_result_totext(code)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define FAILQ(code, msg, question, rdclass) \
+ do { \
+ char _buf1[DNS_NAME_FORMATSIZE]; \
+ char _buf2[DNS_RDATACLASS_FORMATSIZE]; \
+ result = (code); \
+ dns_name_format(question, _buf1, sizeof(_buf1)); \
+ dns_rdataclass_format(rdclass, _buf2, sizeof(_buf2)); \
+ ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT, \
+ NS_LOGMODULE_XFER_OUT, ISC_LOG_INFO, \
+ "bad zone transfer request: '%s/%s': %s (%s)", \
+ _buf1, _buf2, msg, isc_result_totext(code)); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+#define CHECK(op) \
+ do { result = (op); \
+ if (result != ISC_R_SUCCESS) goto failure; \
+ } while (0)
+
+/**************************************************************************/
+/*%
+ * A db_rr_iterator_t is an iterator that iterates over an entire database,
+ * returning one RR at a time, in some arbitrary order.
+ */
+
+typedef struct db_rr_iterator db_rr_iterator_t;
+
+/*% db_rr_iterator structure */
+struct db_rr_iterator {
+ isc_result_t result;
+ dns_db_t *db;
+ dns_dbiterator_t *dbit;
+ dns_dbversion_t *ver;
+ isc_stdtime_t now;
+ dns_dbnode_t *node;
+ dns_fixedname_t fixedname;
+ dns_rdatasetiter_t *rdatasetit;
+ dns_rdataset_t rdataset;
+ dns_rdata_t rdata;
+};
+
+static isc_result_t
+db_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
+ isc_stdtime_t now);
+
+static isc_result_t
+db_rr_iterator_first(db_rr_iterator_t *it);
+
+static isc_result_t
+db_rr_iterator_next(db_rr_iterator_t *it);
+
+static void
+db_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name,
+ isc_uint32_t *ttl, dns_rdata_t **rdata);
+
+static void
+db_rr_iterator_destroy(db_rr_iterator_t *it);
+
+static inline void
+inc_stats(dns_zone_t *zone, dns_statscounter_t counter) {
+ dns_generalstats_increment(ns_g_server->nsstats, counter);
+ if (zone != NULL) {
+ dns_stats_t *zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL)
+ dns_generalstats_increment(zonestats, counter);
+ }
+}
+
+static isc_result_t
+db_rr_iterator_init(db_rr_iterator_t *it, dns_db_t *db, dns_dbversion_t *ver,
+ isc_stdtime_t now)
+{
+ isc_result_t result;
+ it->db = db;
+ it->dbit = NULL;
+ it->ver = ver;
+ it->now = now;
+ it->node = NULL;
+ result = dns_db_createiterator(it->db, 0, &it->dbit);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ it->rdatasetit = NULL;
+ dns_rdata_init(&it->rdata);
+ dns_rdataset_init(&it->rdataset);
+ dns_fixedname_init(&it->fixedname);
+ INSIST(! dns_rdataset_isassociated(&it->rdataset));
+ it->result = ISC_R_SUCCESS;
+ return (it->result);
+}
+
+static isc_result_t
+db_rr_iterator_first(db_rr_iterator_t *it) {
+ it->result = dns_dbiterator_first(it->dbit);
+ /*
+ * The top node may be empty when out of zone glue exists.
+ * Walk the tree to find the first node with data.
+ */
+ while (it->result == ISC_R_SUCCESS) {
+ it->result = dns_dbiterator_current(it->dbit, &it->node,
+ dns_fixedname_name(&it->fixedname));
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+
+ it->result = dns_db_allrdatasets(it->db, it->node,
+ it->ver, it->now,
+ &it->rdatasetit);
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+
+ it->result = dns_rdatasetiter_first(it->rdatasetit);
+ if (it->result != ISC_R_SUCCESS) {
+ /*
+ * This node is empty. Try next node.
+ */
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ dns_db_detachnode(it->db, &it->node);
+ it->result = dns_dbiterator_next(it->dbit);
+ continue;
+ }
+ dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
+ it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
+ it->result = dns_rdataset_first(&it->rdataset);
+ return (it->result);
+ }
+ return (it->result);
+}
+
+
+static isc_result_t
+db_rr_iterator_next(db_rr_iterator_t *it) {
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+
+ INSIST(it->dbit != NULL);
+ INSIST(it->node != NULL);
+ INSIST(it->rdatasetit != NULL);
+
+ it->result = dns_rdataset_next(&it->rdataset);
+ if (it->result == ISC_R_NOMORE) {
+ dns_rdataset_disassociate(&it->rdataset);
+ it->result = dns_rdatasetiter_next(it->rdatasetit);
+ /*
+ * The while loop body is executed more than once
+ * only when an empty dbnode needs to be skipped.
+ */
+ while (it->result == ISC_R_NOMORE) {
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ dns_db_detachnode(it->db, &it->node);
+ it->result = dns_dbiterator_next(it->dbit);
+ if (it->result == ISC_R_NOMORE) {
+ /* We are at the end of the entire database. */
+ return (it->result);
+ }
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ it->result = dns_dbiterator_current(it->dbit,
+ &it->node,
+ dns_fixedname_name(&it->fixedname));
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ it->result = dns_db_allrdatasets(it->db, it->node,
+ it->ver, it->now,
+ &it->rdatasetit);
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ it->result = dns_rdatasetiter_first(it->rdatasetit);
+ }
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ dns_rdatasetiter_current(it->rdatasetit, &it->rdataset);
+ it->rdataset.attributes |= DNS_RDATASETATTR_LOADORDER;
+ it->result = dns_rdataset_first(&it->rdataset);
+ if (it->result != ISC_R_SUCCESS)
+ return (it->result);
+ }
+ return (it->result);
+}
+
+static void
+db_rr_iterator_pause(db_rr_iterator_t *it) {
+ RUNTIME_CHECK(dns_dbiterator_pause(it->dbit) == ISC_R_SUCCESS);
+}
+
+static void
+db_rr_iterator_destroy(db_rr_iterator_t *it) {
+ if (dns_rdataset_isassociated(&it->rdataset))
+ dns_rdataset_disassociate(&it->rdataset);
+ if (it->rdatasetit != NULL)
+ dns_rdatasetiter_destroy(&it->rdatasetit);
+ if (it->node != NULL)
+ dns_db_detachnode(it->db, &it->node);
+ dns_dbiterator_destroy(&it->dbit);
+}
+
+static void
+db_rr_iterator_current(db_rr_iterator_t *it, dns_name_t **name,
+ isc_uint32_t *ttl, dns_rdata_t **rdata)
+{
+ REQUIRE(name != NULL && *name == NULL);
+ REQUIRE(it->result == ISC_R_SUCCESS);
+ *name = dns_fixedname_name(&it->fixedname);
+ *ttl = it->rdataset.ttl;
+ dns_rdata_reset(&it->rdata);
+ dns_rdataset_current(&it->rdataset, &it->rdata);
+ *rdata = &it->rdata;
+}
+
+/**************************************************************************/
+
+/*% Log an RR (for debugging) */
+
+static void
+log_rr(dns_name_t *name, dns_rdata_t *rdata, isc_uint32_t ttl) {
+ isc_result_t result;
+ isc_buffer_t buf;
+ char mem[2000];
+ dns_rdatalist_t rdl;
+ dns_rdataset_t rds;
+ dns_rdata_t rd = DNS_RDATA_INIT;
+
+ rdl.type = rdata->type;
+ rdl.rdclass = rdata->rdclass;
+ rdl.ttl = ttl;
+ if (rdata->type == dns_rdatatype_sig ||
+ rdata->type == dns_rdatatype_rrsig)
+ rdl.covers = dns_rdata_covers(rdata);
+ else
+ rdl.covers = dns_rdatatype_none;
+ ISC_LIST_INIT(rdl.rdata);
+ ISC_LINK_INIT(&rdl, link);
+ dns_rdataset_init(&rds);
+ dns_rdata_init(&rd);
+ dns_rdata_clone(rdata, &rd);
+ ISC_LIST_APPEND(rdl.rdata, &rd, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(&rdl, &rds) == ISC_R_SUCCESS);
+
+ isc_buffer_init(&buf, mem, sizeof(mem));
+ result = dns_rdataset_totext(&rds, name,
+ ISC_FALSE, ISC_FALSE, &buf);
+
+ /*
+ * We could use xfrout_log(), but that would produce
+ * very long lines with a repetitive prefix.
+ */
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * Get rid of final newline.
+ */
+ INSIST(buf.used >= 1 &&
+ ((char *) buf.base)[buf.used - 1] == '\n');
+ buf.used--;
+
+ isc_log_write(XFROUT_RR_LOGARGS, "%.*s",
+ (int)isc_buffer_usedlength(&buf),
+ (char *)isc_buffer_base(&buf));
+ } else {
+ isc_log_write(XFROUT_RR_LOGARGS, "<RR too large to print>");
+ }
+}
+
+/**************************************************************************/
+/*
+ * An 'rrstream_t' is a polymorphic iterator that returns
+ * a stream of resource records. There are multiple implementations,
+ * e.g. for generating AXFR and IXFR records streams.
+ */
+
+typedef struct rrstream_methods rrstream_methods_t;
+
+typedef struct rrstream {
+ isc_mem_t *mctx;
+ rrstream_methods_t *methods;
+} rrstream_t;
+
+struct rrstream_methods {
+ isc_result_t (*first)(rrstream_t *);
+ isc_result_t (*next)(rrstream_t *);
+ void (*current)(rrstream_t *,
+ dns_name_t **,
+ isc_uint32_t *,
+ dns_rdata_t **);
+ void (*pause)(rrstream_t *);
+ void (*destroy)(rrstream_t **);
+};
+
+static void
+rrstream_noop_pause(rrstream_t *rs) {
+ UNUSED(rs);
+}
+
+/**************************************************************************/
+/*
+ * An 'ixfr_rrstream_t' is an 'rrstream_t' that returns
+ * an IXFR-like RR stream from a journal file.
+ *
+ * The SOA at the beginning of each sequence of additions
+ * or deletions are included in the stream, but the extra
+ * SOAs at the beginning and end of the entire transfer are
+ * not included.
+ */
+
+typedef struct ixfr_rrstream {
+ rrstream_t common;
+ dns_journal_t *journal;
+} ixfr_rrstream_t;
+
+/* Forward declarations. */
+static void
+ixfr_rrstream_destroy(rrstream_t **sp);
+
+static rrstream_methods_t ixfr_rrstream_methods;
+
+/*
+ * Returns: anything dns_journal_open() or dns_journal_iter_init()
+ * may return.
+ */
+
+static isc_result_t
+ixfr_rrstream_create(isc_mem_t *mctx,
+ const char *journal_filename,
+ isc_uint32_t begin_serial,
+ isc_uint32_t end_serial,
+ rrstream_t **sp)
+{
+ ixfr_rrstream_t *s;
+ isc_result_t result;
+
+ INSIST(sp != NULL && *sp == NULL);
+
+ s = isc_mem_get(mctx, sizeof(*s));
+ if (s == NULL)
+ return (ISC_R_NOMEMORY);
+ s->common.mctx = mctx;
+ s->common.methods = &ixfr_rrstream_methods;
+ s->journal = NULL;
+
+ CHECK(dns_journal_open(mctx, journal_filename,
+ ISC_FALSE, &s->journal));
+ CHECK(dns_journal_iter_init(s->journal, begin_serial, end_serial));
+
+ *sp = (rrstream_t *) s;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ ixfr_rrstream_destroy((rrstream_t **) (void *)&s);
+ return (result);
+}
+
+static isc_result_t
+ixfr_rrstream_first(rrstream_t *rs) {
+ ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
+ return (dns_journal_first_rr(s->journal));
+}
+
+static isc_result_t
+ixfr_rrstream_next(rrstream_t *rs) {
+ ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
+ return (dns_journal_next_rr(s->journal));
+}
+
+static void
+ixfr_rrstream_current(rrstream_t *rs,
+ dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ ixfr_rrstream_t *s = (ixfr_rrstream_t *) rs;
+ dns_journal_current_rr(s->journal, name, ttl, rdata);
+}
+
+static void
+ixfr_rrstream_destroy(rrstream_t **rsp) {
+ ixfr_rrstream_t *s = (ixfr_rrstream_t *) *rsp;
+ if (s->journal != 0)
+ dns_journal_destroy(&s->journal);
+ isc_mem_put(s->common.mctx, s, sizeof(*s));
+}
+
+static rrstream_methods_t ixfr_rrstream_methods = {
+ ixfr_rrstream_first,
+ ixfr_rrstream_next,
+ ixfr_rrstream_current,
+ rrstream_noop_pause,
+ ixfr_rrstream_destroy
+};
+
+/**************************************************************************/
+/*
+ * An 'axfr_rrstream_t' is an 'rrstream_t' that returns
+ * an AXFR-like RR stream from a database.
+ *
+ * The SOAs at the beginning and end of the transfer are
+ * not included in the stream.
+ */
+
+typedef struct axfr_rrstream {
+ rrstream_t common;
+ db_rr_iterator_t it;
+ isc_boolean_t it_valid;
+} axfr_rrstream_t;
+
+/*
+ * Forward declarations.
+ */
+static void
+axfr_rrstream_destroy(rrstream_t **rsp);
+
+static rrstream_methods_t axfr_rrstream_methods;
+
+static isc_result_t
+axfr_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
+ rrstream_t **sp)
+{
+ axfr_rrstream_t *s;
+ isc_result_t result;
+
+ INSIST(sp != NULL && *sp == NULL);
+
+ s = isc_mem_get(mctx, sizeof(*s));
+ if (s == NULL)
+ return (ISC_R_NOMEMORY);
+ s->common.mctx = mctx;
+ s->common.methods = &axfr_rrstream_methods;
+ s->it_valid = ISC_FALSE;
+
+ CHECK(db_rr_iterator_init(&s->it, db, ver, 0));
+ s->it_valid = ISC_TRUE;
+
+ *sp = (rrstream_t *) s;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ axfr_rrstream_destroy((rrstream_t **) (void *)&s);
+ return (result);
+}
+
+static isc_result_t
+axfr_rrstream_first(rrstream_t *rs) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ isc_result_t result;
+ result = db_rr_iterator_first(&s->it);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ /* Skip SOA records. */
+ for (;;) {
+ dns_name_t *name_dummy = NULL;
+ isc_uint32_t ttl_dummy;
+ dns_rdata_t *rdata = NULL;
+ db_rr_iterator_current(&s->it, &name_dummy,
+ &ttl_dummy, &rdata);
+ if (rdata->type != dns_rdatatype_soa)
+ break;
+ result = db_rr_iterator_next(&s->it);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+ return (result);
+}
+
+static isc_result_t
+axfr_rrstream_next(rrstream_t *rs) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ isc_result_t result;
+
+ /* Skip SOA records. */
+ for (;;) {
+ dns_name_t *name_dummy = NULL;
+ isc_uint32_t ttl_dummy;
+ dns_rdata_t *rdata = NULL;
+ result = db_rr_iterator_next(&s->it);
+ if (result != ISC_R_SUCCESS)
+ break;
+ db_rr_iterator_current(&s->it, &name_dummy,
+ &ttl_dummy, &rdata);
+ if (rdata->type != dns_rdatatype_soa)
+ break;
+ }
+ return (result);
+}
+
+static void
+axfr_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ db_rr_iterator_current(&s->it, name, ttl, rdata);
+}
+
+static void
+axfr_rrstream_pause(rrstream_t *rs) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ db_rr_iterator_pause(&s->it);
+}
+
+static void
+axfr_rrstream_destroy(rrstream_t **rsp) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) *rsp;
+ if (s->it_valid)
+ db_rr_iterator_destroy(&s->it);
+ isc_mem_put(s->common.mctx, s, sizeof(*s));
+}
+
+static rrstream_methods_t axfr_rrstream_methods = {
+ axfr_rrstream_first,
+ axfr_rrstream_next,
+ axfr_rrstream_current,
+ axfr_rrstream_pause,
+ axfr_rrstream_destroy
+};
+
+/**************************************************************************/
+/*
+ * An 'soa_rrstream_t' is a degenerate 'rrstream_t' that returns
+ * a single SOA record.
+ */
+
+typedef struct soa_rrstream {
+ rrstream_t common;
+ dns_difftuple_t *soa_tuple;
+} soa_rrstream_t;
+
+/*
+ * Forward declarations.
+ */
+static void
+soa_rrstream_destroy(rrstream_t **rsp);
+
+static rrstream_methods_t soa_rrstream_methods;
+
+static isc_result_t
+soa_rrstream_create(isc_mem_t *mctx, dns_db_t *db, dns_dbversion_t *ver,
+ rrstream_t **sp)
+{
+ soa_rrstream_t *s;
+ isc_result_t result;
+
+ INSIST(sp != NULL && *sp == NULL);
+
+ s = isc_mem_get(mctx, sizeof(*s));
+ if (s == NULL)
+ return (ISC_R_NOMEMORY);
+ s->common.mctx = mctx;
+ s->common.methods = &soa_rrstream_methods;
+ s->soa_tuple = NULL;
+
+ CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
+ &s->soa_tuple));
+
+ *sp = (rrstream_t *) s;
+ return (ISC_R_SUCCESS);
+
+ failure:
+ soa_rrstream_destroy((rrstream_t **) (void *)&s);
+ return (result);
+}
+
+static isc_result_t
+soa_rrstream_first(rrstream_t *rs) {
+ UNUSED(rs);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+soa_rrstream_next(rrstream_t *rs) {
+ UNUSED(rs);
+ return (ISC_R_NOMORE);
+}
+
+static void
+soa_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ soa_rrstream_t *s = (soa_rrstream_t *) rs;
+ *name = &s->soa_tuple->name;
+ *ttl = s->soa_tuple->ttl;
+ *rdata = &s->soa_tuple->rdata;
+}
+
+static void
+soa_rrstream_destroy(rrstream_t **rsp) {
+ soa_rrstream_t *s = (soa_rrstream_t *) *rsp;
+ if (s->soa_tuple != NULL)
+ dns_difftuple_free(&s->soa_tuple);
+ isc_mem_put(s->common.mctx, s, sizeof(*s));
+}
+
+static rrstream_methods_t soa_rrstream_methods = {
+ soa_rrstream_first,
+ soa_rrstream_next,
+ soa_rrstream_current,
+ rrstream_noop_pause,
+ soa_rrstream_destroy
+};
+
+/**************************************************************************/
+/*
+ * A 'compound_rrstream_t' objects owns a soa_rrstream
+ * and another rrstream, the "data stream". It returns
+ * a concatenated stream consisting of the soa_rrstream, then
+ * the data stream, then the soa_rrstream again.
+ *
+ * The component streams are owned by the compound_rrstream_t
+ * and are destroyed with it.
+ */
+
+typedef struct compound_rrstream {
+ rrstream_t common;
+ rrstream_t *components[3];
+ int state;
+ isc_result_t result;
+} compound_rrstream_t;
+
+/*
+ * Forward declarations.
+ */
+static void
+compound_rrstream_destroy(rrstream_t **rsp);
+
+static isc_result_t
+compound_rrstream_next(rrstream_t *rs);
+
+static rrstream_methods_t compound_rrstream_methods;
+
+/*
+ * Requires:
+ * soa_stream != NULL && *soa_stream != NULL
+ * data_stream != NULL && *data_stream != NULL
+ * sp != NULL && *sp == NULL
+ *
+ * Ensures:
+ * *soa_stream == NULL
+ * *data_stream == NULL
+ * *sp points to a valid compound_rrstream_t
+ * The soa and data streams will be destroyed
+ * when the compound_rrstream_t is destroyed.
+ */
+static isc_result_t
+compound_rrstream_create(isc_mem_t *mctx, rrstream_t **soa_stream,
+ rrstream_t **data_stream, rrstream_t **sp)
+{
+ compound_rrstream_t *s;
+
+ INSIST(sp != NULL && *sp == NULL);
+
+ s = isc_mem_get(mctx, sizeof(*s));
+ if (s == NULL)
+ return (ISC_R_NOMEMORY);
+ s->common.mctx = mctx;
+ s->common.methods = &compound_rrstream_methods;
+ s->components[0] = *soa_stream;
+ s->components[1] = *data_stream;
+ s->components[2] = *soa_stream;
+ s->state = -1;
+ s->result = ISC_R_FAILURE;
+
+ *soa_stream = NULL;
+ *data_stream = NULL;
+ *sp = (rrstream_t *) s;
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+compound_rrstream_first(rrstream_t *rs) {
+ compound_rrstream_t *s = (compound_rrstream_t *) rs;
+ s->state = 0;
+ do {
+ rrstream_t *curstream = s->components[s->state];
+ s->result = curstream->methods->first(curstream);
+ } while (s->result == ISC_R_NOMORE && s->state < 2);
+ return (s->result);
+}
+
+static isc_result_t
+compound_rrstream_next(rrstream_t *rs) {
+ compound_rrstream_t *s = (compound_rrstream_t *) rs;
+ rrstream_t *curstream = s->components[s->state];
+ s->result = curstream->methods->next(curstream);
+ while (s->result == ISC_R_NOMORE) {
+ /*
+ * Make sure locks held by the current stream
+ * are released before we switch streams.
+ */
+ curstream->methods->pause(curstream);
+ if (s->state == 2)
+ return (ISC_R_NOMORE);
+ s->state++;
+ curstream = s->components[s->state];
+ s->result = curstream->methods->first(curstream);
+ }
+ return (s->result);
+}
+
+static void
+compound_rrstream_current(rrstream_t *rs, dns_name_t **name, isc_uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ compound_rrstream_t *s = (compound_rrstream_t *) rs;
+ rrstream_t *curstream;
+ INSIST(0 <= s->state && s->state < 3);
+ INSIST(s->result == ISC_R_SUCCESS);
+ curstream = s->components[s->state];
+ curstream->methods->current(curstream, name, ttl, rdata);
+}
+
+static void
+compound_rrstream_pause(rrstream_t *rs)
+{
+ compound_rrstream_t *s = (compound_rrstream_t *) rs;
+ rrstream_t *curstream;
+ INSIST(0 <= s->state && s->state < 3);
+ curstream = s->components[s->state];
+ curstream->methods->pause(curstream);
+}
+
+static void
+compound_rrstream_destroy(rrstream_t **rsp) {
+ compound_rrstream_t *s = (compound_rrstream_t *) *rsp;
+ s->components[0]->methods->destroy(&s->components[0]);
+ s->components[1]->methods->destroy(&s->components[1]);
+ s->components[2] = NULL; /* Copy of components[0]. */
+ isc_mem_put(s->common.mctx, s, sizeof(*s));
+}
+
+static rrstream_methods_t compound_rrstream_methods = {
+ compound_rrstream_first,
+ compound_rrstream_next,
+ compound_rrstream_current,
+ compound_rrstream_pause,
+ compound_rrstream_destroy
+};
+
+/**************************************************************************/
+/*
+ * An 'xfrout_ctx_t' contains the state of an outgoing AXFR or IXFR
+ * in progress.
+ */
+
+typedef struct {
+ isc_mem_t *mctx;
+ ns_client_t *client;
+ unsigned int id; /* ID of request */
+ dns_name_t *qname; /* Question name of request */
+ dns_rdatatype_t qtype; /* dns_rdatatype_{a,i}xfr */
+ dns_rdataclass_t qclass;
+ dns_zone_t *zone; /* (necessary for stats) */
+ dns_db_t *db;
+ dns_dbversion_t *ver;
+ isc_quota_t *quota;
+ rrstream_t *stream; /* The XFR RR stream */
+ isc_boolean_t end_of_stream; /* EOS has been reached */
+ isc_buffer_t buf; /* Buffer for message owner
+ names and rdatas */
+ isc_buffer_t txlenbuf; /* Transmit length buffer */
+ isc_buffer_t txbuf; /* Transmit message buffer */
+ void *txmem;
+ unsigned int txmemlen;
+ unsigned int nmsg; /* Number of messages sent */
+ dns_tsigkey_t *tsigkey; /* Key used to create TSIG */
+ isc_buffer_t *lasttsig; /* the last TSIG */
+ isc_boolean_t many_answers;
+ int sends; /* Send in progress */
+ isc_boolean_t shuttingdown;
+ const char *mnemonic; /* Style of transfer */
+} xfrout_ctx_t;
+
+static isc_result_t
+xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client,
+ unsigned int id, dns_name_t *qname, dns_rdatatype_t qtype,
+ dns_rdataclass_t qclass, dns_zone_t *zone,
+ dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
+ rrstream_t *stream, dns_tsigkey_t *tsigkey,
+ isc_buffer_t *lasttsig,
+ unsigned int maxtime,
+ unsigned int idletime,
+ isc_boolean_t many_answers,
+ xfrout_ctx_t **xfrp);
+
+static void
+sendstream(xfrout_ctx_t *xfr);
+
+static void
+xfrout_senddone(isc_task_t *task, isc_event_t *event);
+
+static void
+xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg);
+
+static void
+xfrout_maybe_destroy(xfrout_ctx_t *xfr);
+
+static void
+xfrout_ctx_destroy(xfrout_ctx_t **xfrp);
+
+static void
+xfrout_client_shutdown(void *arg, isc_result_t result);
+
+static void
+xfrout_log1(ns_client_t *client, dns_name_t *zonename,
+ dns_rdataclass_t rdclass, int level,
+ const char *fmt, ...) ISC_FORMAT_PRINTF(5, 6);
+
+static void
+xfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...)
+ ISC_FORMAT_PRINTF(3, 4);
+
+/**************************************************************************/
+
+void
+ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
+ isc_result_t result;
+ dns_name_t *question_name;
+ dns_rdataset_t *question_rdataset;
+ dns_zone_t *zone = NULL;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_rdataclass_t question_class;
+ rrstream_t *soa_stream = NULL;
+ rrstream_t *data_stream = NULL;
+ rrstream_t *stream = NULL;
+ dns_difftuple_t *current_soa_tuple = NULL;
+ dns_name_t *soa_name;
+ dns_rdataset_t *soa_rdataset;
+ dns_rdata_t soa_rdata = DNS_RDATA_INIT;
+ isc_boolean_t have_soa = ISC_FALSE;
+ const char *mnemonic = NULL;
+ isc_mem_t *mctx = client->mctx;
+ dns_message_t *request = client->message;
+ xfrout_ctx_t *xfr = NULL;
+ isc_quota_t *quota = NULL;
+ dns_transfer_format_t format = client->view->transfer_format;
+ isc_netaddr_t na;
+ dns_peer_t *peer = NULL;
+ isc_buffer_t *tsigbuf = NULL;
+ char *journalfile;
+ char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")];
+ char keyname[DNS_NAME_FORMATSIZE];
+ isc_boolean_t is_poll = ISC_FALSE;
+#ifdef DLZ
+ isc_boolean_t is_dlz = ISC_FALSE;
+#endif
+
+ switch (reqtype) {
+ case dns_rdatatype_axfr:
+ mnemonic = "AXFR";
+ break;
+ case dns_rdatatype_ixfr:
+ mnemonic = "IXFR";
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+
+ ns_client_log(client,
+ DNS_LOGCATEGORY_XFER_OUT, NS_LOGMODULE_XFER_OUT,
+ ISC_LOG_DEBUG(6), "%s request", mnemonic);
+ /*
+ * Apply quota.
+ */
+ result = isc_quota_attach(&ns_g_server->xfroutquota, &quota);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(XFROUT_COMMON_LOGARGS, ISC_LOG_WARNING,
+ "%s request denied: %s", mnemonic,
+ isc_result_totext(result));
+ goto failure;
+ }
+
+ /*
+ * Interpret the question section.
+ */
+ result = dns_message_firstname(request, DNS_SECTION_QUESTION);
+ INSIST(result == ISC_R_SUCCESS);
+
+ /*
+ * The question section must contain exactly one question, and
+ * it must be for AXFR/IXFR as appropriate.
+ */
+ question_name = NULL;
+ dns_message_currentname(request, DNS_SECTION_QUESTION, &question_name);
+ question_rdataset = ISC_LIST_HEAD(question_name->list);
+ question_class = question_rdataset->rdclass;
+ INSIST(question_rdataset->type == reqtype);
+ if (ISC_LIST_NEXT(question_rdataset, link) != NULL)
+ FAILC(DNS_R_FORMERR, "multiple questions");
+ result = dns_message_nextname(request, DNS_SECTION_QUESTION);
+ if (result != ISC_R_NOMORE)
+ FAILC(DNS_R_FORMERR, "multiple questions");
+
+ result = dns_zt_find(client->view->zonetable, question_name, 0, NULL,
+ &zone);
+
+ if (result != ISC_R_SUCCESS)
+#ifdef DLZ
+ {
+ /*
+ * Normal zone table does not have a match. Try the DLZ database
+ */
+ if (client->view->dlzdatabase != NULL) {
+ result = dns_dlzallowzonexfr(client->view,
+ question_name, &client->peeraddr,
+ &db);
+
+ if (result == ISC_R_NOPERM) {
+ char _buf1[DNS_NAME_FORMATSIZE];
+ char _buf2[DNS_RDATACLASS_FORMATSIZE];
+
+ result = DNS_R_REFUSED;
+ dns_name_format(question_name, _buf1,
+ sizeof(_buf1));
+ dns_rdataclass_format(question_class,
+ _buf2, sizeof(_buf2));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_XFER_OUT,
+ ISC_LOG_ERROR,
+ "zone transfer '%s/%s' denied",
+ _buf1, _buf2);
+ goto failure;
+ }
+ if (result != ISC_R_SUCCESS)
+#endif
+ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
+ question_name, question_class);
+#ifdef DLZ
+ is_dlz = ISC_TRUE;
+ /*
+ * DLZ only support full zone transfer, not incremental
+ */
+ if (reqtype != dns_rdatatype_axfr) {
+ mnemonic = "AXFR-style IXFR";
+ reqtype = dns_rdatatype_axfr;
+ }
+
+ } else {
+ /*
+ * not DLZ and not in normal zone table, we are
+ * not authoritative
+ */
+ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
+ question_name, question_class);
+ }
+ } else {
+ /* zone table has a match */
+#endif
+ switch(dns_zone_gettype(zone)) {
+ case dns_zone_master:
+ case dns_zone_slave:
+ break; /* Master and slave zones are OK for transfer. */
+ default:
+ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class);
+ }
+ CHECK(dns_zone_getdb(zone, &db));
+ dns_db_currentversion(db, &ver);
+#ifdef DLZ
+ }
+#endif
+
+ xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
+ "%s question section OK", mnemonic);
+
+ /*
+ * Check the authority section. Look for a SOA record with
+ * the same name and class as the question.
+ */
+ for (result = dns_message_firstname(request, DNS_SECTION_AUTHORITY);
+ result == ISC_R_SUCCESS;
+ result = dns_message_nextname(request, DNS_SECTION_AUTHORITY))
+ {
+ soa_name = NULL;
+ dns_message_currentname(request, DNS_SECTION_AUTHORITY,
+ &soa_name);
+
+ /*
+ * Ignore data whose owner name is not the zone apex.
+ */
+ if (! dns_name_equal(soa_name, question_name))
+ continue;
+
+ for (soa_rdataset = ISC_LIST_HEAD(soa_name->list);
+ soa_rdataset != NULL;
+ soa_rdataset = ISC_LIST_NEXT(soa_rdataset, link))
+ {
+ /*
+ * Ignore non-SOA data.
+ */
+ if (soa_rdataset->type != dns_rdatatype_soa)
+ continue;
+ if (soa_rdataset->rdclass != question_class)
+ continue;
+
+ CHECK(dns_rdataset_first(soa_rdataset));
+ dns_rdataset_current(soa_rdataset, &soa_rdata);
+ result = dns_rdataset_next(soa_rdataset);
+ if (result == ISC_R_SUCCESS)
+ FAILC(DNS_R_FORMERR,
+ "IXFR authority section "
+ "has multiple SOAs");
+ have_soa = ISC_TRUE;
+ goto got_soa;
+ }
+ }
+ got_soa:
+ if (result != ISC_R_NOMORE)
+ CHECK(result);
+
+ xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
+ "%s authority section OK", mnemonic);
+
+ /*
+ * Decide whether to allow this transfer.
+ */
+#ifdef DLZ
+ /*
+ * if not a DLZ zone decide whether to allow this transfer.
+ */
+ if (!is_dlz) {
+#endif
+ ns_client_aclmsg("zone transfer", question_name, reqtype,
+ client->view->rdclass, msg, sizeof(msg));
+ CHECK(ns_client_checkacl(client, NULL, msg,
+ dns_zone_getxfracl(zone),
+ ISC_TRUE, ISC_LOG_ERROR));
+#ifdef DLZ
+ }
+#endif
+
+ /*
+ * AXFR over UDP is not possible.
+ */
+ if (reqtype == dns_rdatatype_axfr &&
+ (client->attributes & NS_CLIENTATTR_TCP) == 0)
+ FAILC(DNS_R_FORMERR, "attempted AXFR over UDP");
+
+ /*
+ * Look up the requesting server in the peer table.
+ */
+ isc_netaddr_fromsockaddr(&na, &client->peeraddr);
+ (void)dns_peerlist_peerbyaddr(client->view->peers, &na, &peer);
+
+ /*
+ * Decide on the transfer format (one-answer or many-answers).
+ */
+ if (peer != NULL)
+ (void)dns_peer_gettransferformat(peer, &format);
+
+ /*
+ * Get a dynamically allocated copy of the current SOA.
+ */
+#ifdef DLZ
+ if (is_dlz)
+ dns_db_currentversion(db, &ver);
+#endif
+ CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
+ &current_soa_tuple));
+
+ if (reqtype == dns_rdatatype_ixfr) {
+ isc_uint32_t begin_serial, current_serial;
+ isc_boolean_t provide_ixfr;
+
+ /*
+ * Outgoing IXFR may have been disabled for this peer
+ * or globally.
+ */
+ provide_ixfr = client->view->provideixfr;
+ if (peer != NULL)
+ (void) dns_peer_getprovideixfr(peer, &provide_ixfr);
+ if (provide_ixfr == ISC_FALSE)
+ goto axfr_fallback;
+
+ if (! have_soa)
+ FAILC(DNS_R_FORMERR,
+ "IXFR request missing SOA");
+
+ begin_serial = dns_soa_getserial(&soa_rdata);
+ current_serial = dns_soa_getserial(&current_soa_tuple->rdata);
+
+ /*
+ * RFC1995 says "If an IXFR query with the same or
+ * newer version number than that of the server
+ * is received, it is replied to with a single SOA
+ * record of the server's current version, just as
+ * in AXFR". The claim about AXFR is incorrect,
+ * but other than that, we do as the RFC says.
+ *
+ * Sending a single SOA record is also how we refuse
+ * IXFR over UDP (currently, we always do).
+ */
+ if (DNS_SERIAL_GE(begin_serial, current_serial) ||
+ (client->attributes & NS_CLIENTATTR_TCP) == 0)
+ {
+ CHECK(soa_rrstream_create(mctx, db, ver, &stream));
+ is_poll = ISC_TRUE;
+ goto have_stream;
+ }
+ journalfile = dns_zone_getjournal(zone);
+ if (journalfile != NULL)
+ result = ixfr_rrstream_create(mctx,
+ journalfile,
+ begin_serial,
+ current_serial,
+ &data_stream);
+ else
+ result = ISC_R_NOTFOUND;
+ if (result == ISC_R_NOTFOUND ||
+ result == ISC_R_RANGE) {
+ xfrout_log1(client, question_name, question_class,
+ ISC_LOG_DEBUG(4),
+ "IXFR version not in journal, "
+ "falling back to AXFR");
+ mnemonic = "AXFR-style IXFR";
+ goto axfr_fallback;
+ }
+ CHECK(result);
+ } else {
+ axfr_fallback:
+ CHECK(axfr_rrstream_create(mctx, db, ver,
+ &data_stream));
+ }
+
+ /*
+ * Bracket the the data stream with SOAs.
+ */
+ CHECK(soa_rrstream_create(mctx, db, ver, &soa_stream));
+ CHECK(compound_rrstream_create(mctx, &soa_stream, &data_stream,
+ &stream));
+ soa_stream = NULL;
+ data_stream = NULL;
+
+ have_stream:
+ CHECK(dns_message_getquerytsig(request, mctx, &tsigbuf));
+ /*
+ * Create the xfrout context object. This transfers the ownership
+ * of "stream", "db", "ver", and "quota" to the xfrout context object.
+ */
+
+
+
+#ifdef DLZ
+ if (is_dlz)
+ CHECK(xfrout_ctx_create(mctx, client, request->id, question_name,
+ reqtype, question_class, zone, db, ver,
+ quota, stream,
+ dns_message_gettsigkey(request),
+ tsigbuf,
+ 3600,
+ 3600,
+ (format == dns_many_answers) ?
+ ISC_TRUE : ISC_FALSE,
+ &xfr));
+ else
+#endif
+ CHECK(xfrout_ctx_create(mctx, client, request->id, question_name,
+ reqtype, question_class, zone, db, ver,
+ quota, stream,
+ dns_message_gettsigkey(request),
+ tsigbuf,
+ dns_zone_getmaxxfrout(zone),
+ dns_zone_getidleout(zone),
+ (format == dns_many_answers) ?
+ ISC_TRUE : ISC_FALSE,
+ &xfr));
+
+ xfr->mnemonic = mnemonic;
+ stream = NULL;
+ quota = NULL;
+
+ CHECK(xfr->stream->methods->first(xfr->stream));
+
+ if (xfr->tsigkey != NULL) {
+ dns_name_format(&xfr->tsigkey->name, keyname, sizeof(keyname));
+ } else
+ keyname[0] = '\0';
+ if (is_poll)
+ xfrout_log1(client, question_name, question_class,
+ ISC_LOG_DEBUG(1), "IXFR poll up to date%s%s",
+ (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname);
+ else
+ xfrout_log1(client, question_name, question_class,
+ ISC_LOG_INFO, "%s started%s%s", mnemonic,
+ (xfr->tsigkey != NULL) ? ": TSIG " : "", keyname);
+
+ /*
+ * Hand the context over to sendstream(). Set xfr to NULL;
+ * sendstream() is responsible for either passing the
+ * context on to a later event handler or destroying it.
+ */
+ sendstream(xfr);
+ xfr = NULL;
+
+ result = ISC_R_SUCCESS;
+
+ failure:
+ if (result == DNS_R_REFUSED)
+ inc_stats(zone, dns_nsstatscounter_xfrrej);
+ if (quota != NULL)
+ isc_quota_detach(&quota);
+ if (current_soa_tuple != NULL)
+ dns_difftuple_free(&current_soa_tuple);
+ if (stream != NULL)
+ stream->methods->destroy(&stream);
+ if (soa_stream != NULL)
+ soa_stream->methods->destroy(&soa_stream);
+ if (data_stream != NULL)
+ data_stream->methods->destroy(&data_stream);
+ if (ver != NULL)
+ dns_db_closeversion(db, &ver, ISC_FALSE);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ /* XXX kludge */
+ if (xfr != NULL) {
+ xfrout_fail(xfr, result, "setting up zone transfer");
+ } else if (result != ISC_R_SUCCESS) {
+ ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT,
+ NS_LOGMODULE_XFER_OUT,
+ ISC_LOG_DEBUG(3), "zone transfer setup failed");
+ ns_client_error(client, result);
+ }
+}
+
+static isc_result_t
+xfrout_ctx_create(isc_mem_t *mctx, ns_client_t *client, unsigned int id,
+ dns_name_t *qname, dns_rdatatype_t qtype,
+ dns_rdataclass_t qclass, dns_zone_t *zone,
+ dns_db_t *db, dns_dbversion_t *ver, isc_quota_t *quota,
+ rrstream_t *stream, dns_tsigkey_t *tsigkey,
+ isc_buffer_t *lasttsig, unsigned int maxtime,
+ unsigned int idletime, isc_boolean_t many_answers,
+ xfrout_ctx_t **xfrp)
+{
+ xfrout_ctx_t *xfr;
+ isc_result_t result;
+ unsigned int len;
+ void *mem;
+
+ INSIST(xfrp != NULL && *xfrp == NULL);
+ xfr = isc_mem_get(mctx, sizeof(*xfr));
+ if (xfr == NULL)
+ return (ISC_R_NOMEMORY);
+ xfr->mctx = mctx;
+ xfr->client = NULL;
+ ns_client_attach(client, &xfr->client);
+ xfr->id = id;
+ xfr->qname = qname;
+ xfr->qtype = qtype;
+ xfr->qclass = qclass;
+ xfr->zone = NULL;
+ xfr->db = NULL;
+ xfr->ver = NULL;
+ if (zone != NULL) /* zone will be NULL if it's DLZ */
+ dns_zone_attach(zone, &xfr->zone);
+ dns_db_attach(db, &xfr->db);
+ dns_db_attachversion(db, ver, &xfr->ver);
+ xfr->end_of_stream = ISC_FALSE;
+ xfr->tsigkey = tsigkey;
+ xfr->lasttsig = lasttsig;
+ xfr->txmem = NULL;
+ xfr->txmemlen = 0;
+ xfr->nmsg = 0;
+ xfr->many_answers = many_answers,
+ xfr->sends = 0;
+ xfr->shuttingdown = ISC_FALSE;
+ xfr->mnemonic = NULL;
+ xfr->buf.base = NULL;
+ xfr->buf.length = 0;
+ xfr->txmem = NULL;
+ xfr->txmemlen = 0;
+ xfr->stream = NULL;
+ xfr->quota = NULL;
+
+ /*
+ * Allocate a temporary buffer for the uncompressed response
+ * message data. The size should be no more than 65535 bytes
+ * so that the compressed data will fit in a TCP message,
+ * and no less than 65535 bytes so that an almost maximum-sized
+ * RR will fit. Note that although 65535-byte RRs are allowed
+ * in principle, they cannot be zone-transferred (at least not
+ * if uncompressible), because the message and RR headers would
+ * push the size of the TCP message over the 65536 byte limit.
+ */
+ len = 65535;
+ mem = isc_mem_get(mctx, len);
+ if (mem == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ isc_buffer_init(&xfr->buf, mem, len);
+
+ /*
+ * Allocate another temporary buffer for the compressed
+ * response message and its TCP length prefix.
+ */
+ len = 2 + 65535;
+ mem = isc_mem_get(mctx, len);
+ if (mem == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ isc_buffer_init(&xfr->txlenbuf, mem, 2);
+ isc_buffer_init(&xfr->txbuf, (char *) mem + 2, len - 2);
+ xfr->txmem = mem;
+ xfr->txmemlen = len;
+
+ CHECK(dns_timer_setidle(xfr->client->timer,
+ maxtime, idletime, ISC_FALSE));
+
+ /*
+ * Register a shutdown callback with the client, so that we
+ * can stop the transfer immediately when the client task
+ * gets a shutdown event.
+ */
+ xfr->client->shutdown = xfrout_client_shutdown;
+ xfr->client->shutdown_arg = xfr;
+ /*
+ * These MUST be after the last "goto failure;" / CHECK to
+ * prevent a double free by the caller.
+ */
+ xfr->quota = quota;
+ xfr->stream = stream;
+
+ *xfrp = xfr;
+ return (ISC_R_SUCCESS);
+
+failure:
+ xfrout_ctx_destroy(&xfr);
+ return (result);
+}
+
+
+/*
+ * Arrange to send as much as we can of "stream" without blocking.
+ *
+ * Requires:
+ * The stream iterator is initialized and points at an RR,
+ * or possiby at the end of the stream (that is, the
+ * _first method of the iterator has been called).
+ */
+static void
+sendstream(xfrout_ctx_t *xfr) {
+ dns_message_t *tcpmsg = NULL;
+ dns_message_t *msg = NULL; /* Client message if UDP, tcpmsg if TCP */
+ isc_result_t result;
+ isc_region_t used;
+ isc_region_t region;
+ dns_rdataset_t *qrdataset;
+ dns_name_t *msgname = NULL;
+ dns_rdata_t *msgrdata = NULL;
+ dns_rdatalist_t *msgrdl = NULL;
+ dns_rdataset_t *msgrds = NULL;
+ dns_compress_t cctx;
+ isc_boolean_t cleanup_cctx = ISC_FALSE;
+
+ int n_rrs;
+
+ isc_buffer_clear(&xfr->buf);
+ isc_buffer_clear(&xfr->txlenbuf);
+ isc_buffer_clear(&xfr->txbuf);
+
+ if ((xfr->client->attributes & NS_CLIENTATTR_TCP) == 0) {
+ /*
+ * In the UDP case, we put the response data directly into
+ * the client message.
+ */
+ msg = xfr->client->message;
+ CHECK(dns_message_reply(msg, ISC_TRUE));
+ } else {
+ /*
+ * TCP. Build a response dns_message_t, temporarily storing
+ * the raw, uncompressed owner names and RR data contiguously
+ * in xfr->buf. We know that if the uncompressed data fits
+ * in xfr->buf, the compressed data will surely fit in a TCP
+ * message.
+ */
+
+ CHECK(dns_message_create(xfr->mctx,
+ DNS_MESSAGE_INTENTRENDER, &tcpmsg));
+ msg = tcpmsg;
+
+ msg->id = xfr->id;
+ msg->rcode = dns_rcode_noerror;
+ msg->flags = DNS_MESSAGEFLAG_QR | DNS_MESSAGEFLAG_AA;
+ if ((xfr->client->attributes & NS_CLIENTATTR_RA) != 0)
+ msg->flags |= DNS_MESSAGEFLAG_RA;
+ CHECK(dns_message_settsigkey(msg, xfr->tsigkey));
+ CHECK(dns_message_setquerytsig(msg, xfr->lasttsig));
+ if (xfr->lasttsig != NULL)
+ isc_buffer_free(&xfr->lasttsig);
+
+ /*
+ * Include a question section in the first message only.
+ * BIND 8.2.1 will not recognize an IXFR if it does not
+ * have a question section.
+ */
+ if (xfr->nmsg == 0) {
+ dns_name_t *qname = NULL;
+ isc_region_t r;
+
+ /*
+ * Reserve space for the 12-byte message header
+ * and 4 bytes of question.
+ */
+ isc_buffer_add(&xfr->buf, 12 + 4);
+
+ qrdataset = NULL;
+ result = dns_message_gettemprdataset(msg, &qrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ dns_rdataset_init(qrdataset);
+ dns_rdataset_makequestion(qrdataset,
+ xfr->client->message->rdclass,
+ xfr->qtype);
+
+ result = dns_message_gettempname(msg, &qname);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ dns_name_init(qname, NULL);
+ isc_buffer_availableregion(&xfr->buf, &r);
+ INSIST(r.length >= xfr->qname->length);
+ r.length = xfr->qname->length;
+ isc_buffer_putmem(&xfr->buf, xfr->qname->ndata,
+ xfr->qname->length);
+ dns_name_fromregion(qname, &r);
+ ISC_LIST_INIT(qname->list);
+ ISC_LIST_APPEND(qname->list, qrdataset, link);
+
+ dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
+ }
+ else
+ msg->tcp_continuation = 1;
+ }
+
+ /*
+ * Try to fit in as many RRs as possible, unless "one-answer"
+ * format has been requested.
+ */
+ for (n_rrs = 0; ; n_rrs++) {
+ dns_name_t *name = NULL;
+ isc_uint32_t ttl;
+ dns_rdata_t *rdata = NULL;
+
+ unsigned int size;
+ isc_region_t r;
+
+ msgname = NULL;
+ msgrdata = NULL;
+ msgrdl = NULL;
+ msgrds = NULL;
+
+ xfr->stream->methods->current(xfr->stream,
+ &name, &ttl, &rdata);
+ size = name->length + 10 + rdata->length;
+ isc_buffer_availableregion(&xfr->buf, &r);
+ if (size >= r.length) {
+ /*
+ * RR would not fit. If there are other RRs in the
+ * buffer, send them now and leave this RR to the
+ * next message. If this RR overflows the buffer
+ * all by itself, fail.
+ *
+ * In theory some RRs might fit in a TCP message
+ * when compressed even if they do not fit when
+ * uncompressed, but surely we don't want
+ * to send such monstrosities to an unsuspecting
+ * slave.
+ */
+ if (n_rrs == 0) {
+ xfrout_log(xfr, ISC_LOG_WARNING,
+ "RR too large for zone transfer "
+ "(%d bytes)", size);
+ /* XXX DNS_R_RRTOOLARGE? */
+ result = ISC_R_NOSPACE;
+ goto failure;
+ }
+ break;
+ }
+
+ if (isc_log_wouldlog(ns_g_lctx, XFROUT_RR_LOGLEVEL))
+ log_rr(name, rdata, ttl); /* XXX */
+
+ result = dns_message_gettempname(msg, &msgname);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ dns_name_init(msgname, NULL);
+ isc_buffer_availableregion(&xfr->buf, &r);
+ INSIST(r.length >= name->length);
+ r.length = name->length;
+ isc_buffer_putmem(&xfr->buf, name->ndata, name->length);
+ dns_name_fromregion(msgname, &r);
+
+ /* Reserve space for RR header. */
+ isc_buffer_add(&xfr->buf, 10);
+
+ result = dns_message_gettemprdata(msg, &msgrdata);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ isc_buffer_availableregion(&xfr->buf, &r);
+ r.length = rdata->length;
+ isc_buffer_putmem(&xfr->buf, rdata->data, rdata->length);
+ dns_rdata_init(msgrdata);
+ dns_rdata_fromregion(msgrdata,
+ rdata->rdclass, rdata->type, &r);
+
+ result = dns_message_gettemprdatalist(msg, &msgrdl);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ msgrdl->type = rdata->type;
+ msgrdl->rdclass = rdata->rdclass;
+ msgrdl->ttl = ttl;
+ if (rdata->type == dns_rdatatype_sig ||
+ rdata->type == dns_rdatatype_rrsig)
+ msgrdl->covers = dns_rdata_covers(rdata);
+ else
+ msgrdl->covers = dns_rdatatype_none;
+ ISC_LINK_INIT(msgrdl, link);
+ ISC_LIST_INIT(msgrdl->rdata);
+ ISC_LIST_APPEND(msgrdl->rdata, msgrdata, link);
+
+ result = dns_message_gettemprdataset(msg, &msgrds);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ dns_rdataset_init(msgrds);
+ result = dns_rdatalist_tordataset(msgrdl, msgrds);
+ INSIST(result == ISC_R_SUCCESS);
+
+ ISC_LIST_APPEND(msgname->list, msgrds, link);
+
+ dns_message_addname(msg, msgname, DNS_SECTION_ANSWER);
+ msgname = NULL;
+
+ result = xfr->stream->methods->next(xfr->stream);
+ if (result == ISC_R_NOMORE) {
+ xfr->end_of_stream = ISC_TRUE;
+ break;
+ }
+ CHECK(result);
+
+ if (! xfr->many_answers)
+ break;
+ }
+
+ if ((xfr->client->attributes & NS_CLIENTATTR_TCP) != 0) {
+ CHECK(dns_compress_init(&cctx, -1, xfr->mctx));
+ dns_compress_setsensitive(&cctx, ISC_TRUE);
+ cleanup_cctx = ISC_TRUE;
+ CHECK(dns_message_renderbegin(msg, &cctx, &xfr->txbuf));
+ CHECK(dns_message_rendersection(msg, DNS_SECTION_QUESTION, 0));
+ CHECK(dns_message_rendersection(msg, DNS_SECTION_ANSWER, 0));
+ CHECK(dns_message_renderend(msg));
+ dns_compress_invalidate(&cctx);
+ cleanup_cctx = ISC_FALSE;
+
+ isc_buffer_usedregion(&xfr->txbuf, &used);
+ isc_buffer_putuint16(&xfr->txlenbuf,
+ (isc_uint16_t)used.length);
+ region.base = xfr->txlenbuf.base;
+ region.length = 2 + used.length;
+ xfrout_log(xfr, ISC_LOG_DEBUG(8),
+ "sending TCP message of %d bytes",
+ used.length);
+ CHECK(isc_socket_send(xfr->client->tcpsocket, /* XXX */
+ &region, xfr->client->task,
+ xfrout_senddone,
+ xfr));
+ xfr->sends++;
+ } else {
+ xfrout_log(xfr, ISC_LOG_DEBUG(8), "sending IXFR UDP response");
+ ns_client_send(xfr->client);
+ xfr->stream->methods->pause(xfr->stream);
+ xfrout_ctx_destroy(&xfr);
+ return;
+ }
+
+ /* Advance lasttsig to be the last TSIG generated */
+ CHECK(dns_message_getquerytsig(msg, xfr->mctx, &xfr->lasttsig));
+
+ xfr->nmsg++;
+
+ failure:
+ if (msgname != NULL) {
+ if (msgrds != NULL) {
+ if (dns_rdataset_isassociated(msgrds))
+ dns_rdataset_disassociate(msgrds);
+ dns_message_puttemprdataset(msg, &msgrds);
+ }
+ if (msgrdl != NULL) {
+ ISC_LIST_UNLINK(msgrdl->rdata, msgrdata, link);
+ dns_message_puttemprdatalist(msg, &msgrdl);
+ }
+ if (msgrdata != NULL)
+ dns_message_puttemprdata(msg, &msgrdata);
+ dns_message_puttempname(msg, &msgname);
+ }
+
+ if (tcpmsg != NULL)
+ dns_message_destroy(&tcpmsg);
+
+ if (cleanup_cctx)
+ dns_compress_invalidate(&cctx);
+ /*
+ * Make sure to release any locks held by database
+ * iterators before returning from the event handler.
+ */
+ xfr->stream->methods->pause(xfr->stream);
+
+ if (result == ISC_R_SUCCESS)
+ return;
+
+ xfrout_fail(xfr, result, "sending zone data");
+}
+
+static void
+xfrout_ctx_destroy(xfrout_ctx_t **xfrp) {
+ xfrout_ctx_t *xfr = *xfrp;
+
+ INSIST(xfr->sends == 0);
+
+ xfr->client->shutdown = NULL;
+ xfr->client->shutdown_arg = NULL;
+
+ if (xfr->stream != NULL)
+ xfr->stream->methods->destroy(&xfr->stream);
+ if (xfr->buf.base != NULL)
+ isc_mem_put(xfr->mctx, xfr->buf.base, xfr->buf.length);
+ if (xfr->txmem != NULL)
+ isc_mem_put(xfr->mctx, xfr->txmem, xfr->txmemlen);
+ if (xfr->lasttsig != NULL)
+ isc_buffer_free(&xfr->lasttsig);
+ if (xfr->quota != NULL)
+ isc_quota_detach(&xfr->quota);
+ if (xfr->ver != NULL)
+ dns_db_closeversion(xfr->db, &xfr->ver, ISC_FALSE);
+ if (xfr->zone != NULL)
+ dns_zone_detach(&xfr->zone);
+ if (xfr->db != NULL)
+ dns_db_detach(&xfr->db);
+
+ ns_client_detach(&xfr->client);
+
+ isc_mem_put(xfr->mctx, xfr, sizeof(*xfr));
+
+ *xfrp = NULL;
+}
+
+static void
+xfrout_senddone(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sev = (isc_socketevent_t *)event;
+ xfrout_ctx_t *xfr = (xfrout_ctx_t *)event->ev_arg;
+ isc_result_t evresult = sev->result;
+
+ UNUSED(task);
+
+ INSIST(event->ev_type == ISC_SOCKEVENT_SENDDONE);
+
+ isc_event_free(&event);
+ xfr->sends--;
+ INSIST(xfr->sends == 0);
+
+ (void)isc_timer_touch(xfr->client->timer);
+ if (xfr->shuttingdown == ISC_TRUE) {
+ xfrout_maybe_destroy(xfr);
+ } else if (evresult != ISC_R_SUCCESS) {
+ xfrout_fail(xfr, evresult, "send");
+ } else if (xfr->end_of_stream == ISC_FALSE) {
+ sendstream(xfr);
+ } else {
+ /* End of zone transfer stream. */
+ inc_stats(xfr->zone, dns_nsstatscounter_xfrdone);
+ xfrout_log(xfr, ISC_LOG_INFO, "%s ended", xfr->mnemonic);
+ ns_client_next(xfr->client, ISC_R_SUCCESS);
+ xfrout_ctx_destroy(&xfr);
+ }
+}
+
+static void
+xfrout_fail(xfrout_ctx_t *xfr, isc_result_t result, const char *msg) {
+ xfr->shuttingdown = ISC_TRUE;
+ xfrout_log(xfr, ISC_LOG_ERROR, "%s: %s",
+ msg, isc_result_totext(result));
+ xfrout_maybe_destroy(xfr);
+}
+
+static void
+xfrout_maybe_destroy(xfrout_ctx_t *xfr) {
+ INSIST(xfr->shuttingdown == ISC_TRUE);
+ if (xfr->sends > 0) {
+ /*
+ * If we are currently sending, cancel it and wait for
+ * cancel event before destroying the context.
+ */
+ isc_socket_cancel(xfr->client->tcpsocket, xfr->client->task,
+ ISC_SOCKCANCEL_SEND);
+ } else {
+ ns_client_next(xfr->client, ISC_R_CANCELED);
+ xfrout_ctx_destroy(&xfr);
+ }
+}
+
+static void
+xfrout_client_shutdown(void *arg, isc_result_t result) {
+ xfrout_ctx_t *xfr = (xfrout_ctx_t *) arg;
+ xfrout_fail(xfr, result, "aborted");
+}
+
+/*
+ * Log outgoing zone transfer messages in a format like
+ * <client>: transfer of <zone>: <message>
+ */
+
+static void
+xfrout_logv(ns_client_t *client, dns_name_t *zonename,
+ dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap)
+ ISC_FORMAT_PRINTF(5, 0);
+
+static void
+xfrout_logv(ns_client_t *client, dns_name_t *zonename,
+ dns_rdataclass_t rdclass, int level, const char *fmt, va_list ap)
+{
+ char msgbuf[2048];
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+
+ dns_name_format(zonename, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+ ns_client_log(client, DNS_LOGCATEGORY_XFER_OUT,
+ NS_LOGMODULE_XFER_OUT, level,
+ "transfer of '%s/%s': %s", namebuf, classbuf, msgbuf);
+}
+
+/*
+ * Logging function for use when a xfrout_ctx_t has not yet been created.
+ */
+static void
+xfrout_log1(ns_client_t *client, dns_name_t *zonename,
+ dns_rdataclass_t rdclass, int level, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ xfrout_logv(client, zonename, rdclass, level, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * Logging function for use when there is a xfrout_ctx_t.
+ */
+static void
+xfrout_log(xfrout_ctx_t *xfr, int level, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ xfrout_logv(xfr->client, xfr->qname, xfr->qclass, level, fmt, ap);
+ va_end(ap);
+}
diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c
new file mode 100644
index 0000000..7a420b5
--- /dev/null
+++ b/bin/named/zoneconf.c
@@ -0,0 +1,1016 @@
+/*
+ * Copyright (C) 2004-2008 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003 Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: zoneconf.c,v 1.147 2008/09/24 02:46:21 marka Exp $ */
+
+/*% */
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/rdatatype.h>
+#include <dns/ssu.h>
+#include <dns/stats.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#include <named/client.h>
+#include <named/config.h>
+#include <named/globals.h>
+#include <named/log.h>
+#include <named/server.h>
+#include <named/zoneconf.h>
+
+/* ACLs associated with zone */
+typedef enum {
+ allow_notify,
+ allow_query,
+ allow_transfer,
+ allow_update,
+ allow_update_forwarding
+} acl_type_t;
+
+/*%
+ * These are BIND9 server defaults, not necessarily identical to the
+ * library defaults defined in zone.c.
+ */
+#define RETERR(x) do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return (_r); \
+ } while (0)
+
+/*%
+ * Convenience function for configuring a single zone ACL.
+ */
+static isc_result_t
+configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
+ const cfg_obj_t *config, acl_type_t acltype,
+ cfg_aclconfctx_t *actx, dns_zone_t *zone,
+ void (*setzacl)(dns_zone_t *, dns_acl_t *),
+ void (*clearzacl)(dns_zone_t *))
+{
+ isc_result_t result;
+ const cfg_obj_t *maps[5] = {NULL, NULL, NULL, NULL, NULL};
+ const cfg_obj_t *aclobj = NULL;
+ int i = 0;
+ dns_acl_t **aclp = NULL, *acl = NULL;
+ const char *aclname;
+ dns_view_t *view;
+
+ view = dns_zone_getview(zone);
+
+ switch (acltype) {
+ case allow_notify:
+ if (view != NULL)
+ aclp = &view->notifyacl;
+ aclname = "allow-notify";
+ break;
+ case allow_query:
+ if (view != NULL)
+ aclp = &view->queryacl;
+ aclname = "allow-query";
+ break;
+ case allow_transfer:
+ if (view != NULL)
+ aclp = &view->transferacl;
+ aclname = "allow-transfer";
+ break;
+ case allow_update:
+ if (view != NULL)
+ aclp = &view->updateacl;
+ aclname = "allow-update";
+ break;
+ case allow_update_forwarding:
+ if (view != NULL)
+ aclp = &view->upfwdacl;
+ aclname = "allow-update-forwarding";
+ break;
+ default:
+ INSIST(0);
+ return (ISC_R_FAILURE);
+ }
+
+ /* First check to see if ACL is defined within the zone */
+ if (zconfig != NULL) {
+ maps[0] = cfg_tuple_get(zconfig, "options");
+ ns_config_get(maps, aclname, &aclobj);
+ if (aclobj != NULL) {
+ aclp = NULL;
+ goto parse_acl;
+ }
+ }
+
+ /* Failing that, see if there's a default ACL already in the view */
+ if (aclp != NULL && *aclp != NULL) {
+ (*setzacl)(zone, *aclp);
+ return (ISC_R_SUCCESS);
+ }
+
+ /* Check for default ACLs that haven't been parsed yet */
+ if (vconfig != NULL)
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ if (config != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL)
+ maps[i++] = options;
+ }
+ maps[i++] = ns_g_defaults;
+ maps[i] = NULL;
+
+ result = ns_config_get(maps, aclname, &aclobj);
+ if (aclobj == NULL) {
+ (*clearzacl)(zone);
+ return (ISC_R_SUCCESS);
+ }
+
+parse_acl:
+ result = cfg_acl_fromconfig(aclobj, config, ns_g_lctx, actx,
+ dns_zone_getmctx(zone), 0, &acl);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ (*setzacl)(zone, acl);
+
+ /* Set the view default now */
+ if (aclp != NULL)
+ dns_acl_attach(acl, aclp);
+
+ dns_acl_detach(&acl);
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Parse the zone update-policy statement.
+ */
+static isc_result_t
+configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone) {
+ const cfg_obj_t *updatepolicy = NULL;
+ const cfg_listelt_t *element, *element2;
+ dns_ssutable_t *table = NULL;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ isc_result_t result;
+
+ (void)cfg_map_get(zconfig, "update-policy", &updatepolicy);
+ if (updatepolicy == NULL) {
+ dns_zone_setssutable(zone, NULL);
+ return (ISC_R_SUCCESS);
+ }
+
+ result = dns_ssutable_create(mctx, &table);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ for (element = cfg_list_first(updatepolicy);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *stmt = cfg_listelt_value(element);
+ const cfg_obj_t *mode = cfg_tuple_get(stmt, "mode");
+ const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
+ const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
+ const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
+ const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
+ const char *str;
+ isc_boolean_t grant = ISC_FALSE;
+ unsigned int mtype = DNS_SSUMATCHTYPE_NAME;
+ dns_fixedname_t fname, fident;
+ isc_buffer_t b;
+ dns_rdatatype_t *types;
+ unsigned int i, n;
+
+ str = cfg_obj_asstring(mode);
+ if (strcasecmp(str, "grant") == 0)
+ grant = ISC_TRUE;
+ else if (strcasecmp(str, "deny") == 0)
+ grant = ISC_FALSE;
+ else
+ INSIST(0);
+
+ str = cfg_obj_asstring(matchtype);
+ if (strcasecmp(str, "name") == 0)
+ mtype = DNS_SSUMATCHTYPE_NAME;
+ else if (strcasecmp(str, "subdomain") == 0)
+ mtype = DNS_SSUMATCHTYPE_SUBDOMAIN;
+ else if (strcasecmp(str, "wildcard") == 0)
+ mtype = DNS_SSUMATCHTYPE_WILDCARD;
+ else if (strcasecmp(str, "self") == 0)
+ mtype = DNS_SSUMATCHTYPE_SELF;
+ else if (strcasecmp(str, "selfsub") == 0)
+ mtype = DNS_SSUMATCHTYPE_SELFSUB;
+ else if (strcasecmp(str, "selfwild") == 0)
+ mtype = DNS_SSUMATCHTYPE_SELFWILD;
+ else if (strcasecmp(str, "ms-self") == 0)
+ mtype = DNS_SSUMATCHTYPE_SELFMS;
+ else if (strcasecmp(str, "krb5-self") == 0)
+ mtype = DNS_SSUMATCHTYPE_SELFKRB5;
+ else if (strcasecmp(str, "ms-subdomain") == 0)
+ mtype = DNS_SSUMATCHTYPE_SUBDOMAINMS;
+ else if (strcasecmp(str, "krb5-subdomain") == 0)
+ mtype = DNS_SSUMATCHTYPE_SUBDOMAINKRB5;
+ else if (strcasecmp(str, "tcp-self") == 0)
+ mtype = DNS_SSUMATCHTYPE_TCPSELF;
+ else if (strcasecmp(str, "6to4-self") == 0)
+ mtype = DNS_SSUMATCHTYPE_6TO4SELF;
+ else
+ INSIST(0);
+
+ dns_fixedname_init(&fident);
+ str = cfg_obj_asstring(identity);
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
+ dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
+ "'%s' is not a valid name", str);
+ goto cleanup;
+ }
+
+ dns_fixedname_init(&fname);
+ str = cfg_obj_asstring(dname);
+ isc_buffer_init(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(dns_fixedname_name(&fname), &b,
+ dns_rootname, ISC_FALSE, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
+ "'%s' is not a valid name", str);
+ goto cleanup;
+ }
+
+ n = ns_config_listcount(typelist);
+ if (n == 0)
+ types = NULL;
+ else {
+ types = isc_mem_get(mctx, n * sizeof(dns_rdatatype_t));
+ if (types == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ }
+
+ i = 0;
+ for (element2 = cfg_list_first(typelist);
+ element2 != NULL;
+ element2 = cfg_list_next(element2))
+ {
+ const cfg_obj_t *typeobj;
+ isc_textregion_t r;
+
+ INSIST(i < n);
+
+ typeobj = cfg_listelt_value(element2);
+ str = cfg_obj_asstring(typeobj);
+ DE_CONST(str, r.base);
+ r.length = strlen(str);
+
+ result = dns_rdatatype_fromtext(&types[i++], &r);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
+ "'%s' is not a valid type", str);
+ isc_mem_put(mctx, types,
+ n * sizeof(dns_rdatatype_t));
+ goto cleanup;
+ }
+ }
+ INSIST(i == n);
+
+ result = dns_ssutable_addrule(table, grant,
+ dns_fixedname_name(&fident),
+ mtype,
+ dns_fixedname_name(&fname),
+ n, types);
+ if (types != NULL)
+ isc_mem_put(mctx, types, n * sizeof(dns_rdatatype_t));
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ }
+
+ result = ISC_R_SUCCESS;
+ dns_zone_setssutable(zone, table);
+
+ cleanup:
+ dns_ssutable_detach(&table);
+ return (result);
+}
+
+/*%
+ * Convert a config file zone type into a server zone type.
+ */
+static inline dns_zonetype_t
+zonetype_fromconfig(const cfg_obj_t *map) {
+ const cfg_obj_t *obj = NULL;
+ isc_result_t result;
+
+ result = cfg_map_get(map, "type", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ return (ns_config_getzonetype(obj));
+}
+
+/*%
+ * Helper function for strtoargv(). Pardon the gratuitous recursion.
+ */
+static isc_result_t
+strtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
+ char ***argvp, unsigned int n)
+{
+ isc_result_t result;
+
+ /* Discard leading whitespace. */
+ while (*s == ' ' || *s == '\t')
+ s++;
+
+ if (*s == '\0') {
+ /* We have reached the end of the string. */
+ *argcp = n;
+ *argvp = isc_mem_get(mctx, n * sizeof(char *));
+ if (*argvp == NULL)
+ return (ISC_R_NOMEMORY);
+ } else {
+ char *p = s;
+ while (*p != ' ' && *p != '\t' && *p != '\0')
+ p++;
+ if (*p != '\0')
+ *p++ = '\0';
+
+ result = strtoargvsub(mctx, p, argcp, argvp, n + 1);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ (*argvp)[n] = s;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Tokenize the string "s" into whitespace-separated words,
+ * return the number of words in '*argcp' and an array
+ * of pointers to the words in '*argvp'. The caller
+ * must free the array using isc_mem_put(). The string
+ * is modified in-place.
+ */
+static isc_result_t
+strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) {
+ return (strtoargvsub(mctx, s, argcp, argvp, 0));
+}
+
+static void
+checknames(dns_zonetype_t ztype, const cfg_obj_t **maps,
+ const cfg_obj_t **objp)
+{
+ const char *zone = NULL;
+ isc_result_t result;
+
+ switch (ztype) {
+ case dns_zone_slave: zone = "slave"; break;
+ case dns_zone_master: zone = "master"; break;
+ default:
+ INSIST(0);
+ }
+ result = ns_checknames_get(maps, zone, objp);
+ INSIST(result == ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
+ const cfg_obj_t *zconfig, cfg_aclconfctx_t *ac,
+ dns_zone_t *zone)
+{
+ isc_result_t result;
+ const char *zname;
+ dns_rdataclass_t zclass;
+ dns_rdataclass_t vclass;
+ const cfg_obj_t *maps[5];
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *obj;
+ const char *filename = NULL;
+ dns_notifytype_t notifytype = dns_notifytype_yes;
+ isc_sockaddr_t *addrs;
+ dns_name_t **keynames;
+ isc_uint32_t count;
+ char *cpval;
+ unsigned int dbargc;
+ char **dbargv;
+ static char default_dbtype[] = "rbt";
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ dns_dialuptype_t dialup = dns_dialuptype_no;
+ dns_zonetype_t ztype;
+ int i;
+ isc_int32_t journal_size;
+ isc_boolean_t multi;
+ isc_boolean_t alt;
+ dns_view_t *view;
+ isc_boolean_t check = ISC_FALSE, fail = ISC_FALSE;
+ isc_boolean_t warn = ISC_FALSE, ignore = ISC_FALSE;
+ isc_boolean_t ixfrdiff;
+ dns_masterformat_t masterformat;
+ dns_stats_t *zoneqrystats;
+ isc_boolean_t zonestats_on;
+ int seconds;
+
+ i = 0;
+ if (zconfig != NULL) {
+ zoptions = cfg_tuple_get(zconfig, "options");
+ maps[i++] = zoptions;
+ }
+ if (vconfig != NULL)
+ maps[i++] = cfg_tuple_get(vconfig, "options");
+ if (config != NULL) {
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL)
+ maps[i++] = options;
+ }
+ maps[i++] = ns_g_defaults;
+ maps[i++] = NULL;
+
+ if (vconfig != NULL)
+ RETERR(ns_config_getclass(cfg_tuple_get(vconfig, "class"),
+ dns_rdataclass_in, &vclass));
+ else
+ vclass = dns_rdataclass_in;
+
+ /*
+ * Configure values common to all zone types.
+ */
+
+ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+
+ RETERR(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
+ vclass, &zclass));
+ dns_zone_setclass(zone, zclass);
+
+ ztype = zonetype_fromconfig(zoptions);
+ dns_zone_settype(zone, ztype);
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "database", &obj);
+ if (result == ISC_R_SUCCESS)
+ cpval = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
+ else
+ cpval = default_dbtype;
+
+ if (cpval == NULL)
+ return(ISC_R_NOMEMORY);
+
+ result = strtoargv(mctx, cpval, &dbargc, &dbargv);
+ if (result != ISC_R_SUCCESS && cpval != default_dbtype) {
+ isc_mem_free(mctx, cpval);
+ return (result);
+ }
+
+ /*
+ * ANSI C is strange here. There is no logical reason why (char **)
+ * cannot be promoted automatically to (const char * const *) by the
+ * compiler w/o generating a warning.
+ */
+ result = dns_zone_setdbtype(zone, dbargc, (const char * const *)dbargv);
+ isc_mem_put(mctx, dbargv, dbargc * sizeof(*dbargv));
+ if (cpval != default_dbtype)
+ isc_mem_free(mctx, cpval);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "file", &obj);
+ if (result == ISC_R_SUCCESS)
+ filename = cfg_obj_asstring(obj);
+
+ masterformat = dns_masterformat_text;
+ obj = NULL;
+ result= ns_config_get(maps, "masterfile-format", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *masterformatstr = cfg_obj_asstring(obj);
+
+ if (strcasecmp(masterformatstr, "text") == 0)
+ masterformat = dns_masterformat_text;
+ else if (strcasecmp(masterformatstr, "raw") == 0)
+ masterformat = dns_masterformat_raw;
+ else
+ INSIST(0);
+ }
+ RETERR(dns_zone_setfile2(zone, filename, masterformat));
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "journal", &obj);
+ if (result == ISC_R_SUCCESS)
+ RETERR(dns_zone_setjournal(zone, cfg_obj_asstring(obj)));
+
+ if (ztype == dns_zone_slave)
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ allow_notify, ac, zone,
+ dns_zone_setnotifyacl,
+ dns_zone_clearnotifyacl));
+ /*
+ * XXXAG This probably does not make sense for stubs.
+ */
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ allow_query, ac, zone,
+ dns_zone_setqueryacl,
+ dns_zone_clearqueryacl));
+
+ obj = NULL;
+ result = ns_config_get(maps, "dialup", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj))
+ dialup = dns_dialuptype_yes;
+ else
+ dialup = dns_dialuptype_no;
+ } else {
+ const char *dialupstr = cfg_obj_asstring(obj);
+ if (strcasecmp(dialupstr, "notify") == 0)
+ dialup = dns_dialuptype_notify;
+ else if (strcasecmp(dialupstr, "notify-passive") == 0)
+ dialup = dns_dialuptype_notifypassive;
+ else if (strcasecmp(dialupstr, "refresh") == 0)
+ dialup = dns_dialuptype_refresh;
+ else if (strcasecmp(dialupstr, "passive") == 0)
+ dialup = dns_dialuptype_passive;
+ else
+ INSIST(0);
+ }
+ dns_zone_setdialup(zone, dialup);
+
+ obj = NULL;
+ result = ns_config_get(maps, "zone-statistics", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ zonestats_on = cfg_obj_asboolean(obj);
+ zoneqrystats = NULL;
+ if (zonestats_on) {
+ RETERR(dns_generalstats_create(mctx, &zoneqrystats,
+ dns_nsstatscounter_max));
+ }
+ dns_zone_setrequeststats(zone, zoneqrystats);
+ if (zoneqrystats != NULL)
+ dns_stats_detach(&zoneqrystats);
+
+ /*
+ * Configure master functionality. This applies
+ * to primary masters (type "master") and slaves
+ * acting as masters (type "slave"), but not to stubs.
+ */
+ if (ztype != dns_zone_stub) {
+ obj = NULL;
+ result = ns_config_get(maps, "notify", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj))
+ notifytype = dns_notifytype_yes;
+ else
+ notifytype = dns_notifytype_no;
+ } else {
+ const char *notifystr = cfg_obj_asstring(obj);
+ if (strcasecmp(notifystr, "explicit") == 0)
+ notifytype = dns_notifytype_explicit;
+ else if (strcasecmp(notifystr, "master-only") == 0)
+ notifytype = dns_notifytype_masteronly;
+ else
+ INSIST(0);
+ }
+ dns_zone_setnotifytype(zone, notifytype);
+
+ obj = NULL;
+ result = ns_config_get(maps, "also-notify", &obj);
+ if (result == ISC_R_SUCCESS) {
+ isc_sockaddr_t *addrs = NULL;
+ isc_uint32_t addrcount;
+ result = ns_config_getiplist(config, obj, 0, mctx,
+ &addrs, &addrcount);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_zone_setalsonotify(zone, addrs,
+ addrcount);
+ ns_config_putiplist(mctx, &addrs, addrcount);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else
+ RETERR(dns_zone_setalsonotify(zone, NULL, 0));
+
+ obj = NULL;
+ result = ns_config_get(maps, "notify-source", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setnotifysrc4(zone, cfg_obj_assockaddr(obj)));
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "notify-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setnotifysrc6(zone, cfg_obj_assockaddr(obj)));
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "notify-to-soa", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOTIFYTOSOA,
+ cfg_obj_asboolean(obj));
+
+ dns_zone_setisself(zone, ns_client_isself, NULL);
+
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ allow_transfer, ac, zone,
+ dns_zone_setxfracl,
+ dns_zone_clearxfracl));
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-time-out", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setmaxxfrout(zone, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-idle-out", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setidleout(zone, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-journal-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setjournalsize(zone, -1);
+ if (cfg_obj_isstring(obj)) {
+ const char *str = cfg_obj_asstring(obj);
+ INSIST(strcasecmp(str, "unlimited") == 0);
+ journal_size = ISC_UINT32_MAX / 2;
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > ISC_UINT32_MAX / 2) {
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_ERROR,
+ "'max-journal-size "
+ "%" ISC_PRINT_QUADFORMAT "d' "
+ "is too large",
+ value);
+ RETERR(ISC_R_RANGE);
+ }
+ journal_size = (isc_uint32_t)value;
+ }
+ dns_zone_setjournalsize(zone, journal_size);
+
+ obj = NULL;
+ result = ns_config_get(maps, "ixfr-from-differences", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj))
+ ixfrdiff = cfg_obj_asboolean(obj);
+ else if (strcasecmp(cfg_obj_asstring(obj), "master") &&
+ ztype == dns_zone_master)
+ ixfrdiff = ISC_TRUE;
+ else if (strcasecmp(cfg_obj_asstring(obj), "slave") &&
+ ztype == dns_zone_slave)
+ ixfrdiff = ISC_TRUE;
+ else
+ ixfrdiff = ISC_FALSE;
+ dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS, ixfrdiff);
+
+ checknames(ztype, maps, &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ fail = ISC_FALSE;
+ check = ISC_TRUE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ fail = check = ISC_TRUE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ fail = check = ISC_FALSE;
+ } else
+ INSIST(0);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, check);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL, fail);
+
+ obj = NULL;
+ result = ns_config_get(maps, "notify-delay", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setnotifydelay(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-sibling", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKSIBLING,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "zero-no-soa-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setzeronosoattl(zone, cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "nsec3-test-zone", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NSEC3TESTZONE,
+ cfg_obj_asboolean(obj));
+ }
+
+ /*
+ * Configure update-related options. These apply to
+ * primary masters only.
+ */
+ if (ztype == dns_zone_master) {
+ dns_acl_t *updateacl;
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ allow_update, ac, zone,
+ dns_zone_setupdateacl,
+ dns_zone_clearupdateacl));
+
+ updateacl = dns_zone_getupdateacl(zone);
+ if (updateacl != NULL && dns_acl_isinsecure(updateacl))
+ isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "zone '%s' allows updates by IP "
+ "address, which is insecure",
+ zname);
+
+ RETERR(configure_zone_ssutable(zoptions, zone));
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-validity-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ {
+ const cfg_obj_t *validity, *resign;
+
+ validity = cfg_tuple_get(obj, "validity");
+ seconds = cfg_obj_asuint32(validity) * 86400;
+ dns_zone_setsigvalidityinterval(zone, seconds);
+
+ resign = cfg_tuple_get(obj, "re-sign");
+ if (cfg_obj_isvoid(resign)) {
+ seconds /= 4;
+ } else {
+ if (seconds > 7 * 86400)
+ seconds = cfg_obj_asuint32(resign) *
+ 86400;
+ else
+ seconds = cfg_obj_asuint32(resign) *
+ 3600;
+ }
+ dns_zone_setsigresigninginterval(zone, seconds);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "key-directory", &obj);
+ if (result == ISC_R_SUCCESS) {
+ filename = cfg_obj_asstring(obj);
+ if (!isc_file_isabsolute(filename)) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "key-directory '%s' "
+ "is not absolute", filename);
+ return (ISC_R_FAILURE);
+ }
+ RETERR(dns_zone_setkeydirectory(zone, filename));
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-signing-signatures", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setsignatures(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-signing-nodes", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setnodes(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-signing-type", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setprivatetype(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "update-check-ksk", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setoption(zone, DNS_ZONEOPT_UPDATECHECKKSK,
+ cfg_obj_asboolean(obj));
+
+ } else if (ztype == dns_zone_slave) {
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ allow_update_forwarding, ac, zone,
+ dns_zone_setforwardacl,
+ dns_zone_clearforwardacl));
+ }
+
+
+ /*%
+ * Primary master functionality.
+ */
+ if (ztype == dns_zone_master) {
+ obj = NULL;
+ result = ns_config_get(maps, "check-wildcard", &obj);
+ if (result == ISC_R_SUCCESS)
+ check = cfg_obj_asboolean(obj);
+ else
+ check = ISC_FALSE;
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKWILDCARD, check);
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-mx", &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ fail = ISC_FALSE;
+ check = ISC_TRUE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ fail = check = ISC_TRUE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ fail = check = ISC_FALSE;
+ } else
+ INSIST(0);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKMX, check);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKMXFAIL, fail);
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-integrity", &obj);
+ INSIST(obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKINTEGRITY,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-mx-cname", &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ warn = ISC_TRUE;
+ ignore = ISC_FALSE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ warn = ignore = ISC_FALSE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ warn = ignore = ISC_TRUE;
+ } else
+ INSIST(0);
+ dns_zone_setoption(zone, DNS_ZONEOPT_WARNMXCNAME, warn);
+ dns_zone_setoption(zone, DNS_ZONEOPT_IGNOREMXCNAME, ignore);
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-srv-cname", &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ warn = ISC_TRUE;
+ ignore = ISC_FALSE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ warn = ignore = ISC_FALSE;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ warn = ignore = ISC_TRUE;
+ } else
+ INSIST(0);
+ dns_zone_setoption(zone, DNS_ZONEOPT_WARNSRVCNAME, warn);
+ dns_zone_setoption(zone, DNS_ZONEOPT_IGNORESRVCNAME, ignore);
+ }
+
+ /*
+ * Configure slave functionality.
+ */
+ switch (ztype) {
+ case dns_zone_slave:
+ case dns_zone_stub:
+ count = 0;
+ obj = NULL;
+ result = cfg_map_get(zoptions, "masters", &obj);
+ if (obj != NULL) {
+ addrs = NULL;
+ keynames = NULL;
+ RETERR(ns_config_getipandkeylist(config, obj, mctx,
+ &addrs, &keynames,
+ &count));
+ result = dns_zone_setmasterswithkeys(zone, addrs,
+ keynames, count);
+ ns_config_putipandkeylist(mctx, &addrs, &keynames,
+ count);
+ } else
+ result = dns_zone_setmasters(zone, NULL, 0);
+ RETERR(result);
+
+ multi = ISC_FALSE;
+ if (count > 1) {
+ obj = NULL;
+ result = ns_config_get(maps, "multi-master", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ multi = cfg_obj_asboolean(obj);
+ }
+ dns_zone_setoption(zone, DNS_ZONEOPT_MULTIMASTER, multi);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-time-in", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setmaxxfrin(zone, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-idle-in", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setidlein(zone, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-refresh-time", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setmaxrefreshtime(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "min-refresh-time", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setminrefreshtime(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-retry-time", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setmaxretrytime(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "min-retry-time", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setminretrytime(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "transfer-source", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setxfrsource4(zone, cfg_obj_assockaddr(obj)));
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "transfer-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setxfrsource6(zone, cfg_obj_assockaddr(obj)));
+ ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "alt-transfer-source", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setaltxfrsource4(zone, cfg_obj_assockaddr(obj)));
+
+ obj = NULL;
+ result = ns_config_get(maps, "alt-transfer-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ RETERR(dns_zone_setaltxfrsource6(zone, cfg_obj_assockaddr(obj)));
+
+ obj = NULL;
+ (void)ns_config_get(maps, "use-alt-transfer-source", &obj);
+ if (obj == NULL) {
+ /*
+ * Default off when views are in use otherwise
+ * on for BIND 8 compatibility.
+ */
+ view = dns_zone_getview(zone);
+ if (view != NULL && strcmp(view->name, "_default") == 0)
+ alt = ISC_TRUE;
+ else
+ alt = ISC_FALSE;
+ } else
+ alt = cfg_obj_asboolean(obj);
+ dns_zone_setoption(zone, DNS_ZONEOPT_USEALTXFRSRC, alt);
+
+ obj = NULL;
+ (void)ns_config_get(maps, "try-tcp-refresh", &obj);
+ dns_zone_setoption(zone, DNS_ZONEOPT_TRYTCPREFRESH,
+ cfg_obj_asboolean(obj));
+ break;
+
+ default:
+ break;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_boolean_t
+ns_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig) {
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *obj = NULL;
+ const char *cfilename;
+ const char *zfilename;
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+
+ if (zonetype_fromconfig(zoptions) != dns_zone_gettype(zone))
+ return (ISC_FALSE);
+
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "file", &obj);
+ if (obj != NULL)
+ cfilename = cfg_obj_asstring(obj);
+ else
+ cfilename = NULL;
+ zfilename = dns_zone_getfile(zone);
+ if (!((cfilename == NULL && zfilename == NULL) ||
+ (cfilename != NULL && zfilename != NULL &&
+ strcmp(cfilename, zfilename) == 0)))
+ return (ISC_FALSE);
+
+ return (ISC_TRUE);
+}