summaryrefslogtreecommitdiffstats
path: root/bin/named
diff options
context:
space:
mode:
Diffstat (limited to 'bin/named')
-rw-r--r--bin/named/Makefile.in192
-rw-r--r--bin/named/bind9.xsl1019
-rw-r--r--bin/named/bind9.xsl.h1024
-rw-r--r--bin/named/builtin.c571
-rw-r--r--bin/named/client.c3964
-rw-r--r--bin/named/config.c1018
-rw-r--r--bin/named/control.c293
-rw-r--r--bin/named/controlconf.c1530
-rwxr-xr-xbin/named/convertxsl.pl51
-rw-r--r--bin/named/fuzz.c483
-rw-r--r--bin/named/geoip.c145
-rw-r--r--bin/named/include/dlz/dlz_dlopen_driver.h21
-rw-r--r--bin/named/include/named/builtin.h25
-rw-r--r--bin/named/include/named/client.h426
-rw-r--r--bin/named/include/named/config.h77
-rw-r--r--bin/named/include/named/control.h108
-rw-r--r--bin/named/include/named/fuzz.h30
-rw-r--r--bin/named/include/named/geoip.h26
-rw-r--r--bin/named/include/named/globals.h200
-rw-r--r--bin/named/include/named/interfacemgr.h183
-rw-r--r--bin/named/include/named/listenlist.h101
-rw-r--r--bin/named/include/named/log.h94
-rw-r--r--bin/named/include/named/logconf.h27
-rw-r--r--bin/named/include/named/lwaddr.h30
-rw-r--r--bin/named/include/named/lwdclient.h229
-rw-r--r--bin/named/include/named/lwresd.h117
-rw-r--r--bin/named/include/named/lwsearch.h106
-rw-r--r--bin/named/include/named/main.h36
-rw-r--r--bin/named/include/named/notify.h49
-rw-r--r--bin/named/include/named/ns_smf_globals.h39
-rw-r--r--bin/named/include/named/query.h115
-rw-r--r--bin/named/include/named/seccomp.h248
-rw-r--r--bin/named/include/named/server.h762
-rw-r--r--bin/named/include/named/sortlist.h81
-rw-r--r--bin/named/include/named/statschannel.h56
-rw-r--r--bin/named/include/named/tkeyconf.h47
-rw-r--r--bin/named/include/named/tsigconf.h44
-rw-r--r--bin/named/include/named/types.h49
-rw-r--r--bin/named/include/named/update.h44
-rw-r--r--bin/named/include/named/xfrout.h33
-rw-r--r--bin/named/include/named/zoneconf.h72
-rw-r--r--bin/named/interfacemgr.c1246
-rw-r--r--bin/named/listenlist.c135
-rw-r--r--bin/named/log.c261
-rw-r--r--bin/named/logconf.c338
-rw-r--r--bin/named/lwaddr.c88
-rw-r--r--bin/named/lwdclient.c510
-rw-r--r--bin/named/lwderror.c74
-rw-r--r--bin/named/lwdgabn.c651
-rw-r--r--bin/named/lwdgnba.c264
-rw-r--r--bin/named/lwdgrbn.c514
-rw-r--r--bin/named/lwdnoop.c81
-rw-r--r--bin/named/lwresd.8250
-rw-r--r--bin/named/lwresd.c892
-rw-r--r--bin/named/lwresd.docbook364
-rw-r--r--bin/named/lwresd.html295
-rw-r--r--bin/named/lwsearch.c199
-rw-r--r--bin/named/main.c1543
-rw-r--r--bin/named/named.8379
-rw-r--r--bin/named/named.conf.51028
-rw-r--r--bin/named/named.conf.docbook1006
-rw-r--r--bin/named/named.conf.html1002
-rw-r--r--bin/named/named.docbook547
-rw-r--r--bin/named/named.html456
-rw-r--r--bin/named/notify.c167
-rw-r--r--bin/named/query.c9615
-rw-r--r--bin/named/server.c14277
-rw-r--r--bin/named/sortlist.c164
-rw-r--r--bin/named/statschannel.c3583
-rw-r--r--bin/named/tkeyconf.c127
-rw-r--r--bin/named/tsigconf.c179
-rw-r--r--bin/named/unix/Makefile.in29
-rw-r--r--bin/named/unix/dlz_dlopen_driver.c633
-rw-r--r--bin/named/unix/include/named/os.h79
-rw-r--r--bin/named/unix/os.c1114
-rw-r--r--bin/named/update.c3501
-rw-r--r--bin/named/win32/dlz_dlopen_driver.c618
-rw-r--r--bin/named/win32/include/named/ntservice.h28
-rw-r--r--bin/named/win32/include/named/os.h76
-rw-r--r--bin/named/win32/named.dsp.in341
-rw-r--r--bin/named/win32/named.dsw29
-rw-r--r--bin/named/win32/named.mak.in1233
-rw-r--r--bin/named/win32/named.vcxproj.filters.in211
-rw-r--r--bin/named/win32/named.vcxproj.in177
-rw-r--r--bin/named/win32/named.vcxproj.user3
-rw-r--r--bin/named/win32/ntservice.c171
-rw-r--r--bin/named/win32/os.c460
-rw-r--r--bin/named/xfrout.c1714
-rw-r--r--bin/named/zoneconf.c1824
89 files changed, 66241 insertions, 0 deletions
diff --git a/bin/named/Makefile.in b/bin/named/Makefile.in
new file mode 100644
index 0000000..1c41397
--- /dev/null
+++ b/bin/named/Makefile.in
@@ -0,0 +1,192 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+# Attempt to disable parallel processing.
+.NOTPARALLEL:
+.NO_PARALLEL:
+
+VERSION=@BIND9_VERSION@
+
+@BIND9_PRODUCT@
+
+@BIND9_DESCRIPTION@
+
+@BIND9_SRCID@
+
+@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} @DST_OPENSSL_INC@
+
+CDEFINES = @CONTRIB_DLZ@ @USE_PKCS11@ @PKCS11_ENGINE@ @CRYPTO@
+
+CWARNINGS =
+
+DNSLIBS = ../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@
+ISCCFGLIBS = ../../lib/isccfg/libisccfg.@A@
+ISCCCLIBS = ../../lib/isccc/libisccc.@A@
+ISCLIBS = ../../lib/isc/libisc.@A@
+ISCNOSYMLIBS = ../../lib/isc/libisc-nosymtbl.@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@
+
+NOSYMLIBS = ${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} \
+ ${ISCCFGLIBS} ${ISCCCLIBS} ${ISCNOSYMLIBS} \
+ ${DLZDRIVER_LIBS} ${DBDRIVER_LIBS} @LIBS@
+
+SUBDIRS = unix
+
+TARGETS = named@EXEEXT@ lwresd@EXEEXT@
+
+GEOIPLINKOBJS = geoip.@O@
+
+OBJS = builtin.@O@ client.@O@ config.@O@ control.@O@ \
+ controlconf.@O@ fuzz.@O@ @GEOIPLINKOBJS@ 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@ unix/dlz_dlopen_driver.@O@
+
+SYMOBJS = symtbl.@O@
+
+GEOIPLINKSRCS = geoip.c
+
+SRCS = builtin.c client.c config.c control.c \
+ controlconf.c fuzz.c @GEOIPLINKSRCS@ 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}\" \
+ -DPRODUCT=\"${PRODUCT}\" \
+ -DDESCRIPTION=\"${DESCRIPTION}\" \
+ -DSRCID=\"${SRCID}\" \
+ -DCONFIGARGS="\"${CONFIGARGS}\"" \
+ -DBUILDER="\"make\"" \
+ -DNS_LOCALSTATEDIR=\"${localstatedir}\" \
+ -DNS_SYSCONFDIR=\"${sysconfdir}\" -c ${srcdir}/main.c
+
+config.@O@: config.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \
+ -DVERSION=\"${VERSION}\" \
+ -DSRCID=\"${SRCID}\" \
+ -DDYNDB_LIBDIR=\"@libdir@/bind\" \
+ -DNS_LOCALSTATEDIR=\"${localstatedir}\" \
+ -DNS_SYSCONFDIR=\"${sysconfdir}\" \
+ -c ${srcdir}/config.c
+
+server.@O@: server.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} \
+ -DPRODUCT=\"${PRODUCT}\" \
+ -DVERSION=\"${VERSION}\" -c ${srcdir}/server.c
+
+named@EXEEXT@: ${OBJS} ${DEPLIBS}
+ export MAKE_SYMTABLE="yes"; \
+ export BASEOBJS="${OBJS} ${UOBJS}"; \
+ ${FINALBUILDCMD}
+
+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}
+
+maintainer-clean::
+
+bind9.xsl.h: bind9.xsl ${srcdir}/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
+
+uninstall::
+ rm -f ${DESTDIR}${mandir}/man5/named.conf.5
+ rm -f ${DESTDIR}${mandir}/man8/lwresd.8
+ rm -f ${DESTDIR}${mandir}/man8/named.8
+ rm -f ${DESTDIR}${sbindir}/lwresd@EXEEXT@
+ ${LIBTOOL_MODE_UNINSTALL} rm -f ${DESTDIR}${sbindir}/named@EXEEXT@
+
+@DLZ_DRIVER_RULES@
+
+named-symtbl.@O@: named-symtbl.c
+ ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c named-symtbl.c
diff --git a/bin/named/bind9.xsl b/bin/named/bind9.xsl
new file mode 100644
index 0000000..890654c
--- /dev/null
+++ b/bin/named/bind9.xsl
@@ -0,0 +1,1019 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ -
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ -
+ - See the COPYRIGHT file distributed with this work for additional
+ - information regarding copyright ownership.
+-->
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
+ <xsl:output method="html" indent="yes" version="4.0"/>
+ <xsl:template match="statistics[@version=&quot;3.8&quot;]">
+ <html>
+ <head>
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript" src="https://www.google.com/jsapi"/>
+ <script type="text/javascript">
+
+ google.load("visualization", "1", {packages:["corechart"]});
+ google.setOnLoadCallback(loadGraphs);
+
+ var graphs=[];
+
+ function drawChart(chart_title,target,style,data) {
+ var data = google.visualization.arrayToDataTable(data);
+
+ var options = {
+ title: chart_title
+ };
+
+ var chart;
+ if (style == "barchart") {
+ chart = new google.visualization.BarChart(document.getElementById(target));
+ chart.draw(data, options);
+ } else if (style == "piechart") {
+ chart = new google.visualization.PieChart(document.getElementById(target));
+ chart.draw(data, options);
+ }
+ }
+
+ function loadGraphs(){
+ var g;
+
+ while(g = graphs.shift()){
+ // alert("going for: " + g.target);
+ if(g.data.length > 1){
+ drawChart(g.title,g.target,g.style,g.data);
+ }
+ }
+ }
+
+ <xsl:if test="server/counters[@type=&quot;qtype&quot;]/counter">
+ // Server Incoming Query Types
+ graphs.push({
+ 'title' : "Server Incoming Query Types",
+ 'target': 'chart_incoming_qtypes',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="server/counters[@type=&quot;qtype&quot;]/counter">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+ </xsl:if>
+
+ <xsl:if test="server/counters[@type=&quot;opcode&quot;]/counter">
+ // Server Incoming Requests by opcode
+ graphs.push({
+ 'title' : "Server Incoming Requests by DNS Opcode",
+ 'target': 'chart_incoming_opcodes',
+ 'style': 'barchart',
+ 'data': [['Opcode','Counter'],<xsl:for-each select="server/counters[@type=&quot;opcode&quot;]/counter[. &gt; 0 or substring(@name,1,3) != 'RES']">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]});
+ </xsl:if>
+ </script>
+ </xsl:if>
+ <style type="text/css">
+ body {
+ font-family: sans-serif;
+ background-color: #ffffff;
+ color: #000000;
+ font-size: 10pt;
+ }
+
+ .odd{
+ background-color: #f0f0f0;
+ }
+
+ .even{
+ background-color: #ffffff;
+ }
+
+ p.footer{
+ font-style:italic;
+ color: grey;
+ }
+
+ table {
+ border-collapse: collapse;
+ border: 1px solid grey;
+ }
+
+ table.counters{
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.counters th {
+ text-align: right;
+ border: 1px solid grey;
+ width: 150px;
+ }
+ table.counters td {
+ text-align: right;
+ font-family: monospace;
+ }
+ table.counters tr:hover{
+ background-color: #99ddff;
+ }
+
+ table.info {
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.info th {
+ text-align: center;
+ border: 1px solid grey;
+ width: 150px;
+ }
+ table.info td {
+ text-align: center;
+ }
+ table.info tr:hover{
+ background-color: #99ddff;
+ }
+
+ table.tasks {
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.tasks th {
+ text-align: center;
+ border: 1px solid grey;
+ width: 150px;
+ }
+ table.tasks td {
+ text-align: right;
+ font-family: monospace;
+ }
+ table.tasks td:nth-child(2) {
+ text-align: center;
+ }
+ table.tasks td:nth-child(4) {
+ text-align: center;
+ }
+ table.tasks tr:hover{
+ background-color: #99ddff;
+ }
+
+ table.netstat {
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.netstat th {
+ text-align: center;
+ border: 1px solid grey;
+ width: 150px;
+ }
+ table.netstat td {
+ text-align: center;
+ }
+ table.netstat td:nth-child(4) {
+ text-align: right;
+ font-family: monospace;
+ }
+ table.netstat td:nth-child(7) {
+ text-align: left;
+ }
+ table.netstat tr:hover{
+ background-color: #99ddff;
+ }
+
+ table.mctx {
+ border: 1px solid grey;
+ width: 500px;
+ }
+ table.mctx th {
+ text-align: center;
+ border: 1px solid grey;
+ }
+ table.mctx td {
+ text-align: right;
+ font-family: monospace;
+ }
+ table.mctx td:nth-child(-n+2) {
+ text-align: left;
+ width: 100px;
+ }
+ table.mctx tr:hover{
+ background-color: #99ddff;
+ }
+
+ .totals {
+ background-color: rgb(1,169,206);
+ color: #ffffff;
+ }
+
+ td, th {
+ padding-right: 5px;
+ padding-left: 5px;
+ border: 1px solid grey;
+ }
+
+ .header h1 {
+ color: rgb(1,169,206);
+ padding: 0px;
+ }
+
+ .content {
+ background-color: #ffffff;
+ color: #000000;
+ padding: 4px;
+ }
+
+ .item {
+ padding: 4px;
+ text-align: right;
+ }
+
+ .value {
+ padding: 4px;
+ font-weight: bold;
+ }
+
+
+ h2 {
+ color: grey;
+ font-size: 14pt;
+ width:500px;
+ text-align:center;
+ }
+
+ h3 {
+ color: #444444;
+ font-size: 12pt;
+ width:500px;
+ text-align:center;
+ }
+ h4 {
+ color: rgb(1,169,206);
+ font-size: 10pt;
+ width:500px;
+ text-align:center;
+ }
+
+ .pie {
+ width:500px;
+ height: 500px;
+ }
+
+ </style>
+ <title>ISC BIND 9 Statistics</title>
+ </head>
+ <body>
+ <div class="header">
+ <h1>ISC Bind 9 Configuration and Statistics</h1>
+ </div>
+ <p>Alternate statistics views: <a href="/">All</a>,
+ <a href="/xml/v3/status">Status</a>,
+ <a href="/xml/v3/server">Server</a>,
+ <a href="/xml/v3/zones">Zones</a>,
+ <a href="/xml/v3/net">Network</a>,
+ <a href="/xml/v3/tasks">Tasks</a>,
+ <a href="/xml/v3/mem">Memory</a> and
+ <a href="/xml/v3/traffic">Traffic Size</a></p>
+ <hr/>
+ <h2>Server Status</h2>
+ <table class="info">
+ <tr>
+ <th>Boot time:</th>
+ <td>
+ <xsl:value-of select="server/boot-time"/>
+ </td>
+ </tr>
+ <tr>
+ <th>Last reconfigured:</th>
+ <td>
+ <xsl:value-of select="server/config-time"/>
+ </td>
+ </tr>
+ <tr>
+ <th>Current time:</th>
+ <td>
+ <xsl:value-of select="server/current-time"/>
+ </td>
+ </tr>
+ <tr>
+ <th>Server version:</th>
+ <td>
+ <xsl:value-of select="server/version"/>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ <xsl:if test="server/counters[@type=&quot;opcode&quot;]/counter[. &gt; 0]">
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <h2>Incoming Requests by DNS Opcode</h2>
+ <!-- Non Mozilla specific markup -->
+ <div class="pie" id="chart_incoming_opcodes">
+ [cannot display chart]
+ </div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;opcode&quot;]/counter[. &gt; 0 or substring(@name,1,3) != 'RES']">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <tr>
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ <tr>
+ <th class="totals">Total:</th>
+ <td class="totals">
+ <xsl:value-of select="sum(server/counters[@type=&quot;opcode&quot;]/counter)"/>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;qtype&quot;]/counter">
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <h3>Incoming Queries by Query Type</h3>
+ <div class="pie" id="chart_incoming_qtypes">
+ [cannot display chart]
+ </div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;qtype&quot;]/counter">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ <tr>
+ <th class="totals">Total:</th>
+ <td class="totals">
+ <xsl:value-of select="sum(server/counters[@type=&quot;qtype&quot;]/counter)"/>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="views/view[count(counters[@type=&quot;resqtype&quot;]/counter) &gt; 0]">
+ <h2>Outgoing Queries per view</h2>
+ <xsl:for-each select="views/view[count(counters[@type=&quot;resqtype&quot;]/counter) &gt; 0]">
+ <h3>View <xsl:value-of select="@name"/></h3>
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript">
+ graphs.push({
+ 'title': "Outgoing Queries for view: <xsl:value-of select="@name"/>",
+ 'target': 'chart_outgoing_queries_view_<xsl:value-of select="@name"/>',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="counters[@type=&quot;resqtype&quot;]/counter">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+ </script>
+ <xsl:variable name="target">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <div class="pie" id="chart_outgoing_queries_view_{$target}">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;resqtype&quot;]/counter">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class1">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class1}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:for-each>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;nsstat&quot;]/counter[.&gt;0]">
+ <h2>Server Statistics</h2>
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript">
+ graphs.push({
+ 'title' : "Server Counters",
+ 'target': 'chart_server_nsstat_restype',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="server/counters[@type=&quot;nsstat&quot;]/counter[.&gt;0]">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+ </script>
+ <div class="pie" id="chart_server_nsstat_restype">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;nsstat&quot;]/counter[.&gt;0]">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class2">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class2}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;zonestat&quot;]/counter[.&gt;0]">
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <h2>Zone Maintenance Statistics</h2>
+ <script type="text/javascript">
+ graphs.push({
+ 'title' : "Zone Maintenance Stats",
+ 'target': 'chart_server_zone_maint',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="server/counters[@type=&quot;zonestat&quot;]/counter[.&gt;0]">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+ </script>
+ <!-- Non Mozilla specific markup -->
+ <div class="pie" id="chart_server_zone_maint">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;zonestat&quot;]/counter">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class3">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class3}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;resstat&quot;]/counter[.&gt;0]">
+ <h2>Resolver Statistics (Common)</h2>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;resstat&quot;]/counter">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class4">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class4}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ <xsl:for-each select="views/view">
+ <xsl:if test="counters[@type=&quot;resstats&quot;]/counter[.&gt;0]">
+ <h3>Resolver Statistics for View <xsl:value-of select="@name"/></h3>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;resstats&quot;]/counter[.&gt;0]">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class5">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class5}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:for-each select="views/view">
+ <xsl:if test="counters[@type=&quot;adbstat&quot;]/counter[.&gt;0]">
+ <h3>ADB Statistics for View <xsl:value-of select="@name"/></h3>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;adbstat&quot;]/counter[.&gt;0]">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class5">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class5}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:for-each select="views/view">
+ <xsl:if test="counters[@type=&quot;cachestats&quot;]/counter[.&gt;0]">
+ <h3>Cache Statistics for View <xsl:value-of select="@name"/></h3>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;cachestats&quot;]/counter[.&gt;0]">
+ <xsl:sort select="." data-type="number" order="descending"/>
+ <xsl:variable name="css-class5">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class5}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:for-each select="views/view">
+ <xsl:if test="cache/rrset">
+ <h3>Cache DB RRsets for View <xsl:value-of select="@name"/></h3>
+ <table class="counters">
+ <xsl:for-each select="cache/rrset">
+ <xsl:variable name="css-class6">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class6}">
+ <th>
+ <xsl:value-of select="name"/>
+ </th>
+ <td>
+ <xsl:value-of select="counter"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:if test="traffic/udp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0] or traffic/udp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0] or traffic/tcp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0] or traffic/tcp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <h2>Traffic Size Statistics</h2>
+ </xsl:if>
+ <xsl:if test="traffic/udp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]">
+ <h4>UDP Requests Received</h4>
+ <table class="counters">
+ <xsl:for-each select="traffic/udp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="traffic/udp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <h4>UDP Responses Sent</h4>
+ <table class="counters">
+ <xsl:for-each select="traffic/udp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="traffic/tcp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]">
+ <h4>TCP Requests Received</h4>
+ <table class="counters">
+ <xsl:for-each select="traffic/tcp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="traffic/tcp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <h4>TCP Responses Sent</h4>
+ <table class="counters">
+ <xsl:for-each select="traffic/tcp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="server/counters[@type=&quot;sockstat&quot;]/counter[.&gt;0]">
+ <h2>Socket I/O Statistics</h2>
+ <table class="counters">
+ <xsl:for-each select="server/counters[@type=&quot;sockstat&quot;]/counter[.&gt;0]">
+ <xsl:variable name="css-class7">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class7}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="views/view[zones/zone/counters[@type=&quot;qtype&quot;]/counter &gt;0]">
+ <h2>Received QTYPES per view/zone</h2>
+ <xsl:for-each select="views/view[zones/zone/counters[@type=&quot;qtype&quot;]/counter &gt;0]">
+ <h3>View <xsl:value-of select="@name"/></h3>
+ <xsl:variable name="thisview">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <xsl:for-each select="zones/zone">
+ <xsl:if test="counters[@type=&quot;qtype&quot;]/counter[count(.) &gt; 0]">
+ <h4>Zone <xsl:value-of select="@name"/></h4>
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript">
+ graphs.push({
+ 'title': "Query types for zone <xsl:value-of select="@name"/>",
+ 'target': 'chart_qtype_<xsl:value-of select="../../@name"/>_<xsl:value-of select="@name"/>',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="counters[@type=&quot;qtype&quot;]/counter[.&gt;0 and @name != &quot;QryAuthAns&quot;]">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+
+ </script>
+ <xsl:variable name="target">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <div class="pie" id="chart_qtype_{$thisview}_{$target}">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;qtype&quot;]/counter">
+ <xsl:sort select="."/>
+ <xsl:variable name="css-class10">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class10}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:for-each>
+ </xsl:if>
+ <xsl:if test="views/view[zones/zone/counters[@type=&quot;rcode&quot;]/counter &gt;0]">
+ <h2>Response Codes per view/zone</h2>
+ <xsl:for-each select="views/view[zones/zone/counters[@type=&quot;rcode&quot;]/counter &gt;0]">
+ <h3>View <xsl:value-of select="@name"/></h3>
+ <xsl:variable name="thisview2">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <xsl:for-each select="zones/zone">
+ <xsl:if test="counters[@type=&quot;rcode&quot;]/counter[. &gt; 0]">
+ <h4>Zone <xsl:value-of select="@name"/></h4>
+ <xsl:if test="system-property('xsl:vendor')!='Transformiix'">
+ <!-- Non Mozilla specific markup -->
+ <script type="text/javascript">
+ graphs.push({
+ 'title': "Response codes for zone <xsl:value-of select="@name"/>",
+ 'target': 'chart_rescode_<xsl:value-of select="../../@name"/>_<xsl:value-of select="@name"/>',
+ 'style': 'barchart',
+ 'data': [['Type','Counter'],<xsl:for-each select="counters[@type=&quot;rcode&quot;]/counter[.&gt;0 and @name != &quot;QryAuthAns&quot;]">['<xsl:value-of select="@name"/>',<xsl:value-of select="."/>],</xsl:for-each>]
+ });
+
+ </script>
+ <xsl:variable name="target">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <div class="pie" id="chart_rescode_{$thisview2}_{$target}">[no data to display]</div>
+ </xsl:if>
+ <table class="counters">
+ <xsl:for-each select="counters[@type=&quot;rcode&quot;]/counter[.&gt;0 and @name != &quot;QryAuthAns&quot;]">
+ <xsl:sort select="."/>
+ <xsl:variable name="css-class11">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class11}">
+ <th>
+ <xsl:value-of select="@name"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:for-each>
+ </xsl:if>
+ <xsl:if test="socketmgr/sockets/socket">
+ <h2>Network Status</h2>
+ <table class="netstat">
+ <tr>
+ <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">
+ <xsl:sort select="id"/>
+ <xsl:variable name="css-class12">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class12}">
+ <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/>
+ </xsl:if>
+ <xsl:if test="taskmgr/thread-model/type">
+ <h2>Task Manager Configuration</h2>
+ <table class="counters">
+ <tr>
+ <th class="even">Thread-Model</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/type"/>
+ </td>
+ </tr>
+ <tr class="odd">
+ <th>Worker Threads</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/worker-threads"/>
+ </td>
+ </tr>
+ <tr class="even">
+ <th>Default Quantum</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/default-quantum"/>
+ </td>
+ </tr>
+ <tr class="odd">
+ <th>Tasks Running</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/tasks-running"/>
+ </td>
+ </tr>
+ <tr class="even">
+ <th>Tasks Ready</th>
+ <td>
+ <xsl:value-of select="taskmgr/thread-model/tasks-ready"/>
+ </td>
+ </tr>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="taskmgr/tasks/task">
+ <h2>Tasks</h2>
+ <table class="tasks">
+ <tr>
+ <th>ID</th>
+ <th>Name</th>
+ <th>References</th>
+ <th>State</th>
+ <th>Quantum</th>
+ <th>Events</th>
+ </tr>
+ <xsl:for-each select="taskmgr/tasks/task">
+ <xsl:sort select="name"/>
+ <xsl:variable name="css-class14">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class14}">
+ <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>
+ <td>
+ <xsl:value-of select="events"/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="memory/summary">
+ <h2>Memory Usage Summary</h2>
+ <table class="counters">
+ <xsl:for-each select="memory/summary/*">
+ <xsl:variable name="css-class13">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class13}">
+ <th>
+ <xsl:value-of select="name()"/>
+ </th>
+ <td>
+ <xsl:value-of select="."/>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ <br/>
+ </xsl:if>
+ <xsl:if test="memory/contexts/context">
+ <h2>Memory Contexts</h2>
+ <table class="mctx">
+ <tr>
+ <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">
+ <xsl:sort select="total" data-type="number" order="descending"/>
+ <xsl:variable name="css-class14">
+ <xsl:choose>
+ <xsl:when test="position() mod 2 = 0">even</xsl:when>
+ <xsl:otherwise>odd</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <tr class="{$css-class14}">
+ <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>
+ </xsl:if>
+ <hr/>
+ <p class="footer">Internet Systems Consortium Inc.<br/><a href="http://www.isc.org">http://www.isc.org</a></p>
+ </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..31425cf
--- /dev/null
+++ b/bin/named/bind9.xsl.h
@@ -0,0 +1,1024 @@
+/*
+ * Generated by convertxsl.pl 1.14 2008/07/17 23:43:26 jinmei Exp
+ * From unknown
+ */
+static char xslmsg[] =
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!--\n"
+ " - Copyright (C) Internet Systems Consortium, Inc. (\"ISC\")\n"
+ " -\n"
+ " - This Source Code Form is subject to the terms of the Mozilla Public\n"
+ " - License, v. 2.0. If a copy of the MPL was not distributed with this\n"
+ " - file, You can obtain one at http://mozilla.org/MPL/2.0/.\n"
+ " -\n"
+ " - See the COPYRIGHT file distributed with this work for additional\n"
+ " - information regarding copyright ownership.\n"
+ "-->\n"
+ "\n"
+ "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns=\"http://www.w3.org/1999/xhtml\" version=\"1.0\">\n"
+ " <xsl:output method=\"html\" indent=\"yes\" version=\"4.0\"/>\n"
+ " <xsl:template match=\"statistics[@version=&quot;3.8&quot;]\">\n"
+ " <html>\n"
+ " <head>\n"
+ " <xsl:if test=\"system-property('xsl:vendor')!='Transformiix'\">\n"
+ " <!-- Non Mozilla specific markup -->\n"
+ " <script type=\"text/javascript\" src=\"https://www.google.com/jsapi\"/>\n"
+ " <script type=\"text/javascript\">\n"
+ "\n"
+ " google.load(\"visualization\", \"1\", {packages:[\"corechart\"]});\n"
+ " google.setOnLoadCallback(loadGraphs);\n"
+ "\n"
+ " var graphs=[];\n"
+ "\n"
+ " function drawChart(chart_title,target,style,data) {\n"
+ " var data = google.visualization.arrayToDataTable(data);\n"
+ "\n"
+ " var options = {\n"
+ " title: chart_title\n"
+ " };\n"
+ "\n"
+ " var chart;\n"
+ " if (style == \"barchart\") {\n"
+ " chart = new google.visualization.BarChart(document.getElementById(target));\n"
+ " chart.draw(data, options);\n"
+ " } else if (style == \"piechart\") {\n"
+ " chart = new google.visualization.PieChart(document.getElementById(target));\n"
+ " chart.draw(data, options);\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " function loadGraphs(){\n"
+ " var g;\n"
+ "\n"
+ " while(g = graphs.shift()){\n"
+ " // alert(\"going for: \" + g.target);\n"
+ " if(g.data.length > 1){\n"
+ " drawChart(g.title,g.target,g.style,g.data);\n"
+ " }\n"
+ " }\n"
+ " }\n"
+ "\n"
+ " <xsl:if test=\"server/counters[@type=&quot;qtype&quot;]/counter\">\n"
+ " // Server Incoming Query Types\n"
+ " graphs.push({\n"
+ " 'title' : \"Server Incoming Query Types\",\n"
+ " 'target': 'chart_incoming_qtypes',\n"
+ " 'style': 'barchart',\n"
+ " 'data': [['Type','Counter'],<xsl:for-each select=\"server/counters[@type=&quot;qtype&quot;]/counter\">['<xsl:value-of select=\"@name\"/>',<xsl:value-of select=\".\"/>],</xsl:for-each>]\n"
+ " });\n"
+ " </xsl:if>\n"
+ "\n"
+ " <xsl:if test=\"server/counters[@type=&quot;opcode&quot;]/counter\">\n"
+ " // Server Incoming Requests by opcode\n"
+ " graphs.push({\n"
+ " 'title' : \"Server Incoming Requests by DNS Opcode\",\n"
+ " 'target': 'chart_incoming_opcodes',\n"
+ " 'style': 'barchart',\n"
+ " 'data': [['Opcode','Counter'],<xsl:for-each select=\"server/counters[@type=&quot;opcode&quot;]/counter[. &gt; 0 or substring(@name,1,3) != 'RES']\">['<xsl:value-of select=\"@name\"/>',<xsl:value-of select=\".\"/>],</xsl:for-each>]});\n"
+ " </xsl:if>\n"
+ " </script>\n"
+ " </xsl:if>\n"
+ " <style type=\"text/css\">\n"
+ " body {\n"
+ " font-family: sans-serif;\n"
+ " background-color: #ffffff;\n"
+ " color: #000000;\n"
+ " font-size: 10pt;\n"
+ " }\n"
+ "\n"
+ " .odd{\n"
+ " background-color: #f0f0f0;\n"
+ " }\n"
+ "\n"
+ " .even{\n"
+ " background-color: #ffffff;\n"
+ " }\n"
+ "\n"
+ " p.footer{\n"
+ " font-style:italic;\n"
+ " color: grey;\n"
+ " }\n"
+ "\n"
+ " table {\n"
+ " border-collapse: collapse;\n"
+ " border: 1px solid grey;\n"
+ " }\n"
+ "\n"
+ " table.counters{\n"
+ " border: 1px solid grey;\n"
+ " width: 500px;\n"
+ " }\n"
+ " table.counters th {\n"
+ " text-align: right;\n"
+ " border: 1px solid grey;\n"
+ " width: 150px;\n"
+ " }\n"
+ " table.counters td {\n"
+ " text-align: right;\n"
+ " font-family: monospace;\n"
+ " }\n"
+ " table.counters tr:hover{\n"
+ " background-color: #99ddff;\n"
+ " }\n"
+ "\n"
+ " table.info {\n"
+ " border: 1px solid grey;\n"
+ " width: 500px;\n"
+ " }\n"
+ " table.info th {\n"
+ " text-align: center;\n"
+ " border: 1px solid grey;\n"
+ " width: 150px;\n"
+ " }\n"
+ " table.info td {\n"
+ " text-align: center;\n"
+ " }\n"
+ " table.info tr:hover{\n"
+ " background-color: #99ddff;\n"
+ " }\n"
+ "\n"
+ " table.tasks {\n"
+ " border: 1px solid grey;\n"
+ " width: 500px;\n"
+ " }\n"
+ " table.tasks th {\n"
+ " text-align: center;\n"
+ " border: 1px solid grey;\n"
+ " width: 150px;\n"
+ " }\n"
+ " table.tasks td {\n"
+ " text-align: right;\n"
+ " font-family: monospace;\n"
+ " }\n"
+ " table.tasks td:nth-child(2) {\n"
+ " text-align: center;\n"
+ " }\n"
+ " table.tasks td:nth-child(4) {\n"
+ " text-align: center;\n"
+ " }\n"
+ " table.tasks tr:hover{\n"
+ " background-color: #99ddff;\n"
+ " }\n"
+ "\n"
+ " table.netstat {\n"
+ " border: 1px solid grey;\n"
+ " width: 500px;\n"
+ " }\n"
+ " table.netstat th {\n"
+ " text-align: center;\n"
+ " border: 1px solid grey;\n"
+ " width: 150px;\n"
+ " }\n"
+ " table.netstat td {\n"
+ " text-align: center;\n"
+ " }\n"
+ " table.netstat td:nth-child(4) {\n"
+ " text-align: right;\n"
+ " font-family: monospace;\n"
+ " }\n"
+ " table.netstat td:nth-child(7) {\n"
+ " text-align: left;\n"
+ " }\n"
+ " table.netstat tr:hover{\n"
+ " background-color: #99ddff;\n"
+ " }\n"
+ "\n"
+ " table.mctx {\n"
+ " border: 1px solid grey;\n"
+ " width: 500px;\n"
+ " }\n"
+ " table.mctx th {\n"
+ " text-align: center;\n"
+ " border: 1px solid grey;\n"
+ " }\n"
+ " table.mctx td {\n"
+ " text-align: right;\n"
+ " font-family: monospace;\n"
+ " }\n"
+ " table.mctx td:nth-child(-n+2) {\n"
+ " text-align: left;\n"
+ " width: 100px;\n"
+ " }\n"
+ " table.mctx tr:hover{\n"
+ " background-color: #99ddff;\n"
+ " }\n"
+ "\n"
+ " .totals {\n"
+ " background-color: rgb(1,169,206);\n"
+ " color: #ffffff;\n"
+ " }\n"
+ "\n"
+ " td, th {\n"
+ " padding-right: 5px;\n"
+ " padding-left: 5px;\n"
+ " border: 1px solid grey;\n"
+ " }\n"
+ "\n"
+ " .header h1 {\n"
+ " color: rgb(1,169,206);\n"
+ " padding: 0px;\n"
+ " }\n"
+ "\n"
+ " .content {\n"
+ " background-color: #ffffff;\n"
+ " color: #000000;\n"
+ " padding: 4px;\n"
+ " }\n"
+ "\n"
+ " .item {\n"
+ " padding: 4px;\n"
+ " text-align: right;\n"
+ " }\n"
+ "\n"
+ " .value {\n"
+ " padding: 4px;\n"
+ " font-weight: bold;\n"
+ " }\n"
+ "\n"
+ "\n"
+ " h2 {\n"
+ " color: grey;\n"
+ " font-size: 14pt;\n"
+ " width:500px;\n"
+ " text-align:center;\n"
+ " }\n"
+ "\n"
+ " h3 {\n"
+ " color: #444444;\n"
+ " font-size: 12pt;\n"
+ " width:500px;\n"
+ " text-align:center;\n"
+ " }\n"
+ " h4 {\n"
+ " color: rgb(1,169,206);\n"
+ " font-size: 10pt;\n"
+ " width:500px;\n"
+ " text-align:center;\n"
+ " }\n"
+ "\n"
+ " .pie {\n"
+ " width:500px;\n"
+ " height: 500px;\n"
+ " }\n"
+ "\n"
+ " </style>\n"
+ " <title>ISC BIND 9 Statistics</title>\n"
+ " </head>\n"
+ " <body>\n"
+ " <div class=\"header\">\n"
+ " <h1>ISC Bind 9 Configuration and Statistics</h1>\n"
+ " </div>\n"
+ " <p>Alternate statistics views: <a href=\"/\">All</a>,\n"
+ " <a href=\"/xml/v3/status\">Status</a>,\n"
+ " <a href=\"/xml/v3/server\">Server</a>,\n"
+ " <a href=\"/xml/v3/zones\">Zones</a>,\n"
+ " <a href=\"/xml/v3/net\">Network</a>,\n"
+ " <a href=\"/xml/v3/tasks\">Tasks</a>,\n"
+ " <a href=\"/xml/v3/mem\">Memory</a> and\n"
+ " <a href=\"/xml/v3/traffic\">Traffic Size</a></p>\n"
+ " <hr/>\n"
+ " <h2>Server Status</h2>\n"
+ " <table class=\"info\">\n"
+ " <tr>\n"
+ " <th>Boot time:</th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"server/boot-time\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr>\n"
+ " <th>Last reconfigured:</th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"server/config-time\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr>\n"
+ " <th>Current time:</th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"server/current-time\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr>\n"
+ " <th>Server version:</th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"server/version\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </table>\n"
+ " <br/>\n"
+ " <xsl:if test=\"server/counters[@type=&quot;opcode&quot;]/counter[. &gt; 0]\">\n"
+ " <xsl:if test=\"system-property('xsl:vendor')!='Transformiix'\">\n"
+ " <h2>Incoming Requests by DNS Opcode</h2>\n"
+ " <!-- Non Mozilla specific markup -->\n"
+ " <div class=\"pie\" id=\"chart_incoming_opcodes\">\n"
+ " [cannot display chart]\n"
+ " </div>\n"
+ " </xsl:if>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"server/counters[@type=&quot;opcode&quot;]/counter[. &gt; 0 or substring(@name,1,3) != 'RES']\">\n"
+ " <xsl:sort select=\".\" data-type=\"number\" order=\"descending\"/>\n"
+ " <tr>\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " <tr>\n"
+ " <th class=\"totals\">Total:</th>\n"
+ " <td class=\"totals\">\n"
+ " <xsl:value-of select=\"sum(server/counters[@type=&quot;opcode&quot;]/counter)\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"server/counters[@type=&quot;qtype&quot;]/counter\">\n"
+ " <xsl:if test=\"system-property('xsl:vendor')!='Transformiix'\">\n"
+ " <!-- Non Mozilla specific markup -->\n"
+ " <h3>Incoming Queries by Query Type</h3>\n"
+ " <div class=\"pie\" id=\"chart_incoming_qtypes\">\n"
+ " [cannot display chart]\n"
+ " </div>\n"
+ " </xsl:if>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"server/counters[@type=&quot;qtype&quot;]/counter\">\n"
+ " <xsl:sort select=\".\" data-type=\"number\" order=\"descending\"/>\n"
+ " <xsl:variable name=\"css-class\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " <tr>\n"
+ " <th class=\"totals\">Total:</th>\n"
+ " <td class=\"totals\">\n"
+ " <xsl:value-of select=\"sum(server/counters[@type=&quot;qtype&quot;]/counter)\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"views/view[count(counters[@type=&quot;resqtype&quot;]/counter) &gt; 0]\">\n"
+ " <h2>Outgoing Queries per view</h2>\n"
+ " <xsl:for-each select=\"views/view[count(counters[@type=&quot;resqtype&quot;]/counter) &gt; 0]\">\n"
+ " <h3>View <xsl:value-of select=\"@name\"/></h3>\n"
+ " <xsl:if test=\"system-property('xsl:vendor')!='Transformiix'\">\n"
+ " <!-- Non Mozilla specific markup -->\n"
+ " <script type=\"text/javascript\">\n"
+ " graphs.push({\n"
+ " 'title': \"Outgoing Queries for view: <xsl:value-of select=\"@name\"/>\",\n"
+ " 'target': 'chart_outgoing_queries_view_<xsl:value-of select=\"@name\"/>',\n"
+ " 'style': 'barchart',\n"
+ " 'data': [['Type','Counter'],<xsl:for-each select=\"counters[@type=&quot;resqtype&quot;]/counter\">['<xsl:value-of select=\"@name\"/>',<xsl:value-of select=\".\"/>],</xsl:for-each>]\n"
+ " });\n"
+ " </script>\n"
+ " <xsl:variable name=\"target\">\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </xsl:variable>\n"
+ " <div class=\"pie\" id=\"chart_outgoing_queries_view_{$target}\">[no data to display]</div>\n"
+ " </xsl:if>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"counters[@type=&quot;resqtype&quot;]/counter\">\n"
+ " <xsl:sort select=\".\" data-type=\"number\" order=\"descending\"/>\n"
+ " <xsl:variable name=\"css-class1\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class1}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:for-each>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"server/counters[@type=&quot;nsstat&quot;]/counter[.&gt;0]\">\n"
+ " <h2>Server Statistics</h2>\n"
+ " <xsl:if test=\"system-property('xsl:vendor')!='Transformiix'\">\n"
+ " <!-- Non Mozilla specific markup -->\n"
+ " <script type=\"text/javascript\">\n"
+ " graphs.push({\n"
+ " 'title' : \"Server Counters\",\n"
+ " 'target': 'chart_server_nsstat_restype',\n"
+ " 'style': 'barchart',\n"
+ " 'data': [['Type','Counter'],<xsl:for-each select=\"server/counters[@type=&quot;nsstat&quot;]/counter[.&gt;0]\">['<xsl:value-of select=\"@name\"/>',<xsl:value-of select=\".\"/>],</xsl:for-each>]\n"
+ " });\n"
+ " </script>\n"
+ " <div class=\"pie\" id=\"chart_server_nsstat_restype\">[no data to display]</div>\n"
+ " </xsl:if>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"server/counters[@type=&quot;nsstat&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:sort select=\".\" data-type=\"number\" order=\"descending\"/>\n"
+ " <xsl:variable name=\"css-class2\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class2}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"server/counters[@type=&quot;zonestat&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:if test=\"system-property('xsl:vendor')!='Transformiix'\">\n"
+ " <h2>Zone Maintenance Statistics</h2>\n"
+ " <script type=\"text/javascript\">\n"
+ " graphs.push({\n"
+ " 'title' : \"Zone Maintenance Stats\",\n"
+ " 'target': 'chart_server_zone_maint',\n"
+ " 'style': 'barchart',\n"
+ " 'data': [['Type','Counter'],<xsl:for-each select=\"server/counters[@type=&quot;zonestat&quot;]/counter[.&gt;0]\">['<xsl:value-of select=\"@name\"/>',<xsl:value-of select=\".\"/>],</xsl:for-each>]\n"
+ " });\n"
+ " </script>\n"
+ " <!-- Non Mozilla specific markup -->\n"
+ " <div class=\"pie\" id=\"chart_server_zone_maint\">[no data to display]</div>\n"
+ " </xsl:if>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"server/counters[@type=&quot;zonestat&quot;]/counter\">\n"
+ " <xsl:sort select=\".\" data-type=\"number\" order=\"descending\"/>\n"
+ " <xsl:variable name=\"css-class3\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class3}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"server/counters[@type=&quot;resstat&quot;]/counter[.&gt;0]\">\n"
+ " <h2>Resolver Statistics (Common)</h2>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"server/counters[@type=&quot;resstat&quot;]/counter\">\n"
+ " <xsl:sort select=\".\" data-type=\"number\" order=\"descending\"/>\n"
+ " <xsl:variable name=\"css-class4\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class4}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " </xsl:if>\n"
+ " <xsl:for-each select=\"views/view\">\n"
+ " <xsl:if test=\"counters[@type=&quot;resstats&quot;]/counter[.&gt;0]\">\n"
+ " <h3>Resolver Statistics for View <xsl:value-of select=\"@name\"/></h3>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"counters[@type=&quot;resstats&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:sort select=\".\" data-type=\"number\" order=\"descending\"/>\n"
+ " <xsl:variable name=\"css-class5\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class5}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " </xsl:if>\n"
+ " </xsl:for-each>\n"
+ " <xsl:for-each select=\"views/view\">\n"
+ " <xsl:if test=\"counters[@type=&quot;adbstat&quot;]/counter[.&gt;0]\">\n"
+ " <h3>ADB Statistics for View <xsl:value-of select=\"@name\"/></h3>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"counters[@type=&quot;adbstat&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:sort select=\".\" data-type=\"number\" order=\"descending\"/>\n"
+ " <xsl:variable name=\"css-class5\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class5}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " </xsl:if>\n"
+ " </xsl:for-each>\n"
+ " <xsl:for-each select=\"views/view\">\n"
+ " <xsl:if test=\"counters[@type=&quot;cachestats&quot;]/counter[.&gt;0]\">\n"
+ " <h3>Cache Statistics for View <xsl:value-of select=\"@name\"/></h3>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"counters[@type=&quot;cachestats&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:sort select=\".\" data-type=\"number\" order=\"descending\"/>\n"
+ " <xsl:variable name=\"css-class5\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class5}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " </xsl:if>\n"
+ " </xsl:for-each>\n"
+ " <xsl:for-each select=\"views/view\">\n"
+ " <xsl:if test=\"cache/rrset\">\n"
+ " <h3>Cache DB RRsets for View <xsl:value-of select=\"@name\"/></h3>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"cache/rrset\">\n"
+ " <xsl:variable name=\"css-class6\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class6}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"counter\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " </xsl:for-each>\n"
+ " <xsl:if test=\"traffic/udp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0] or traffic/udp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0] or traffic/tcp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0] or traffic/tcp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]\">\n"
+ " <h2>Traffic Size Statistics</h2>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"traffic/udp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]\">\n"
+ " <h4>UDP Requests Received</h4>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"traffic/udp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:variable name=\"css-class7\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class7}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"traffic/udp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]\">\n"
+ " <h4>UDP Responses Sent</h4>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"traffic/udp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:variable name=\"css-class7\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class7}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"traffic/tcp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]\">\n"
+ " <h4>TCP Requests Received</h4>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"traffic/tcp/counters[@type=&quot;request-size&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:variable name=\"css-class7\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class7}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"traffic/tcp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]\">\n"
+ " <h4>TCP Responses Sent</h4>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"traffic/tcp/counters[@type=&quot;response-size&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:variable name=\"css-class7\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class7}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"server/counters[@type=&quot;sockstat&quot;]/counter[.&gt;0]\">\n"
+ " <h2>Socket I/O Statistics</h2>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"server/counters[@type=&quot;sockstat&quot;]/counter[.&gt;0]\">\n"
+ " <xsl:variable name=\"css-class7\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class7}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"views/view[zones/zone/counters[@type=&quot;qtype&quot;]/counter &gt;0]\">\n"
+ " <h2>Received QTYPES per view/zone</h2>\n"
+ " <xsl:for-each select=\"views/view[zones/zone/counters[@type=&quot;qtype&quot;]/counter &gt;0]\">\n"
+ " <h3>View <xsl:value-of select=\"@name\"/></h3>\n"
+ " <xsl:variable name=\"thisview\">\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </xsl:variable>\n"
+ " <xsl:for-each select=\"zones/zone\">\n"
+ " <xsl:if test=\"counters[@type=&quot;qtype&quot;]/counter[count(.) &gt; 0]\">\n"
+ " <h4>Zone <xsl:value-of select=\"@name\"/></h4>\n"
+ " <xsl:if test=\"system-property('xsl:vendor')!='Transformiix'\">\n"
+ " <!-- Non Mozilla specific markup -->\n"
+ " <script type=\"text/javascript\">\n"
+ " graphs.push({\n"
+ " 'title': \"Query types for zone <xsl:value-of select=\"@name\"/>\",\n"
+ " 'target': 'chart_qtype_<xsl:value-of select=\"../../@name\"/>_<xsl:value-of select=\"@name\"/>',\n"
+ " 'style': 'barchart',\n"
+ " 'data': [['Type','Counter'],<xsl:for-each select=\"counters[@type=&quot;qtype&quot;]/counter[.&gt;0 and @name != &quot;QryAuthAns&quot;]\">['<xsl:value-of select=\"@name\"/>',<xsl:value-of select=\".\"/>],</xsl:for-each>]\n"
+ " });\n"
+ "\n"
+ " </script>\n"
+ " <xsl:variable name=\"target\">\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </xsl:variable>\n"
+ " <div class=\"pie\" id=\"chart_qtype_{$thisview}_{$target}\">[no data to display]</div>\n"
+ " </xsl:if>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"counters[@type=&quot;qtype&quot;]/counter\">\n"
+ " <xsl:sort select=\".\"/>\n"
+ " <xsl:variable name=\"css-class10\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class10}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " </xsl:if>\n"
+ " </xsl:for-each>\n"
+ " </xsl:for-each>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"views/view[zones/zone/counters[@type=&quot;rcode&quot;]/counter &gt;0]\">\n"
+ " <h2>Response Codes per view/zone</h2>\n"
+ " <xsl:for-each select=\"views/view[zones/zone/counters[@type=&quot;rcode&quot;]/counter &gt;0]\">\n"
+ " <h3>View <xsl:value-of select=\"@name\"/></h3>\n"
+ " <xsl:variable name=\"thisview2\">\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </xsl:variable>\n"
+ " <xsl:for-each select=\"zones/zone\">\n"
+ " <xsl:if test=\"counters[@type=&quot;rcode&quot;]/counter[. &gt; 0]\">\n"
+ " <h4>Zone <xsl:value-of select=\"@name\"/></h4>\n"
+ " <xsl:if test=\"system-property('xsl:vendor')!='Transformiix'\">\n"
+ " <!-- Non Mozilla specific markup -->\n"
+ " <script type=\"text/javascript\">\n"
+ " graphs.push({\n"
+ " 'title': \"Response codes for zone <xsl:value-of select=\"@name\"/>\",\n"
+ " 'target': 'chart_rescode_<xsl:value-of select=\"../../@name\"/>_<xsl:value-of select=\"@name\"/>',\n"
+ " 'style': 'barchart',\n"
+ " 'data': [['Type','Counter'],<xsl:for-each select=\"counters[@type=&quot;rcode&quot;]/counter[.&gt;0 and @name != &quot;QryAuthAns&quot;]\">['<xsl:value-of select=\"@name\"/>',<xsl:value-of select=\".\"/>],</xsl:for-each>]\n"
+ " });\n"
+ "\n"
+ " </script>\n"
+ " <xsl:variable name=\"target\">\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </xsl:variable>\n"
+ " <div class=\"pie\" id=\"chart_rescode_{$thisview2}_{$target}\">[no data to display]</div>\n"
+ " </xsl:if>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"counters[@type=&quot;rcode&quot;]/counter[.&gt;0 and @name != &quot;QryAuthAns&quot;]\">\n"
+ " <xsl:sort select=\".\"/>\n"
+ " <xsl:variable name=\"css-class11\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class11}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"@name\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " </xsl:if>\n"
+ " </xsl:for-each>\n"
+ " </xsl:for-each>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"socketmgr/sockets/socket\">\n"
+ " <h2>Network Status</h2>\n"
+ " <table class=\"netstat\">\n"
+ " <tr>\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"
+ " <xsl:sort select=\"id\"/>\n"
+ " <xsl:variable name=\"css-class12\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class12}\">\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"
+ " </xsl:if>\n"
+ " <xsl:if test=\"taskmgr/thread-model/type\">\n"
+ " <h2>Task Manager Configuration</h2>\n"
+ " <table class=\"counters\">\n"
+ " <tr>\n"
+ " <th class=\"even\">Thread-Model</th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"taskmgr/thread-model/type\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr class=\"odd\">\n"
+ " <th>Worker Threads</th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"taskmgr/thread-model/worker-threads\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr class=\"even\">\n"
+ " <th>Default Quantum</th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"taskmgr/thread-model/default-quantum\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr class=\"odd\">\n"
+ " <th>Tasks Running</th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"taskmgr/thread-model/tasks-running\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " <tr class=\"even\">\n"
+ " <th>Tasks Ready</th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\"taskmgr/thread-model/tasks-ready\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"taskmgr/tasks/task\">\n"
+ " <h2>Tasks</h2>\n"
+ " <table class=\"tasks\">\n"
+ " <tr>\n"
+ " <th>ID</th>\n"
+ " <th>Name</th>\n"
+ " <th>References</th>\n"
+ " <th>State</th>\n"
+ " <th>Quantum</th>\n"
+ " <th>Events</th>\n"
+ " </tr>\n"
+ " <xsl:for-each select=\"taskmgr/tasks/task\">\n"
+ " <xsl:sort select=\"name\"/>\n"
+ " <xsl:variable name=\"css-class14\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class14}\">\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"
+ " <td>\n"
+ " <xsl:value-of select=\"events\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"memory/summary\">\n"
+ " <h2>Memory Usage Summary</h2>\n"
+ " <table class=\"counters\">\n"
+ " <xsl:for-each select=\"memory/summary/*\">\n"
+ " <xsl:variable name=\"css-class13\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class13}\">\n"
+ " <th>\n"
+ " <xsl:value-of select=\"name()\"/>\n"
+ " </th>\n"
+ " <td>\n"
+ " <xsl:value-of select=\".\"/>\n"
+ " </td>\n"
+ " </tr>\n"
+ " </xsl:for-each>\n"
+ " </table>\n"
+ " <br/>\n"
+ " </xsl:if>\n"
+ " <xsl:if test=\"memory/contexts/context\">\n"
+ " <h2>Memory Contexts</h2>\n"
+ " <table class=\"mctx\">\n"
+ " <tr>\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"
+ " <xsl:sort select=\"total\" data-type=\"number\" order=\"descending\"/>\n"
+ " <xsl:variable name=\"css-class14\">\n"
+ " <xsl:choose>\n"
+ " <xsl:when test=\"position() mod 2 = 0\">even</xsl:when>\n"
+ " <xsl:otherwise>odd</xsl:otherwise>\n"
+ " </xsl:choose>\n"
+ " </xsl:variable>\n"
+ " <tr class=\"{$css-class14}\">\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"
+ " </xsl:if>\n"
+ " <hr/>\n"
+ " <p class=\"footer\">Internet Systems Consortium Inc.<br/><a href=\"http://www.isc.org\">http://www.isc.org</a></p>\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..d6562ee
--- /dev/null
+++ b/bin/named/builtin.c
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file
+ * \brief
+ * 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);
+static isc_result_t do_dns64_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 builtin_t dns64_builtin = { do_dns64_lookup, NULL, NULL };
+
+static dns_sdbimplementation_t *builtin_impl;
+static dns_sdbimplementation_t *dns64_impl;
+
+/*
+ * Pre computed HEX * 16 or 1 table.
+ */
+static const unsigned char hex16[256] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*00*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*10*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*20*/
+ 0, 16, 32, 48, 64, 80, 96,112,128,144, 1, 1, 1, 1, 1, 1, /*30*/
+ 1,160,176,192,208,224,240, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*40*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*50*/
+ 1,160,176,192,208,224,240, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*60*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*70*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*80*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*90*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*A0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*B0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*C0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*D0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /*E0*/
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /*F0*/
+};
+
+const unsigned char decimal[] = "0123456789";
+
+static size_t
+dns64_rdata(unsigned char *v, size_t start, unsigned char *rdata) {
+ size_t i, j = 0;
+
+ for (i = 0; i < 4U; i++) {
+ unsigned char c = v[start++];
+ if (start == 7U)
+ start++;
+ if (c > 99) {
+ rdata[j++] = 3;
+ rdata[j++] = decimal[c/100]; c = c % 100;
+ rdata[j++] = decimal[c/10]; c = c % 10;
+ rdata[j++] = decimal[c];
+ } else if (c > 9) {
+ rdata[j++] = 2;
+ rdata[j++] = decimal[c/10]; c = c % 10;
+ rdata[j++] = decimal[c];
+ } else {
+ rdata[j++] = 1;
+ rdata[j++] = decimal[c];
+ }
+ }
+ memmove(&rdata[j], "\07in-addr\04arpa", 14);
+ return (j + 14);
+}
+
+static isc_result_t
+dns64_cname(const dns_name_t *zone, const dns_name_t *name,
+ dns_sdblookup_t *lookup)
+{
+ size_t zlen, nlen, j, len;
+ unsigned char v[16], n;
+ unsigned int i;
+ unsigned char rdata[sizeof("123.123.123.123.in-addr.arpa.")];
+ unsigned char *ndata;
+
+ /*
+ * The combined length of the zone and name is 74.
+ *
+ * The minimum zone length is 10 ((3)ip6(4)arpa(0)).
+ *
+ * The length of name should always be even as we are expecting
+ * a series of nibbles.
+ */
+ zlen = zone->length;
+ nlen = name->length;
+ if ((zlen + nlen) > 74U || zlen < 10U || (nlen % 2) != 0U)
+ return (ISC_R_NOTFOUND);
+
+ /*
+ * We assume the zone name is well formed.
+ */
+
+ /*
+ * XXXMPA We could check the dns64 suffix here if we need to.
+ */
+ /*
+ * Check that name is a series of nibbles.
+ * Compute the byte values that correspond to the nibbles as we go.
+ *
+ * Shift the final result 4 bits, by setting 'i' to 1, if we if we
+ * have a odd number of nibbles so that "must be zero" tests below
+ * are byte aligned and we correctly return ISC_R_NOTFOUND or
+ * ISC_R_SUCCESS. We will not generate a CNAME in this case.
+ */
+ ndata = name->ndata;
+ i = (nlen % 4) == 2U ? 1 : 0;
+ j = nlen;
+ memset(v, 0, sizeof(v));
+ while (j != 0U) {
+ INSIST((i/2) < sizeof(v));
+ if (ndata[0] != 1)
+ return (ISC_R_NOTFOUND);
+ n = hex16[ndata[1]&0xff];
+ if (n == 1)
+ return (ISC_R_NOTFOUND);
+ v[i/2] = n | (v[i/2]>>4);
+ j -= 2;
+ ndata += 2;
+ i++;
+ }
+
+ /*
+ * If we get here then we know name only consisted of nibbles.
+ * Now we need to determine if the name exists or not and whether
+ * it corresponds to a empty node in the zone or there should be
+ * a CNAME.
+ */
+#define ZLEN(x) (10 + (x)/2)
+ switch (zlen) {
+ case ZLEN(32): /* prefix len 32 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (nlen > 16U && v[(nlen-1)/4 - 4] != 0)
+ return (ISC_R_NOTFOUND);
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U)
+ return (ISC_R_SUCCESS);
+ len = dns64_rdata(v, 8, rdata);
+ break;
+ case ZLEN(40): /* prefix len 40 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (nlen > 12U && v[(nlen-1)/4 - 3] != 0)
+ return (ISC_R_NOTFOUND);
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U)
+ return (ISC_R_SUCCESS);
+ len = dns64_rdata(v, 6, rdata);
+ break;
+ case ZLEN(48): /* prefix len 48 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (nlen > 8U && v[(nlen-1)/4 - 2] != 0)
+ return (ISC_R_NOTFOUND);
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U)
+ return (ISC_R_SUCCESS);
+ len = dns64_rdata(v, 5, rdata);
+ break;
+ case ZLEN(56): /* prefix len 56 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (nlen > 4U && v[(nlen-1)/4 - 1] != 0)
+ return (ISC_R_NOTFOUND);
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U)
+ return (ISC_R_SUCCESS);
+ len = dns64_rdata(v, 4, rdata);
+ break;
+ case ZLEN(64): /* prefix len 64 */
+ /*
+ * The nibbles that map to this byte must be zero for 'name'
+ * to exist in the zone.
+ */
+ if (v[(nlen-1)/4] != 0)
+ return (ISC_R_NOTFOUND);
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U)
+ return (ISC_R_SUCCESS);
+ len = dns64_rdata(v, 3, rdata);
+ break;
+ case ZLEN(96): /* prefix len 96 */
+ /*
+ * If the total length is not 74 then this is a empty node
+ * so return success.
+ */
+ if (nlen + zlen != 74U)
+ return (ISC_R_SUCCESS);
+ len = dns64_rdata(v, 0, rdata);
+ break;
+ default:
+ /*
+ * This should never be reached unless someone adds a
+ * zone declaration with this internal type to named.conf.
+ */
+ return (ISC_R_NOTFOUND);
+ }
+ return (dns_sdb_putrdata(lookup, dns_rdatatype_cname, 600,
+ rdata, (unsigned int)len));
+}
+
+static isc_result_t
+builtin_lookup(const char *zone, const char *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+{
+ builtin_t *b = (builtin_t *) dbdata;
+
+ UNUSED(zone);
+ UNUSED(methods);
+ UNUSED(clientinfo);
+
+ if (strcmp(name, "@") == 0)
+ return (b->do_lookup(lookup));
+ else
+ return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+dns64_lookup(const dns_name_t *zone, const dns_name_t *name, void *dbdata,
+ dns_sdblookup_t *lookup, dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+{
+ builtin_t *b = (builtin_t *) dbdata;
+
+ UNUSED(methods);
+ UNUSED(clientinfo);
+
+ if (name->labels == 0 && name->length == 0)
+ return (b->do_lookup(lookup));
+ else
+ return (dns64_cname(zone, name, lookup));
+}
+
+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;
+ memmove(&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",
+ "Curtis Blackburn",
+ "James Brister",
+ "Ben Cottrell",
+ "John H. DuBois III",
+ "Francis Dupont",
+ "Michael Graff",
+ "Andreas Gustafsson",
+ "Bob Halley",
+ "Evan Hunt",
+ "JINMEI Tatuya",
+ "Witold Krecicki",
+ "David Lawrence",
+ "Scott Mann",
+ "Danny Mayer",
+ "Damien Neil",
+ "Matt Nelson",
+ "Jeremy C. Reed",
+ "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_dns64_lookup(dns_sdblookup_t *lookup) {
+ UNUSED(lookup);
+ return (ISC_R_SUCCESS);
+}
+
+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 || strcmp(argv[0], "dns64") == 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 ||
+ strcmp(argv[0], "dns64") == 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) {
+ if (strcmp(argv[0], "empty") == 0)
+ *dbdata = &empty_builtin;
+ else
+ *dbdata = &dns64_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 {
+ if (strcmp(argv[0], "empty") == 0)
+ memmove(empty, &empty_builtin,
+ sizeof (empty_builtin));
+ else
+ memmove(empty, &dns64_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 || *dbdata == &dns64_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,
+ NULL
+};
+
+static dns_sdbmethods_t dns64_methods = {
+ NULL,
+ builtin_authority,
+ NULL, /* allnodes */
+ builtin_create,
+ builtin_destroy,
+ dns64_lookup,
+};
+
+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);
+ RUNTIME_CHECK(dns_sdb_register("_dns64", &dns64_methods, NULL,
+ DNS_SDBFLAG_RELATIVEOWNER |
+ DNS_SDBFLAG_RELATIVERDATA |
+ DNS_SDBFLAG_DNS64,
+ ns_g_mctx, &dns64_impl)
+ == ISC_R_SUCCESS);
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_builtin_deinit(void) {
+ dns_sdb_unregister(&builtin_impl);
+ dns_sdb_unregister(&dns64_impl);
+}
diff --git a/bin/named/client.c b/bin/named/client.c
new file mode 100644
index 0000000..4d26eff
--- /dev/null
+++ b/bin/named/client.c
@@ -0,0 +1,3964 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/aes.h>
+#include <isc/formatcheck.h>
+#include <isc/hmacsha.h>
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/queue.h>
+#include <isc/random.h>
+#include <isc/safe.h>
+#include <isc/serial.h>
+#include <isc/stats.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/timer.h>
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/badcache.h>
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#include <dns/dnstap.h>
+#include <dns/cache.h>
+#include <dns/edns.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/fuzz.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
+
+#define COOKIE_SIZE 24U /* 8 + 4 + 4 + 8 */
+#define ECS_SIZE 20U /* 2 + 1 + 1 + [0..16] */
+
+#define WANTNSID(x) (((x)->attributes & NS_CLIENTATTR_WANTNSID) != 0)
+#define WANTEXPIRE(x) (((x)->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0)
+
+/*% nameserver client manager structure */
+struct ns_clientmgr {
+ /* Unlocked. */
+ unsigned int magic;
+
+ /* The queue object has its own locks */
+ client_queue_t inactive; /*%< To be recycled */
+
+ isc_mem_t * mctx;
+ isc_taskmgr_t * taskmgr;
+ isc_timermgr_t * timermgr;
+
+ /* Lock covers manager state. */
+ isc_mutex_t lock;
+ bool exiting;
+
+ /* Lock covers the clients list */
+ isc_mutex_t listlock;
+ client_list_t clients; /*%< All active clients */
+
+ /* Lock covers the recursing list */
+ isc_mutex_t reclock;
+ client_list_t recursing; /*%< Recursing clients */
+
+#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_RECURSING 5
+/*%<
+ * The client object is recursing. It will be on the 'recursing'
+ * list.
+ */
+
+#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 bool exit_check(ns_client_t *client);
+static void ns_client_endrequest(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);
+static isc_result_t get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
+ dns_dispatch_t *disp, bool tcp);
+static isc_result_t get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp,
+ isc_socket_t *sock);
+static inline bool
+allowed(isc_netaddr_t *addr, dns_name_t *signer, isc_netaddr_t *ecs_addr,
+ uint8_t ecs_addrlen, uint8_t *ecs_scope, dns_acl_t *acl);
+static void compute_cookie(ns_client_t *client, uint32_t when,
+ uint32_t nonce, const unsigned char *secret,
+ isc_buffer_t *buf);
+
+void
+ns_client_recursing(ns_client_t *client) {
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(client->state == NS_CLIENTSTATE_WORKING);
+
+ LOCK(&client->manager->reclock);
+ client->newstate = client->state = NS_CLIENTSTATE_RECURSING;
+ ISC_LIST_APPEND(client->manager->recursing, client, rlink);
+ UNLOCK(&client->manager->reclock);
+}
+
+void
+ns_client_killoldestquery(ns_client_t *client) {
+ ns_client_t *oldest;
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ LOCK(&client->manager->reclock);
+ oldest = ISC_LIST_HEAD(client->manager->recursing);
+ if (oldest != NULL) {
+ ISC_LIST_UNLINK(client->manager->recursing, oldest, rlink);
+ UNLOCK(&client->manager->reclock);
+ ns_query_cancel(oldest);
+ } else
+ UNLOCK(&client->manager->reclock);
+}
+
+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, false);
+ client->timerset = 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 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 bool
+exit_check(ns_client_t *client) {
+ bool destroy_manager = false;
+ ns_clientmgr_t *manager = NULL;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+ manager = client->manager;
+
+ if (client->state <= client->newstate)
+ return (false); /* Business as usual. */
+
+ INSIST(client->newstate < NS_CLIENTSTATE_RECURSING);
+
+ /*
+ * 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 ||
+ client->state == NS_CLIENTSTATE_RECURSING)
+ {
+ INSIST(client->newstate <= NS_CLIENTSTATE_READING);
+ /*
+ * Let the update processing complete.
+ */
+ if (client->nupdates > 0)
+ return (true);
+
+ /*
+ * We are trying to abort request processing.
+ */
+ if (client->nsends > 0) {
+ isc_socket_t *sock;
+ if (TCP_CLIENT(client))
+ sock = client->tcpsocket;
+ else
+ sock = client->udpsocket;
+ isc_socket_cancel(sock, 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 (true);
+ }
+
+ /*
+ * I/O cancel is complete. Burn down all state
+ * related to the current request. Ensure that
+ * the client is no longer on the recursing list.
+ *
+ * We need to check whether the client is still linked,
+ * because it may already have been removed from the
+ * recursing list by ns_client_killoldestquery()
+ */
+ if (client->state == NS_CLIENTSTATE_RECURSING) {
+ LOCK(&manager->reclock);
+ if (ISC_LINK_LINKED(client, rlink))
+ ISC_LIST_UNLINK(manager->recursing,
+ client, rlink);
+ UNLOCK(&manager->reclock);
+ }
+ ns_client_endrequest(client);
+
+ client->state = NS_CLIENTSTATE_READING;
+ INSIST(client->recursionquota == NULL);
+
+ if (NS_CLIENTSTATE_READING == client->newstate) {
+ if (!client->pipelined) {
+ client_read(client);
+ client->newstate = NS_CLIENTSTATE_MAX;
+ return (true); /* We're done. */
+ } else if (client->mortal) {
+ client->newstate = NS_CLIENTSTATE_INACTIVE;
+ } else
+ return (false);
+ }
+ }
+
+ 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 (true);
+ }
+
+ if (client->tcpmsg_valid) {
+ dns_tcpmsg_invalidate(&client->tcpmsg);
+ client->tcpmsg_valid = 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, true);
+ client->timerset = false;
+ }
+
+ client->pipelined = false;
+
+ client->peeraddr_valid = 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.
+ *
+ * UDP clients go inactive at this point, but TCP clients
+ * may remain active if we have fewer active TCP client
+ * objects than desired due to an earlier quota exhaustion.
+ */
+ if (client->mortal && TCP_CLIENT(client) && !ns_g_clienttest) {
+ LOCK(&client->interface->lock);
+ if (client->interface->ntcpcurrent <
+ client->interface->ntcptarget)
+ client->mortal = false;
+ UNLOCK(&client->interface->lock);
+ }
+
+ /*
+ * We don't need the client; send it to the inactive
+ * queue for recycling.
+ */
+ if (client->mortal) {
+ if (client->newstate > NS_CLIENTSTATE_INACTIVE)
+ client->newstate = NS_CLIENTSTATE_INACTIVE;
+ }
+
+ if (NS_CLIENTSTATE_READY == client->newstate) {
+ if (TCP_CLIENT(client)) {
+ client_accept(client);
+ } else
+ client_udprecv(client);
+ client->newstate = NS_CLIENTSTATE_MAX;
+ return (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);
+
+ /* Still waiting for accept cancel completion. */
+ if (! (client->naccepts == 0))
+ return (true);
+
+ /* Accept cancel is complete. */
+ if (client->nrecvs > 0)
+ isc_socket_cancel(client->udpsocket, client->task,
+ ISC_SOCKCANCEL_RECV);
+
+ /* Still waiting for recv cancel completion. */
+ if (! (client->nrecvs == 0))
+ return (true);
+
+ /* Still waiting for control event to be delivered */
+ if (client->nctls > 0)
+ return (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 = false;
+
+ if (client->keytag != NULL) {
+ isc_mem_put(client->mctx, client->keytag,
+ client->keytag_len);
+ client->keytag_len = 0;
+ }
+
+ /*
+ * 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.
+ */
+ client->state = NS_CLIENTSTATE_INACTIVE;
+ INSIST(client->recursionquota == NULL);
+
+ if (client->state == client->newstate) {
+ client->newstate = NS_CLIENTSTATE_MAX;
+ if (!ns_g_clienttest && manager != NULL &&
+ !manager->exiting)
+ ISC_QUEUE_PUSH(manager->inactive, client,
+ ilink);
+ if (client->needshutdown)
+ isc_task_shutdown(client->task);
+ return (true);
+ }
+ }
+
+ 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);
+ INSIST(!ISC_QLINK_LINKED(client, ilink));
+
+ if (manager != NULL) {
+ LOCK(&manager->listlock);
+ ISC_LIST_UNLINK(manager->clients, client, link);
+ LOCK(&manager->lock);
+ if (manager->exiting &&
+ ISC_LIST_EMPTY(manager->clients))
+ destroy_manager = true;
+ UNLOCK(&manager->lock);
+ UNLOCK(&manager->listlock);
+ }
+
+ 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->delaytimer != NULL)
+ isc_timer_detach(&client->delaytimer);
+
+ 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);
+ }
+ if (client->keytag != NULL) {
+ isc_mem_put(client->mctx, client->keytag,
+ client->keytag_len);
+ client->keytag_len = 0;
+ }
+
+ dns_message_destroy(&client->message);
+
+ /*
+ * 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);
+ }
+
+ /*
+ * Destroy the fetchlock mutex that was created in
+ * ns_query_init().
+ */
+ DESTROYLOCK(&client->query.fetchlock);
+
+ isc_mem_putanddetach(&client->mctx, client, sizeof(*client));
+ }
+
+ if (destroy_manager && manager != NULL)
+ clientmgr_destroy(manager);
+
+ return (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)) {
+ if (client->pipelined) {
+ client_read(client);
+ } else {
+ 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;
+ }
+
+ if (ISC_QLINK_LINKED(client, ilink))
+ ISC_QUEUE_UNLINK(client->manager->inactive, client, ilink);
+
+ client->newstate = NS_CLIENTSTATE_FREED;
+ client->needshutdown = 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 ||
+ client->state == NS_CLIENTSTATE_RECURSING);
+
+ CTRACE("endrequest");
+
+ if (client->next != NULL) {
+ (client->next)(client);
+ client->next = NULL;
+ }
+
+ if (client->view != NULL) {
+#ifdef ENABLE_AFL
+ if (ns_g_fuzz_type == ns_fuzz_resolver) {
+ dns_cache_clean(client->view->cache, INT_MAX);
+ dns_adb_flush(client->view->adb);
+ }
+#endif
+ 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->signer = NULL;
+ 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);
+ isc_stats_decrement(ns_g_server->nsstats,
+ dns_nsstatscounter_recursclients);
+ }
+
+ /*
+ * Clear all client attributes that are specific to
+ * the request; that's all except the TCP flag.
+ */
+ client->attributes &= NS_CLIENTATTR_TCP;
+#ifdef ENABLE_AFL
+ if (ns_g_fuzz_type == ns_fuzz_client ||
+ ns_g_fuzz_type == ns_fuzz_tcpclient ||
+ ns_g_fuzz_type == ns_fuzz_resolver) {
+ named_fuzz_notify();
+ }
+#endif /* ENABLE_AFL */
+
+}
+
+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_RECURSING ||
+ 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;
+ }
+
+ 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, uint32_t length,
+ unsigned char *sendbuf, unsigned char **datap)
+{
+ unsigned char *data;
+ 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, (uint16_t)length);
+ }
+ } else {
+ data = sendbuf;
+ if ((client->attributes & NS_CLIENTATTR_HAVECOOKIE) == 0) {
+ if (client->view != NULL)
+ bufsize = client->view->nocookieudp;
+ else
+ bufsize = 512;
+ } else
+ bufsize = client->udpsize;
+ if (bufsize > client->udpsize)
+ bufsize = client->udpsize;
+ if (bufsize > SEND_BUFFER_SIZE)
+ 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 *sock;
+ isc_netaddr_t netaddr;
+ int match;
+ unsigned int sockflags = ISC_SOCKFLAG_IMMEDIATE;
+ isc_dscp_t dispdscp = -1;
+
+ if (TCP_CLIENT(client)) {
+ sock = client->tcpsocket;
+ address = NULL;
+ } else {
+ sock = 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;
+
+ if (client->dispatch != NULL) {
+ dispdscp = dns_dispatch_getdscp(client->dispatch);
+ if (dispdscp != -1)
+ client->dscp = dispdscp;
+ }
+
+ if (client->dscp == -1) {
+ client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_DSCP;
+ client->sendevent->dscp = 0;
+ } else {
+ client->sendevent->attributes |= ISC_SOCKEVENTATTR_DSCP;
+ client->sendevent->dscp = client->dscp;
+ }
+
+ isc_buffer_usedregion(buffer, &r);
+
+ /*
+ * If this is a UDP client and the IPv6 packet can't be
+ * encapsulated without generating a PTB on a 1500 octet
+ * MTU link force fragmentation at 1280 if it is a IPv6
+ * response.
+ */
+ client->sendevent->attributes &= ~ISC_SOCKEVENTATTR_USEMINMTU;
+ if (!TCP_CLIENT(client) && r.length > 1432)
+ client->sendevent->attributes |= ISC_SOCKEVENTATTR_USEMINMTU;
+
+ CTRACE("sendto");
+
+ result = isc_socket_sendto2(sock, &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);
+}
+
+static void
+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;
+ bool cleanup_cctx = false;
+ unsigned char sendbuf[SEND_BUFFER_SIZE];
+ unsigned int render_opts;
+ unsigned int preferred_glue;
+ bool opt_included = false;
+ size_t respsize;
+#ifdef HAVE_DNSTAP
+ unsigned char zone[DNS_NAME_MAXWIRE];
+ dns_dtmsgtype_t dtmsgtype;
+ isc_region_t zr;
+#endif /* HAVE_DNSTAP */
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ CTRACE("send");
+
+ if (client->message->opcode == dns_opcode_query &&
+ (client->attributes & NS_CLIENTATTR_RA) != 0)
+ client->message->flags |= DNS_MESSAGEFLAG_RA;
+
+ if ((client->attributes & NS_CLIENTATTR_WANTDNSSEC) != 0)
+ render_opts = 0;
+ else
+ render_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;
+ }
+ if (preferred_glue == 0) {
+ if (isc_sockaddr_pf(&client->peeraddr) == AF_INET)
+ preferred_glue = DNS_MESSAGERENDER_PREFER_A;
+ else
+ preferred_glue = DNS_MESSAGERENDER_PREFER_AAAA;
+ }
+
+#ifdef ALLOW_FILTER_AAAA
+ /*
+ * filter-aaaa-on-v4 yes or break-dnssec option to suppress
+ * AAAA records.
+ *
+ * We already know that request came via IPv4,
+ * that we have both AAAA and A records,
+ * and that we either have no signatures that the client wants
+ * or we are supposed to break DNSSEC.
+ *
+ * Override preferred glue if necessary.
+ */
+ if ((client->attributes & NS_CLIENTATTR_FILTER_AAAA) != 0) {
+ render_opts |= DNS_MESSAGERENDER_FILTER_AAAA;
+ if (preferred_glue == DNS_MESSAGERENDER_PREFER_AAAA)
+ preferred_glue = DNS_MESSAGERENDER_PREFER_A;
+ }
+#endif
+
+ /*
+ * Create an OPT for our reply.
+ */
+ if ((client->attributes & NS_CLIENTATTR_WANTOPT) != 0) {
+ result = ns_client_addopt(client, client->message,
+ &client->opt);
+ if (result != ISC_R_SUCCESS)
+ goto done;
+ }
+
+ /*
+ * 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;
+ if (client->peeraddr_valid && client->view != NULL) {
+ isc_netaddr_t netaddr;
+ dns_name_t *name = NULL;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ if (client->message->tsigkey != NULL)
+ name = &client->message->tsigkey->name;
+
+ if (client->view->nocasecompress == NULL ||
+ !allowed(&netaddr, name, NULL, 0, NULL,
+ client->view->nocasecompress))
+ {
+ dns_compress_setsensitive(&cctx, true);
+ }
+
+ if (client->view->msgcompression == false) {
+ dns_compress_disable(&cctx);
+ }
+ }
+ cleanup_cctx = 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 = 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;
+ /*
+ * Stop after the question if TC was set for rate limiting.
+ */
+ if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
+ goto renderend;
+ result = dns_message_rendersection(client->message,
+ DNS_SECTION_ANSWER,
+ DNS_MESSAGERENDER_PARTIAL |
+ render_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 |
+ render_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 | render_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;
+
+#ifdef HAVE_DNSTAP
+ memset(&zr, 0, sizeof(zr));
+ if (((client->message->flags & DNS_MESSAGEFLAG_AA) != 0) &&
+ (client->query.authzone != NULL))
+ {
+ isc_buffer_t b;
+ dns_name_t *zo =
+ dns_zone_getorigin(client->query.authzone);
+
+ isc_buffer_init(&b, zone, sizeof(zone));
+ dns_compress_setmethods(&cctx, DNS_COMPRESS_NONE);
+ result = dns_name_towire(zo, &cctx, &b);
+ if (result == ISC_R_SUCCESS)
+ isc_buffer_usedregion(&b, &zr);
+ }
+
+ if ((client->message->flags & DNS_MESSAGEFLAG_RD) != 0)
+ dtmsgtype = DNS_DTTYPE_CR;
+ else
+ dtmsgtype = DNS_DTTYPE_AR;
+#endif /* HAVE_DNSTAP */
+
+ if (cleanup_cctx) {
+ dns_compress_invalidate(&cctx);
+ cleanup_cctx = false;
+ }
+
+ if (TCP_CLIENT(client)) {
+ isc_buffer_usedregion(&buffer, &r);
+ isc_buffer_putuint16(&tcpbuffer, (uint16_t) r.length);
+ isc_buffer_add(&tcpbuffer, r.length);
+#ifdef HAVE_DNSTAP
+ if (client->view != NULL) {
+ dns_dt_send(client->view, dtmsgtype,
+ &client->peeraddr, &client->destsockaddr,
+ true, &zr, &client->requesttime, NULL,
+ &buffer);
+ }
+#endif /* HAVE_DNSTAP */
+
+ /* don't count the 2-octet length header */
+ respsize = isc_buffer_usedlength(&tcpbuffer) - 2;
+ result = client_sendpkg(client, &tcpbuffer);
+
+ switch (isc_sockaddr_pf(&client->peeraddr)) {
+ case AF_INET:
+ isc_stats_increment(ns_g_server->tcpoutstats4,
+ ISC_MIN((int)respsize / 16, 256));
+ break;
+ case AF_INET6:
+ isc_stats_increment(ns_g_server->tcpoutstats6,
+ ISC_MIN((int)respsize / 16, 256));
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ } else {
+ respsize = isc_buffer_usedlength(&buffer);
+ result = client_sendpkg(client, &buffer);
+#ifdef HAVE_DNSTAP
+ if (client->view != NULL) {
+ dns_dt_send(client->view, dtmsgtype,
+ &client->peeraddr,
+ &client->destsockaddr,
+ false, &zr,
+ &client->requesttime, NULL, &buffer);
+ }
+#endif /* HAVE_DNSTAP */
+
+ switch (isc_sockaddr_pf(&client->peeraddr)) {
+ case AF_INET:
+ isc_stats_increment(ns_g_server->udpoutstats4,
+ ISC_MIN((int)respsize / 16, 256));
+ break;
+ case AF_INET6:
+ isc_stats_increment(ns_g_server->udpoutstats6,
+ ISC_MIN((int)respsize / 16, 256));
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ }
+
+ /* update statistics (XXXJT: is it okay to access message->xxxkey?) */
+ isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_response);
+
+ dns_rcodestats_increment(ns_g_server->rcodestats,
+ client->message->rcode);
+ if (opt_included) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_edns0out);
+ }
+ if (client->message->tsigkey != NULL) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_tsigout);
+ }
+ if (client->message->sig0key != NULL) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_sig0out);
+ }
+ if ((client->message->flags & DNS_MESSAGEFLAG_TC) != 0)
+ isc_stats_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);
+}
+
+/*
+ * Completes the sending of a delayed client response.
+ */
+static void
+client_delay(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->delaytimer != NULL);
+
+ UNUSED(task);
+
+ CTRACE("client_delay");
+
+ isc_event_free(&event);
+ isc_timer_detach(&client->delaytimer);
+
+ client_send(client);
+ ns_client_detach(&client);
+}
+
+void
+ns_client_send(ns_client_t *client) {
+
+ /*
+ * Delay the response by ns_g_delay ms.
+ */
+ if (ns_g_delay != 0) {
+ ns_client_t *dummy = NULL;
+ isc_result_t result;
+ isc_interval_t interval;
+
+ /*
+ * Replace ourselves if we have not already been replaced.
+ */
+ if (!client->mortal) {
+ result = ns_client_replace(client);
+ if (result != ISC_R_SUCCESS)
+ goto nodelay;
+ }
+
+ ns_client_attach(client, &dummy);
+ if (ns_g_delay >= 1000)
+ isc_interval_set(&interval, ns_g_delay / 1000,
+ (ns_g_delay % 1000) * 1000000);
+ else
+ isc_interval_set(&interval, 0, ns_g_delay * 1000000);
+ result = isc_timer_create(client->manager->timermgr,
+ isc_timertype_once, NULL, &interval,
+ client->task, client_delay,
+ client, &client->delaytimer);
+ if (result == ISC_R_SUCCESS)
+ return;
+
+ ns_client_detach(&dummy);
+ }
+
+ nodelay:
+ client_send(client);
+}
+
+#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
+
+ /*
+ * Try to rate limit error responses.
+ */
+ if (client->view != NULL && client->view->rrl != NULL) {
+ bool wouldlog;
+ char log_buf[DNS_RRL_LOG_BUF_LEN];
+ dns_rrl_result_t rrl_result;
+ int loglevel;
+
+ INSIST(rcode != dns_rcode_noerror &&
+ rcode != dns_rcode_nxdomain);
+ if (ns_g_server->log_queries)
+ loglevel = DNS_RRL_LOG_DROP;
+ else
+ loglevel = ISC_LOG_DEBUG(1);
+ wouldlog = isc_log_wouldlog(ns_g_lctx, loglevel);
+ rrl_result = dns_rrl(client->view, &client->peeraddr,
+ TCP_CLIENT(client),
+ dns_rdataclass_in, dns_rdatatype_none,
+ NULL, result, client->now,
+ wouldlog, log_buf, sizeof(log_buf));
+ if (rrl_result != DNS_RRL_RESULT_OK) {
+ /*
+ * Log dropped errors in the query category
+ * so that they are not lost in silence.
+ * Starts of rate-limited bursts are logged in
+ * NS_LOGCATEGORY_RRL.
+ */
+ if (wouldlog) {
+ ns_client_log(client,
+ NS_LOGCATEGORY_QUERY_ERRORS,
+ NS_LOGMODULE_CLIENT,
+ loglevel,
+ "%s", log_buf);
+ }
+ /*
+ * Some error responses cannot be 'slipped',
+ * so don't try to slip any error responses.
+ */
+ if (!client->view->rrl->log_only) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_ratedropped);
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_dropped);
+ ns_client_next(client, DNS_R_DROP);
+ return;
+ }
+ }
+ }
+
+ /*
+ * 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, 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 false.
+ */
+ result = dns_message_reply(message, false);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_next(client, result);
+ return;
+ }
+ }
+ message->rcode = rcode;
+
+ if (rcode == dns_rcode_formerr) {
+ /*
+ * 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 (isc_sockaddr_equal(&client->peeraddr,
+ &client->formerrcache.addr) &&
+ message->id == client->formerrcache.id &&
+ (isc_time_seconds(&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 =
+ isc_time_seconds(&client->requesttime);
+ client->formerrcache.id = message->id;
+ } else if (rcode == dns_rcode_servfail && client->query.qname != NULL &&
+ client->view != NULL && client->view->fail_ttl != 0 &&
+ ((client->attributes & NS_CLIENTATTR_NOSETFC) == 0))
+ {
+ /*
+ * SERVFAIL caching: store qname/qtype of failed queries
+ */
+ isc_time_t expire;
+ isc_interval_t i;
+ uint32_t flags = 0;
+
+ if ((message->flags & DNS_MESSAGEFLAG_CD) != 0)
+ flags = NS_FAILCACHE_CD;
+
+ isc_interval_set(&i, client->view->fail_ttl, 0);
+ result = isc_time_nowplusinterval(&expire, &i);
+ if (result == ISC_R_SUCCESS)
+ dns_badcache_add(client->view->failcache,
+ client->query.qname,
+ client->query.qtype,
+ true, flags, &expire);
+ }
+ ns_client_send(client);
+}
+
+isc_result_t
+ns_client_addopt(ns_client_t *client, dns_message_t *message,
+ dns_rdataset_t **opt)
+{
+ unsigned char ecs[ECS_SIZE];
+ char nsid[BUFSIZ], *nsidp;
+ unsigned char cookie[COOKIE_SIZE];
+ isc_result_t result;
+ dns_view_t *view;
+ dns_resolver_t *resolver;
+ uint16_t udpsize;
+ dns_ednsopt_t ednsopts[DNS_EDNSOPTIONS];
+ int count = 0;
+ unsigned int flags;
+ unsigned char expire[4];
+
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(opt != NULL && *opt == NULL);
+ REQUIRE(message != NULL);
+
+ view = client->view;
+ resolver = (view != NULL) ? view->resolver : NULL;
+ if (resolver != NULL)
+ udpsize = dns_resolver_getudpsize(resolver);
+ else
+ udpsize = ns_g_udpsize;
+
+ flags = client->extflags & DNS_MESSAGEEXTFLAG_REPLYPRESERVE;
+
+ /* Set EDNS options if applicable */
+ if (WANTNSID(client) &&
+ (ns_g_server->server_id != NULL ||
+ ns_g_server->server_usehostname)) {
+ if (ns_g_server->server_usehostname) {
+ result = ns_os_gethostname(nsid, sizeof(nsid));
+ if (result != ISC_R_SUCCESS) {
+ goto no_nsid;
+ }
+ nsidp = nsid;
+ } else
+ nsidp = ns_g_server->server_id;
+
+ INSIST(count < DNS_EDNSOPTIONS);
+ ednsopts[count].code = DNS_OPT_NSID;
+ ednsopts[count].length = (uint16_t)strlen(nsidp);
+ ednsopts[count].value = (unsigned char *)nsidp;
+ count++;
+ }
+ no_nsid:
+ if ((client->attributes & NS_CLIENTATTR_WANTCOOKIE) != 0) {
+ isc_buffer_t buf;
+ isc_stdtime_t now;
+ uint32_t nonce;
+
+ isc_buffer_init(&buf, cookie, sizeof(cookie));
+ isc_stdtime_get(&now);
+ isc_random_get(&nonce);
+
+ compute_cookie(client, now, nonce, ns_g_server->secret, &buf);
+
+ INSIST(count < DNS_EDNSOPTIONS);
+ ednsopts[count].code = DNS_OPT_COOKIE;
+ ednsopts[count].length = COOKIE_SIZE;
+ ednsopts[count].value = cookie;
+ count++;
+ }
+ if ((client->attributes & NS_CLIENTATTR_HAVEEXPIRE) != 0) {
+ isc_buffer_t buf;
+
+ INSIST(count < DNS_EDNSOPTIONS);
+
+ isc_buffer_init(&buf, expire, sizeof(expire));
+ isc_buffer_putuint32(&buf, client->expire);
+ ednsopts[count].code = DNS_OPT_EXPIRE;
+ ednsopts[count].length = 4;
+ ednsopts[count].value = expire;
+ count++;
+ }
+ if (((client->attributes & NS_CLIENTATTR_HAVEECS) != 0) &&
+ (client->ecs_addr.family == AF_INET ||
+ client->ecs_addr.family == AF_INET6 ||
+ client->ecs_addr.family == AF_UNSPEC))
+ {
+ isc_buffer_t buf;
+ uint8_t addr[16];
+ uint32_t plen, addrl;
+ uint16_t family;
+
+ /* Add CLIENT-SUBNET option. */
+
+ plen = client->ecs_addrlen;
+
+ /* Round up prefix len to a multiple of 8 */
+ addrl = (plen + 7) / 8;
+
+ switch (client->ecs_addr.family) {
+ case AF_UNSPEC:
+ INSIST(plen == 0);
+ family = 0;
+ break;
+ case AF_INET:
+ INSIST(plen <= 32);
+ family = 1;
+ memmove(addr, &client->ecs_addr.type, addrl);
+ break;
+ case AF_INET6:
+ INSIST(plen <= 128);
+ family = 2;
+ memmove(addr, &client->ecs_addr.type, addrl);
+ break;
+ default:
+ INSIST(0);
+ }
+
+ isc_buffer_init(&buf, ecs, sizeof(ecs));
+ /* family */
+ isc_buffer_putuint16(&buf, family);
+ /* source prefix-length */
+ isc_buffer_putuint8(&buf, client->ecs_addrlen);
+ /* scope prefix-length */
+ isc_buffer_putuint8(&buf, client->ecs_scope);
+
+ /* address */
+ if (addrl > 0) {
+ /* Mask off last address byte */
+ if ((plen % 8) != 0)
+ addr[addrl - 1] &=
+ ~0U << (8 - (plen % 8));
+ isc_buffer_putmem(&buf, addr,
+ (unsigned) addrl);
+ }
+
+ ednsopts[count].code = DNS_OPT_CLIENT_SUBNET;
+ ednsopts[count].length = addrl + 4;
+ ednsopts[count].value = ecs;
+ count++;
+ }
+
+ result = dns_message_buildopt(message, opt, 0, udpsize, flags,
+ ednsopts, count);
+ return (result);
+}
+
+static inline bool
+allowed(isc_netaddr_t *addr, dns_name_t *signer,
+ isc_netaddr_t *ecs_addr, uint8_t ecs_addrlen,
+ uint8_t *ecs_scope, dns_acl_t *acl)
+{
+ int match;
+ isc_result_t result;
+
+ if (acl == NULL)
+ return (true);
+ result = dns_acl_match2(addr, signer, ecs_addr, ecs_addrlen, ecs_scope,
+ acl, &ns_g_server->aclenv, &match, NULL);
+ if (result == ISC_R_SUCCESS && match > 0)
+ return (true);
+ return (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 appropriate task has exclusivity.
+ */
+bool
+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);
+
+ /*
+ * ns_g_server->interfacemgr is task exclusive locked.
+ */
+ if (ns_g_server->interfacemgr == NULL)
+ return (true);
+
+ if (!ns_interfacemgr_listeningon(ns_g_server->interfacemgr, dstaddr))
+ return (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) {
+ bool 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, NULL, 0, NULL,
+ view->matchclients) &&
+ allowed(&netdst, tsig, NULL, 0, NULL,
+ view->matchdestinations))
+ break;
+ }
+ return (view == myview);
+}
+
+static void
+compute_cookie(ns_client_t *client, uint32_t when, uint32_t nonce,
+ const unsigned char *secret, isc_buffer_t *buf)
+{
+ switch (ns_g_server->cookiealg) {
+#if defined(HAVE_OPENSSL_AES) || defined(HAVE_OPENSSL_EVP_AES)
+ case ns_cookiealg_aes: {
+ unsigned char digest[ISC_AES_BLOCK_LENGTH];
+ unsigned char input[4 + 4 + 16];
+ isc_netaddr_t netaddr;
+ unsigned char *cp;
+ unsigned int i;
+
+ memset(input, 0, sizeof(input));
+ cp = isc_buffer_used(buf);
+ isc_buffer_putmem(buf, client->cookie, 8);
+ isc_buffer_putuint32(buf, nonce);
+ isc_buffer_putuint32(buf, when);
+ memmove(input, cp, 16);
+ isc_aes128_crypt(secret, input, digest);
+ for (i = 0; i < 8; i++)
+ input[i] = digest[i] ^ digest[i + 8];
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ switch (netaddr.family) {
+ case AF_INET:
+ cp = (unsigned char *)&netaddr.type.in;
+ memmove(input + 8, cp, 4);
+ memset(input + 12, 0, 4);
+ isc_aes128_crypt(secret, input, digest);
+ break;
+ case AF_INET6:
+ cp = (unsigned char *)&netaddr.type.in6;
+ memmove(input + 8, cp, 16);
+ isc_aes128_crypt(secret, input, digest);
+ for (i = 0; i < 8; i++)
+ input[i + 8] = digest[i] ^ digest[i + 8];
+ isc_aes128_crypt(ns_g_server->secret, input + 8,
+ digest);
+ break;
+ }
+ for (i = 0; i < 8; i++)
+ digest[i] ^= digest[i + 8];
+ isc_buffer_putmem(buf, digest, 8);
+ break;
+ }
+#endif
+
+ case ns_cookiealg_sha1: {
+ unsigned char digest[ISC_SHA1_DIGESTLENGTH];
+ isc_netaddr_t netaddr;
+ unsigned char *cp;
+ isc_hmacsha1_t hmacsha1;
+ unsigned int length;
+
+ cp = isc_buffer_used(buf);
+ isc_buffer_putmem(buf, client->cookie, 8);
+ isc_buffer_putuint32(buf, nonce);
+ isc_buffer_putuint32(buf, when);
+
+ isc_hmacsha1_init(&hmacsha1, secret, ISC_SHA1_DIGESTLENGTH);
+ isc_hmacsha1_update(&hmacsha1, cp, 16);
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ switch (netaddr.family) {
+ case AF_INET:
+ cp = (unsigned char *)&netaddr.type.in;
+ length = 4;
+ break;
+ case AF_INET6:
+ cp = (unsigned char *)&netaddr.type.in6;
+ length = 16;
+ break;
+ default:
+ INSIST(0);
+ }
+ isc_hmacsha1_update(&hmacsha1, cp, length);
+ isc_hmacsha1_sign(&hmacsha1, digest, sizeof(digest));
+ isc_buffer_putmem(buf, digest, 8);
+ isc_hmacsha1_invalidate(&hmacsha1);
+ break;
+ }
+
+ case ns_cookiealg_sha256: {
+ unsigned char digest[ISC_SHA256_DIGESTLENGTH];
+ isc_netaddr_t netaddr;
+ unsigned char *cp;
+ isc_hmacsha256_t hmacsha256;
+ unsigned int length;
+
+ cp = isc_buffer_used(buf);
+ isc_buffer_putmem(buf, client->cookie, 8);
+ isc_buffer_putuint32(buf, nonce);
+ isc_buffer_putuint32(buf, when);
+
+ isc_hmacsha256_init(&hmacsha256, secret,
+ ISC_SHA256_DIGESTLENGTH);
+ isc_hmacsha256_update(&hmacsha256, cp, 16);
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ switch (netaddr.family) {
+ case AF_INET:
+ cp = (unsigned char *)&netaddr.type.in;
+ length = 4;
+ break;
+ case AF_INET6:
+ cp = (unsigned char *)&netaddr.type.in6;
+ length = 16;
+ break;
+ default:
+ INSIST(0);
+ }
+ isc_hmacsha256_update(&hmacsha256, cp, length);
+ isc_hmacsha256_sign(&hmacsha256, digest, sizeof(digest));
+ isc_buffer_putmem(buf, digest, 8);
+ isc_hmacsha256_invalidate(&hmacsha256);
+ break;
+ }
+ default:
+ INSIST(0);
+ }
+}
+
+static void
+process_cookie(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
+ ns_altsecret_t *altsecret;
+ unsigned char dbuf[COOKIE_SIZE];
+ unsigned char *old;
+ isc_stdtime_t now;
+ uint32_t when;
+ uint32_t nonce;
+ isc_buffer_t db;
+
+ /*
+ * If we have already seen a cookie option skip this cookie option.
+ */
+ if ((!ns_g_server->answercookie) ||
+ (client->attributes & NS_CLIENTATTR_WANTCOOKIE) != 0)
+ {
+ isc_buffer_forward(buf, (unsigned int)optlen);
+ return;
+ }
+
+ client->attributes |= NS_CLIENTATTR_WANTCOOKIE;
+
+ isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_cookiein);
+
+ if (optlen != COOKIE_SIZE) {
+ /*
+ * Not our token.
+ */
+ INSIST(optlen >= 8U);
+ memmove(client->cookie, isc_buffer_current(buf), 8);
+ isc_buffer_forward(buf, (unsigned int)optlen);
+
+ if (optlen == 8U)
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_cookienew);
+ else
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_cookiebadsize);
+ return;
+ }
+
+ /*
+ * Process all of the incoming buffer.
+ */
+ old = isc_buffer_current(buf);
+ memmove(client->cookie, old, 8);
+ isc_buffer_forward(buf, 8);
+ nonce = isc_buffer_getuint32(buf);
+ when = isc_buffer_getuint32(buf);
+ isc_buffer_forward(buf, 8);
+
+ /*
+ * Allow for a 5 minute clock skew between servers sharing a secret.
+ * Only accept COOKIE if we have talked to the client in the last hour.
+ */
+ isc_stdtime_get(&now);
+ if (isc_serial_gt(when, (now + 300)) || /* In the future. */
+ isc_serial_lt(when, (now - 3600))) { /* In the past. */
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_cookiebadtime);
+ return;
+ }
+
+ isc_buffer_init(&db, dbuf, sizeof(dbuf));
+ compute_cookie(client, when, nonce, ns_g_server->secret, &db);
+
+ if (isc_safe_memequal(old, dbuf, COOKIE_SIZE)) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_cookiematch);
+ client->attributes |= NS_CLIENTATTR_HAVECOOKIE;
+ return;
+ }
+
+ for (altsecret = ISC_LIST_HEAD(ns_g_server->altsecrets);
+ altsecret != NULL;
+ altsecret = ISC_LIST_NEXT(altsecret, link))
+ {
+ isc_buffer_init(&db, dbuf, sizeof(dbuf));
+ compute_cookie(client, when, nonce, altsecret->secret, &db);
+ if (isc_safe_memequal(old, dbuf, COOKIE_SIZE)) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_cookiematch);
+ client->attributes |= NS_CLIENTATTR_HAVECOOKIE;
+ return;
+ }
+ }
+
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_cookienomatch);
+}
+
+static isc_result_t
+process_ecs(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
+ uint16_t family;
+ uint8_t addrlen, addrbytes, scope, *paddr;
+ isc_netaddr_t caddr;
+
+ /*
+ * If we have already seen a ECS option skip this ECS option.
+ */
+ if ((client->attributes & NS_CLIENTATTR_HAVEECS) != 0) {
+ isc_buffer_forward(buf, (unsigned int)optlen);
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * XXXMUKS: Is there any need to repeat these checks here
+ * (except query's scope length) when they are done in the OPT
+ * RDATA fromwire code?
+ */
+
+ if (optlen < 4U) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+ "EDNS client-subnet option too short");
+ return (DNS_R_FORMERR);
+ }
+
+ family = isc_buffer_getuint16(buf);
+ addrlen = isc_buffer_getuint8(buf);
+ scope = isc_buffer_getuint8(buf);
+ optlen -= 4;
+
+ if (scope != 0U) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+ "EDNS client-subnet option: invalid scope");
+ return (DNS_R_OPTERR);
+ }
+
+ memset(&caddr, 0, sizeof(caddr));
+ switch (family) {
+ case 0:
+ /*
+ * XXXMUKS: In queries, if FAMILY is set to 0, SOURCE
+ * PREFIX-LENGTH must be 0 and ADDRESS should not be
+ * present as the address and prefix lengths don't make
+ * sense because the family is unknown.
+ */
+ if (addrlen != 0U) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+ "EDNS client-subnet option: invalid "
+ "address length (%u) for FAMILY=0",
+ addrlen);
+ return (DNS_R_OPTERR);
+ }
+ caddr.family = AF_UNSPEC;
+ break;
+ case 1:
+ if (addrlen > 32U) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+ "EDNS client-subnet option: invalid "
+ "address length (%u) for IPv4",
+ addrlen);
+ return (DNS_R_OPTERR);
+ }
+ caddr.family = AF_INET;
+ break;
+ case 2:
+ if (addrlen > 128U) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+ "EDNS client-subnet option: invalid "
+ "address length (%u) for IPv6",
+ addrlen);
+ return (DNS_R_OPTERR);
+ }
+ caddr.family = AF_INET6;
+ break;
+ default:
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+ "EDNS client-subnet option: invalid family");
+ return (DNS_R_OPTERR);
+ }
+
+ addrbytes = (addrlen + 7) / 8;
+ if (isc_buffer_remaininglength(buf) < addrbytes) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(2),
+ "EDNS client-subnet option: address too short");
+ return (DNS_R_OPTERR);
+ }
+
+ paddr = (uint8_t *) &caddr.type;
+ if (addrbytes != 0U) {
+ memmove(paddr, isc_buffer_current(buf), addrbytes);
+ isc_buffer_forward(buf, addrbytes);
+ optlen -= addrbytes;
+
+ if ((addrlen % 8) != 0) {
+ uint8_t bits = ~0U << (8 - (addrlen % 8));
+ bits &= paddr[addrbytes - 1];
+ if (bits != paddr[addrbytes - 1])
+ return (DNS_R_OPTERR);
+ }
+ }
+
+ memmove(&client->ecs_addr, &caddr, sizeof(caddr));
+ client->ecs_addrlen = addrlen;
+ client->ecs_scope = 0;
+ client->attributes |= NS_CLIENTATTR_HAVEECS;
+
+ isc_buffer_forward(buf, (unsigned int)optlen);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+process_keytag(ns_client_t *client, isc_buffer_t *buf, size_t optlen) {
+
+ if (optlen == 0 || (optlen % 2) != 0) {
+ isc_buffer_forward(buf, (unsigned int)optlen);
+ return (DNS_R_OPTERR);
+ }
+
+ /* Silently drop additional keytag options. */
+ if (client->keytag != NULL) {
+ isc_buffer_forward(buf, (unsigned int)optlen);
+ return (ISC_R_SUCCESS);
+ }
+
+ client->keytag = isc_mem_get(client->mctx, optlen);
+ if (client->keytag != NULL) {
+ client->keytag_len = (uint16_t)optlen;
+ memmove(client->keytag, isc_buffer_current(buf), optlen);
+ }
+ isc_buffer_forward(buf, (unsigned int)optlen);
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+process_opt(ns_client_t *client, dns_rdataset_t *opt) {
+ dns_rdata_t rdata;
+ isc_buffer_t optbuf;
+ isc_result_t result;
+ uint16_t optcode;
+ uint16_t optlen;
+
+ /*
+ * 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 = (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 > DNS_EDNS_VERSION) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_badednsver);
+ result = ns_client_addopt(client, client->message,
+ &client->opt);
+ 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);
+ isc_buffer_init(&optbuf, rdata.data, rdata.length);
+ isc_buffer_add(&optbuf, rdata.length);
+ while (isc_buffer_remaininglength(&optbuf) >= 4) {
+ optcode = isc_buffer_getuint16(&optbuf);
+ optlen = isc_buffer_getuint16(&optbuf);
+ switch (optcode) {
+ case DNS_OPT_NSID:
+ if (!WANTNSID(client))
+ isc_stats_increment(
+ ns_g_server->nsstats,
+ dns_nsstatscounter_nsidopt);
+ client->attributes |= NS_CLIENTATTR_WANTNSID;
+ isc_buffer_forward(&optbuf, optlen);
+ break;
+ case DNS_OPT_COOKIE:
+ process_cookie(client, &optbuf, optlen);
+ break;
+ case DNS_OPT_EXPIRE:
+ if (!WANTEXPIRE(client))
+ isc_stats_increment(
+ ns_g_server->nsstats,
+ dns_nsstatscounter_expireopt);
+ client->attributes |= NS_CLIENTATTR_WANTEXPIRE;
+ isc_buffer_forward(&optbuf, optlen);
+ break;
+ case DNS_OPT_CLIENT_SUBNET:
+ result = process_ecs(client, &optbuf, optlen);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_error(client, result);
+ goto cleanup;
+ }
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_ecsopt);
+ break;
+ case DNS_OPT_KEY_TAG:
+ result = process_keytag(client, &optbuf,
+ optlen);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_error(client, result);
+ return (result);
+ }
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_keytagopt);
+ break;
+ default:
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_otheropt);
+ isc_buffer_forward(&optbuf, optlen);
+ break;
+ }
+ }
+ }
+
+ isc_stats_increment(ns_g_server->nsstats, dns_nsstatscounter_edns0in);
+ client->attributes |= NS_CLIENTATTR_WANTOPT;
+
+ cleanup:
+ return (result);
+}
+
+/*
+ * 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;
+ bool ra; /* Recursion available. */
+ isc_netaddr_t netaddr;
+ int match;
+ dns_messageid_t id;
+ unsigned int flags;
+ bool notimp;
+ size_t reqsize;
+#ifdef HAVE_DNSTAP
+ dns_dtmsgtype_t dtmsgtype;
+#endif
+
+ 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 = true;
+ }
+ if ((sevent->attributes & ISC_SOCKEVENTATTR_DSCP) != 0) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(90),
+ "received DSCP %d", sevent->dscp);
+ if (client->dscp == -1)
+ client->dscp = sevent->dscp;
+ }
+ 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--;
+ }
+
+ reqsize = isc_buffer_usedlength(buffer);
+ /* don't count the length header */
+ if (TCP_CLIENT(client))
+ reqsize -= 2;
+
+ if (exit_check(client))
+ goto cleanup;
+ client->state = client->newstate = NS_CLIENTSTATE_WORKING;
+
+ isc_task_getcurrenttimex(task, &client->requesttime);
+ client->tnow = client->requesttime;
+ client->now = isc_time_seconds(&client->tnow);
+
+ 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 revisit this as mDNS spec was published.
+ */
+ 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) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_requestv4);
+ } else {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_requestv6);
+ }
+ if (TCP_CLIENT(client)) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_requesttcp);
+ switch (isc_sockaddr_pf(&client->peeraddr)) {
+ case AF_INET:
+ isc_stats_increment(ns_g_server->tcpinstats4,
+ ISC_MIN((int)reqsize / 16, 18));
+ break;
+ case AF_INET6:
+ isc_stats_increment(ns_g_server->tcpinstats6,
+ ISC_MIN((int)reqsize / 16, 18));
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ } else {
+ switch (isc_sockaddr_pf(&client->peeraddr)) {
+ case AF_INET:
+ isc_stats_increment(ns_g_server->udpinstats4,
+ ISC_MIN((int)reqsize / 16, 18));
+ break;
+ case AF_INET6:
+ isc_stats_increment(ns_g_server->udpinstats6,
+ ISC_MIN((int)reqsize / 16, 18));
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+ }
+
+ /*
+ * 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).
+ */
+ if (result == DNS_R_OPTERR)
+ (void)ns_client_addopt(client, client->message,
+ &client->opt);
+
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
+ "message parsing failed: %s",
+ isc_result_totext(result));
+ ns_client_error(client, result);
+ goto cleanup;
+ }
+
+ /*
+ * Pipeline TCP query processing.
+ */
+ if (client->message->opcode != dns_opcode_query)
+ client->pipelined = false;
+ if (TCP_CLIENT(client) && client->pipelined) {
+ result = isc_quota_reserve(&ns_g_server->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(read): %s",
+ isc_result_totext(result));
+ client->pipelined = false;
+ }
+ }
+
+ 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 = false;
+ break;
+ case dns_opcode_iquery:
+ default:
+ notimp = 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.
+ */
+ if (ns_g_noedns)
+ opt = NULL;
+ else
+ opt = dns_message_getopt(client->message);
+
+ client->ecs_addrlen = 0;
+ client->ecs_scope = 0;
+
+ if (opt != NULL) {
+ /*
+ * Are we dropping all EDNS queries?
+ */
+ if (ns_g_dropedns) {
+ ns_client_next(client, ISC_R_SUCCESS);
+ goto cleanup;
+ }
+ result = process_opt(client, opt);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ if (client->message->rdclass == 0) {
+ if ((client->attributes & NS_CLIENTATTR_WANTCOOKIE) != 0 &&
+ client->message->opcode == dns_opcode_query &&
+ client->message->counts[DNS_SECTION_QUESTION] == 0U)
+ {
+ result = dns_message_reply(client->message, true);
+ if (result != ISC_R_SUCCESS) {
+ ns_client_error(client, result);
+ return;
+ }
+ if (notimp)
+ client->message->rcode = dns_rcode_notimp;
+ ns_client_send(client);
+ return;
+ }
+ 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(&client->destaddr,
+ &client->interface->addr);
+ else {
+ isc_sockaddr_t sockaddr;
+ result = ISC_R_FAILURE;
+
+ if (TCP_CLIENT(client))
+ result = isc_socket_getsockname(client->tcpsocket,
+ &sockaddr);
+ if (result == ISC_R_SUCCESS)
+ isc_netaddr_fromsockaddr(&client->destaddr, &sockaddr);
+ if (result != ISC_R_SUCCESS &&
+ client->interface->addr.type.sa.sa_family == AF_INET6 &&
+ (client->attributes & NS_CLIENTATTR_PKTINFO) != 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.
+ */
+ isc_netaddr_fromin6(&client->destaddr,
+ &client->pktinfo.ipi6_addr);
+ if (IN6_IS_ADDR_LINKLOCAL(&client->pktinfo.ipi6_addr))
+ isc_netaddr_setzone(&client->destaddr,
+ client->pktinfo.ipi6_ifindex);
+ 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;
+ }
+ }
+
+ isc_sockaddr_fromnetaddr(&client->destsockaddr, &client->destaddr, 0);
+
+ /*
+ * 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;
+ isc_netaddr_t *addr = NULL;
+ uint8_t *scope = NULL;
+
+ sigresult = dns_message_rechecksig(client->message,
+ view);
+ if (sigresult == ISC_R_SUCCESS) {
+ dns_tsigkey_t *tsigkey;
+
+ tsigkey = client->message->tsigkey;
+ tsig = dns_tsigkey_identity(tsigkey);
+ }
+
+ if ((client->attributes & NS_CLIENTATTR_HAVEECS) != 0) {
+ addr = &client->ecs_addr;
+ scope = &client->ecs_scope;
+ }
+
+ if (allowed(&netaddr, tsig, addr, client->ecs_addrlen,
+ scope, view->matchclients) &&
+ allowed(&client->destaddr, tsig, NULL,
+ 0, NULL, view->matchdestinations) &&
+ !(view->matchrecursiveonly &&
+ (client->message->flags & DNS_MESSAGEFLAG_RD) == 0))
+ {
+ 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) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_tsigin);
+ } else {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_sig0in);
+ }
+
+ }
+ if (result == ISC_R_SUCCESS) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(&client->signername, namebuf, sizeof(namebuf));
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(3),
+ "request has valid signature: %s", namebuf);
+ 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. */
+ isc_stats_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 = false;
+ if (client->view->resolver != NULL &&
+ client->view->recursion == true &&
+ ns_client_checkaclsilent(client, NULL,
+ client->view->recursionacl,
+ true) == ISC_R_SUCCESS &&
+ ns_client_checkaclsilent(client, NULL,
+ client->view->cacheacl,
+ true) == ISC_R_SUCCESS &&
+ ns_client_checkaclsilent(client, &client->destaddr,
+ client->view->recursiononacl,
+ true) == ISC_R_SUCCESS &&
+ ns_client_checkaclsilent(client, &client->destaddr,
+ client->view->cacheonacl,
+ true) == ISC_R_SUCCESS)
+ ra = true;
+
+ if (ra == 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;
+ 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");
+#ifdef HAVE_DNSTAP
+ if ((client->message->flags & DNS_MESSAGEFLAG_RD) != 0)
+ dtmsgtype = DNS_DTTYPE_CQ;
+ else
+ dtmsgtype = DNS_DTTYPE_AQ;
+
+ dns_dt_send(view, dtmsgtype, &client->peeraddr,
+ &client->destsockaddr, TCP_CLIENT(client), NULL,
+ &client->requesttime, NULL, buffer);
+#endif /* HAVE_DNSTAP */
+
+ 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;
+#if NMCTXS > 0
+ unsigned int nextmctx;
+#endif
+
+ MTRACE("clientmctx");
+
+ /*
+ * 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
+ nextmctx = manager->nextmctx++;
+ if (manager->nextmctx == NMCTXS)
+ manager->nextmctx = 0;
+
+ INSIST(nextmctx < NMCTXS);
+
+ clientmctx = manager->mctxpool[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[nextmctx] = clientmctx;
+ }
+#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 = false;
+
+ client->delaytimer = NULL;
+
+ 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_socket_socketevent(client->mctx, client,
+ ISC_SOCKEVENT_SENDDONE,
+ client_senddone, client);
+ 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_socket_socketevent(client->mctx, client,
+ ISC_SOCKEVENT_RECVDONE,
+ client_request, client);
+ 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 = false;
+ client->tcpbuf = NULL;
+ client->opt = NULL;
+ client->udpsize = 512;
+ client->dscp = -1;
+ client->extflags = 0;
+ client->ednsversion = -1;
+ client->next = NULL;
+ client->shutdown = NULL;
+ client->shutdown_arg = NULL;
+ client->signer = NULL;
+ dns_name_init(&client->signername, NULL);
+ client->mortal = false;
+ client->pipelined = false;
+ client->tcpquota = NULL;
+ client->recursionquota = NULL;
+ client->interface = NULL;
+ client->peeraddr_valid = false;
+ client->ecs_addrlen = 0;
+ client->ecs_scope = 0;
+#ifdef ALLOW_FILTER_AAAA
+ client->filter_aaaa = dns_aaaa_ok;
+#endif
+ client->needshutdown = ns_g_clienttest;
+
+ 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);
+ ISC_LINK_INIT(client, rlink);
+ ISC_QLINK_INIT(client, ilink);
+ client->keytag = NULL;
+ client->keytag_len = 0;
+
+ /*
+ * 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;
+
+ 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 = 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 == false);
+ dns_tcpmsg_init(client->mctx, client->tcpsocket,
+ &client->tcpmsg);
+ client->tcpmsg_valid = 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 legitimate TCP clients.
+ */
+ client->pipelined = false;
+ 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(accept): %s",
+ isc_result_totext(result));
+ } else if (ns_g_server->keepresporder == NULL ||
+ !allowed(&netaddr, NULL, NULL, 0, NULL,
+ ns_g_server->keepresporder)) {
+ client->pipelined = true;
+ }
+
+ 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);
+}
+
+bool
+ns_client_shuttingdown(ns_client_t *client) {
+ return (client->newstate == NS_CLIENTSTATE_FREED);
+}
+
+isc_result_t
+ns_client_replace(ns_client_t *client) {
+ isc_result_t result;
+ bool tcp;
+
+ CTRACE("replace");
+
+ REQUIRE(client != NULL);
+ REQUIRE(client->manager != NULL);
+
+ tcp = TCP_CLIENT(client);
+ if (tcp && client->pipelined) {
+ result = get_worker(client->manager, client->interface,
+ client->tcpsocket);
+ } else {
+ result = get_client(client->manager, client->interface,
+ client->dispatch, tcp);
+ }
+ 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 = 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->clients));
+
+ MTRACE("clientmgr_destroy");
+
+#if NMCTXS > 0
+ for (i = 0; i < NMCTXS; i++) {
+ if (manager->mctxpool[i] != NULL)
+ isc_mem_detach(&manager->mctxpool[i]);
+ }
+#endif
+
+ ISC_QUEUE_DESTROY(manager->inactive);
+ DESTROYLOCK(&manager->lock);
+ DESTROYLOCK(&manager->listlock);
+ DESTROYLOCK(&manager->reclock);
+ 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;
+
+ result = isc_mutex_init(&manager->listlock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ result = isc_mutex_init(&manager->reclock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_listlock;
+
+ manager->mctx = mctx;
+ manager->taskmgr = taskmgr;
+ manager->timermgr = timermgr;
+ manager->exiting = false;
+ ISC_LIST_INIT(manager->clients);
+ ISC_LIST_INIT(manager->recursing);
+ ISC_QUEUE_INIT(manager->inactive, ilink);
+#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_listlock:
+ (void) isc_mutex_destroy(&manager->listlock);
+
+ cleanup_lock:
+ (void) isc_mutex_destroy(&manager->lock);
+
+ cleanup_manager:
+ isc_mem_put(manager->mctx, manager, sizeof(*manager));
+
+ return (result);
+}
+
+void
+ns_clientmgr_destroy(ns_clientmgr_t **managerp) {
+ isc_result_t result;
+ ns_clientmgr_t *manager;
+ ns_client_t *client;
+ bool need_destroy = false, unlock = false;
+
+ REQUIRE(managerp != NULL);
+ manager = *managerp;
+ REQUIRE(VALID_MANAGER(manager));
+
+ MTRACE("destroy");
+
+ /*
+ * Check for success because we may already be task-exclusive
+ * at this point. Only if we succeed at obtaining an exclusive
+ * lock now will we need to relinquish it later.
+ */
+ result = isc_task_beginexclusive(ns_g_server->task);
+ if (result == ISC_R_SUCCESS)
+ unlock = true;
+
+ manager->exiting = true;
+
+ for (client = ISC_LIST_HEAD(manager->clients);
+ client != NULL;
+ client = ISC_LIST_NEXT(client, link))
+ isc_task_shutdown(client->task);
+
+ if (ISC_LIST_EMPTY(manager->clients))
+ need_destroy = true;
+
+ if (unlock)
+ isc_task_endexclusive(ns_g_server->task);
+
+ if (need_destroy)
+ clientmgr_destroy(manager);
+
+ *managerp = NULL;
+}
+
+static isc_result_t
+get_client(ns_clientmgr_t *manager, ns_interface_t *ifp,
+ dns_dispatch_t *disp, bool tcp)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_event_t *ev;
+ ns_client_t *client;
+ MTRACE("get client");
+
+ REQUIRE(manager != NULL);
+
+ if (manager->exiting)
+ return (ISC_R_SHUTTINGDOWN);
+
+ /*
+ * Allocate a client. First try to get a recycled one;
+ * if that fails, make a new one.
+ */
+ client = NULL;
+ if (!ns_g_clienttest)
+ ISC_QUEUE_POP(manager->inactive, ilink, client);
+
+ if (client != NULL)
+ MTRACE("recycle");
+ else {
+ MTRACE("create new");
+
+ LOCK(&manager->lock);
+ result = client_create(manager, &client);
+ UNLOCK(&manager->lock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ LOCK(&manager->listlock);
+ ISC_LIST_APPEND(manager->clients, client, link);
+ UNLOCK(&manager->listlock);
+ }
+
+ client->manager = manager;
+ ns_interface_attach(ifp, &client->interface);
+ client->state = NS_CLIENTSTATE_READY;
+ INSIST(client->recursionquota == NULL);
+
+ client->dscp = ifp->dscp;
+
+ if (tcp) {
+ client->attributes |= NS_CLIENTATTR_TCP;
+ isc_socket_attach(ifp->tcpsocket,
+ &client->tcplistener);
+ } else {
+ isc_socket_t *sock;
+
+ dns_dispatch_attach(disp, &client->dispatch);
+ sock = dns_dispatch_getsocket(client->dispatch);
+ isc_socket_attach(sock, &client->udpsocket);
+ }
+
+ INSIST(client->nctls == 0);
+ client->nctls++;
+ ev = &client->ctlevent;
+ isc_task_send(client->task, &ev);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+get_worker(ns_clientmgr_t *manager, ns_interface_t *ifp, isc_socket_t *sock)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ isc_event_t *ev;
+ ns_client_t *client;
+ MTRACE("get worker");
+
+ REQUIRE(manager != NULL);
+
+ if (manager->exiting)
+ return (ISC_R_SHUTTINGDOWN);
+
+ /*
+ * Allocate a client. First try to get a recycled one;
+ * if that fails, make a new one.
+ */
+ client = NULL;
+ if (!ns_g_clienttest)
+ ISC_QUEUE_POP(manager->inactive, ilink, client);
+
+ if (client != NULL)
+ MTRACE("recycle");
+ else {
+ MTRACE("create new");
+
+ LOCK(&manager->lock);
+ result = client_create(manager, &client);
+ UNLOCK(&manager->lock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ LOCK(&manager->listlock);
+ ISC_LIST_APPEND(manager->clients, client, link);
+ UNLOCK(&manager->listlock);
+ }
+
+ client->manager = manager;
+ ns_interface_attach(ifp, &client->interface);
+ client->newstate = client->state = NS_CLIENTSTATE_WORKING;
+ INSIST(client->recursionquota == NULL);
+ client->tcpquota = &ns_g_server->tcpquota;
+
+ client->dscp = ifp->dscp;
+
+ client->attributes |= NS_CLIENTATTR_TCP;
+ client->pipelined = true;
+ client->mortal = true;
+
+ isc_socket_attach(ifp->tcpsocket, &client->tcplistener);
+ isc_socket_attach(sock, &client->tcpsocket);
+ isc_socket_setname(client->tcpsocket, "worker-tcp", NULL);
+ (void)isc_socket_getpeername(client->tcpsocket, &client->peeraddr);
+ client->peeraddr_valid = true;
+
+ INSIST(client->tcpmsg_valid == false);
+ dns_tcpmsg_init(client->mctx, client->tcpsocket, &client->tcpmsg);
+ client->tcpmsg_valid = true;
+
+ INSIST(client->nctls == 0);
+ client->nctls++;
+ ev = &client->ctlevent;
+ isc_task_send(client->task, &ev);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_clientmgr_createclients(ns_clientmgr_t *manager, unsigned int n,
+ ns_interface_t *ifp, bool tcp)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ unsigned int disp;
+
+ REQUIRE(VALID_MANAGER(manager));
+ REQUIRE(n > 0);
+
+ MTRACE("createclients");
+
+ for (disp = 0; disp < n; disp++) {
+ result = get_client(manager, ifp, ifp->udpdispatch[disp], tcp);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+
+ return (result);
+}
+
+isc_sockaddr_t *
+ns_client_getsockaddr(ns_client_t *client) {
+ return (&client->peeraddr);
+}
+
+isc_sockaddr_t *
+ns_client_getdestaddr(ns_client_t *client) {
+ return (&client->destsockaddr);
+}
+
+isc_result_t
+ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr,
+ dns_acl_t *acl, bool default_allow)
+{
+ isc_result_t result;
+ isc_netaddr_t tmpnetaddr;
+ isc_netaddr_t *ecs_addr = NULL;
+ uint8_t ecs_addrlen = 0;
+ int match;
+
+ if (acl == NULL) {
+ if (default_allow)
+ goto allow;
+ else
+ goto deny;
+ }
+
+ if (netaddr == NULL) {
+ isc_netaddr_fromsockaddr(&tmpnetaddr, &client->peeraddr);
+ netaddr = &tmpnetaddr;
+ }
+
+ if ((client->attributes & NS_CLIENTATTR_HAVEECS) != 0) {
+ ecs_addr = &client->ecs_addr;
+ ecs_addrlen = client->ecs_addrlen;
+ }
+
+ result = dns_acl_match2(netaddr, client->signer,
+ ecs_addr, ecs_addrlen, NULL, 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,
+ bool default_allow, int log_level)
+{
+ isc_result_t result;
+ isc_netaddr_t netaddr;
+
+ if (sockaddr != NULL)
+ isc_netaddr_fromsockaddr(&netaddr, sockaddr);
+
+ result = ns_client_checkaclsilent(client, sockaddr ? &netaddr : NULL,
+ 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,
+ (unsigned int)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[4096];
+ char signerbuf[DNS_NAME_FORMATSIZE], qnamebuf[DNS_NAME_FORMATSIZE];
+ char peerbuf[ISC_SOCKADDR_FORMATSIZE];
+ const char *viewname = "";
+ const char *sep1 = "", *sep2 = "", *sep3 = "", *sep4 = "";
+ const char *signer = "", *qname = "";
+ dns_name_t *q = NULL;
+
+ vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
+
+ if (client->signer != NULL) {
+ dns_name_format(client->signer, signerbuf, sizeof(signerbuf));
+ sep1 = "/key ";
+ signer = signerbuf;
+ }
+
+ q = client->query.origqname != NULL
+ ? client->query.origqname : client->query.qname;
+ if (q != NULL) {
+ dns_name_format(q, qnamebuf, sizeof(qnamebuf));
+ sep2 = " (";
+ sep3 = ")";
+ qname = qnamebuf;
+ }
+
+ if (client->view != NULL && strcmp(client->view->name, "_bind") != 0 &&
+ strcmp(client->view->name, "_default") != 0) {
+ sep4 = ": view ";
+ viewname = client->view->name;
+ }
+
+ if (client->peeraddr_valid) {
+ isc_sockaddr_format(&client->peeraddr,
+ peerbuf, sizeof(peerbuf));
+ } else {
+ snprintf(peerbuf, sizeof(peerbuf), "(no-peer)");
+ }
+
+ isc_log_write(ns_g_lctx, category, module, level,
+ "client @%p %s%s%s%s%s%s%s%s: %s",
+ client, peerbuf, sep1, signer, sep2, qname, sep3,
+ sep4, viewname, 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;
+
+ if (!isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(1)))
+ return;
+
+ /*
+ * 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 original[DNS_NAME_FORMATSIZE];
+ char peerbuf[ISC_SOCKADDR_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+ const char *name;
+ const char *sep;
+ const char *origfor;
+ dns_rdataset_t *rdataset;
+
+ REQUIRE(VALID_MANAGER(manager));
+
+ LOCK(&manager->reclock);
+ client = ISC_LIST_HEAD(manager->recursing);
+ while (client != NULL) {
+ INSIST(client->state == NS_CLIENTSTATE_RECURSING);
+
+ 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 = "";
+ }
+
+ LOCK(&client->query.fetchlock);
+ INSIST(client->query.qname != NULL);
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+ if (client->query.qname != client->query.origqname &&
+ client->query.origqname != NULL) {
+ origfor = " for ";
+ dns_name_format(client->query.origqname, original,
+ sizeof(original));
+ } else {
+ origfor = "";
+ original[0] = '\0';
+ }
+ rdataset = ISC_LIST_HEAD(client->query.qname->list);
+ if (rdataset == NULL && client->query.origqname != NULL)
+ rdataset = ISC_LIST_HEAD(client->query.origqname->list);
+ if (rdataset != NULL) {
+ dns_rdatatype_format(rdataset->type, typebuf,
+ sizeof(typebuf));
+ dns_rdataclass_format(rdataset->rdclass, classbuf,
+ sizeof(classbuf));
+ } else {
+ strlcpy(typebuf, "-", sizeof(typebuf));
+ strlcpy(classbuf, "-", sizeof(classbuf));
+ }
+ UNLOCK(&client->query.fetchlock);
+ fprintf(f, "; client %s%s%s: id %u '%s/%s/%s'%s%s "
+ "requesttime %u\n", peerbuf, sep, name,
+ client->message->id, namebuf, typebuf, classbuf,
+ origfor, original,
+ isc_time_seconds(&client->requesttime));
+ client = ISC_LIST_NEXT(client, rlink);
+ }
+ UNLOCK(&manager->reclock);
+}
+
+void
+ns_client_qnamereplace(ns_client_t *client, dns_name_t *name) {
+ LOCK(&client->query.fetchlock);
+ if (client->query.restarts > 0) {
+ /*
+ * client->query.qname was dynamically allocated.
+ */
+ dns_message_puttempname(client->message,
+ &client->query.qname);
+ }
+ client->query.qname = name;
+ client->query.attributes &= ~NS_QUERYATTR_REDIRECT;
+ UNLOCK(&client->query.fetchlock);
+}
+
+isc_result_t
+ns_client_sourceip(dns_clientinfo_t *ci, isc_sockaddr_t **addrp) {
+ ns_client_t *client = (ns_client_t *) ci->data;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(addrp != NULL);
+
+ *addrp = &client->peeraddr;
+ return (ISC_R_SUCCESS);
+}
diff --git a/bin/named/config.c b/bin/named/config.c
new file mode 100644
index 0000000..2732a8f
--- /dev/null
+++ b/bin/named/config.c
@@ -0,0 +1,1018 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <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 <pk11/site.h>
+
+#include <isccfg/grammar.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 <dst/dst.h>
+
+#include <named/config.h>
+#include <named/globals.h>
+
+#include <bind.keys.h>
+
+/*% default configuration */
+static char defaultconf[] = "\
+options {\n\
+ answer-cookie true;\n\
+ automatic-interface-scan yes;\n\
+ bindkeys-file \"" NS_SYSCONFDIR "/bind.keys\";\n\
+# blackhole {none;};\n"
+#if defined(HAVE_OPENSSL_AES) || defined(HAVE_OPENSSL_EVP_AES)
+" cookie-algorithm aes;\n"
+#else
+" cookie-algorithm sha256;\n"
+#endif
+#ifndef WIN32
+" coresize default;\n\
+ datasize default;\n"
+#endif
+"\
+# deallocate-on-exit <obsolete>;\n\
+# directory <none>\n\
+ dump-file \"named_dump.db\";\n\
+ edns-udp-size 4096;\n\
+# fake-iquery <obsolete>;\n"
+#ifndef WIN32
+" files unlimited;\n"
+#endif
+"\
+# has-old-clients <obsolete>;\n\
+ heartbeat-interval 60;\n\
+# host-statistics <obsolete>;\n\
+ interface-interval 60;\n\
+# keep-response-order {none;};\n\
+ listen-on {any;};\n\
+ listen-on-v6 {any;};\n\
+# lock-file \"" NS_LOCALSTATEDIR "/run/named/named.lock\";\n\
+ match-mapped-addresses no;\n\
+ max-rsa-exponent-size 0; /* no limit */\n\
+ max-udp-size 4096;\n\
+ memstatistics-file \"named.memstats\";\n\
+# multiple-cnames <obsolete>;\n\
+# named-xfer <obsolete>;\n\
+ nocookie-udp-size 4096;\n\
+ notify-rate 20;\n\
+ nta-lifetime 3600;\n\
+ nta-recheck 300;\n\
+# pid-file \"" NS_LOCALSTATEDIR "/run/named/named.pid\"; /* or /lwresd.pid */\n\
+ port 53;\n\
+ prefetch 2 9;\n"
+#ifdef PATH_RANDOMDEV
+" random-device \"" PATH_RANDOMDEV "\";\n"
+#endif
+" recursing-file \"named.recursing\";\n\
+ recursive-clients 1000;\n\
+ request-nsid false;\n\
+ reserved-sockets 512;\n\
+ resolver-query-timeout 10;\n\
+ rrset-order { order random; };\n\
+ secroots-file \"named.secroots\";\n\
+ send-cookie true;\n\
+# serial-queries <obsolete>;\n\
+ serial-query-rate 20;\n\
+ server-id none;\n\
+ session-keyalg hmac-sha256;\n\
+# session-keyfile \"" NS_LOCALSTATEDIR "/run/named/session.key\";\n\
+ session-keyname local-ddns;\n"
+#ifndef WIN32
+" stacksize default;\n"
+#endif
+" startup-notify-rate 20;\n\
+ statistics-file \"named.stats\";\n\
+# statistics-interval <obsolete>;\n\
+ tcp-clients 150;\n\
+ tcp-listen-queue 10;\n\
+# tkey-dhkey <none>\n\
+# tkey-domain <none>\n\
+# tkey-gssapi-credential <none>\n\
+ transfer-message-size 20480;\n\
+ transfers-in 10;\n\
+ transfers-out 10;\n\
+ transfers-per-ns 2;\n\
+# treat-cr-as-space <obsolete>;\n\
+ trust-anchor-telemetry yes;\n\
+# use-id-pool <obsolete>;\n\
+# use-ixfr <obsolete>;\n\
+\n\
+ /* view */\n\
+ acache-cleaning-interval 60;\n\
+ acache-enable no;\n\
+ additional-from-auth true;\n\
+ additional-from-cache true;\n\
+ allow-new-zones no;\n\
+ allow-notify {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-update-forwarding {none;};\n\
+# allow-v6-synthesis <obsolete>;\n\
+ auth-nxdomain false;\n\
+ check-dup-records warn;\n\
+ check-mx warn;\n\
+ check-names master fail;\n\
+ check-names response ignore;\n\
+ check-names slave warn;\n\
+ check-spf warn;\n\
+ cleaning-interval 0; /* now meaningless */\n\
+ clients-per-query 10;\n\
+ dnssec-accept-expired no;\n\
+ dnssec-enable yes;\n\
+ dnssec-validation yes; \n"
+#ifdef HAVE_DNSTAP
+" dnstap-identity hostname;\n"
+#endif
+"\
+# fetch-glue <obsolete>;\n\
+ fetch-quota-params 100 0.1 0.3 0.7;\n\
+ fetches-per-server 0;\n\
+ fetches-per-zone 0;\n"
+#ifdef ALLOW_FILTER_AAAA
+" filter-aaaa-on-v4 no;\n\
+ filter-aaaa-on-v6 no;\n\
+ filter-aaaa { any; };\n"
+#endif
+#ifdef HAVE_GEOIP
+" geoip-use-ecs yes;\n"
+#endif
+" lame-ttl 600;\n"
+#ifdef HAVE_LMDB
+" lmdb-mapsize 32M;\n"
+#endif
+" max-acache-size 16M;\n\
+ max-cache-size 90%;\n\
+ max-cache-ttl 604800; /* 1 week */\n\
+ max-clients-per-query 100;\n\
+ max-ncache-ttl 10800; /* 3 hours */\n\
+ max-recursion-depth 7;\n\
+ max-recursion-queries 75;\n\
+ message-compression yes;\n\
+# min-roots <obsolete>;\n\
+ minimal-any false;\n\
+ minimal-responses false;\n\
+ notify-source *;\n\
+ notify-source-v6 *;\n\
+ nsec3-test-zone no;\n\
+ provide-ixfr true;\n\
+ query-source address *;\n\
+ query-source-v6 address *;\n\
+ recursion true;\n\
+ request-expire true;\n\
+ request-ixfr true;\n\
+ require-server-cookie no;\n\
+# rfc2308-type1 <obsolete>;\n\
+ root-key-sentinel yes;\n\
+ servfail-ttl 1;\n\
+# sortlist <none>\n\
+# topology <none>\n\
+ transfer-format many-answers;\n\
+ v6-bias 50;\n\
+ zero-no-soa-ttl-cache no;\n\
+\n\
+ /* zone */\n\
+ allow-query {any;};\n\
+ allow-query-on {any;};\n\
+ allow-transfer {any;};\n\
+# also-notify <none>\n\
+ alt-transfer-source *;\n\
+ alt-transfer-source-v6 *;\n\
+ check-integrity yes;\n\
+ check-mx-cname warn;\n\
+ check-sibling yes;\n\
+ check-srv-cname warn;\n\
+ check-wildcard yes;\n\
+ dialup no;\n\
+ dnssec-dnskey-kskonly no;\n\
+ dnssec-loadkeys-interval 60;\n\
+ dnssec-secure-to-insecure no;\n\
+ dnssec-update-mode maintain;\n\
+# forward <none>\n\
+# forwarders <none>\n\
+ inline-signing no;\n\
+ ixfr-from-differences false;\n\
+# maintain-ixfr-base <obsolete>;\n\
+# max-ixfr-log-size <obsolete>\n\
+ max-journal-size unlimited;\n\
+ max-records 0;\n\
+ max-refresh-time 2419200; /* 4 weeks */\n\
+ max-retry-time 1209600; /* 2 weeks */\n\
+ max-transfer-idle-in 60;\n\
+ max-transfer-idle-out 60;\n\
+ max-transfer-time-in 120;\n\
+ max-transfer-time-out 120;\n\
+ min-refresh-time 300;\n\
+ min-retry-time 500;\n\
+ multi-master no;\n\
+ notify yes;\n\
+ notify-delay 5;\n\
+ notify-to-soa no;\n\
+ serial-update-method increment;\n\
+ sig-signing-nodes 100;\n\
+ sig-signing-signatures 10;\n\
+ sig-signing-type 65534;\n\
+ sig-validity-interval 30; /* days */\n\
+ transfer-source *;\n\
+ transfer-source-v6 *;\n\
+ try-tcp-refresh yes; /* BIND 8 compat */\n\
+ update-check-ksk yes;\n\
+ zero-no-soa-ttl yes;\n\
+ zone-statistics terse;\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\
+ allow-new-zones no;\n\
+\n\
+ # Prevent use of this zone in DNS amplified reflection DoS attacks\n\
+ rate-limit {\n\
+ responses-per-second 3;\n\
+ slip 0;\n\
+ min-table-size 10;\n\
+ };\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\
+\n\
+ zone \"id.server\" chaos {\n\
+ type master;\n\
+ database \"_builtin id\";\n\
+ };\n\
+};\n\
+"
+"#\n\
+# Default trusted key(s), used if \n\
+# \"dnssec-validation auto;\" is set and\n\
+# sysconfdir/bind.keys doesn't exist).\n\
+#\n\
+# BEGIN MANAGED KEYS\n"
+
+/* Imported from bind.keys.h: */
+MANAGED_KEYS
+
+"# END MANAGED KEYS\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_buffer4(parser, &b, __FILE__, 0,
+ &cfg_type_namedconf,
+ CFG_PCTX_NODEPRECATED, conf));
+}
+
+isc_result_t
+ns_config_get(cfg_obj_t const * const *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 if (strcasecmp(str, "static-stub") == 0)
+ ztype = dns_zone_staticstub;
+ else if (strcasecmp(str, "redirect") == 0)
+ ztype = dns_zone_redirect;
+ 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_dscp_t **dscpsp,
+ uint32_t *countp)
+{
+ int count, i = 0;
+ const cfg_obj_t *addrlist;
+ const cfg_obj_t *portobj, *dscpobj;
+ const cfg_listelt_t *element;
+ isc_sockaddr_t *addrs;
+ in_port_t port;
+ isc_dscp_t dscp = -1, *dscps = NULL;
+ isc_result_t result;
+
+ INSIST(addrsp != NULL && *addrsp == NULL);
+ INSIST(dscpsp == NULL || *dscpsp == 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)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > 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);
+ }
+
+ if (dscpsp != NULL) {
+ dscpobj = cfg_tuple_get(list, "dscp");
+ if (dscpobj != NULL && cfg_obj_isuint32(dscpobj)) {
+ if (cfg_obj_asuint32(dscpobj) > 63) {
+ cfg_obj_log(dscpobj, ns_g_lctx, ISC_LOG_ERROR,
+ "dscp value '%u' is out of range",
+ cfg_obj_asuint32(dscpobj));
+ return (ISC_R_RANGE);
+ }
+ dscp = (isc_dscp_t)cfg_obj_asuint32(dscpobj);
+ }
+
+ dscps = isc_mem_get(mctx, count * sizeof(isc_dscp_t));
+ if (dscps == NULL)
+ return (ISC_R_NOMEMORY);
+ }
+
+ addrs = isc_mem_get(mctx, count * sizeof(isc_sockaddr_t));
+ if (addrs == NULL) {
+ if (dscps != NULL)
+ isc_mem_put(mctx, dscps, count * sizeof(isc_dscp_t));
+ return (ISC_R_NOMEMORY);
+ }
+
+ for (element = cfg_list_first(addrlist);
+ element != NULL;
+ element = cfg_list_next(element), i++)
+ {
+ const cfg_obj_t *addr;
+ INSIST(i < count);
+ addr = cfg_listelt_value(element);
+ addrs[i] = *cfg_obj_assockaddr(addr);
+ if (dscpsp != NULL) {
+ isc_dscp_t innerdscp;
+ innerdscp = cfg_obj_getdscp(addr);
+ if (innerdscp == -1)
+ innerdscp = dscp;
+ dscps[i] = innerdscp;
+ }
+ if (isc_sockaddr_getport(&addrs[i]) == 0)
+ isc_sockaddr_setport(&addrs[i], port);
+ }
+ INSIST(i == count);
+
+ *addrsp = addrs;
+ *countp = count;
+
+ if (dscpsp != NULL)
+ *dscpsp = dscps;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+ns_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ isc_dscp_t **dscpsp, uint32_t count)
+{
+ INSIST(addrsp != NULL && *addrsp != NULL);
+ INSIST(dscpsp == NULL || *dscpsp != NULL);
+
+ isc_mem_put(mctx, *addrsp, count * sizeof(isc_sockaddr_t));
+ *addrsp = NULL;
+
+ if (dscpsp != NULL) {
+ isc_mem_put(mctx, *dscpsp, count * sizeof(isc_dscp_t));
+ *dscpsp = 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, dns_ipkeylist_t *ipkl)
+{
+ uint32_t addrcount = 0, dscpcount = 0, keycount = 0, i = 0;
+ uint32_t listcount = 0, l = 0, j;
+ 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;
+ const cfg_obj_t *dscpobj;
+ in_port_t port;
+ isc_dscp_t dscp = -1;
+ dns_fixedname_t fname;
+ isc_sockaddr_t *addrs = NULL;
+ isc_dscp_t *dscps = NULL;
+ dns_name_t **keys = NULL;
+ struct { const char *name; } *lists = NULL;
+ struct {
+ const cfg_listelt_t *element;
+ in_port_t port;
+ isc_dscp_t dscp;
+ } *stack = NULL;
+
+ REQUIRE(ipkl != NULL);
+ REQUIRE(ipkl->count == 0);
+ REQUIRE(ipkl->addrs == NULL);
+ REQUIRE(ipkl->keys == NULL);
+ REQUIRE(ipkl->dscps == NULL);
+ REQUIRE(ipkl->labels == NULL);
+ REQUIRE(ipkl->allocated == 0);
+
+ /*
+ * Get system defaults.
+ */
+ result = ns_config_getport(config, &port);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = ns_config_getdscp(config, &dscp);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ newlist:
+ addrlist = cfg_tuple_get(list, "addresses");
+ portobj = cfg_tuple_get(list, "port");
+ dscpobj = cfg_tuple_get(list, "dscp");
+
+ if (cfg_obj_isuint32(portobj)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > 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;
+ }
+
+ if (dscpobj != NULL && cfg_obj_isuint32(dscpobj)) {
+ if (cfg_obj_asuint32(dscpobj) > 63) {
+ cfg_obj_log(dscpobj, ns_g_lctx, ISC_LOG_ERROR,
+ "dscp value '%u' is out of range",
+ cfg_obj_asuint32(dscpobj));
+ result = ISC_R_RANGE;
+ goto cleanup;
+ }
+ dscp = (isc_dscp_t)cfg_obj_asuint32(dscpobj);
+ }
+
+ 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 * tmp;
+ uint32_t newlen = listcount + 16;
+ size_t newsize, oldsize;
+
+ newsize = newlen * sizeof(*lists);
+ oldsize = listcount * sizeof(*lists);
+ tmp = isc_mem_get(mctx, newsize);
+ if (tmp == NULL)
+ goto cleanup;
+ if (listcount != 0) {
+ memmove(tmp, lists, oldsize);
+ isc_mem_put(mctx, lists, oldsize);
+ }
+ lists = tmp;
+ 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 * tmp;
+ uint32_t newlen = stackcount + 16;
+ size_t newsize, oldsize;
+
+ newsize = newlen * sizeof(*stack);
+ oldsize = stackcount * sizeof(*stack);
+ tmp = isc_mem_get(mctx, newsize);
+ if (tmp == NULL)
+ goto cleanup;
+ if (stackcount != 0) {
+ memmove(tmp, stack, oldsize);
+ isc_mem_put(mctx, stack, oldsize);
+ }
+ stack = tmp;
+ stackcount = newlen;
+ }
+ /*
+ * We want to resume processing this list on the
+ * next element.
+ */
+ stack[pushed].element = cfg_list_next(element);
+ stack[pushed].port = port;
+ stack[pushed].dscp = dscp;
+ pushed++;
+ goto newlist;
+ }
+
+ if (i == addrcount) {
+ void * tmp;
+ uint32_t newlen = addrcount + 16;
+ size_t newsize, oldsize;
+
+ newsize = newlen * sizeof(isc_sockaddr_t);
+ oldsize = addrcount * sizeof(isc_sockaddr_t);
+ tmp = isc_mem_get(mctx, newsize);
+ if (tmp == NULL)
+ goto cleanup;
+ if (addrcount != 0) {
+ memmove(tmp, addrs, oldsize);
+ isc_mem_put(mctx, addrs, oldsize);
+ }
+ addrs = tmp;
+ addrcount = newlen;
+
+ newsize = newlen * sizeof(isc_dscp_t);
+ oldsize = dscpcount * sizeof(isc_dscp_t);
+ tmp = isc_mem_get(mctx, newsize);
+ if (tmp == NULL)
+ goto cleanup;
+ if (dscpcount != 0) {
+ memmove(tmp, dscps, oldsize);
+ isc_mem_put(mctx, dscps, oldsize);
+ }
+ dscps = tmp;
+ dscpcount = newlen;
+
+ newsize = newlen * sizeof(dns_name_t *);
+ oldsize = keycount * sizeof(dns_name_t *);
+ tmp = isc_mem_get(mctx, newsize);
+ if (tmp == NULL)
+ goto cleanup;
+ if (keycount != 0) {
+ memmove(tmp, keys, oldsize);
+ isc_mem_put(mctx, keys, oldsize);
+ }
+ keys = tmp;
+ keycount = newlen;
+ }
+
+ addrs[i] = *cfg_obj_assockaddr(addr);
+ if (isc_sockaddr_getport(&addrs[i]) == 0)
+ isc_sockaddr_setport(&addrs[i], port);
+ dscps[i] = cfg_obj_getdscp(addr);
+ if (dscps[i] == -1)
+ dscps[i] = dscp;
+ keys[i] = NULL;
+ i++; /* Increment here so that cleanup on error works. */
+ if (!cfg_obj_isstring(key))
+ continue;
+ keys[i - 1] = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (keys[i - 1] == NULL)
+ goto cleanup;
+ dns_name_init(keys[i - 1], NULL);
+
+ keystr = cfg_obj_asstring(key);
+ isc_buffer_constinit(&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, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_name_dup(dns_fixedname_name(&fname), mctx,
+ keys[i - 1]);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ if (pushed != 0) {
+ pushed--;
+ element = stack[pushed].element;
+ port = stack[pushed].port;
+ dscp = stack[pushed].dscp;
+ goto resume;
+ }
+ if (i < addrcount) {
+ void * tmp;
+ size_t newsize, oldsize;
+
+ newsize = i * sizeof(isc_sockaddr_t);
+ oldsize = addrcount * sizeof(isc_sockaddr_t);
+ if (i != 0) {
+ tmp = isc_mem_get(mctx, newsize);
+ if (tmp == NULL)
+ goto cleanup;
+ memmove(tmp, addrs, newsize);
+ } else
+ tmp = NULL;
+ isc_mem_put(mctx, addrs, oldsize);
+ addrs = tmp;
+ addrcount = i;
+
+ newsize = i * sizeof(isc_dscp_t);
+ oldsize = dscpcount * sizeof(isc_dscp_t);
+ if (i != 0) {
+ tmp = isc_mem_get(mctx, newsize);
+ if (tmp == NULL)
+ goto cleanup;
+ memmove(tmp, dscps, newsize);
+ } else
+ tmp = NULL;
+ isc_mem_put(mctx, dscps, oldsize);
+ dscps = tmp;
+ dscpcount = i;
+
+ newsize = i * sizeof(dns_name_t *);
+ oldsize = keycount * sizeof(dns_name_t *);
+ if (i != 0) {
+ tmp = isc_mem_get(mctx, newsize);
+ if (tmp == NULL)
+ goto cleanup;
+ memmove(tmp, keys, newsize);
+ } else
+ tmp = NULL;
+ isc_mem_put(mctx, keys, oldsize);
+ keys = tmp;
+ 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);
+
+ ipkl->addrs = addrs;
+ ipkl->dscps = dscps;
+ ipkl->keys = keys;
+ ipkl->count = addrcount;
+ ipkl->allocated = addrcount;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (addrs != NULL)
+ isc_mem_put(mctx, addrs, addrcount * sizeof(isc_sockaddr_t));
+ if (dscps != NULL)
+ isc_mem_put(mctx, dscps, dscpcount * sizeof(isc_dscp_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);
+}
+
+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) >= 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);
+}
+
+isc_result_t
+ns_config_getdscp(const cfg_obj_t *config, isc_dscp_t *dscpp) {
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *dscpobj = NULL;
+ isc_result_t result;
+
+ (void)cfg_map_get(config, "options", &options);
+ if (options == NULL)
+ return (ISC_R_SUCCESS);
+
+ result = cfg_map_get(options, "dscp", &dscpobj);
+ if (result != ISC_R_SUCCESS || dscpobj == NULL) {
+ *dscpp = -1;
+ return (ISC_R_SUCCESS);
+ }
+ if (cfg_obj_asuint32(dscpobj) >= 64) {
+ cfg_obj_log(dscpobj, ns_g_lctx, ISC_LOG_ERROR,
+ "dscp '%u' out of range",
+ cfg_obj_asuint32(dscpobj));
+ return (ISC_R_RANGE);
+ }
+ *dscpp = (isc_dscp_t)cfg_obj_asuint32(dscpobj);
+ return (ISC_R_SUCCESS);
+}
+
+struct keyalgorithms {
+ const char *str;
+ enum { hmacnone, hmacmd5, hmacsha1, hmacsha224,
+ hmacsha256, hmacsha384, hmacsha512 } hmac;
+ unsigned int type;
+ uint16_t size;
+} algorithms[] = {
+#ifndef PK11_MD5_DISABLE
+ { "hmac-md5", hmacmd5, DST_ALG_HMACMD5, 128 },
+ { "hmac-md5.sig-alg.reg.int", hmacmd5, DST_ALG_HMACMD5, 0 },
+ { "hmac-md5.sig-alg.reg.int.", hmacmd5, DST_ALG_HMACMD5, 0 },
+#endif
+ { "hmac-sha1", hmacsha1, DST_ALG_HMACSHA1, 160 },
+ { "hmac-sha224", hmacsha224, DST_ALG_HMACSHA224, 224 },
+ { "hmac-sha256", hmacsha256, DST_ALG_HMACSHA256, 256 },
+ { "hmac-sha384", hmacsha384, DST_ALG_HMACSHA384, 384 },
+ { "hmac-sha512", hmacsha512, DST_ALG_HMACSHA512, 512 },
+ { NULL, hmacnone, DST_ALG_UNKNOWN, 0 }
+};
+
+isc_result_t
+ns_config_getkeyalgorithm(const char *str, dns_name_t **name,
+ uint16_t *digestbits)
+{
+ return (ns_config_getkeyalgorithm2(str, name, NULL, digestbits));
+}
+
+isc_result_t
+ns_config_getkeyalgorithm2(const char *str, dns_name_t **name,
+ unsigned int *typep, uint16_t *digestbits)
+{
+ int i;
+ size_t len = 0;
+ 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) {
+#ifndef PK11_MD5_DISABLE
+ case hmacmd5: *name = dns_tsig_hmacmd5_name; break;
+#endif
+ 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 (typep != NULL)
+ *typep = algorithms[i].type;
+ 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..c53d8d0
--- /dev/null
+++ b/bin/named/control.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <isc/app.h>
+#include <isc/event.h>
+#include <isc/lex.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/globals.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_result_t
+getcommand(isc_lex_t *lex, char **cmdp) {
+ isc_result_t result;
+ isc_token_t token;
+
+ REQUIRE(cmdp != NULL && *cmdp == NULL);
+
+ result = isc_lex_gettoken(lex, ISC_LEXOPT_EOF, &token);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_lex_ungettoken(lex, &token);
+
+ if (token.type != isc_tokentype_string)
+ return (ISC_R_FAILURE);
+
+ *cmdp = token.value.as_textregion.base;
+
+ return (ISC_R_SUCCESS);
+}
+
+static inline bool
+command_compare(const char *str, const char *command) {
+ return (strcasecmp(str, command) == 0);
+}
+
+/*%
+ * 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, bool readonly,
+ isc_buffer_t **text)
+{
+ isccc_sexpr_t *data;
+ char *cmdline = NULL;
+ char *command = NULL;
+ isc_result_t result;
+ int log_level;
+ isc_buffer_t src;
+ isc_lex_t *lex = NULL;
+#ifdef HAVE_LIBSCF
+ ns_smf_want_disable = 0;
+#endif
+
+ data = isccc_alist_lookup(message, "_data");
+ if (!isccc_alist_alistp(data)) {
+ /*
+ * No data section.
+ */
+ return (ISC_R_FAILURE);
+ }
+
+ result = isccc_cc_lookupstring(data, "type", &cmdline);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * We have no idea what this is.
+ */
+ return (result);
+ }
+
+ result = isc_lex_create(ns_g_mctx, strlen(cmdline), &lex);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ isc_buffer_init(&src, cmdline, strlen(cmdline));
+ isc_buffer_add(&src, strlen(cmdline));
+ result = isc_lex_openbuffer(lex, &src);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = getcommand(lex, &command);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * 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;
+ }
+
+ /*
+ * If this listener should have read-only access, reject
+ * restricted commands here. rndc nta is handled specially
+ * below.
+ */
+ if (readonly &&
+ !command_compare(command, NS_COMMAND_NTA) &&
+ !command_compare(command, NS_COMMAND_NULL) &&
+ !command_compare(command, NS_COMMAND_STATUS) &&
+ !command_compare(command, NS_COMMAND_SHOWZONE) &&
+ !command_compare(command, NS_COMMAND_TESTGEN) &&
+ !command_compare(command, NS_COMMAND_ZONESTATUS))
+ {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, log_level,
+ "rejecting restricted control channel "
+ "command '%s'", cmdline);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, log_level,
+ "received control channel command '%s'",
+ cmdline);
+
+ if (command_compare(command, NS_COMMAND_RELOAD)) {
+ result = ns_server_reloadcommand(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_RECONFIG)) {
+ result = ns_server_reconfigcommand(ns_g_server);
+ } else if (command_compare(command, NS_COMMAND_REFRESH)) {
+ result = ns_server_refreshcommand(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_RETRANSFER)) {
+ result = ns_server_retransfercommand(ns_g_server,
+ lex, text);
+ } 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);
+ goto cleanup;
+ }
+ /*
+ * 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
+ /* Do not flush master files */
+ ns_server_flushonshutdown(ns_g_server, false);
+ ns_os_shutdownmsg(cmdline, *text);
+ isc_app_shutdown();
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_STOP)) {
+ /*
+ * "stop" is the same as "halt" except it does
+ * flush master files.
+ */
+#ifdef HAVE_LIBSCF
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 1) {
+ result = ns_smf_add_message(text);
+ goto cleanup;
+ }
+ if (ns_smf_got_instance == 1 && ns_smf_chroot == 0)
+ ns_smf_want_disable = 1;
+#endif
+ ns_server_flushonshutdown(ns_g_server, true);
+ ns_os_shutdownmsg(cmdline, *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, lex);
+ } else if (command_compare(command, NS_COMMAND_DUMPDB)) {
+ ns_server_dumpdb(ns_g_server, lex, text);
+ result = ISC_R_SUCCESS;
+ } else if (command_compare(command, NS_COMMAND_SECROOTS)) {
+ result = ns_server_dumpsecroots(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_TRACE)) {
+ result = ns_server_setdebuglevel(ns_g_server, lex);
+ } 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, lex);
+ } else if (command_compare(command, NS_COMMAND_FLUSHNAME)) {
+ result = ns_server_flushnode(ns_g_server, lex, false);
+ } else if (command_compare(command, NS_COMMAND_FLUSHTREE)) {
+ result = ns_server_flushnode(ns_g_server, lex, true);
+ } 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, lex, text);
+ } else if (command_compare(command, NS_COMMAND_FREEZE)) {
+ result = ns_server_freeze(ns_g_server, true, lex,
+ text);
+ } else if (command_compare(command, NS_COMMAND_UNFREEZE) ||
+ command_compare(command, NS_COMMAND_THAW)) {
+ result = ns_server_freeze(ns_g_server, false, lex,
+ text);
+ } else if (command_compare(command, NS_COMMAND_SCAN)) {
+ result = ISC_R_SUCCESS;
+ ns_server_scan_interfaces(ns_g_server);
+ } else if (command_compare(command, NS_COMMAND_SYNC)) {
+ result = ns_server_sync(ns_g_server, lex, text);
+ } 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, lex, text);
+ } else if (command_compare(command, NS_COMMAND_VALIDATION)) {
+ result = ns_server_validation(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_SIGN) ||
+ command_compare(command, NS_COMMAND_LOADKEYS)) {
+ result = ns_server_rekey(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_ADDZONE) ||
+ command_compare(command, NS_COMMAND_MODZONE)) {
+ result = ns_server_changezone(ns_g_server, cmdline, text);
+ } else if (command_compare(command, NS_COMMAND_DELZONE)) {
+ result = ns_server_delzone(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_SHOWZONE)) {
+ result = ns_server_showzone(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_SIGNING)) {
+ result = ns_server_signing(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_ZONESTATUS)) {
+ result = ns_server_zonestatus(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_NTA)) {
+ result = ns_server_nta(ns_g_server, lex, readonly, text);
+ } else if (command_compare(command, NS_COMMAND_TESTGEN)) {
+ result = ns_server_testgen(lex, text);
+ } else if (command_compare(command, NS_COMMAND_MKEYS)) {
+ result = ns_server_mkeys(ns_g_server, lex, text);
+ } else if (command_compare(command, NS_COMMAND_DNSTAP) ||
+ command_compare(command, NS_COMMAND_DNSTAPREOPEN)) {
+ result = ns_server_dnstap(ns_g_server, lex, text);
+ } 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;
+ }
+
+ cleanup:
+ if (lex != NULL)
+ isc_lex_destroy(&lex);
+
+ return (result);
+}
diff --git a/bin/named/controlconf.c b/bin/named/controlconf.c
new file mode 100644
index 0000000..d955c2f
--- /dev/null
+++ b/bin/named/controlconf.c
@@ -0,0 +1,1530 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/event.h>
+#include <isc/file.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;
+ uint32_t algorithm;
+ isc_region_t secret;
+ ISC_LINK(controlkey_t) link;
+};
+
+struct controlconnection {
+ isc_socket_t * sock;
+ isccc_ccmsg_t ccmsg;
+ bool ccmsg_valid;
+ bool sending;
+ isc_timer_t * timer;
+ isc_buffer_t * buffer;
+ controllistener_t * listener;
+ 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;
+ bool listening;
+ bool exiting;
+ controlkeylist_t keys;
+ controlconnectionlist_t connections;
+ isc_sockettype_t type;
+ uint32_t perm;
+ uint32_t owner;
+ uint32_t group;
+ bool readonly;
+ ISC_LINK(controllistener_t) link;
+};
+
+struct ns_controls {
+ ns_server_t *server;
+ controllistenerlist_t listeners;
+ bool 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_putanddetach(&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->buffer != NULL)
+ isc_buffer_free(&conn->buffer);
+
+ 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);
+#ifdef ENABLE_AFL
+ if (ns_g_fuzz_type == ns_fuzz_rndc) {
+ named_fuzz_notify();
+ }
+#endif
+ 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, true);
+ listener->exiting = 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 bool
+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 (false);
+ else
+ return (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 = 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 = 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;
+ uint32_t algorithm;
+ isccc_region_t secret;
+ isc_stdtime_t now;
+ isc_buffer_t b;
+ isc_region_t r;
+ isc_buffer_t *text;
+ isc_result_t result;
+ isc_result_t eresult;
+ isccc_sexpr_t *_ctrl;
+ isccc_time_t sent;
+ isccc_time_t exp;
+ uint32_t nonce;
+ isccc_sexpr_t *data;
+
+ REQUIRE(event->ev_type == ISCCC_EVENT_CCMSG);
+
+ conn = event->ev_arg;
+ listener = conn->listener;
+ algorithm = DST_ALG_UNKNOWN;
+ secret.rstart = NULL;
+ text = 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))
+ {
+ isccc_region_t ccregion;
+
+ 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;
+ memmove(secret.rstart, key->secret.base, key->secret.length);
+ secret.rend = secret.rstart + key->secret.length;
+ algorithm = key->algorithm;
+ result = isccc_cc_fromwire(&ccregion, &request,
+ algorithm, &secret);
+ if (result == ISC_R_SUCCESS)
+ break;
+ isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
+ if (result != ISCCC_R_BADAUTH) {
+ 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 (!isccc_alist_alistp(_ctrl)) {
+ 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;
+ }
+
+ result = isc_buffer_allocate(listener->mctx, &text, 2 * 2048);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_request;
+
+ /*
+ * Establish nonce.
+ */
+ if (conn->nonce == 0) {
+ while (conn->nonce == 0)
+ isc_random_get(&conn->nonce);
+ eresult = ISC_R_SUCCESS;
+ } else
+ eresult = ns_control_docommand(request, listener->readonly, &text);
+
+ result = isccc_cc_createresponse(request, now, now + 60, &response);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_request;
+
+ data = isccc_alist_lookup(response, "_data");
+ if (data != NULL) {
+ if (isccc_cc_defineuint32(data, "result", eresult) == NULL)
+ goto cleanup_response;
+ }
+
+ if (eresult != ISC_R_SUCCESS) {
+ 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) {
+ 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;
+
+ if (conn->buffer == NULL) {
+ result = isc_buffer_allocate(listener->mctx,
+ &conn->buffer, 2 * 2048);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_response;
+ }
+
+ isc_buffer_clear(conn->buffer);
+ /* Skip the length field (4 bytes) */
+ isc_buffer_add(conn->buffer, 4);
+
+ result = isccc_cc_towire(response, &conn->buffer, algorithm, &secret);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_response;
+
+ isc_buffer_init(&b, conn->buffer->base, 4);
+ isc_buffer_putuint32(&b, conn->buffer->used - 4);
+
+ r.base = conn->buffer->base;
+ r.length = conn->buffer->used;
+
+ result = isc_socket_send(conn->sock, &r, task, control_senddone, conn);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_response;
+ conn->sending = true;
+
+ isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
+ isccc_sexpr_free(&request);
+ isccc_sexpr_free(&response);
+ isc_buffer_free(&text);
+ return;
+
+ cleanup_response:
+ isccc_sexpr_free(&response);
+
+ cleanup_request:
+ isccc_sexpr_free(&request);
+ isc_mem_put(listener->mctx, secret.rstart, REGION_SIZE(secret));
+ if (text != NULL)
+ isc_buffer_free(&text);
+
+ cleanup:
+ isc_socket_detach(&conn->sock);
+ isccc_ccmsg_invalidate(&conn->ccmsg);
+ conn->ccmsg_valid = 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);
+
+ /* Set a 32 KiB upper limit on incoming message. */
+ isccc_ccmsg_setmaxsize(&conn->ccmsg, 32768);
+
+ conn->ccmsg_valid = true;
+ conn->sending = false;
+ conn->buffer = NULL;
+ 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;
+
+ ISC_LIST_APPEND(listener->connections, conn, link);
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (conn->buffer != NULL)
+ isc_buffer_free(&conn->buffer);
+ isccc_ccmsg_invalidate(&conn->ccmsg);
+ if (conn->timer != NULL)
+ isc_timer_detach(&conn->timer);
+ isc_mem_put(listener->mctx, conn, sizeof(*conn));
+#ifdef ENABLE_AFL
+ if (ns_g_fuzz_type == ns_fuzz_rndc) {
+ named_fuzz_notify();
+ }
+#endif
+ 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 = 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 = 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->algorithm = DST_ALG_UNKNOWN;
+ 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;
+ unsigned int algtype;
+
+ (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_getkeyalgorithm2(algstr, NULL,
+ &algtype, 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;
+ }
+
+ keyid->algorithm = algtype;
+ 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;
+ }
+ memmove(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];
+ unsigned int algtype;
+ isc_buffer_t b;
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_INFO,
+ "configuring command channel from '%s'",
+ ns_g_keyfile);
+ if (! isc_file_exists(ns_g_keyfile))
+ return (ISC_R_FILENOTFOUND);
+
+ 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;
+ keyid->algorithm = DST_ALG_UNKNOWN;
+ 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_getkeyalgorithm2(algstr, NULL,
+ &algtype, 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;
+ }
+
+ keyid->algorithm = algtype;
+ 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));
+ goto cleanup;
+ }
+
+ 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);
+ }
+ memmove(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 (control != NULL) {
+ const cfg_obj_t *readonly;
+
+ readonly = cfg_tuple_get(control, "read-only");
+ if (!cfg_obj_isvoid(readonly))
+ listener->readonly = cfg_obj_asboolean(readonly);
+ }
+
+ 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) {
+ 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->mctx = NULL;
+ isc_mem_attach(mctx, &listener->mctx);
+ listener->controls = cp;
+ listener->task = cp->server->task;
+ listener->address = *addr;
+ listener->sock = NULL;
+ listener->listening = false;
+ listener->exiting = false;
+ listener->acl = NULL;
+ listener->type = type;
+ listener->perm = 0;
+ listener->owner = 0;
+ listener->group = 0;
+ listener->readonly = false;
+ 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) && (control != NULL)) {
+ const cfg_obj_t *readonly;
+
+ readonly = cfg_tuple_get(control, "read-only");
+ if (!cfg_obj_isvoid(readonly))
+ listener->readonly = cfg_obj_asboolean(readonly);
+ }
+
+ 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, 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);
+
+#ifndef ISC_ALLOW_MAPPED
+ if (result == ISC_R_SUCCESS)
+ isc_socket_ipv6only(listener->sock, true);
+#endif
+
+ 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 = 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 = 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..8f78be3
--- /dev/null
+++ b/bin/named/convertxsl.pl
@@ -0,0 +1,51 @@
+#!/usr/bin/env perl
+#
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+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/[\ \t]+$//g;
+ 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/fuzz.c b/bin/named/fuzz.c
new file mode 100644
index 0000000..92f3f8b
--- /dev/null
+++ b/bin/named/fuzz.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include "config.h"
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <named/fuzz.h>
+
+#ifdef ENABLE_AFL
+#include <named/globals.h>
+#include <named/server.h>
+#include <sys/errno.h>
+
+#include <isc/app.h>
+#include <isc/condition.h>
+#include <isc/mutex.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+#include <named/log.h>
+#include <dns/log.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#ifndef __AFL_LOOP
+#error To use American Fuzzy Lop you have to set CC to afl-clang-fast!!!
+#endif
+
+/*
+ * We are using pthreads directly because we might be using it with unthreaded
+ * version of BIND, where all thread functions are mocks. Since AFL for now only
+ * works on Linux it's not a problem.
+ */
+static pthread_cond_t cond;
+static pthread_mutex_t mutex;
+static bool ready;
+
+
+static void *
+fuzz_main_client(void *arg) {
+ char *host;
+ char *port;
+ struct sockaddr_in servaddr;
+ int sockfd;
+ int loop;
+ void *buf;
+
+ UNUSED(arg);
+
+ /*
+ * Parse named -A argument in the "address:port" syntax. Due to
+ * the syntax used, this only supports IPv4 addresses.
+ */
+
+ host = strdup(ns_g_fuzz_named_addr);
+ RUNTIME_CHECK(host != NULL);
+ port = strchr(host, ':');
+ RUNTIME_CHECK(port != NULL);
+ *port = 0;
+ ++port;
+
+ memset(&servaddr, 0, sizeof (servaddr));
+ servaddr.sin_family = AF_INET;
+ RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1);
+ servaddr.sin_port = htons(atoi(port));
+
+ free(host);
+
+ /* Wait for named to start. */
+ while (!ns_g_run_done) {
+ usleep(10000);
+ }
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ RUNTIME_CHECK(sockfd != -1);
+
+ buf = malloc(65536);
+ RUNTIME_CHECK(buf != NULL);
+
+ loop = 100000;
+ while (loop--) {
+ ssize_t length;
+
+ length = read(0, buf, 65536);
+ if (length <= 0) {
+ usleep(1000000);
+ continue;
+ }
+
+ if (length > 4096) {
+ if (getenv("AFL_CMIN")) {
+ ns_server_flushonshutdown(ns_g_server,
+ false);
+ isc_app_shutdown();
+ return (NULL);
+ }
+ raise(SIGSTOP);
+ continue;
+ }
+
+ RUNTIME_CHECK(pthread_mutex_lock(&mutex) == ISC_R_SUCCESS);
+
+ ready = false;
+
+ ssize_t sent;
+
+ sent = sendto(sockfd, buf, length, 0,
+ (struct sockaddr *) &servaddr, sizeof(servaddr));
+ RUNTIME_CHECK(sent == length);
+
+ /* unclog */
+ recvfrom(sockfd, buf, 65536, MSG_DONTWAIT, NULL, NULL);
+
+ while (!ready)
+ pthread_cond_wait(&cond, &mutex);
+
+ RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == ISC_R_SUCCESS);
+ }
+
+ free(buf);
+ close(sockfd);
+
+ ns_server_flushonshutdown(ns_g_server, false);
+ isc_app_shutdown();
+
+ return (NULL);
+}
+
+static void *
+fuzz_main_resolver(void *arg) {
+ char *shost, *sport, *rhost, *rport;
+ /* Query for A? aaaaaaaaaa.example. */
+ char respacket[] =
+ "\0\0\1 \0\1\0\0\0\0\0\0\naaaaaaaaaa\7example\0\0\1\0\1";
+ struct sockaddr_in servaddr, recaddr, recvaddr;
+ int sockfd;
+ int listenfd;
+ int loop;
+ char *buf, *rbuf;
+
+ UNUSED(arg);
+
+ /*
+ * Parse named -A argument in the "laddress:sport:raddress:rport"
+ * syntax. Due to the syntax used, this only supports IPv4 addresses.
+ */
+
+ shost = strdup(ns_g_fuzz_named_addr);
+ RUNTIME_CHECK(shost != NULL);
+ sport = strchr(shost, ':');
+ RUNTIME_CHECK(sport != NULL);
+ *sport = 0;
+ sport++;
+ rhost = strchr(sport, ':');
+ RUNTIME_CHECK(rhost != NULL);
+ *rhost = 0;
+ rhost++;
+ rport = strchr(rhost, ':');
+ RUNTIME_CHECK(rport != NULL);
+ *rport = 0;
+ rport++;
+
+ memset(&servaddr, 0, sizeof (servaddr));
+ servaddr.sin_family = AF_INET;
+ RUNTIME_CHECK(inet_pton(AF_INET, shost, &servaddr.sin_addr) == 1);
+ servaddr.sin_port = htons(atoi(sport));
+
+ memset(&recaddr, 0, sizeof (recaddr));
+ recaddr.sin_family = AF_INET;
+ RUNTIME_CHECK(inet_pton(AF_INET, rhost, &recaddr.sin_addr) == 1);
+ recaddr.sin_port = htons(atoi(rport));
+
+ free(shost);
+
+ /* Wait for named to start */
+ while (!ns_g_run_done) {
+ usleep(10000);
+ }
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ RUNTIME_CHECK(sockfd != -1);
+
+ listenfd = socket(AF_INET, SOCK_DGRAM, 0);
+ RUNTIME_CHECK(listenfd != -1);
+ RUNTIME_CHECK(bind(listenfd, (struct sockaddr *)&recaddr,
+ sizeof(struct sockaddr_in)) == 0);
+
+ buf = malloc(65536);
+ rbuf = malloc(65536);
+ RUNTIME_CHECK(buf != NULL);
+ RUNTIME_CHECK(rbuf != NULL);
+
+ loop = 100000;
+ while (loop--) {
+ ssize_t length;
+ memset(buf, 0, 16);
+ length = read(0, buf, 65536);
+ if (length <= 0) {
+ usleep(1000000);
+ continue;
+ }
+
+ if (length > 4096) {
+ if (getenv("AFL_CMIN")) {
+ ns_server_flushonshutdown(ns_g_server,
+ false);
+ isc_app_shutdown();
+ return (NULL);
+ }
+ raise(SIGSTOP);
+ continue;
+ }
+
+ if (length < 16) {
+ length = 16;
+ }
+
+ RUNTIME_CHECK(pthread_mutex_lock(&mutex) == ISC_R_SUCCESS);
+
+ ready = false;
+
+ ssize_t sent;
+ /* Randomize query ID. */
+ int id = random();
+ respacket[0] = id >> 8;
+ respacket[1] = id & 0xff;
+
+ /* flush */
+ socklen_t socklen = sizeof(recvaddr);
+ sent = recvfrom(listenfd, rbuf, 65536, MSG_DONTWAIT,
+ (struct sockaddr *) &recvaddr, &socklen);
+
+ sent = sendto(sockfd, respacket, sizeof(respacket), 0,
+ (struct sockaddr *) &servaddr, sizeof(servaddr));
+ RUNTIME_CHECK(sent == sizeof(respacket));
+
+ socklen = sizeof(recvaddr);
+ sent = recvfrom(listenfd, rbuf, 65536, 0,
+ (struct sockaddr *) &recvaddr, &socklen);
+ RUNTIME_CHECK(sent > 0);
+
+ /* Copy QID and set QR so that response is always processed. */
+ buf[0] = rbuf[0];
+ buf[1] = rbuf[1];
+ buf[2] |= 0x80;
+
+ sent = sendto(listenfd, buf, length, 0,
+ (struct sockaddr *) &recvaddr, sizeof(recvaddr));
+ RUNTIME_CHECK(sent == length);
+
+ /* We might get additional questions here (e.g. for CNAME). */
+ for (;;) {
+ fd_set fds;
+ struct timeval tv;
+ int rv;
+ int max;
+
+ FD_ZERO(&fds);
+ FD_SET(listenfd, &fds);
+ FD_SET(sockfd, &fds);
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+ max = (listenfd > sockfd ? listenfd : sockfd)+1;
+
+ rv = select(max, &fds, NULL, NULL, &tv);
+ RUNTIME_CHECK(rv > 0);
+
+ if (FD_ISSET(sockfd, &fds)) {
+ /* It's the reply, we're done. */
+ recvfrom(sockfd, buf, 65536, 0, NULL, NULL);
+ break;
+ }
+
+ /*
+ * We've got additional question (eg. cname chain)
+ * We are bouncing it - setting QR flag and NOERROR
+ * rcode and sending it back.
+ */
+
+ length = recvfrom(listenfd, buf, 65536, 0,
+ (struct sockaddr *) &recvaddr, &socklen);
+ buf[2] |= 0x80;
+ buf[3] &= 0xF0;
+ sent = sendto(listenfd, buf, length, 0,
+ (struct sockaddr *) &recvaddr,
+ sizeof(recvaddr));
+ RUNTIME_CHECK(sent == length);
+ }
+
+ while (!ready)
+ pthread_cond_wait(&cond, &mutex);
+
+ RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
+ }
+
+ free(buf);
+ free(rbuf);
+ close(sockfd);
+ ns_server_flushonshutdown(ns_g_server, false);
+ isc_app_shutdown();
+
+ /*
+ * It's here just for the signature, that's how AFL detects if it's
+ * a 'persistent mode' binary.
+ */
+ __AFL_LOOP(0);
+
+ return (NULL);
+}
+
+static void *
+fuzz_main_tcp(void *arg) {
+ char *host;
+ char *port;
+ struct sockaddr_in servaddr;
+ int sockfd;
+ char *buf;
+ int loop;
+
+ UNUSED(arg);
+
+ /*
+ * Parse named -A argument in the "address:port" syntax. Due to
+ * the syntax used, this only supports IPv4 addresses.
+ */
+
+ host = strdup(ns_g_fuzz_named_addr);
+ RUNTIME_CHECK(host != NULL);
+ port = strchr(host, ':');
+ RUNTIME_CHECK(port != NULL);
+ *port = 0;
+ ++port;
+
+ memset(&servaddr, 0, sizeof (servaddr));
+ servaddr.sin_family = AF_INET;
+ RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1);
+ servaddr.sin_port = htons(atoi(port));
+
+ free(host);
+
+ /* Wait for named to start */
+ while (!ns_g_run_done) {
+ usleep(10000);
+ }
+
+ buf = malloc(65539);
+ RUNTIME_CHECK(buf != NULL);
+
+ loop = 100000;
+ while (loop--) {
+ ssize_t length;
+
+ if (ns_g_fuzz_type == ns_fuzz_tcpclient) {
+ /*
+ * To fuzz TCP client we have to put length at
+ * the start of packet.
+ */
+ length = read(0, buf+2, 65535);
+ buf[0] = length >> 8;
+ buf[1] = length & 0xff;
+ length += 2;
+ } else {
+ length = read(0, buf, 65535);
+ }
+ if (length <= 0) {
+ usleep(1000000);
+ continue;
+ }
+ if (ns_g_fuzz_type == ns_fuzz_http) {
+ /*
+ * This guarantees that the request will be processed.
+ */
+ buf[length++]='\r';
+ buf[length++]='\n';
+ buf[length++]='\r';
+ buf[length++]='\n';
+ }
+
+ RUNTIME_CHECK(pthread_mutex_lock(&mutex) == ISC_R_SUCCESS);
+
+ ready = false;
+
+ ssize_t sent;
+ int yes = 1;
+ int r;
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+
+ RUNTIME_CHECK(sockfd != -1);
+ RUNTIME_CHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+ &yes, sizeof(int)) == 0);
+
+ do {
+ r = connect(sockfd, (struct sockaddr*)&servaddr,
+ sizeof(servaddr));
+ } while (r != 0);
+
+ sent = write(sockfd, buf, length);
+ RUNTIME_CHECK(sent == length);
+ close(sockfd);
+
+ /* unclog */
+ recvfrom(sockfd, buf, 65537, MSG_DONTWAIT, NULL, NULL);
+
+ while (!ready)
+ pthread_cond_wait(&cond, &mutex);
+
+ RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == ISC_R_SUCCESS);
+ }
+
+ free(buf);
+ close(sockfd);
+ ns_server_flushonshutdown(ns_g_server, false);
+ isc_app_shutdown();
+
+ return (NULL);
+}
+
+#endif /* ENABLE_AFL */
+
+void
+named_fuzz_notify(void) {
+#ifdef ENABLE_AFL
+ if (getenv("AFL_CMIN")) {
+ ns_server_flushonshutdown(ns_g_server, false);
+ isc_app_shutdown();
+ return;
+ }
+
+ raise(SIGSTOP);
+
+ RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0);
+
+ ready = true;
+
+ RUNTIME_CHECK(pthread_cond_signal(&cond) == 0);
+ RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0);
+#endif /* ENABLE_AFL */
+}
+
+void
+named_fuzz_setup(void) {
+#ifdef ENABLE_AFL
+ if (getenv("__AFL_PERSISTENT") || getenv("AFL_CMIN")) {
+ pthread_t thread;
+ void *(fn) = NULL;
+
+ switch (ns_g_fuzz_type) {
+ case ns_fuzz_client:
+ fn = fuzz_main_client;
+ break;
+
+ case ns_fuzz_http:
+ case ns_fuzz_tcpclient:
+ case ns_fuzz_rndc:
+ fn = fuzz_main_tcp;
+ break;
+ case ns_fuzz_resolver:
+ fn = fuzz_main_resolver;
+ break;
+ default:
+ RUNTIME_CHECK(fn != NULL);
+ }
+
+ RUNTIME_CHECK(pthread_mutex_init(&mutex, NULL) == 0);
+ RUNTIME_CHECK(pthread_cond_init(&cond, NULL) == 0);
+ RUNTIME_CHECK(pthread_create(&thread, NULL, fn, NULL) == 0);
+ }
+#endif /* ENABLE_AFL */
+}
diff --git a/bin/named/geoip.c b/bin/named/geoip.c
new file mode 100644
index 0000000..5bc504e
--- /dev/null
+++ b/bin/named/geoip.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/util.h>
+
+#include <named/log.h>
+#include <named/geoip.h>
+
+#include <dns/geoip.h>
+
+#ifdef HAVE_GEOIP
+static dns_geoip_databases_t geoip_table = {
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static void
+init_geoip_db(GeoIP **dbp, GeoIPDBTypes edition, GeoIPDBTypes fallback,
+ GeoIPOptions method, const char *name)
+{
+ char *info;
+ GeoIP *db;
+
+ REQUIRE(dbp != NULL);
+
+ db = *dbp;
+
+ if (db != NULL) {
+ GeoIP_delete(db);
+ db = *dbp = NULL;
+ }
+
+ if (! GeoIP_db_avail(edition)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "GeoIP %s (type %d) DB not available", name, edition);
+ goto fail;
+ }
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "initializing GeoIP %s (type %d) DB", name, edition);
+
+ db = GeoIP_open_type(edition, method);
+ if (db == NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed to initialize GeoIP %s (type %d) DB%s",
+ name, edition, fallback == 0
+ ? "geoip matches using this database will fail" : "");
+ goto fail;
+ }
+
+ info = GeoIP_database_info(db);
+ if (info != NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s", info);
+ free(info);
+ }
+
+ *dbp = db;
+ return;
+ fail:
+ if (fallback != 0)
+ init_geoip_db(dbp, fallback, 0, method, name);
+
+}
+#endif /* HAVE_GEOIP */
+
+void
+ns_geoip_init(void) {
+#ifndef HAVE_GEOIP
+ return;
+#else
+ GeoIP_cleanup();
+ if (ns_g_geoip == NULL)
+ ns_g_geoip = &geoip_table;
+#endif
+}
+
+void
+ns_geoip_load(char *dir) {
+#ifndef HAVE_GEOIP
+
+ UNUSED(dir);
+
+ return;
+#else
+ GeoIPOptions method;
+
+#ifdef _WIN32
+ method = GEOIP_STANDARD;
+#else
+ method = GEOIP_MMAP_CACHE;
+#endif
+
+ ns_geoip_init();
+ if (dir != NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "using \"%s\" as GeoIP directory", dir);
+ GeoIP_setup_custom_directory(dir);
+ }
+
+ init_geoip_db(&ns_g_geoip->country_v4, GEOIP_COUNTRY_EDITION, 0,
+ method, "Country (IPv4)");
+#ifdef HAVE_GEOIP_V6
+ init_geoip_db(&ns_g_geoip->country_v6, GEOIP_COUNTRY_EDITION_V6, 0,
+ method, "Country (IPv6)");
+#endif
+
+ init_geoip_db(&ns_g_geoip->city_v4, GEOIP_CITY_EDITION_REV1,
+ GEOIP_CITY_EDITION_REV0, method, "City (IPv4)");
+#if defined(HAVE_GEOIP_V6) && defined(HAVE_GEOIP_CITY_V6)
+ init_geoip_db(&ns_g_geoip->city_v6, GEOIP_CITY_EDITION_REV1_V6,
+ GEOIP_CITY_EDITION_REV0_V6, method, "City (IPv6)");
+#endif
+
+ init_geoip_db(&ns_g_geoip->region, GEOIP_REGION_EDITION_REV1,
+ GEOIP_REGION_EDITION_REV0, method, "Region");
+
+ init_geoip_db(&ns_g_geoip->isp, GEOIP_ISP_EDITION, 0,
+ method, "ISP");
+ init_geoip_db(&ns_g_geoip->org, GEOIP_ORG_EDITION, 0,
+ method, "Org");
+ init_geoip_db(&ns_g_geoip->as, GEOIP_ASNUM_EDITION, 0,
+ method, "AS");
+ init_geoip_db(&ns_g_geoip->domain, GEOIP_DOMAIN_EDITION, 0,
+ method, "Domain");
+ init_geoip_db(&ns_g_geoip->netspeed, GEOIP_NETSPEED_EDITION, 0,
+ method, "NetSpeed");
+#endif /* HAVE_GEOIP */
+}
diff --git a/bin/named/include/dlz/dlz_dlopen_driver.h b/bin/named/include/dlz/dlz_dlopen_driver.h
new file mode 100644
index 0000000..14a637b
--- /dev/null
+++ b/bin/named/include/dlz/dlz_dlopen_driver.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef DLZ_DLOPEN_DRIVER_H
+#define DLZ_DLOPEN_DRIVER_H
+
+isc_result_t
+dlz_dlopen_init(isc_mem_t *mctx);
+
+void
+dlz_dlopen_clear(void);
+#endif
diff --git a/bin/named/include/named/builtin.h b/bin/named/include/named/builtin.h
new file mode 100644
index 0000000..97ffdee
--- /dev/null
+++ b/bin/named/include/named/builtin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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..b23a7b1
--- /dev/null
+++ b/bin/named/include/named/client.h
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: client.h,v 1.96 2012/01/31 23:47:31 tbox 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 <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/stdtime.h>
+#include <isc/quota.h>
+#include <isc/queue.h>
+
+#include <dns/db.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
+ ***/
+
+/*% 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;
+ bool 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;
+ bool tcpmsg_valid;
+ isc_timer_t * timer;
+ isc_timer_t * delaytimer;
+ bool timerset;
+ dns_message_t * message;
+ isc_socketevent_t * sendevent;
+ isc_socketevent_t * recvevent;
+ unsigned char * recvbuf;
+ dns_rdataset_t * opt;
+ uint16_t udpsize;
+ uint16_t extflags;
+ 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_time_t requesttime;
+ isc_stdtime_t now;
+ isc_time_t tnow;
+ dns_name_t signername; /*%< [T]SIG key name */
+ dns_name_t * signer; /*%< NULL if not valid sig */
+ bool mortal; /*%< Die after handling request */
+ bool pipelined; /*%< TCP queries not in sequence */
+ isc_quota_t *tcpquota;
+ isc_quota_t *recursionquota;
+ ns_interface_t *interface;
+
+ isc_sockaddr_t peeraddr;
+ bool peeraddr_valid;
+ isc_netaddr_t destaddr;
+ isc_sockaddr_t destsockaddr;
+
+ isc_netaddr_t ecs_addr; /*%< EDNS client subnet */
+ uint8_t ecs_addrlen;
+ uint8_t ecs_scope;
+
+ struct in6_pktinfo pktinfo;
+ isc_dscp_t dscp;
+ isc_event_t ctlevent;
+#ifdef ALLOW_FILTER_AAAA
+ dns_aaaa_t filter_aaaa;
+#endif
+ /*%
+ * 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;
+ ISC_LINK(ns_client_t) rlink;
+ ISC_QLINK(ns_client_t) ilink;
+ unsigned char cookie[8];
+ uint32_t expire;
+ unsigned char *keytag;
+ uint16_t keytag_len;
+};
+
+typedef ISC_QUEUE(ns_client_t) client_queue_t;
+typedef ISC_LIST(ns_client_t) client_list_t;
+
+#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 0x0001
+#define NS_CLIENTATTR_RA 0x0002 /*%< Client gets recursive service */
+#define NS_CLIENTATTR_PKTINFO 0x0004 /*%< pktinfo is valid */
+#define NS_CLIENTATTR_MULTICAST 0x0008 /*%< recv'd from multicast */
+#define NS_CLIENTATTR_WANTDNSSEC 0x0010 /*%< include dnssec records */
+#define NS_CLIENTATTR_WANTNSID 0x0020 /*%< include nameserver ID */
+#ifdef ALLOW_FILTER_AAAA
+#define NS_CLIENTATTR_FILTER_AAAA 0x0040 /*%< suppress AAAAs */
+#define NS_CLIENTATTR_FILTER_AAAA_RC 0x0080 /*%< recursing for A against AAAA */
+#endif
+#define NS_CLIENTATTR_WANTAD 0x0100 /*%< want AD in response if possible */
+#define NS_CLIENTATTR_WANTCOOKIE 0x0200 /*%< return a COOKIE */
+#define NS_CLIENTATTR_HAVECOOKIE 0x0400 /*%< has a valid COOKIE */
+#define NS_CLIENTATTR_WANTEXPIRE 0x0800 /*%< return seconds to expire */
+#define NS_CLIENTATTR_HAVEEXPIRE 0x1000 /*%< return seconds to expire */
+#define NS_CLIENTATTR_WANTOPT 0x2000 /*%< add opt to reply */
+#define NS_CLIENTATTR_HAVEECS 0x4000 /*%< received an ECS option */
+
+#define NS_CLIENTATTR_NOSETFC 0x8000 /*%< don't set servfail cache */
+
+/*
+ * Flag to use with the SERVFAIL cache to indicate
+ * that a query had the CD bit set.
+ */
+#define NS_FAILCACHE_CD 0x01
+
+
+
+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.
+ */
+
+bool
+ns_client_shuttingdown(ns_client_t *client);
+/*%
+ * Return 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, bool tcp);
+/*%
+ * Create up to 'n' clients listening on interface 'ifp'.
+ * If 'tcp' is 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_sockaddr_t *
+ns_client_getdestaddr(ns_client_t *client);
+/*%<
+ * Get the destination address (server) for the request that is
+ * currently being processed.
+ */
+
+isc_result_t
+ns_client_checkaclsilent(ns_client_t *client, isc_netaddr_t *netaddr,
+ dns_acl_t *acl, bool 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 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 'netaddr' 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 DNS_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,
+ bool 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.
+ */
+
+bool
+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.
+ */
+
+isc_result_t
+ns_client_sourceip(dns_clientinfo_t *ci, isc_sockaddr_t **addrp);
+
+isc_result_t
+ns_client_addopt(ns_client_t *client, dns_message_t *message,
+ dns_rdataset_t **opt);
+
+#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..965c2b0
--- /dev/null
+++ b/bin/named/include/named/config.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef NAMED_CONFIG_H
+#define NAMED_CONFIG_H 1
+
+/*! \file */
+
+#include <inttypes.h>
+
+#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(cfg_obj_t const * const *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_dscp_t **dscpsp,
+ uint32_t *countp);
+
+void
+ns_config_putiplist(isc_mem_t *mctx, isc_sockaddr_t **addrsp,
+ isc_dscp_t **dscpsp, uint32_t count);
+
+isc_result_t
+ns_config_getipandkeylist(const cfg_obj_t *config, const cfg_obj_t *list,
+ isc_mem_t *mctx, dns_ipkeylist_t *ipkl);
+
+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,
+ uint16_t *digestbits);
+isc_result_t
+ns_config_getkeyalgorithm2(const char *str, dns_name_t **name,
+ unsigned int *typep, uint16_t *digestbits);
+
+isc_result_t
+ns_config_getdscp(const cfg_obj_t *config, isc_dscp_t *dscpp);
+
+#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..8705fdd
--- /dev/null
+++ b/bin/named/include/named/control.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: control.h,v 1.38 2012/01/31 23:47:31 tbox Exp $ */
+
+#ifndef NAMED_CONTROL_H
+#define NAMED_CONTROL_H 1
+
+/*! \file
+ * \brief
+ * The name server command channel.
+ */
+
+#include <stdbool.h>
+#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_SECROOTS "secroots"
+#define NS_COMMAND_TRACE "trace"
+#define NS_COMMAND_NOTRACE "notrace"
+#define NS_COMMAND_FLUSH "flush"
+#define NS_COMMAND_FLUSHNAME "flushname"
+#define NS_COMMAND_FLUSHTREE "flushtree"
+#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"
+#define NS_COMMAND_SCAN "scan"
+#define NS_COMMAND_SIGN "sign"
+#define NS_COMMAND_LOADKEYS "loadkeys"
+#define NS_COMMAND_ADDZONE "addzone"
+#define NS_COMMAND_MODZONE "modzone"
+#define NS_COMMAND_DELZONE "delzone"
+#define NS_COMMAND_SHOWZONE "showzone"
+#define NS_COMMAND_SYNC "sync"
+#define NS_COMMAND_SIGNING "signing"
+#define NS_COMMAND_ZONESTATUS "zonestatus"
+#define NS_COMMAND_NTA "nta"
+#define NS_COMMAND_TESTGEN "testgen"
+#define NS_COMMAND_MKEYS "managed-keys"
+#define NS_COMMAND_DNSTAPREOPEN "dnstap-reopen"
+#define NS_COMMAND_DNSTAP "dnstap"
+
+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, bool readonly,
+ isc_buffer_t **text);
+
+#endif /* NAMED_CONTROL_H */
diff --git a/bin/named/include/named/fuzz.h b/bin/named/include/named/fuzz.h
new file mode 100644
index 0000000..6e5eac5
--- /dev/null
+++ b/bin/named/include/named/fuzz.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef NAMED_FUZZ_H
+#define NAMED_FUZZ_H
+
+void
+named_fuzz_notify(void);
+
+void
+named_fuzz_setup(void);
+
+typedef enum {
+ ns_fuzz_none,
+ ns_fuzz_client,
+ ns_fuzz_tcpclient,
+ ns_fuzz_resolver,
+ ns_fuzz_http,
+ ns_fuzz_rndc
+} ns_fuzz_t;
+
+#endif /* NAMED_FUZZ_H */
diff --git a/bin/named/include/named/geoip.h b/bin/named/include/named/geoip.h
new file mode 100644
index 0000000..04a5a39
--- /dev/null
+++ b/bin/named/include/named/geoip.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef _GEOIP_H
+#define _GEOIP_H
+
+#ifdef HAVE_GEOIP
+#include <GeoIP.h>
+#include <GeoIPCity.h>
+#endif /* HAVE_GEOIP */
+
+void ns_geoip_init(void);
+void ns_geoip_load(char *dir);
+
+#ifdef HAVE_GEOIP
+extern dns_geoip_databases_t *ns_g_geoip;
+#endif /* HAVE_GEOIP */
+#endif
diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h
new file mode 100644
index 0000000..494e005
--- /dev/null
+++ b/bin/named/include/named/globals.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef NAMED_GLOBALS_H
+#define NAMED_GLOBALS_H 1
+
+/*! \file */
+
+#include <stdbool.h>
+#include <isc/rwlock.h>
+#include <isc/log.h>
+#include <isc/net.h>
+
+#include <isccfg/aclconf.h>
+#include <isccfg/cfg.h>
+
+#include <dns/acl.h>
+#include <dns/zone.h>
+
+#include <dst/dst.h>
+
+#include <named/types.h>
+#include <named/fuzz.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 unsigned int ns_g_udpdisp 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);
+
+#ifdef ENABLE_AFL
+EXTERN bool ns_g_run_done INIT(false);
+#endif
+/*
+ * 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 cfg_parser_t * ns_g_addparser INIT(NULL);
+EXTERN const char * ns_g_version INIT(VERSION);
+EXTERN const char * ns_g_product INIT(PRODUCT);
+EXTERN const char * ns_g_description INIT(DESCRIPTION);
+EXTERN const char * ns_g_srcid INIT(SRCID);
+EXTERN const char * ns_g_configargs INIT(CONFIGARGS);
+EXTERN const char * ns_g_builder INIT(BUILDER);
+EXTERN in_port_t ns_g_port INIT(0);
+EXTERN isc_dscp_t ns_g_dscp INIT(-1);
+EXTERN in_port_t lwresd_g_listenport INIT(0);
+
+EXTERN ns_server_t * ns_g_server INIT(NULL);
+
+EXTERN bool ns_g_lwresdonly INIT(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 cfg_obj_t * ns_g_bindkeys INIT(NULL);
+EXTERN const char * ns_g_keyfile INIT(NS_SYSCONFDIR
+ "/rndc.key");
+
+EXTERN dns_tsigkey_t * ns_g_sessionkey INIT(NULL);
+EXTERN dns_name_t ns_g_sessionkeyname;
+
+EXTERN const char * lwresd_g_conffile INIT(NS_SYSCONFDIR
+ "/lwresd.conf");
+EXTERN const char * lwresd_g_resolvconffile INIT("/etc"
+ "/resolv.conf");
+EXTERN bool ns_g_conffileset INIT(false);
+EXTERN bool lwresd_g_useresolvconf INIT(false);
+EXTERN uint16_t ns_g_udpsize INIT(4096);
+EXTERN cfg_aclconfctx_t * ns_g_aclconfctx INIT(NULL);
+
+/*
+ * 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 bool ns_g_coreok INIT(true);
+EXTERN const char * ns_g_chrootdir INIT(NULL);
+EXTERN bool ns_g_foreground INIT(false);
+EXTERN bool ns_g_logstderr INIT(false);
+EXTERN bool ns_g_nosyslog INIT(false);
+EXTERN const char * ns_g_logfile INIT(NULL);
+
+EXTERN const char * ns_g_defaultsessionkeyfile
+ INIT(NS_LOCALSTATEDIR "/run/named/"
+ "session.key");
+EXTERN const char * ns_g_defaultlockfile INIT(NS_LOCALSTATEDIR
+ "/run/named/"
+ "named.lock");
+EXTERN bool ns_g_forcelock INIT(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
+
+#ifdef HAVE_DNSTAP
+EXTERN const char * ns_g_defaultdnstap
+ INIT(NS_LOCALSTATEDIR "/run/named/"
+ "dnstap.sock");
+#else
+EXTERN const char * ns_g_defaultdnstap INIT(NULL);
+#endif /* HAVE_DNSTAP */
+
+EXTERN const char * ns_g_username INIT(NULL);
+
+#if defined(USE_PKCS11)
+EXTERN const char * ns_g_engine INIT(PKCS11_ENGINE);
+#else
+EXTERN const char * ns_g_engine INIT(NULL);
+#endif
+
+EXTERN int ns_g_listen INIT(3);
+EXTERN isc_time_t ns_g_boottime;
+EXTERN isc_time_t ns_g_configtime;
+EXTERN bool ns_g_memstatistics INIT(false);
+EXTERN bool ns_g_clienttest INIT(false);
+EXTERN bool ns_g_dropedns INIT(false);
+EXTERN bool ns_g_noedns INIT(false);
+EXTERN bool ns_g_nosoa INIT(false);
+EXTERN bool ns_g_noaa INIT(false);
+EXTERN bool ns_g_keepstderr INIT(false);
+EXTERN unsigned int ns_g_delay INIT(0);
+EXTERN bool ns_g_nonearest INIT(false);
+EXTERN bool ns_g_notcp INIT(false);
+EXTERN bool ns_g_disable6 INIT(false);
+EXTERN bool ns_g_disable4 INIT(false);
+EXTERN unsigned int ns_g_tat_interval INIT(24*3600);
+EXTERN bool ns_g_fixedlocal INIT(false);
+EXTERN bool ns_g_sigvalinsecs INIT(false);
+
+#ifdef HAVE_GEOIP
+EXTERN dns_geoip_databases_t *ns_g_geoip INIT(NULL);
+#endif
+
+EXTERN const char * ns_g_fuzz_named_addr INIT(NULL);
+EXTERN ns_fuzz_t ns_g_fuzz_type INIT(ns_fuzz_none);
+
+EXTERN dns_acl_t * ns_g_mapped INIT(NULL);
+
+#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..7d1883e
--- /dev/null
+++ b/bin/named/include/named/interfacemgr.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: interfacemgr.h,v 1.35 2011/07/28 23:47:58 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 <stdbool.h>
+
+#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 */
+#define MAX_UDP_DISPATCH 128 /*%< Maximum number of UDP dispatchers
+ to start per interface */
+/*% 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[MAX_UDP_DISPATCH];
+ /*%< UDP dispatchers. */
+ isc_socket_t * tcpsocket; /*%< TCP socket. */
+ isc_dscp_t dscp; /*%< "listen-on" DSCP value */
+ int ntcptarget; /*%< Desired number of concurrent
+ TCP accepts */
+ int ntcpcurrent; /*%< Current ditto, locked */
+ int nudpdispatch; /*%< Number of UDP dispatches */
+ 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,
+ isc_task_t *task, 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);
+
+bool
+ns_interfacemgr_islistening(ns_interfacemgr_t *mgr);
+/*%
+ * Return if the manager is listening on any interface. It can be called
+ * after a scan or adjust.
+ */
+
+isc_result_t
+ns_interfacemgr_scan(ns_interfacemgr_t *mgr, bool 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.
+ */
+
+isc_result_t
+ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list,
+ bool 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);
+
+bool
+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..d44594c
--- /dev/null
+++ b/bin/named/include/named/listenlist.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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 <stdbool.h>
+#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;
+ isc_dscp_t dscp; /* -1 = not set, 0..63 */
+ 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, isc_dscp_t dscp,
+ 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_dscp_t dscp,
+ bool enabled, ns_listenlist_t **target);
+/*%
+ * Create a listen-on list with default contents, matching
+ * all addresses with port 'port' (if 'enabled' is true),
+ * or no addresses (if 'enabled' is 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..56bfcd4
--- /dev/null
+++ b/bin/named/include/named/log.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: log.h,v 1.27 2009/01/07 23:47:46 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])
+#define NS_LOGCATEGORY_QUERY_ERRORS (&ns_g_categories[7])
+#define NS_LOGCATEGORY_TAT (&ns_g_categories[8])
+
+/*
+ * 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(bool 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..90dc9c4
--- /dev/null
+++ b/bin/named/include/named/logconf.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef 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..a48d756
--- /dev/null
+++ b/bin/named/include/named/lwaddr.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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..65e630d
--- /dev/null
+++ b/bin/named/include/named/lwdclient.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: lwdclient.h,v 1.20 2009/01/17 23:47:42 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)
+
+/*% Lightweight Resolver Daemon Client */
+struct ns_lwdclient {
+ isc_sockaddr_t address; /*%< where to reply */
+ struct in6_pktinfo pktinfo;
+ bool 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 */
+ 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;
+ 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];
+ 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_mutex_t lock;
+ 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 *, 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..90e5113
--- /dev/null
+++ b/bin/named/include/named/lwresd.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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;
+ unsigned int ntasks;
+ unsigned int nclients;
+ isc_mem_t *mctx;
+ bool 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..cf3bc6a
--- /dev/null
+++ b/bin/named/include/named/lwsearch.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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;
+ bool doneexact;
+ bool 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..2860bc2
--- /dev/null
+++ b/bin/named/include/named/main.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef NAMED_MAIN_H
+#define NAMED_MAIN_H 1
+
+/*! \file */
+
+#ifdef ISC_MAIN_HOOK
+#define main(argc, argv) bindmain(argc, argv)
+#endif
+
+/*
+ * Commandline arguments for named; also referenced in win32/ntservice.c
+ */
+#define NS_MAIN_ARGS "46A:c:C:d:D:E:fFgi:lL:M:m:n:N:p:P:sS:t:T:U:u:vVx:X:"
+
+ISC_PLATFORM_NORETURN_PRE void
+ns_main_earlyfatal(const char *format, ...)
+ISC_FORMAT_PRINTF(1, 2) ISC_PLATFORM_NORETURN_POST;
+
+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..293a6a8
--- /dev/null
+++ b/bin/named/include/named/notify.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: notify.h,v 1.16 2009/01/17 23:47:42 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 appropriate 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..9fc49e6
--- /dev/null
+++ b/bin/named/include/named/ns_smf_globals.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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..9661f56
--- /dev/null
+++ b/bin/named/include/named/query.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef NAMED_QUERY_H
+#define NAMED_QUERY_H 1
+
+/*! \file */
+
+#include <stdbool.h>
+
+#include <isc/types.h>
+#include <isc/buffer.h>
+#include <isc/netaddr.h>
+
+#include <dns/rdataset.h>
+#include <dns/rpz.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;
+ bool acl_checked;
+ bool queryok;
+ ISC_LINK(struct ns_dbversion) link;
+} ns_dbversion_t;
+
+/*% nameserver query structure */
+struct ns_query {
+ unsigned int attributes;
+ unsigned int restarts;
+ bool timerset;
+ dns_name_t * qname;
+ dns_name_t * origqname;
+ dns_rdatatype_t qtype;
+ unsigned int dboptions;
+ unsigned int fetchoptions;
+ dns_db_t * gluedb;
+ dns_db_t * authdb;
+ dns_zone_t * authzone;
+ bool authdbset;
+ bool isreferral;
+ isc_mutex_t fetchlock;
+ dns_fetch_t * fetch;
+ dns_fetch_t * prefetch;
+ dns_rpz_st_t * rpz_st;
+ isc_bufferlist_t namebufs;
+ ISC_LIST(ns_dbversion_t) activeversions;
+ ISC_LIST(ns_dbversion_t) freeversions;
+ dns_rdataset_t * dns64_aaaa;
+ dns_rdataset_t * dns64_sigaaaa;
+ bool * dns64_aaaaok;
+ unsigned int dns64_aaaaoklen;
+ unsigned int dns64_options;
+ unsigned int dns64_ttl;
+ struct {
+ dns_db_t * db;
+ dns_zone_t * zone;
+ dns_dbnode_t * node;
+ dns_rdatatype_t qtype;
+ dns_name_t * fname;
+ dns_fixedname_t fixed;
+ isc_result_t result;
+ dns_rdataset_t * rdataset;
+ dns_rdataset_t * sigrdataset;
+ bool authoritative;
+ bool is_zone;
+ } redirect;
+ dns_keytag_t root_key_sentinel_keyid;
+ bool root_key_sentinel_is_ta;
+ bool root_key_sentinel_not_ta;
+};
+
+#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
+#define NS_QUERYATTR_CACHEACLOKVALID 0x1000
+#define NS_QUERYATTR_CACHEACLOK 0x2000
+#define NS_QUERYATTR_DNS64 0x4000
+#define NS_QUERYATTR_DNS64EXCLUDE 0x8000
+#define NS_QUERYATTR_RRL_CHECKED 0x10000
+#define NS_QUERYATTR_REDIRECT 0x20000
+
+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/seccomp.h b/bin/named/include/named/seccomp.h
new file mode 100644
index 0000000..4e2c2d9
--- /dev/null
+++ b/bin/named/include/named/seccomp.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef NAMED_SECCOMP_H
+#define NAMED_SECCOMP_H 1
+
+/*! \file */
+
+#ifdef HAVE_LIBSECCOMP
+#include <sys/types.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <seccomp.h>
+#include <isc/platform.h>
+
+/*%
+ * For each architecture, the scmp_syscalls and
+ * scmp_syscall_names arrays MUST be kept in sync.
+ */
+#ifdef __x86_64__
+int scmp_syscalls[] = {
+ SCMP_SYS(access),
+ SCMP_SYS(open),
+ SCMP_SYS(openat),
+ SCMP_SYS(lseek),
+ SCMP_SYS(clock_gettime),
+ SCMP_SYS(time),
+ SCMP_SYS(read),
+ SCMP_SYS(write),
+ SCMP_SYS(close),
+ SCMP_SYS(brk),
+ SCMP_SYS(poll),
+ SCMP_SYS(select),
+ SCMP_SYS(madvise),
+ SCMP_SYS(mmap),
+ SCMP_SYS(munmap),
+ SCMP_SYS(exit_group),
+ SCMP_SYS(rt_sigprocmask),
+ SCMP_SYS(rt_sigaction),
+ SCMP_SYS(fsync),
+ SCMP_SYS(rt_sigreturn),
+ SCMP_SYS(setsid),
+ SCMP_SYS(chdir),
+ SCMP_SYS(futex),
+ SCMP_SYS(stat),
+ SCMP_SYS(rt_sigsuspend),
+ SCMP_SYS(fstat),
+ SCMP_SYS(epoll_ctl),
+ SCMP_SYS(gettimeofday),
+ SCMP_SYS(getpid),
+#ifdef HAVE_GETRANDOM
+ SCMP_SYS(getrandom),
+#endif
+ SCMP_SYS(rename),
+ SCMP_SYS(unlink),
+ SCMP_SYS(socket),
+ SCMP_SYS(sendto),
+#ifndef ISC_PLATFORM_USETHREADS
+ SCMP_SYS(bind),
+ SCMP_SYS(accept),
+ SCMP_SYS(connect),
+ SCMP_SYS(listen),
+ SCMP_SYS(fcntl),
+ SCMP_SYS(sendmsg),
+ SCMP_SYS(recvmsg),
+ SCMP_SYS(uname),
+ SCMP_SYS(setrlimit),
+ SCMP_SYS(getrlimit),
+ SCMP_SYS(setsockopt),
+ SCMP_SYS(getsockopt),
+ SCMP_SYS(getsockname),
+ SCMP_SYS(lstat),
+ SCMP_SYS(getgid),
+ SCMP_SYS(getegid),
+ SCMP_SYS(getuid),
+ SCMP_SYS(geteuid),
+ SCMP_SYS(setresgid),
+ SCMP_SYS(setresuid),
+ SCMP_SYS(setgid),
+ SCMP_SYS(setuid),
+ SCMP_SYS(prctl),
+ SCMP_SYS(epoll_wait),
+ SCMP_SYS(getdents),
+ SCMP_SYS(utimes),
+ SCMP_SYS(dup),
+#endif
+};
+const char *scmp_syscall_names[] = {
+ "access",
+ "open",
+ "openat",
+ "lseek",
+ "clock_gettime",
+ "time",
+ "read",
+ "write",
+ "close",
+ "brk",
+ "poll",
+ "select",
+ "madvise",
+ "mmap",
+ "munmap",
+ "exit_group",
+ "rt_sigprocmask",
+ "rt_sigaction",
+ "fsync",
+ "rt_sigreturn",
+ "setsid",
+ "chdir",
+ "futex",
+ "stat",
+ "rt_sigsuspend",
+ "fstat",
+ "epoll_ctl",
+ "gettimeofday",
+ "getpid",
+#ifdef HAVE_GETRANDOM
+ "getrandom",
+#endif
+ "rename",
+ "unlink",
+ "socket",
+ "sendto",
+#ifndef ISC_PLATFORM_USETHREADS
+ "bind",
+ "accept",
+ "connect",
+ "listen",
+ "fcntl",
+ "sendmsg",
+ "recvmsg",
+ "uname",
+ "setrlimit",
+ "getrlimit",
+ "setsockopt",
+ "getsockopt",
+ "getsockname",
+ "lstat",
+ "getgid",
+ "getegid",
+ "getuid",
+ "geteuid",
+ "setresgid",
+ "setresuid",
+ "setgid",
+ "setuid",
+ "prctl",
+ "epoll_wait",
+ "getdents",
+ "utimes",
+ "dup",
+#endif
+};
+#endif /* __x86_64__ */
+#ifdef __i386__
+int scmp_syscalls[] = {
+ SCMP_SYS(access),
+ SCMP_SYS(open),
+ SCMP_SYS(clock_gettime),
+ SCMP_SYS(time),
+ SCMP_SYS(read),
+ SCMP_SYS(write),
+ SCMP_SYS(close),
+ SCMP_SYS(brk),
+ SCMP_SYS(poll),
+ SCMP_SYS(_newselect),
+ SCMP_SYS(select),
+ SCMP_SYS(madvise),
+ SCMP_SYS(mmap2),
+ SCMP_SYS(mmap),
+ SCMP_SYS(munmap),
+ SCMP_SYS(exit_group),
+ SCMP_SYS(rt_sigprocmask),
+ SCMP_SYS(sigprocmask),
+ SCMP_SYS(rt_sigaction),
+ SCMP_SYS(socketcall),
+ SCMP_SYS(fsync),
+ SCMP_SYS(sigreturn),
+ SCMP_SYS(setsid),
+ SCMP_SYS(chdir),
+ SCMP_SYS(futex),
+ SCMP_SYS(stat64),
+ SCMP_SYS(rt_sigsuspend),
+ SCMP_SYS(fstat64),
+ SCMP_SYS(epoll_ctl),
+ SCMP_SYS(gettimeofday),
+ SCMP_SYS(getpid),
+#ifdef HAVE_GETRANDOM
+ SCMP_SYS(getrandom),
+#endif
+ SCMP_SYS(unlink),
+#ifndef ISC_PLATFORM_USETHREADS
+ SCMP_SYS(fcntl64),
+#endif
+};
+const char *scmp_syscall_names[] = {
+ "access",
+ "open",
+ "clock_gettime",
+ "time",
+ "read",
+ "write",
+ "close",
+ "brk",
+ "poll",
+ "_newselect",
+ "select",
+ "madvise",
+ "mmap2",
+ "mmap",
+ "munmap",
+ "exit_group",
+ "rt_sigprocmask",
+ "sigprocmask",
+ "rt_sigaction",
+ "socketcall",
+ "fsync",
+ "sigreturn",
+ "setsid",
+ "chdir",
+ "futex",
+ "stat64",
+ "rt_sigsuspend",
+ "fstat64",
+ "epoll_ctl",
+ "gettimeofday",
+ "getpid",
+#ifdef HAVE_GETRANDOM
+ "getrandom",
+#endif
+ "unlink",
+#ifndef ISC_PLATFORM_USETHREADS
+ "fcntl64",
+#endif
+};
+#endif /* __i386__ */
+#endif /* HAVE_LIBSECCOMP */
+
+#endif /* NAMED_SECCOMP_H */
diff --git a/bin/named/include/named/server.h b/bin/named/include/named/server.h
new file mode 100644
index 0000000..f5ed2b7
--- /dev/null
+++ b/bin/named/include/named/server.h
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef NAMED_SERVER_H
+#define NAMED_SERVER_H 1
+
+/*! \file */
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#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/dnstap.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)
+#define NS_EVENT_DELZONE (NS_EVENTCLASS + 2)
+
+/*%
+ * 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;
+ dns_acl_t *keepresporder;
+ char * statsfile; /*%< Statistics file name */
+ char * dumpfile; /*%< Dump file name */
+ char * secrootsfile; /*%< Secroots file name */
+ char * bindkeysfile; /*%< bind.keys file name */
+ char * recfile; /*%< Recursive file name */
+ bool version_set; /*%< User has set version */
+ char * version; /*%< User-specified version */
+ bool hostname_set; /*%< User has set hostname */
+ char * hostname; /*%< User-specified hostname */
+ /*% Use hostname for server id */
+ bool 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_timer_t * tat_timer;
+
+ uint32_t interface_interval;
+ uint32_t heartbeat_interval;
+
+ isc_mutex_t reload_event_lock;
+ isc_event_t * reload_event;
+
+ bool flushonshutdown;
+ bool log_queries; /*%< For BIND 8 compatibility */
+
+ ns_cachelist_t cachelist; /*%< Possibly shared caches */
+ isc_stats_t * nsstats; /*%< Server stats */
+ dns_stats_t * rcvquerystats; /*% Incoming query stats */
+ dns_stats_t * opcodestats; /*%< Incoming message stats */
+ isc_stats_t * zonestats; /*% Zone management stats */
+ isc_stats_t * resolverstats; /*% Resolver stats */
+ isc_stats_t * sockstats; /*%< Socket stats */
+ isc_stats_t * udpinstats4; /*%< Traffic size: UDPv4 in */
+ isc_stats_t * udpoutstats4; /*%< Traffic size: UDPv4 out */
+ isc_stats_t * udpinstats6; /*%< Traffic size: UDPv6 in */
+ isc_stats_t * udpoutstats6; /*%< Traffic size: UDPv6 out */
+ isc_stats_t * tcpinstats4; /*%< Traffic size: TCPv4 in */
+ isc_stats_t * tcpoutstats4; /*%< Traffic size: TCPv4 out */
+ isc_stats_t * tcpinstats6; /*%< Traffic size: TCPv6 in */
+ isc_stats_t * tcpoutstats6; /*%< Traffic size: TCPv6 out */
+ dns_stats_t * rcodestats; /*%< Sent Response code stats */
+
+ ns_controls_t * controls; /*%< Control channels */
+ unsigned int dispatchgen;
+ ns_dispatchlist_t dispatches;
+
+ dns_acache_t *acache;
+
+ ns_statschannellist_t statschannels;
+
+ dns_tsigkey_t *sessionkey;
+ char *session_keyfile;
+ dns_name_t *session_keyname;
+ unsigned int session_keyalg;
+ uint16_t session_keybits;
+ bool interface_auto;
+ unsigned char secret[32]; /*%< Server Cookie Secret */
+ ns_altsecretlist_t altsecrets;
+ ns_cookiealg_t cookiealg;
+ bool answercookie;
+
+ dns_dtenv_t *dtenv; /*%< Dnstap environment */
+
+ char * lockfile;
+
+ uint16_t transfer_tcp_message_size;
+};
+
+struct ns_altsecret {
+ ISC_LINK(ns_altsecret_t) link;
+ unsigned char secret[32];
+};
+
+#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 isc_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_requesttcp = 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_recursclients = 36,
+
+ dns_nsstatscounter_dns64 = 37,
+
+ dns_nsstatscounter_ratedropped = 38,
+ dns_nsstatscounter_rateslipped = 39,
+
+ dns_nsstatscounter_rpz_rewrites = 40,
+
+ dns_nsstatscounter_udp = 41,
+ dns_nsstatscounter_tcp = 42,
+
+ dns_nsstatscounter_nsidopt = 43,
+ dns_nsstatscounter_expireopt = 44,
+ dns_nsstatscounter_otheropt = 45,
+ dns_nsstatscounter_ecsopt = 46,
+
+ dns_nsstatscounter_nxdomainredirect = 47,
+ dns_nsstatscounter_nxdomainredirect_rlookup = 48,
+
+ dns_nsstatscounter_cookiein = 49,
+ dns_nsstatscounter_cookiebadsize = 50,
+ dns_nsstatscounter_cookiebadtime = 51,
+ dns_nsstatscounter_cookienomatch = 52,
+ dns_nsstatscounter_cookiematch = 53,
+ dns_nsstatscounter_cookienew = 54,
+ dns_nsstatscounter_badcookie = 55,
+
+ dns_nsstatscounter_keytagopt = 56,
+
+ dns_nsstatscounter_max = 57
+};
+
+/*%
+ * Traffic size statistics counters. Used as isc_statscounter_t values.
+ */
+enum {
+ dns_sizecounter_in_0 = 0,
+ dns_sizecounter_in_16 = 1,
+ dns_sizecounter_in_32 = 2,
+ dns_sizecounter_in_48 = 3,
+ dns_sizecounter_in_64 = 4,
+ dns_sizecounter_in_80 = 5,
+ dns_sizecounter_in_96 = 6,
+ dns_sizecounter_in_112 = 7,
+ dns_sizecounter_in_128 = 8,
+ dns_sizecounter_in_144 = 9,
+ dns_sizecounter_in_160 = 10,
+ dns_sizecounter_in_176 = 11,
+ dns_sizecounter_in_192 = 12,
+ dns_sizecounter_in_208 = 13,
+ dns_sizecounter_in_224 = 14,
+ dns_sizecounter_in_240 = 15,
+ dns_sizecounter_in_256 = 16,
+ dns_sizecounter_in_272 = 17,
+ dns_sizecounter_in_288 = 18,
+
+ dns_sizecounter_in_max = 19,
+};
+
+enum {
+ dns_sizecounter_out_0 = 0,
+ dns_sizecounter_out_16 = 1,
+ dns_sizecounter_out_32 = 2,
+ dns_sizecounter_out_48 = 3,
+ dns_sizecounter_out_64 = 4,
+ dns_sizecounter_out_80 = 5,
+ dns_sizecounter_out_96 = 6,
+ dns_sizecounter_out_112 = 7,
+ dns_sizecounter_out_128 = 8,
+ dns_sizecounter_out_144 = 9,
+ dns_sizecounter_out_160 = 10,
+ dns_sizecounter_out_176 = 11,
+ dns_sizecounter_out_192 = 12,
+ dns_sizecounter_out_208 = 13,
+ dns_sizecounter_out_224 = 14,
+ dns_sizecounter_out_240 = 15,
+ dns_sizecounter_out_256 = 16,
+ dns_sizecounter_out_272 = 17,
+ dns_sizecounter_out_288 = 18,
+ dns_sizecounter_out_304 = 19,
+ dns_sizecounter_out_320 = 20,
+ dns_sizecounter_out_336 = 21,
+ dns_sizecounter_out_352 = 22,
+ dns_sizecounter_out_368 = 23,
+ dns_sizecounter_out_384 = 24,
+ dns_sizecounter_out_400 = 25,
+ dns_sizecounter_out_416 = 26,
+ dns_sizecounter_out_432 = 27,
+ dns_sizecounter_out_448 = 28,
+ dns_sizecounter_out_464 = 29,
+ dns_sizecounter_out_480 = 30,
+ dns_sizecounter_out_496 = 31,
+ dns_sizecounter_out_512 = 32,
+ dns_sizecounter_out_528 = 33,
+ dns_sizecounter_out_544 = 34,
+ dns_sizecounter_out_560 = 35,
+ dns_sizecounter_out_576 = 36,
+ dns_sizecounter_out_592 = 37,
+ dns_sizecounter_out_608 = 38,
+ dns_sizecounter_out_624 = 39,
+ dns_sizecounter_out_640 = 40,
+ dns_sizecounter_out_656 = 41,
+ dns_sizecounter_out_672 = 42,
+ dns_sizecounter_out_688 = 43,
+ dns_sizecounter_out_704 = 44,
+ dns_sizecounter_out_720 = 45,
+ dns_sizecounter_out_736 = 46,
+ dns_sizecounter_out_752 = 47,
+ dns_sizecounter_out_768 = 48,
+ dns_sizecounter_out_784 = 49,
+ dns_sizecounter_out_800 = 50,
+ dns_sizecounter_out_816 = 51,
+ dns_sizecounter_out_832 = 52,
+ dns_sizecounter_out_848 = 53,
+ dns_sizecounter_out_864 = 54,
+ dns_sizecounter_out_880 = 55,
+ dns_sizecounter_out_896 = 56,
+ dns_sizecounter_out_912 = 57,
+ dns_sizecounter_out_928 = 58,
+ dns_sizecounter_out_944 = 59,
+ dns_sizecounter_out_960 = 60,
+ dns_sizecounter_out_976 = 61,
+ dns_sizecounter_out_992 = 62,
+ dns_sizecounter_out_1008 = 63,
+ dns_sizecounter_out_1024 = 64,
+ dns_sizecounter_out_1040 = 65,
+ dns_sizecounter_out_1056 = 66,
+ dns_sizecounter_out_1072 = 67,
+ dns_sizecounter_out_1088 = 68,
+ dns_sizecounter_out_1104 = 69,
+ dns_sizecounter_out_1120 = 70,
+ dns_sizecounter_out_1136 = 71,
+ dns_sizecounter_out_1152 = 72,
+ dns_sizecounter_out_1168 = 73,
+ dns_sizecounter_out_1184 = 74,
+ dns_sizecounter_out_1200 = 75,
+ dns_sizecounter_out_1216 = 76,
+ dns_sizecounter_out_1232 = 77,
+ dns_sizecounter_out_1248 = 78,
+ dns_sizecounter_out_1264 = 79,
+ dns_sizecounter_out_1280 = 80,
+ dns_sizecounter_out_1296 = 81,
+ dns_sizecounter_out_1312 = 82,
+ dns_sizecounter_out_1328 = 83,
+ dns_sizecounter_out_1344 = 84,
+ dns_sizecounter_out_1360 = 85,
+ dns_sizecounter_out_1376 = 86,
+ dns_sizecounter_out_1392 = 87,
+ dns_sizecounter_out_1408 = 88,
+ dns_sizecounter_out_1424 = 89,
+ dns_sizecounter_out_1440 = 90,
+ dns_sizecounter_out_1456 = 91,
+ dns_sizecounter_out_1472 = 92,
+ dns_sizecounter_out_1488 = 93,
+ dns_sizecounter_out_1504 = 94,
+ dns_sizecounter_out_1520 = 95,
+ dns_sizecounter_out_1536 = 96,
+ dns_sizecounter_out_1552 = 97,
+ dns_sizecounter_out_1568 = 98,
+ dns_sizecounter_out_1584 = 99,
+ dns_sizecounter_out_1600 = 100,
+ dns_sizecounter_out_1616 = 101,
+ dns_sizecounter_out_1632 = 102,
+ dns_sizecounter_out_1648 = 103,
+ dns_sizecounter_out_1664 = 104,
+ dns_sizecounter_out_1680 = 105,
+ dns_sizecounter_out_1696 = 106,
+ dns_sizecounter_out_1712 = 107,
+ dns_sizecounter_out_1728 = 108,
+ dns_sizecounter_out_1744 = 109,
+ dns_sizecounter_out_1760 = 110,
+ dns_sizecounter_out_1776 = 111,
+ dns_sizecounter_out_1792 = 112,
+ dns_sizecounter_out_1808 = 113,
+ dns_sizecounter_out_1824 = 114,
+ dns_sizecounter_out_1840 = 115,
+ dns_sizecounter_out_1856 = 116,
+ dns_sizecounter_out_1872 = 117,
+ dns_sizecounter_out_1888 = 118,
+ dns_sizecounter_out_1904 = 119,
+ dns_sizecounter_out_1920 = 120,
+ dns_sizecounter_out_1936 = 121,
+ dns_sizecounter_out_1952 = 122,
+ dns_sizecounter_out_1968 = 123,
+ dns_sizecounter_out_1984 = 124,
+ dns_sizecounter_out_2000 = 125,
+ dns_sizecounter_out_2016 = 126,
+ dns_sizecounter_out_2032 = 127,
+ dns_sizecounter_out_2048 = 128,
+ dns_sizecounter_out_2064 = 129,
+ dns_sizecounter_out_2080 = 130,
+ dns_sizecounter_out_2096 = 131,
+ dns_sizecounter_out_2112 = 132,
+ dns_sizecounter_out_2128 = 133,
+ dns_sizecounter_out_2144 = 134,
+ dns_sizecounter_out_2160 = 135,
+ dns_sizecounter_out_2176 = 136,
+ dns_sizecounter_out_2192 = 137,
+ dns_sizecounter_out_2208 = 138,
+ dns_sizecounter_out_2224 = 139,
+ dns_sizecounter_out_2240 = 140,
+ dns_sizecounter_out_2256 = 141,
+ dns_sizecounter_out_2272 = 142,
+ dns_sizecounter_out_2288 = 143,
+ dns_sizecounter_out_2304 = 144,
+ dns_sizecounter_out_2320 = 145,
+ dns_sizecounter_out_2336 = 146,
+ dns_sizecounter_out_2352 = 147,
+ dns_sizecounter_out_2368 = 148,
+ dns_sizecounter_out_2384 = 149,
+ dns_sizecounter_out_2400 = 150,
+ dns_sizecounter_out_2416 = 151,
+ dns_sizecounter_out_2432 = 152,
+ dns_sizecounter_out_2448 = 153,
+ dns_sizecounter_out_2464 = 154,
+ dns_sizecounter_out_2480 = 155,
+ dns_sizecounter_out_2496 = 156,
+ dns_sizecounter_out_2512 = 157,
+ dns_sizecounter_out_2528 = 158,
+ dns_sizecounter_out_2544 = 159,
+ dns_sizecounter_out_2560 = 160,
+ dns_sizecounter_out_2576 = 161,
+ dns_sizecounter_out_2592 = 162,
+ dns_sizecounter_out_2608 = 163,
+ dns_sizecounter_out_2624 = 164,
+ dns_sizecounter_out_2640 = 165,
+ dns_sizecounter_out_2656 = 166,
+ dns_sizecounter_out_2672 = 167,
+ dns_sizecounter_out_2688 = 168,
+ dns_sizecounter_out_2704 = 169,
+ dns_sizecounter_out_2720 = 170,
+ dns_sizecounter_out_2736 = 171,
+ dns_sizecounter_out_2752 = 172,
+ dns_sizecounter_out_2768 = 173,
+ dns_sizecounter_out_2784 = 174,
+ dns_sizecounter_out_2800 = 175,
+ dns_sizecounter_out_2816 = 176,
+ dns_sizecounter_out_2832 = 177,
+ dns_sizecounter_out_2848 = 178,
+ dns_sizecounter_out_2864 = 179,
+ dns_sizecounter_out_2880 = 180,
+ dns_sizecounter_out_2896 = 181,
+ dns_sizecounter_out_2912 = 182,
+ dns_sizecounter_out_2928 = 183,
+ dns_sizecounter_out_2944 = 184,
+ dns_sizecounter_out_2960 = 185,
+ dns_sizecounter_out_2976 = 186,
+ dns_sizecounter_out_2992 = 187,
+ dns_sizecounter_out_3008 = 188,
+ dns_sizecounter_out_3024 = 189,
+ dns_sizecounter_out_3040 = 190,
+ dns_sizecounter_out_3056 = 191,
+ dns_sizecounter_out_3072 = 192,
+ dns_sizecounter_out_3088 = 193,
+ dns_sizecounter_out_3104 = 194,
+ dns_sizecounter_out_3120 = 195,
+ dns_sizecounter_out_3136 = 196,
+ dns_sizecounter_out_3152 = 197,
+ dns_sizecounter_out_3168 = 198,
+ dns_sizecounter_out_3184 = 199,
+ dns_sizecounter_out_3200 = 200,
+ dns_sizecounter_out_3216 = 201,
+ dns_sizecounter_out_3232 = 202,
+ dns_sizecounter_out_3248 = 203,
+ dns_sizecounter_out_3264 = 204,
+ dns_sizecounter_out_3280 = 205,
+ dns_sizecounter_out_3296 = 206,
+ dns_sizecounter_out_3312 = 207,
+ dns_sizecounter_out_3328 = 208,
+ dns_sizecounter_out_3344 = 209,
+ dns_sizecounter_out_3360 = 210,
+ dns_sizecounter_out_3376 = 211,
+ dns_sizecounter_out_3392 = 212,
+ dns_sizecounter_out_3408 = 213,
+ dns_sizecounter_out_3424 = 214,
+ dns_sizecounter_out_3440 = 215,
+ dns_sizecounter_out_3456 = 216,
+ dns_sizecounter_out_3472 = 217,
+ dns_sizecounter_out_3488 = 218,
+ dns_sizecounter_out_3504 = 219,
+ dns_sizecounter_out_3520 = 220,
+ dns_sizecounter_out_3536 = 221,
+ dns_sizecounter_out_3552 = 222,
+ dns_sizecounter_out_3568 = 223,
+ dns_sizecounter_out_3584 = 224,
+ dns_sizecounter_out_3600 = 225,
+ dns_sizecounter_out_3616 = 226,
+ dns_sizecounter_out_3632 = 227,
+ dns_sizecounter_out_3648 = 228,
+ dns_sizecounter_out_3664 = 229,
+ dns_sizecounter_out_3680 = 230,
+ dns_sizecounter_out_3696 = 231,
+ dns_sizecounter_out_3712 = 232,
+ dns_sizecounter_out_3728 = 233,
+ dns_sizecounter_out_3744 = 234,
+ dns_sizecounter_out_3760 = 235,
+ dns_sizecounter_out_3776 = 236,
+ dns_sizecounter_out_3792 = 237,
+ dns_sizecounter_out_3808 = 238,
+ dns_sizecounter_out_3824 = 239,
+ dns_sizecounter_out_3840 = 240,
+ dns_sizecounter_out_3856 = 241,
+ dns_sizecounter_out_3872 = 242,
+ dns_sizecounter_out_3888 = 243,
+ dns_sizecounter_out_3904 = 244,
+ dns_sizecounter_out_3920 = 245,
+ dns_sizecounter_out_3936 = 246,
+ dns_sizecounter_out_3952 = 247,
+ dns_sizecounter_out_3968 = 248,
+ dns_sizecounter_out_3984 = 249,
+ dns_sizecounter_out_4000 = 250,
+ dns_sizecounter_out_4016 = 251,
+ dns_sizecounter_out_4032 = 252,
+ dns_sizecounter_out_4048 = 253,
+ dns_sizecounter_out_4064 = 254,
+ dns_sizecounter_out_4080 = 255,
+ dns_sizecounter_out_4096 = 256,
+
+ dns_sizecounter_out_max = 257
+};
+
+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_scan_interfaces(ns_server_t *server);
+/*%<
+ * Trigger a interface scan.
+ * Must only be called when running under server->task.
+ */
+
+void
+ns_server_flushonshutdown(ns_server_t *server, bool flush);
+/*%<
+ * Inform the server that the zones should be flushed to disk on shutdown.
+ */
+
+isc_result_t
+ns_server_reloadcommand(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+/*%<
+ * Act on a "reload" command from the command channel.
+ */
+
+isc_result_t
+ns_server_reconfigcommand(ns_server_t *server);
+/*%<
+ * Act on a "reconfig" command from the command channel.
+ */
+
+isc_result_t
+ns_server_notifycommand(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+/*%<
+ * Act on a "notify" command from the command channel.
+ */
+
+isc_result_t
+ns_server_refreshcommand(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+/*%<
+ * Act on a "refresh" command from the command channel.
+ */
+
+isc_result_t
+ns_server_retransfercommand(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+/*%<
+ * Act on a "retransfer" command from the command channel.
+ */
+
+isc_result_t
+ns_server_togglequerylog(ns_server_t *server, isc_lex_t *lex);
+/*%<
+ * Enable/disable logging of queries. (Takes "yes" or "no" argument,
+ * but can also be used as a toggle for backward comptibility.)
+ */
+
+/*%
+ * Save the current NTAs for all views to files.
+ */
+isc_result_t
+ns_server_saventa(ns_server_t *server);
+
+/*%
+ * Load NTAs for all views from files.
+ */
+isc_result_t
+ns_server_loadnta(ns_server_t *server);
+
+/*%
+ * 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, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Dump the current security roots to the secroots file.
+ */
+isc_result_t
+ns_server_dumpsecroots(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Change or increment the server debug level.
+ */
+isc_result_t
+ns_server_setdebuglevel(ns_server_t *server, isc_lex_t *lex);
+
+/*%
+ * Flush the server's cache(s)
+ */
+isc_result_t
+ns_server_flushcache(ns_server_t *server, isc_lex_t *lex);
+
+/*%
+ * Flush a particular name from the server's cache. If 'tree' is false,
+ * also flush the name from the ADB and badcache. If 'tree' is true, also
+ * flush all the names under the specified name.
+ */
+isc_result_t
+ns_server_flushnode(ns_server_t *server, isc_lex_t *lex,
+ bool tree);
+
+/*%
+ * 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, isc_lex_t *lex,
+ isc_buffer_t **text);
+
+/*%
+ * Enable or disable updates for a zone.
+ */
+isc_result_t
+ns_server_freeze(ns_server_t *server, bool freeze,
+ isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Dump zone updates to disk, optionally removing the journal file
+ */
+isc_result_t
+ns_server_sync(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Update a zone's DNSKEY set from the key repository. If
+ * the command that triggered the call to this function was "sign",
+ * then force a full signing of the zone. If it was "loadkeys",
+ * then don't sign the zone; any needed changes to signatures can
+ * take place incrementally.
+ */
+isc_result_t
+ns_server_rekey(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * 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, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Add a zone to a running process, or modify an existing zone
+ */
+isc_result_t
+ns_server_changezone(ns_server_t *server, char *command, isc_buffer_t **text);
+
+/*%
+ * Deletes a zone from a running process
+ */
+isc_result_t
+ns_server_delzone(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Show current configuration for a given zone
+ */
+isc_result_t
+ns_server_showzone(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Lists the status of the signing records for a given zone.
+ */
+isc_result_t
+ns_server_signing(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Lists status information for a given zone (e.g., name, type, files,
+ * load time, expiry, etc).
+ */
+isc_result_t
+ns_server_zonestatus(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Adds/updates a Negative Trust Anchor (NTA) for a specified name and
+ * duration, in a particular view if specified, or in all views.
+ */
+isc_result_t
+ns_server_nta(ns_server_t *server, isc_lex_t *lex, bool readonly,
+ isc_buffer_t **text);
+
+/*%
+ * Generates a test sequence that is only for use in system tests. The
+ * argument is the size of required output in bytes.
+ */
+isc_result_t
+ns_server_testgen(isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Force fefresh or print status for managed keys zones.
+ */
+isc_result_t
+ns_server_mkeys(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+/*%
+ * Close and reopen DNSTAP output file.
+ */
+isc_result_t
+ns_server_dnstap(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text);
+
+#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..15bf2a6
--- /dev/null
+++ b/bin/named/include/named/sortlist.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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..95f71b3
--- /dev/null
+++ b/bin/named/include/named/statschannel.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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..5370913
--- /dev/null
+++ b/bin/named/include/named/tkeyconf.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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..0bdd02b
--- /dev/null
+++ b/bin/named/include/named/tsigconf.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: tsigconf.h,v 1.18 2009/06/11 23:47:55 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 'vconfig' is not NULL.
+ * \li 'mctx' is not NULL
+ * \li 'ringp' is not NULL, and '*ringp' 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..486ec31
--- /dev/null
+++ b/bin/named/include/named/types.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef NAMED_TYPES_H
+#define NAMED_TYPES_H 1
+
+/*! \file */
+
+#include <dns/types.h>
+
+typedef struct ns_cache ns_cache_t;
+typedef ISC_LIST(ns_cache_t) ns_cachelist_t;
+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;
+typedef struct ns_altsecret ns_altsecret_t;
+typedef ISC_LIST(ns_altsecret_t) ns_altsecretlist_t;
+
+typedef enum {
+ ns_cookiealg_aes,
+ ns_cookiealg_sha1,
+ ns_cookiealg_sha256
+} ns_cookiealg_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..3ee6623
--- /dev/null
+++ b/bin/named/include/named/update.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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..41c7f27
--- /dev/null
+++ b/bin/named/include/named/xfrout.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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..5e016f7
--- /dev/null
+++ b/bin/named/include/named/zoneconf.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: zoneconf.h,v 1.30 2011/08/30 23:46:51 tbox Exp $ */
+
+#ifndef NS_ZONECONF_H
+#define NS_ZONECONF_H 1
+
+/*! \file */
+
+#include <stdbool.h>
+#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, dns_zone_t *raw);
+/*%<
+ * 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.
+ */
+
+bool
+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 true. If the configuration data is so
+ * different from the current zone state that the zone needs to be destroyed
+ * and recreated, return false.
+ */
+
+isc_result_t
+ns_zone_configure_writeable_dlz(dns_dlzdb_t *dlzdatabase, dns_zone_t *zone,
+ dns_rdataclass_t rdclass, dns_name_t *name);
+/*%>
+ * configure a DLZ zone, setting up the database methods and calling
+ * postload to load the origin values
+ *
+ * Require:
+ * \li 'dlzdatabase' to be a valid dlz database
+ * \li 'zone' to be initialized.
+ * \li 'rdclass' to be a valid rdataclass
+ * \li 'name' to be a valid zone origin name
+ */
+
+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..419927b
--- /dev/null
+++ b/bin/named/interfacemgr.c
@@ -0,0 +1,1246 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.h>
+
+#include <isc/interfaceiter.h>
+#include <isc/os.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>
+#include <named/server.h>
+
+#ifdef HAVE_NET_ROUTE_H
+#include <net/route.h>
+#if defined(RTM_VERSION) && defined(RTM_NEWADDR) && defined(RTM_DELADDR)
+#define USE_ROUTE_SOCKET 1
+#define ROUTE_SOCKET_PROTOCOL PF_ROUTE
+#define MSGHDR rt_msghdr
+#define MSGTYPE rtm_type
+#endif
+#endif
+
+#if defined(HAVE_LINUX_NETLINK_H) && defined(HAVE_LINUX_RTNETLINK_H)
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#if defined(RTM_NEWADDR) && defined(RTM_DELADDR)
+#define USE_ROUTE_SOCKET 1
+#define ROUTE_SOCKET_PROTOCOL PF_NETLINK
+#define MSGHDR nlmsghdr
+#define MSGTYPE nlmsg_type
+#endif
+#endif
+
+#ifdef TUNE_LARGE
+#define UDPBUFFERS 32768
+#else
+#define UDPBUFFERS 1000
+#endif /* TUNE_LARGE */
+
+#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;
+#ifdef USE_ROUTE_SOCKET
+ isc_task_t * task;
+ isc_socket_t * route;
+ unsigned char buf[2048];
+#endif
+};
+
+static void
+purge_old_interfaces(ns_interfacemgr_t *mgr);
+
+static void
+clearlistenon(ns_interfacemgr_t *mgr);
+
+#ifdef USE_ROUTE_SOCKET
+static void
+route_event(isc_task_t *task, isc_event_t *event) {
+ isc_socketevent_t *sevent = NULL;
+ ns_interfacemgr_t *mgr = NULL;
+ isc_region_t r;
+ isc_result_t result;
+ struct MSGHDR *rtm;
+ bool done = true;
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_type == ISC_SOCKEVENT_RECVDONE);
+ mgr = event->ev_arg;
+ sevent = (isc_socketevent_t *)event;
+
+ if (sevent->result != ISC_R_SUCCESS) {
+ if (sevent->result != ISC_R_CANCELED)
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "automatic interface scanning "
+ "terminated: %s",
+ isc_result_totext(sevent->result));
+ ns_interfacemgr_detach(&mgr);
+ isc_event_free(&event);
+ return;
+ }
+
+ rtm = (struct MSGHDR *)mgr->buf;
+#ifdef RTM_VERSION
+ if (rtm->rtm_version != RTM_VERSION) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_ERROR,
+ "automatic interface rescanning disabled: "
+ "rtm->rtm_version mismatch (%u != %u) "
+ "recompile required", rtm->rtm_version,
+ RTM_VERSION);
+ ns_interfacemgr_detach(&mgr);
+ isc_event_free(&event);
+ return;
+ }
+#endif
+
+ switch (rtm->MSGTYPE) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ if (mgr->route != NULL && ns_g_server->interface_auto)
+ ns_server_scan_interfaces(ns_g_server);
+ break;
+ default:
+ break;
+ }
+
+ LOCK(&mgr->lock);
+ if (mgr->route != NULL) {
+ /*
+ * Look for next route event.
+ */
+ r.base = mgr->buf;
+ r.length = sizeof(mgr->buf);
+ result = isc_socket_recv(mgr->route, &r, 1, mgr->task,
+ route_event, mgr);
+ if (result == ISC_R_SUCCESS)
+ done = false;
+ }
+ UNLOCK(&mgr->lock);
+
+ if (done)
+ ns_interfacemgr_detach(&mgr);
+ isc_event_free(&event);
+ return;
+}
+#endif
+
+isc_result_t
+ns_interfacemgr_create(isc_mem_t *mctx, isc_taskmgr_t *taskmgr,
+ isc_socketmgr_t *socketmgr,
+ dns_dispatchmgr_t *dispatchmgr,
+ isc_task_t *task, ns_interfacemgr_t **mgrp)
+{
+ isc_result_t result;
+ ns_interfacemgr_t *mgr;
+
+#ifndef USE_ROUTE_SOCKET
+ UNUSED(task);
+#endif
+
+ REQUIRE(mctx != NULL);
+ REQUIRE(mgrp != NULL);
+ REQUIRE(*mgrp == NULL);
+
+ mgr = isc_mem_get(mctx, sizeof(*mgr));
+ if (mgr == NULL)
+ return (ISC_R_NOMEMORY);
+
+ mgr->mctx = NULL;
+ isc_mem_attach(mctx, &mgr->mctx);
+
+ result = isc_mutex_init(&mgr->lock);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_mem;
+
+ 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;
+#ifdef HAVE_GEOIP
+ mgr->aclenv.geoip = ns_g_geoip;
+#endif
+
+#ifdef USE_ROUTE_SOCKET
+ mgr->route = NULL;
+ result = isc_socket_create(mgr->socketmgr, ROUTE_SOCKET_PROTOCOL,
+ isc_sockettype_raw, &mgr->route);
+ switch (result) {
+ case ISC_R_NOPERM:
+ case ISC_R_SUCCESS:
+ case ISC_R_NOTIMPLEMENTED:
+ case ISC_R_FAMILYNOSUPPORT:
+ break;
+ default:
+ goto cleanup_aclenv;
+ }
+
+ mgr->task = NULL;
+ if (mgr->route != NULL)
+ isc_task_attach(task, &mgr->task);
+ mgr->references = (mgr->route != NULL) ? 2 : 1;
+#else
+ mgr->references = 1;
+#endif
+ mgr->magic = IFMGR_MAGIC;
+ *mgrp = mgr;
+
+#ifdef USE_ROUTE_SOCKET
+ if (mgr->route != NULL) {
+ isc_region_t r = { mgr->buf, sizeof(mgr->buf) };
+
+ result = isc_socket_recv(mgr->route, &r, 1, mgr->task,
+ route_event, mgr);
+ if (result != ISC_R_SUCCESS) {
+ isc_task_detach(&mgr->task);
+ isc_socket_detach(&mgr->route);
+ ns_interfacemgr_detach(&mgr);
+ }
+ }
+#endif
+ return (ISC_R_SUCCESS);
+
+#ifdef USE_ROUTE_SOCKET
+ cleanup_aclenv:
+ dns_aclenv_destroy(&mgr->aclenv);
+#endif
+ cleanup_listenon:
+ ns_listenlist_detach(&mgr->listenon4);
+ ns_listenlist_detach(&mgr->listenon6);
+ cleanup_mem:
+ isc_mem_putanddetach(&mgr->mctx, mgr, sizeof(*mgr));
+ return (result);
+}
+
+static void
+ns_interfacemgr_destroy(ns_interfacemgr_t *mgr) {
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+
+#ifdef USE_ROUTE_SOCKET
+ if (mgr->route != NULL)
+ isc_socket_detach(&mgr->route);
+ if (mgr->task != NULL)
+ isc_task_detach(&mgr->task);
+#endif
+ 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_putanddetach(&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 = 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 = 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++;
+#ifdef USE_ROUTE_SOCKET
+ LOCK(&mgr->lock);
+ if (mgr->route != NULL) {
+ isc_socket_cancel(mgr->route, mgr->task, ISC_SOCKCANCEL_RECV);
+ isc_socket_detach(&mgr->route);
+ isc_task_detach(&mgr->task);
+ }
+ UNLOCK(&mgr->lock);
+#endif
+ 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;
+ int disp;
+
+ 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;
+ strlcpy(ifp->name, name, sizeof(ifp->name));
+ 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;
+ }
+
+ for (disp = 0; disp < MAX_UDP_DISPATCH; disp++)
+ ifp->udpdispatch[disp] = 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;
+ ifp->nudpdispatch = 0;
+
+ ifp->dscp = -1;
+
+ 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;
+ int disp, i;
+
+ 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;
+
+ ifp->nudpdispatch = ISC_MIN(ns_g_udpdisp, MAX_UDP_DISPATCH);
+ for (disp = 0; disp < ifp->nudpdispatch; disp++) {
+ result = dns_dispatch_getudp_dup(ifp->mgr->dispatchmgr,
+ ns_g_socketmgr,
+ ns_g_taskmgr, &ifp->addr,
+ 4096, UDPBUFFERS,
+ 32768, 8219, 8237,
+ attrs, attrmask,
+ &ifp->udpdispatch[disp],
+ disp == 0
+ ? NULL
+ : ifp->udpdispatch[0]);
+ 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, ifp->nudpdispatch,
+ ifp, 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:
+ for (i = disp - 1; i >= 0; i--) {
+ dns_dispatch_changeattributes(ifp->udpdispatch[i], 0,
+ DNS_DISPATCHATTR_NOLISTEN);
+ dns_dispatch_detach(&(ifp->udpdispatch[i]));
+ }
+ ifp->nudpdispatch = 0;
+
+ 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, 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;
+ }
+
+ if (ifp->dscp != -1)
+ isc_socket_dscp(ifp->tcpsocket, ifp->dscp);
+
+ 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,
+ 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 (result);
+}
+
+static isc_result_t
+ns_interface_setup(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr,
+ const char *name, ns_interface_t **ifpret,
+ bool accept_tcp, isc_dscp_t dscp,
+ bool *addr_in_use)
+{
+ isc_result_t result;
+ ns_interface_t *ifp = NULL;
+ REQUIRE(ifpret != NULL && *ifpret == NULL);
+ REQUIRE(addr_in_use == NULL || *addr_in_use == false);
+
+ result = ns_interface_create(mgr, addr, name, &ifp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ ifp->dscp = dscp;
+
+ result = ns_interface_listenudp(ifp);
+ if (result != ISC_R_SUCCESS) {
+ if ((result == ISC_R_ADDRINUSE) && (addr_in_use != NULL))
+ *addr_in_use = true;
+ goto cleanup_interface;
+ }
+
+ if (!ns_g_notcp && accept_tcp == true) {
+ result = ns_interface_accepttcp(ifp);
+ if (result != ISC_R_SUCCESS) {
+ if ((result == ISC_R_ADDRINUSE) &&
+ (addr_in_use != NULL))
+ *addr_in_use = true;
+
+ /*
+ * 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 (result);
+
+ 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;
+ int disp;
+
+ REQUIRE(NS_INTERFACE_VALID(ifp));
+
+ ns_interface_shutdown(ifp);
+
+ for (disp = 0; disp < ifp->nudpdispatch; disp++)
+ if (ifp->udpdispatch[disp] != NULL) {
+ dns_dispatch_changeattributes(ifp->udpdispatch[disp], 0,
+ DNS_DISPATCHATTR_NOLISTEN);
+ dns_dispatch_detach(&(ifp->udpdispatch[disp]));
+ }
+
+ 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 = 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 = 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 bool
+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, true);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* Then add localnets prefix */
+ result = isc_netaddr_masktoprefixlen(&interface->netmask,
+ &prefixlen);
+
+ /* Non contiguous 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);
+ }
+
+ if (prefixlen == 0U) {
+ isc_log_write(IFMGR_COMMON_LOGARGS, ISC_LOG_WARNING,
+ "omitting %s interface %s from localnets ACL: "
+ "zero prefix length detected",
+ (netaddr->family == AF_INET) ? "IPv4" : "IPv6",
+ interface->name);
+ return (ISC_R_SUCCESS);
+ }
+
+ result = dns_iptable_addprefix(mgr->aclenv.localnets->iptable,
+ netaddr, prefixlen, 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,
+ bool verbose)
+{
+ isc_interfaceiter_t *iter = NULL;
+ bool scan_ipv4 = false;
+ bool scan_ipv6 = false;
+ bool adjusting = false;
+ bool ipv6only = true;
+ bool ipv6pktinfo = 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;
+ bool log_explicit = false;
+ bool dolistenon;
+ char sabuf[ISC_SOCKADDR_FORMATSIZE];
+ bool tried_listening;
+ bool all_addresses_in_use;
+
+ if (ext_listen != NULL)
+ adjusting = true;
+
+ if (isc_net_probeipv6() == ISC_R_SUCCESS)
+ scan_ipv6 = true;
+#ifdef WANT_IPV6
+ else if (!ns_g_disable6)
+ 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 = true;
+ else if (!ns_g_disable4)
+ 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 == true &&
+ isc_net_probe_ipv6only() != ISC_R_SUCCESS) {
+ ipv6only = false;
+ log_explicit = true;
+ }
+#endif
+ if (scan_ipv6 == true &&
+ isc_net_probe_ipv6pktinfo() != ISC_R_SUCCESS) {
+ ipv6pktinfo = false;
+ log_explicit = true;
+ }
+ if (scan_ipv6 == 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;
+ if (le->dscp != -1 && ifp->dscp == -1)
+ ifp->dscp = le->dscp;
+ else if (le->dscp != ifp->dscp) {
+ isc_sockaddr_format(&listen_addr,
+ sabuf,
+ sizeof(sabuf));
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_WARNING,
+ "%s: conflicting DSCP "
+ "values, using %d",
+ sabuf, ifp->dscp);
+ }
+ } 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,
+ true,
+ le->dscp,
+ NULL);
+ 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 == 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);
+ }
+
+ tried_listening = false;
+ all_addresses_in_use = true;
+ 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 == false && family == AF_INET)
+ continue;
+ if (scan_ipv6 == 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 == false) {
+ /*
+ * If running with -T fixedlocal, then we only
+ * want 127.0.0.1 and ::1 in the localhost ACL.
+ */
+ if (ns_g_fixedlocal &&
+ !isc_netaddr_isloopback(&interface.address))
+ {
+ goto listenon;
+ }
+
+ result = setup_locals(mgr, &interface);
+ if (result != ISC_R_SUCCESS)
+ goto ignore_interface;
+ }
+
+ listenon:
+ ll = (family == AF_INET) ? mgr->listenon4 : mgr->listenon6;
+ dolistenon = true;
+ for (le = ISC_LIST_HEAD(ll->elts);
+ le != NULL;
+ le = ISC_LIST_NEXT(le, link))
+ {
+ int match;
+ bool ipv6_wildcard = 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 == false && dolistenon == true) {
+ setup_listenon(mgr, &interface, le->port);
+ dolistenon = 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 = 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 == 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 == true && match == 0)
+ continue;
+ }
+
+ ifp = find_matching_interface(mgr, &listen_sockaddr);
+ if (ifp != NULL) {
+ ifp->generation = mgr->generation;
+ if (le->dscp != -1 && ifp->dscp == -1)
+ ifp->dscp = le->dscp;
+ else if (le->dscp != ifp->dscp) {
+ isc_sockaddr_format(&listen_sockaddr,
+ sabuf,
+ sizeof(sabuf));
+ isc_log_write(IFMGR_COMMON_LOGARGS,
+ ISC_LOG_WARNING,
+ "%s: conflicting DSCP "
+ "values, using %d",
+ sabuf, ifp->dscp);
+ }
+ } else {
+ bool addr_in_use = false;
+
+ if (adjusting == false &&
+ ipv6_wildcard == 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 = 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 == true) ?
+ "additionally " : "",
+ (family == AF_INET) ?
+ "IPv4" : "IPv6",
+ interface.name, sabuf);
+
+ result = ns_interface_setup(mgr,
+ &listen_sockaddr,
+ interface.name,
+ &ifp,
+ (adjusting == true) ?
+ false : true,
+ le->dscp,
+ &addr_in_use);
+
+ tried_listening = true;
+ if (!addr_in_use)
+ all_addresses_in_use = false;
+
+ 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 = ((tried_listening && all_addresses_in_use) ?
+ ISC_R_ADDRINUSE : ISC_R_SUCCESS);
+ cleanup_iter:
+ isc_interfaceiter_destroy(&iter);
+ return (result);
+}
+
+static isc_result_t
+ns_interfacemgr_scan0(ns_interfacemgr_t *mgr, ns_listenlist_t *ext_listen,
+ bool verbose)
+{
+ isc_result_t result;
+ bool purge = true;
+
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+
+ mgr->generation++; /* Increment the generation count. */
+
+ result = do_scan(mgr, ext_listen, verbose);
+ if ((result != ISC_R_SUCCESS) && (result != ISC_R_ADDRINUSE))
+ purge = 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");
+ }
+
+ return (result);
+}
+
+bool
+ns_interfacemgr_islistening(ns_interfacemgr_t *mgr) {
+ REQUIRE(NS_INTERFACEMGR_VALID(mgr));
+
+ return (ISC_LIST_EMPTY(mgr->interfaces) ? false : true);
+}
+
+isc_result_t
+ns_interfacemgr_scan(ns_interfacemgr_t *mgr, bool verbose) {
+ return (ns_interfacemgr_scan0(mgr, NULL, verbose));
+}
+
+isc_result_t
+ns_interfacemgr_adjust(ns_interfacemgr_t *mgr, ns_listenlist_t *list,
+ bool verbose)
+{
+ return (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);
+}
+
+bool
+ns_interfacemgr_listeningon(ns_interfacemgr_t *mgr, isc_sockaddr_t *addr) {
+ isc_sockaddr_t *old;
+
+ for (old = ISC_LIST_HEAD(mgr->listenon);
+ old != NULL;
+ old = ISC_LIST_NEXT(old, link))
+ if (isc_sockaddr_equal(old, addr))
+ return (true);
+ return (false);
+}
diff --git a/bin/named/listenlist.c b/bin/named/listenlist.c
new file mode 100644
index 0000000..234ea75
--- /dev/null
+++ b/bin/named/listenlist.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: listenlist.c,v 1.14 2007/06/19 23:46:59 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.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, isc_dscp_t dscp,
+ 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->dscp = dscp;
+ 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_dscp_t dscp,
+ bool 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, dscp, 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..3aa25e9
--- /dev/null
+++ b/bin/named/log.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/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 },
+ { "query-errors", 0 },
+ { "trust-anchor-telemetry", 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(bool 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 -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 (ns_g_logfile != NULL) {
+ destination.file.stream = NULL;
+ destination.file.name = ns_g_logfile;
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.maximum_size = 0;
+ result = isc_log_createchannel(lcfg, "default_logfile",
+ ISC_LOG_TOFILE,
+ ISC_LOG_DYNAMIC,
+ &destination,
+ ISC_LOG_PRINTTIME|
+ ISC_LOG_PRINTCATEGORY|
+ ISC_LOG_PRINTLEVEL);
+ 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;
+ isc_logdestination_t destination;
+
+ 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 (ns_g_logfile != NULL) {
+ destination.file.stream = NULL;
+ destination.file.name = ns_g_logfile;
+ destination.file.versions = ISC_LOG_ROLLNEVER;
+ destination.file.maximum_size = 0;
+ result = isc_log_createchannel(lcfg, "default_logfile",
+ ISC_LOG_TOFILE,
+ ISC_LOG_DYNAMIC,
+ &destination,
+ ISC_LOG_PRINTTIME|
+ ISC_LOG_PRINTCATEGORY|
+ ISC_LOG_PRINTLEVEL);
+ 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
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+ns_log_setdefaultcategory(isc_logconfig_t *lcfg) {
+ isc_result_t result = ISC_R_SUCCESS;
+
+ result = isc_log_usechannel(lcfg, "default_debug",
+ ISC_LOGCATEGORY_DEFAULT, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ if (! ns_g_logstderr) {
+ if (ns_g_logfile != NULL)
+ result = isc_log_usechannel(lcfg, "default_logfile",
+ ISC_LOGCATEGORY_DEFAULT,
+ NULL);
+ else if (! ns_g_nosyslog)
+ result = isc_log_usechannel(lcfg, "default_syslog",
+ ISC_LOGCATEGORY_DEFAULT,
+ NULL);
+ }
+
+ 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..e1cd762
--- /dev/null
+++ b/bin/named/logconf.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: logconf.c,v 1.45 2011/03/05 23:52:29 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/file.h>
+#include <isc/offset.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/syslog.h>
+#include <isc/util.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 'logconfig'.
+ */
+static isc_result_t
+category_fromconf(const cfg_obj_t *ccat, isc_logconfig_t *logconfig) {
+ 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);
+ }
+
+ if (logconfig == NULL)
+ 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(logconfig, 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 'logconfig'.
+ */
+static isc_result_t
+channel_fromconf(const cfg_obj_t *channel, isc_logconfig_t *logconfig)
+{
+ 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");
+ int32_t versions = ISC_LOG_ROLLNEVER;
+ isc_offset_t size = 0;
+ uint64_t maxoffset;
+
+ /*
+ * isc_offset_t is a signed integer type, so the maximum
+ * value is all 1s except for the MSB.
+ */
+ switch (sizeof(isc_offset_t)) {
+ case 4:
+ maxoffset = 0x7fffffffULL;
+ break;
+ case 8:
+ maxoffset = 0x7fffffffffffffffULL;
+ break;
+ default:
+ INSIST(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) < maxoffset)
+ 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;
+ const cfg_obj_t *buffered = 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);
+ (void)cfg_map_get(channel, "buffered", &buffered);
+
+ 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;
+ if (buffered != NULL && cfg_obj_asboolean(buffered))
+ flags |= ISC_LOG_BUFFERED;
+ }
+
+ 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);
+ }
+
+ if (logconfig == NULL)
+ result = ISC_R_SUCCESS;
+ else
+ result = isc_log_createchannel(logconfig, channelname,
+ type, level, &dest, flags);
+
+ if (result == ISC_R_SUCCESS && type == ISC_LOG_TOFILE) {
+ FILE *fp;
+
+ /*
+ * Test to make sure that file is a plain file.
+ * Fix defect #22771
+ */
+ result = isc_file_isplainfile(dest.file.name);
+ if (result == ISC_R_SUCCESS || result == ISC_R_FILENOTFOUND) {
+ /*
+ * 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) {
+ if (logconfig != NULL && !ns_g_nosyslog)
+ syslog(LOG_ERR,
+ "isc_stdio_open '%s' failed: "
+ "%s", dest.file.name,
+ isc_result_totext(result));
+ fprintf(stderr,
+ "isc_stdio_open '%s' failed: %s\n",
+ dest.file.name,
+ isc_result_totext(result));
+ } else
+ (void)isc_stdio_close(fp);
+ goto done;
+ }
+ if (logconfig != NULL && !ns_g_nosyslog)
+ syslog(LOG_ERR, "isc_file_isplainfile '%s' failed: %s",
+ dest.file.name, isc_result_totext(result));
+ fprintf(stderr, "isc_file_isplainfile '%s' failed: %s\n",
+ dest.file.name, isc_result_totext(result));
+ }
+
+ done:
+ return (result);
+}
+
+isc_result_t
+ns_log_configure(isc_logconfig_t *logconfig, 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;
+ bool default_set = false;
+ bool unmatched_set = false;
+ const cfg_obj_t *catname;
+
+ if (logconfig != NULL)
+ CHECK(ns_log_setdefaultchannels(logconfig));
+
+ (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, logconfig));
+ }
+
+ (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, logconfig));
+ if (!default_set) {
+ catname = cfg_tuple_get(category, "name");
+ if (strcmp(cfg_obj_asstring(catname), "default") == 0)
+ default_set = true;
+ }
+ if (!unmatched_set) {
+ catname = cfg_tuple_get(category, "name");
+ if (strcmp(cfg_obj_asstring(catname), "unmatched") == 0)
+ unmatched_set = true;
+ }
+ }
+
+ if (logconfig != NULL && !default_set)
+ CHECK(ns_log_setdefaultcategory(logconfig));
+
+ if (logconfig != NULL && !unmatched_set)
+ CHECK(ns_log_setunmatchedcategory(logconfig));
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ return (result);
+}
diff --git a/bin/named/lwaddr.c b/bin/named/lwaddr.c
new file mode 100644
index 0000000..9a58ee4
--- /dev/null
+++ b/bin/named/lwaddr.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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;
+ memmove(&ina.s_addr, la->address, 4);
+ isc_netaddr_fromin(na, &ina);
+ } else {
+ struct in6_addr ina6;
+ memmove(&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;
+ memmove(la->address, &na->type.in, 4);
+ } else {
+ la->family = LWRES_ADDRTYPE_V6;
+ la->length = 16;
+ memmove(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..d34f831
--- /dev/null
+++ b/bin/named/lwdclient.c
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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;
+
+ cm = isc_mem_get(lwresd->mctx, sizeof(ns_lwdclientmgr_t));
+ if (cm == NULL)
+ return (ISC_R_NOMEMORY);
+
+ result = isc_mutex_init(&cm->lock);
+ if (result != ISC_R_SUCCESS)
+ goto freecm;
+
+ 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);
+
+ result = lwres_context_create(&cm->lwctx, cm->mctx,
+ ns__lwresd_memalloc, ns__lwresd_memfree,
+ LWRES_CONTEXT_SERVERMODE);
+ if (result != 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)) {
+ result = ISC_R_NOMEMORY;
+ 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);
+
+ DESTROYLOCK(&cm->lock);
+
+ freecm:
+ 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;
+
+ LOCK(&cm->lock);
+ if (!SHUTTINGDOWN(cm)) {
+ UNLOCK(&cm->lock);
+ 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)) {
+ UNLOCK(&cm->lock);
+ return;
+ }
+
+ UNLOCK(&cm->lock);
+
+ lwres_context_destroy(&cm->lwctx);
+ cm->view = NULL;
+ isc_socket_detach(&cm->sock);
+ isc_task_detach(&cm->task);
+
+ DESTROYLOCK(&cm->lock);
+
+ 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);
+
+ LOCK(&cm->lock);
+ INSIST((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0);
+ cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
+ UNLOCK(&cm->lock);
+
+ 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 = true;
+ } else
+ client->pktinfo_valid = 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;
+ bool destroy = false;
+
+
+ LOCK(&cm->lock);
+ if (SHUTTINGDOWN(cm)) {
+ destroy = true;
+ result = ISC_R_SUCCESS;
+ goto unlock;
+ }
+
+ /*
+ * If a recv is already running, don't bother.
+ */
+ if ((cm->flags & NS_LWDCLIENTMGR_FLAGRECVPENDING) != 0) {
+ result = ISC_R_SUCCESS;
+ goto unlock;
+ }
+
+ /*
+ * If we have no idle slots, just return success.
+ */
+ client = ISC_LIST_HEAD(cm->idle);
+ if (client == NULL) {
+ result = ISC_R_SUCCESS;
+ goto unlock;
+ }
+
+ INSIST(NS_LWDCLIENT_ISIDLE(client));
+
+ /*
+ * Set the flag to say there is a recv pending. If isc_socket_recv
+ * fails we will clear the flag otherwise it will be cleared by
+ * ns_lwdclient_recv.
+ */
+ cm->flags |= NS_LWDCLIENTMGR_FLAGRECVPENDING;
+
+ /*
+ * 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) {
+ cm->flags &= ~NS_LWDCLIENTMGR_FLAGRECVPENDING;
+ goto unlock;
+ }
+
+ /*
+ * 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);
+
+ unlock:
+ UNLOCK(&cm->lock);
+
+ if (destroy)
+ lwdclientmgr_destroy(cm);
+
+ return (result);
+}
+
+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.
+ */
+ LOCK(&cm->lock);
+ 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);
+ }
+ UNLOCK(&cm->lock);
+
+ /*
+ * 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.
+ */
+ LOCK(&cm->lock);
+ 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;
+
+ UNLOCK(&cm->lock);
+
+ 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);
+
+ LOCK(&cm->lock);
+ ISC_LIST_UNLINK(cm->running, client, link);
+ ISC_LIST_PREPEND(cm->idle, client, link);
+ UNLOCK(&cm->lock);
+
+ 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 = false;
+
+ LOCK(&cmgr->lock);
+ ISC_LIST_APPEND(cmgr->idle, client, link);
+ UNLOCK(&cmgr->lock);
+}
diff --git a/bin/named/lwderror.c b/bin/named/lwderror.c
new file mode 100644
index 0000000..32ed2d8
--- /dev/null
+++ b/bin/named/lwderror.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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, 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..01faa26
--- /dev/null
+++ b/bin/named/lwdgabn.c
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: lwdgabn.c,v 1.24 2009/09/02 23:48:01 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;
+ uint16_t naliases;
+
+ b = client->recv_buffer;
+
+ /*
+ * Render the new name to the buffer.
+ */
+ result = dns_name_totext(dns_fixedname_name(&client->target_name),
+ 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, 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;
+ bool 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 = false;
+ if (evtype == DNS_EVENT_ADBNOMOREADDRESSES) {
+ if (NEED_V4(client)) {
+ client->v4find = client->find;
+ claimed = true;
+ }
+ if (NEED_V6(client)) {
+ client->v6find = client->find;
+ claimed = 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;
+ bool 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 = 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 = 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 = 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, 0, 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..9e600f5
--- /dev/null
+++ b/bin/named/lwdgnba.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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;
+ 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, 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;
+ memmove(&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;
+ memmove(&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..407267d
--- /dev/null
+++ b/bin/named/lwdgrbn.c
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: lwdgrbn.c,v 1.22 2009/09/02 23:48:01 tbox Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.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, 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;
+ uint16_t *lens = NULL, *oldlens = NULL, *newlens = NULL;
+ dns_rdatasetiter_t *iter = NULL;
+ dns_rdataset_t set;
+ dns_ttl_t ttl = INT32_MAX;
+ 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;
+ memmove(rdatas, oldrdatas, used * sizeof(*rdatas));
+ memmove(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;
+ memmove(newrdatas, rdatas, used * sizeof(*rdatas));
+ memmove(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(*newrdatas));
+ 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;
+
+ REQUIRE(event != NULL);
+
+ 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, 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(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(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");
+ }
+
+ /*
+ * 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(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(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);
+
+ dns_lookup_destroy(&client->lookup);
+ isc_event_free(&event);
+
+ 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(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(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);
+
+ 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);
+
+ /*
+ * Perform search across all search domains until success
+ * is returned. Return in case of failure.
+ */
+ while (ns_lwsearchctx_current(&client->searchctx,
+ dns_fixedname_name(&absname)) != ISC_R_SUCCESS) {
+ if (ns_lwsearchctx_next(&client->searchctx) != ISC_R_SUCCESS) {
+ ns_lwdclient_errorpktsend(client, LWRES_R_FAILURE);
+ return;
+ }
+ }
+
+ 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, 0, 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..75769c9
--- /dev/null
+++ b/bin/named/lwdnoop.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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..e0eb0d0
--- /dev/null
+++ b/bin/named/lwresd.8
@@ -0,0 +1,250 @@
+.\" Copyright (C) 2000, 2001, 2004, 2005, 2007-2009, 2014-2019 Internet Systems Consortium, Inc. ("ISC")
+.\"
+.\" This Source Code Form is subject to the terms of the Mozilla Public
+.\" License, v. 2.0. If a copy of the MPL was not distributed with this
+.\" file, You can obtain one at http://mozilla.org/MPL/2.0/.
+.\"
+.hy 0
+.ad l
+'\" t
+.\" Title: lwresd
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
+.\" Date: 2009-01-20
+.\" Manual: BIND9
+.\" Source: ISC
+.\" Language: English
+.\"
+.TH "LWRESD" "8" "2009\-01\-20" "ISC" "BIND9"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+lwresd \- lightweight resolver daemon
+.SH "SYNOPSIS"
+.HP \w'\fBlwresd\fR\ 'u
+\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
+/etc/resolv\&.conf
+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,
+/etc/lwresd\&.conf\&.
+\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,
+/etc/resolv\&.conf\&.
+\fB\-C\fR
+can not be used with
+\fB\-c\fR\&.
+.RE
+.PP
+\-d \fIdebug\-level\fR
+.RS 4
+Set the daemon\*(Aqs 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
+stderr\&.
+.RE
+.PP
+\-i \fIpid\-file\fR
+.RS 4
+Use
+\fIpid\-file\fR
+as the PID file instead of the default,
+/var/run/lwresd/lwresd\&.pid\&.
+.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
+<isc/mem\&.h>\&.
+.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
+stdout
+on exit\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+This option is mainly of interest to BIND 9 developers and may be removed or changed in a future release\&.
+.sp .5v
+.RE
+.RE
+.PP
+\-t \fIdirectory\fR
+.RS 4
+Chroot to
+\fIdirectory\fR
+after processing the command line arguments, but before reading the configuration file\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBWarning\fR
+.ps -1
+.br
+This option should be used in conjunction with the
+\fB\-u\fR
+option, as chrooting a process running as root doesn\*(Aqt enhance security on most systems; the way
+\fBchroot(2)\fR
+is defined allows a process with root privileges to escape a chroot jail\&.
+.sp .5v
+.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
+/etc/resolv\&.conf
+.RS 4
+The default configuration file\&.
+.RE
+.PP
+/var/run/lwresd\&.pid
+.RS 4
+The default process\-id file\&.
+.RE
+.SH "SEE ALSO"
+.PP
+\fBnamed\fR(8),
+\fBlwres\fR(3),
+\fBresolver\fR(5)\&.
+.SH "AUTHOR"
+.PP
+\fBInternet Systems Consortium, Inc\&.\fR
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2000, 2001, 2004, 2005, 2007-2009, 2014-2019 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/bin/named/lwresd.c b/bin/named/lwresd.c
new file mode 100644
index 0000000..cdf11f5
--- /dev/null
+++ b/bin/named/lwresd.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: lwresd.c,v 1.60 2009/09/02 23:48:01 tbox 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)
+
+#define LWRESD_NCLIENTS_MAX 32768 /*%< 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 = 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_constinit(&namebuf, searchstr,
+ strlen(searchstr));
+ isc_buffer_add(&namebuf, strlen(searchstr));
+ result = dns_name_fromtext(name, &namebuf,
+ dns_rootname, 0, 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;
+ }
+ }
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(lwres, "lwres-tasks", &obj);
+ if (obj != NULL)
+ lwresd->ntasks = cfg_obj_asuint32(obj);
+ else
+ lwresd->ntasks = ns_g_cpus;
+
+ if (lwresd->ntasks == 0)
+ lwresd->ntasks = 1;
+
+ obj = NULL;
+ (void)cfg_map_get(lwres, "lwres-clients", &obj);
+ if (obj != NULL) {
+ lwresd->nclients = cfg_obj_asuint32(obj);
+ if (lwresd->nclients > LWRESD_NCLIENTS_MAX)
+ lwresd->nclients = LWRESD_NCLIENTS_MAX;
+ } else if (ns_g_lwresdonly)
+ lwresd->nclients = 1024;
+ else
+ lwresd->nclients = 256;
+
+ 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;
+ bool done = 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 = 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;
+ bool done = 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 = 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, *next;
+ unsigned int i;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_LWRESD, ISC_LOG_DEBUG(6),
+ "listener_startclients: creating %d "
+ "managers with %d clients each",
+ listener->manager->ntasks, listener->manager->nclients);
+
+ /*
+ * Create the client managers.
+ */
+ for (i = 0; i < listener->manager->ntasks; i++) {
+ result = ns_lwdclientmgr_create(listener,
+ listener->manager->nclients,
+ ns_g_taskmgr);
+ if (result != ISC_R_SUCCESS)
+ break;
+ }
+
+ /*
+ * If the list is empty return now with the previous
+ * ns_lwdclientmgr_create() result.
+ */
+ 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) {
+ next = ISC_LIST_NEXT(cm, link);
+ 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 = next;
+ }
+ 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;
+ 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 {
+ uint32_t i;
+
+ CHECK(ns_config_getiplist(config, listenerslist,
+ port, mctx, &addrs, NULL,
+ &count));
+ for (i = 0; i < count; i++)
+ CHECK(configure_listener(&addrs[i], lwresd,
+ mctx, &newlisteners));
+ ns_config_putiplist(mctx, &addrs, NULL, 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, NULL, 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..f267f0d
--- /dev/null
+++ b/bin/named/lwresd.docbook
@@ -0,0 +1,364 @@
+<!--
+ - Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ -
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ -
+ - See the COPYRIGHT file distributed with this work for additional
+ - information regarding copyright ownership.
+-->
+
+<!-- Converted by db4-upgrade version 1.0 -->
+<refentry xmlns:db="http://docbook.org/ns/docbook" version="5.0" xml:id="man.lwresd">
+ <info>
+ <date>2009-01-20</date>
+ </info>
+ <refentryinfo>
+ <corpname>ISC</corpname>
+ <corpauthor>Internet Systems Consortium, Inc.</corpauthor>
+ </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>2000</year>
+ <year>2001</year>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2007</year>
+ <year>2008</year>
+ <year>2009</year>
+ <year>2014</year>
+ <year>2015</year>
+ <year>2016</year>
+ <year>2017</year>
+ <year>2018</year>
+ <year>2019</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis sepchar=" ">
+ <command>lwresd</command>
+ <arg choice="opt" rep="norepeat"><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-C <replaceable class="parameter">config-file</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-d <replaceable class="parameter">debug-level</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-f</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-g</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-i <replaceable class="parameter">pid-file</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-m <replaceable class="parameter">flag</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-n <replaceable class="parameter">#cpus</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-P <replaceable class="parameter">port</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-p <replaceable class="parameter">port</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-s</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-t <replaceable class="parameter">directory</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-u <replaceable class="parameter">user</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-v</option></arg>
+ <group choice="opt" rep="norepeat">
+ <arg choice="opt" rep="norepeat"><option>-4</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-6</option></arg>
+ </group>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsection><info><title>DESCRIPTION</title></info>
+
+
+ <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>
+ </refsection>
+
+ <refsection><info><title>OPTIONS</title></info>
+
+
+ <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>
+
+ </refsection>
+
+ <refsection><info><title>FILES</title></info>
+
+
+ <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>
+
+ </refsection>
+
+ <refsection><info><title>SEE ALSO</title></info>
+
+ <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>
+ </refsection>
+
+</refentry>
diff --git a/bin/named/lwresd.html b/bin/named/lwresd.html
new file mode 100644
index 0000000..128e89f
--- /dev/null
+++ b/bin/named/lwresd.html
@@ -0,0 +1,295 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!--
+ - Copyright (C) 2000, 2001, 2004, 2005, 2007-2009, 2014-2019 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+<html lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>lwresd</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.78.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry">
+<a name="man.lwresd"></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="refsection">
+<a name="id-1.7"></a><h2>DESCRIPTION</h2>
+
+
+ <p><span class="command"><strong>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 class="command"><strong>lwresd</strong></span>
+ listens for resolver queries on a
+ UDP port on the IPv4 loopback interface, 127.0.0.1. This
+ means that <span class="command"><strong>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 class="command"><strong>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 class="command"><strong>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 class="command"><strong>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="refsection">
+<a name="id-1.8"></a><h2>OPTIONS</h2>
+
+
+ <div class="variablelist"><dl class="variablelist">
+<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 class="command"><strong>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 class="command"><strong>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="refsection">
+<a name="id-1.9"></a><h2>FILES</h2>
+
+
+ <div class="variablelist"><dl class="variablelist">
+<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="refsection">
+<a name="id-1.10"></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></body>
+</html>
diff --git a/bin/named/lwsearch.c b/bin/named/lwsearch.c
new file mode 100644
index 0000000..cd068bd
--- /dev/null
+++ b/bin/named/lwsearch.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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 = false;
+ sctx->exactfirst = 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 = 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) {
+ if (sctx->exactfirst || sctx->doneexact)
+ return (ISC_R_NOMORE);
+ sctx->doneexact = true;
+ } else {
+ if (sctx->exactfirst && !sctx->doneexact)
+ sctx->doneexact = 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;
+ bool useexact = false;
+
+ REQUIRE(sctx != NULL);
+
+ if (sctx->list == NULL ||
+ sctx->searchname == NULL ||
+ (sctx->exactfirst && !sctx->doneexact))
+ useexact = 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..8cec1ad
--- /dev/null
+++ b/bin/named/main.c
@@ -0,0 +1,1543 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <isc/app.h>
+#include <isc/backtrace.h>
+#include <isc/commandline.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/httpd.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/dyndb.h>
+#include <dns/name.h>
+#include <dns/result.h>
+#include <dns/resolver.h>
+#include <dns/view.h>
+
+#include <dst/result.h>
+#ifdef PKCS11CRYPTO
+#include <pk11/result.h>
+#endif
+
+#include <dlz/dlz_dlopen_driver.h>
+
+#ifdef HAVE_GPERFTOOLS_PROFILER
+#include <gperftools/profiler.h>
+#endif
+
+
+/*
+ * 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/fuzz.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>
+#include <named/seccomp.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#endif
+
+#ifdef OPENSSL
+#include <openssl/opensslv.h>
+#include <openssl/crypto.h>
+#endif
+#ifdef HAVE_LIBXML2
+#include <libxml/xmlversion.h>
+#endif
+#ifdef HAVE_ZLIB
+#include <zlib.h>
+#endif
+/*
+ * Include header files for database drivers here.
+ */
+/* #include "xxdb.h" */
+
+#ifdef CONTRIB_DLZ
+/*
+ * Include contributed DLZ drivers if appropriate.
+ */
+#include <dlz/dlz_drivers.h>
+#endif
+
+/*
+ * The maximum number of stack frames to dump on assertion failure.
+ */
+#ifndef BACKTRACE_MAXFRAME
+#define BACKTRACE_MAXFRAME 128
+#endif
+
+LIBISC_EXTERNAL_DATA extern int isc_dscp_check_value;
+LIBDNS_EXTERNAL_DATA extern unsigned int dns_zone_mkey_hour;
+LIBDNS_EXTERNAL_DATA extern unsigned int dns_zone_mkey_day;
+LIBDNS_EXTERNAL_DATA extern unsigned int dns_zone_mkey_month;
+
+static bool want_stats = 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;
+static int maxudp = 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);
+}
+
+ISC_PLATFORM_NORETURN_PRE static void
+assertion_failed(const char *file, int line, isc_assertiontype_t type,
+ const char *cond) ISC_PLATFORM_NORETURN_POST;
+
+static void
+assertion_failed(const char *file, int line, isc_assertiontype_t type,
+ const char *cond)
+{
+ void *tracebuf[BACKTRACE_MAXFRAME];
+ int i, nframes;
+ isc_result_t result;
+ const char *logsuffix = "";
+ const char *fname;
+
+ /*
+ * Handle assertion failures.
+ */
+
+ if (ns_g_lctx != NULL) {
+ /*
+ * Reset the assertion callback in case it is the log
+ * routines causing the assertion.
+ */
+ isc_assertion_setcallback(NULL);
+
+ result = isc_backtrace_gettrace(tracebuf, BACKTRACE_MAXFRAME,
+ &nframes);
+ if (result == ISC_R_SUCCESS && nframes > 0)
+ logsuffix = ", back trace";
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_CRITICAL,
+ "%s:%d: %s(%s) failed%s", file, line,
+ isc_assertion_typetotext(type), cond, logsuffix);
+ if (result == ISC_R_SUCCESS) {
+ for (i = 0; i < nframes; i++) {
+ unsigned long offset;
+
+ fname = NULL;
+ result = isc_backtrace_getsymbol(tracebuf[i],
+ &fname,
+ &offset);
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN,
+ ISC_LOG_CRITICAL,
+ "#%d %p in %s()+0x%lx", i,
+ tracebuf[i], fname,
+ offset);
+ } else {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN,
+ ISC_LOG_CRITICAL,
+ "#%d %p in ??", i,
+ tracebuf[i]);
+ }
+ }
+ }
+ 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);
+}
+
+ISC_PLATFORM_NORETURN_PRE static void
+library_fatal_error(const char *file, int line, const char *format,
+ va_list args)
+ISC_FORMAT_PRINTF(3, 0) ISC_PLATFORM_NORETURN_POST;
+
+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] [-f|-g]\n"
+ " [-i pidfile] [-n number_of_cpus] "
+ "[-p port] [-P listen-port]\n"
+ " [-s] [-S sockets] [-t chrootdir] [-u username] "
+ "[-U listeners]\n"
+ " [-m {usage|trace|record|size|mctx}]\n"
+ "usage: lwresd [-v|-V]\n");
+}
+
+static void
+usage(void) {
+ if (ns_g_lwresdonly) {
+ lwresd_usage();
+ return;
+ }
+ fprintf(stderr,
+ "usage: named [-4|-6] [-c conffile] [-d debuglevel] "
+ "[-E engine] [-f|-g]\n"
+ " [-n number_of_cpus] [-p port] [-s] "
+ "[-S sockets] [-t chrootdir]\n"
+ " [-u username] [-U listeners] "
+ "[-m {usage|trace|record|size|mctx}]\n"
+ "usage: named [-v|-V]\n");
+}
+
+static void
+save_command_line(int argc, char *argv[]) {
+ int i;
+ char *src;
+ char *dst;
+ char *eob;
+ const char truncated[] = "...";
+ bool quoted = 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 = false;
+ } else {
+ *dst++ = '\\';
+ quoted = 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[] = {
+ { "none", 0},
+ { "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) {
+ bool clear = false;
+
+ for (;;) {
+ const struct flag_def *def;
+ const char *end = strchr(arg, ',');
+ int arglen;
+ if (end == NULL)
+ end = arg + strlen(arg);
+ arglen = (int)(end - arg);
+ for (def = defs; def->name != NULL; def++) {
+ if (arglen == (int)strlen(def->name) &&
+ memcmp(arg, def->name, arglen) == 0) {
+ if (def->value == 0)
+ clear = true;
+ *ret |= def->value;
+ goto found;
+ }
+ }
+ ns_main_earlyfatal("unrecognized flag '%.*s'", arglen, arg);
+ found:
+ if (clear || (*end == '\0'))
+ break;
+ arg = end + 1;
+ }
+
+ if (clear)
+ *ret = 0;
+}
+
+static void
+parse_fuzz_arg(void) {
+ if (!strncmp(isc_commandline_argument, "client:", 7)) {
+ ns_g_fuzz_named_addr = isc_commandline_argument + 7;
+ ns_g_fuzz_type = ns_fuzz_client;
+ } else if (!strncmp(isc_commandline_argument, "tcp:", 4)) {
+ ns_g_fuzz_named_addr = isc_commandline_argument + 4;
+ ns_g_fuzz_type = ns_fuzz_tcpclient;
+ } else if (!strncmp(isc_commandline_argument, "resolver:", 9)) {
+ ns_g_fuzz_named_addr = isc_commandline_argument + 9;
+ ns_g_fuzz_type = ns_fuzz_resolver;
+ } else if (!strncmp(isc_commandline_argument, "http:", 5)) {
+ ns_g_fuzz_named_addr = isc_commandline_argument + 5;
+ ns_g_fuzz_type = ns_fuzz_http;
+ } else if (!strncmp(isc_commandline_argument, "rndc:", 5)) {
+ ns_g_fuzz_named_addr = isc_commandline_argument + 5;
+ ns_g_fuzz_type = ns_fuzz_rndc;
+ } else {
+ ns_main_earlyfatal("unknown fuzzing type '%s'",
+ isc_commandline_argument);
+ }
+}
+
+static void
+parse_T_opt(char *option) {
+ const char *p;
+ /*
+ * force the server to behave (or misbehave) in
+ * specified ways for testing purposes.
+ *
+ * clienttest: make clients single shot with their
+ * own memory context.
+ * delay=xxxx: delay client responses by xxxx ms to
+ * simulate remote servers.
+ * dscp=x: check that dscp values are as
+ * expected and assert otherwise.
+ */
+ if (!strcmp(option, "clienttest")) {
+ ns_g_clienttest = true;
+ } else if (!strncmp(option, "delay=", 6)) {
+ ns_g_delay = atoi(option + 6);
+ } else if (!strcmp(option, "dropedns")) {
+ ns_g_dropedns = true;
+ } else if (!strncmp(option, "dscp=", 5)) {
+ isc_dscp_check_value = atoi(option + 5);
+ } else if (!strcmp(option, "fixedlocal")) {
+ ns_g_fixedlocal = true;
+ } else if (!strcmp(option, "keepstderr")) {
+ ns_g_keepstderr = true;
+ } else if (!strcmp(option, "noaa")) {
+ ns_g_noaa = true;
+ } else if (!strcmp(option, "noedns")) {
+ ns_g_noedns = true;
+ } else if (!strcmp(option, "nonearest")) {
+ ns_g_nonearest = true;
+ } else if (!strcmp(option, "nosoa")) {
+ ns_g_nosoa = true;
+ } else if (!strcmp(option, "nosyslog")) {
+ ns_g_nosyslog = true;
+ } else if (!strcmp(option, "notcp")) {
+ ns_g_notcp = true;
+ } else if (!strcmp(option, "maxudp512")) {
+ maxudp = 512;
+ } else if (!strcmp(option, "maxudp1460")) {
+ maxudp = 1460;
+ } else if (!strncmp(option, "maxudp=", 7)) {
+ maxudp = atoi(option + 7);
+ } else if (!strncmp(option, "mkeytimers=", 11)) {
+ p = strtok(option + 11, "/");
+ if (p == NULL) {
+ ns_main_earlyfatal("bad mkeytimer");
+ }
+
+ dns_zone_mkey_hour = atoi(p);
+ if (dns_zone_mkey_hour == 0) {
+ ns_main_earlyfatal("bad mkeytimer");
+ }
+
+ p = strtok(NULL, "/");
+ if (p == NULL) {
+ dns_zone_mkey_day = (24 * dns_zone_mkey_hour);
+ dns_zone_mkey_month = (30 * dns_zone_mkey_day);
+ return;
+ }
+
+ dns_zone_mkey_day = atoi(p);
+ if (dns_zone_mkey_day < dns_zone_mkey_hour)
+ ns_main_earlyfatal("bad mkeytimer");
+
+ p = strtok(NULL, "/");
+ if (p == NULL) {
+ dns_zone_mkey_month = (30 * dns_zone_mkey_day);
+ return;
+ }
+
+ dns_zone_mkey_month = atoi(p);
+ if (dns_zone_mkey_month < dns_zone_mkey_day) {
+ ns_main_earlyfatal("bad mkeytimer");
+ }
+ } else if (!strcmp(option, "sigvalinsecs")) {
+ ns_g_sigvalinsecs = true;
+ } else if (!strncmp(option, "tat=", 4)) {
+ ns_g_tat_interval = atoi(option + 4);
+ } else {
+ fprintf(stderr, "unknown -T flag '%s\n", option);
+ }
+}
+
+static void
+parse_command_line(int argc, char *argv[]) {
+ int ch;
+ int port;
+ const char *p;
+
+ save_command_line(argc, argv);
+
+ /*
+ * NS_MAIN_ARGS is defined in main.h, so that it can be used
+ * both by named and by ntservice hooks.
+ */
+ isc_commandline_errprint = false;
+ while ((ch = isc_commandline_parse(argc, argv, NS_MAIN_ARGS)) != -1) {
+ switch (ch) {
+ case '4':
+ if (ns_g_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();
+ ns_g_disable6 = true;
+ break;
+ case '6':
+ if (ns_g_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();
+ ns_g_disable4 = true;
+ break;
+ case 'A':
+ parse_fuzz_arg();
+ 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 = 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 = true;
+ break;
+ case 'd':
+ ns_g_debuglevel = parse_int(isc_commandline_argument,
+ "debug level");
+ break;
+ case 'D':
+ /* Descriptive comment for 'ps'. */
+ break;
+ case 'E':
+ ns_g_engine = isc_commandline_argument;
+ break;
+ case 'f':
+ ns_g_foreground = true;
+ break;
+ case 'g':
+ ns_g_foreground = true;
+ ns_g_logstderr = true;
+ break;
+ /* XXXBEW -i should be removed */
+ case 'i':
+ lwresd_g_defaultpidfile = isc_commandline_argument;
+ break;
+ case 'l':
+ ns_g_lwresdonly = true;
+ break;
+ case 'L':
+ ns_g_logfile = isc_commandline_argument;
+ break;
+ case 'M':
+ if (strcmp(isc_commandline_argument, "external") == 0)
+ isc_mem_defaultflags = 0;
+ 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 = 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': /* NOT DOCUMENTED */
+ parse_T_opt(isc_commandline_argument);
+ break;
+ case 'U':
+ ns_g_udpdisp = parse_int(isc_commandline_argument,
+ "number of UDP listeners "
+ "per interface");
+ break;
+ case 'u':
+ ns_g_username = isc_commandline_argument;
+ break;
+ case 'v':
+ printf("%s %s%s%s <id:%s>\n",
+ ns_g_product, ns_g_version,
+ (*ns_g_description != '\0') ? " " : "",
+ ns_g_description, ns_g_srcid);
+ exit(0);
+ case 'V':
+ printf("%s %s%s%s <id:%s>\n", ns_g_product, ns_g_version,
+ (*ns_g_description != '\0') ? " " : "",
+ ns_g_description, ns_g_srcid);
+ printf("running on %s\n", ns_os_uname());
+ printf("built by %s with %s\n",
+ ns_g_builder, ns_g_configargs);
+#ifdef __clang__
+ printf("compiled by CLANG %s\n", __VERSION__);
+#else
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ printf("compiled by ICC %s\n", __VERSION__);
+#else
+#ifdef __GNUC__
+ printf("compiled by GCC %s\n", __VERSION__);
+#endif
+#endif
+#endif
+#ifdef _MSC_VER
+ printf("compiled by MSVC %d\n", _MSC_VER);
+#endif
+#ifdef __SUNPRO_C
+ printf("compiled by Solaris Studio %x\n", __SUNPRO_C);
+#endif
+#ifdef OPENSSL
+ printf("compiled with OpenSSL version: %s\n",
+ OPENSSL_VERSION_TEXT);
+#if !defined(LIBRESSL_VERSION_NUMBER) && \
+ OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 or higher */
+ printf("linked to OpenSSL version: %s\n",
+ OpenSSL_version(OPENSSL_VERSION));
+
+#else
+ printf("linked to OpenSSL version: %s\n",
+ SSLeay_version(SSLEAY_VERSION));
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
+#endif
+#ifdef HAVE_LIBXML2
+ printf("compiled with libxml2 version: %s\n",
+ LIBXML_DOTTED_VERSION);
+ printf("linked to libxml2 version: %s\n",
+ xmlParserVersion);
+#endif
+#if defined(HAVE_JSON) && defined(JSON_C_VERSION)
+ printf("compiled with libjson-c version: %s\n",
+ JSON_C_VERSION);
+ printf("linked to libjson-c version: %s\n",
+ json_c_version());
+#endif
+#if defined(HAVE_ZLIB) && defined(ZLIB_VERSION)
+ printf("compiled with zlib version: %s\n",
+ ZLIB_VERSION);
+ printf("linked to zlib version: %s\n",
+ zlibVersion());
+#endif
+#ifdef ISC_PLATFORM_USETHREADS
+ printf("threads support is enabled\n");
+#else
+ printf("threads support is disabled\n");
+#endif
+ exit(0);
+ case 'x':
+ /* Obsolete. No longer in use. Ignore. */
+ break;
+ case 'X':
+ ns_g_forcelock = true;
+ if (strcasecmp(isc_commandline_argument, "none") != 0)
+ ns_g_defaultlockfile = isc_commandline_argument;
+ else
+ ns_g_defaultlockfile = NULL;
+ break;
+ case 'F':
+ /* Reserved for FIPS mode */
+ /* FALLTHROUGH */
+ case '?':
+ usage();
+ if (isc_commandline_option == '?')
+ exit(0);
+ p = strchr(NS_MAIN_ARGS, isc_commandline_option);
+ if (p == NULL || *++p != ':')
+ ns_main_earlyfatal("unknown option '-%c'",
+ isc_commandline_option);
+ else
+ ns_main_earlyfatal("option '-%c' requires "
+ "an argument",
+ isc_commandline_option);
+ /* FALLTHROUGH */
+ default:
+ ns_main_earlyfatal("parsing options returned %d", ch);
+ }
+ }
+
+ argc -= isc_commandline_index;
+ argv += isc_commandline_index;
+ POST(argv);
+
+ 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;
+
+ INSIST(ns_g_cpus_detected > 0);
+
+#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
+#ifdef WIN32
+ ns_g_udpdisp = 1;
+#else
+ if (ns_g_udpdisp == 0) {
+ if (ns_g_cpus_detected == 1)
+ ns_g_udpdisp = 1;
+ else
+ ns_g_udpdisp = ns_g_cpus_detected - 1;
+ }
+ if (ns_g_udpdisp > ns_g_cpus)
+ ns_g_udpdisp = ns_g_cpus;
+#endif
+#ifdef ISC_PLATFORM_USETHREADS
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "using %u UDP listener%s per interface",
+ ns_g_udpdisp, ns_g_udpdisp == 1 ? "" : "s");
+#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);
+ }
+ isc__socketmgr_maxudp(ns_g_socketmgr, maxudp);
+ 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_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
+dump_symboltable(void) {
+ int i;
+ isc_result_t result;
+ const char *fname;
+ const void *addr;
+
+ if (isc__backtrace_nsymbols == 0)
+ return;
+
+ if (!isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(99)))
+ return;
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_DEBUG(99), "Symbol table:");
+
+ for (i = 0, result = ISC_R_SUCCESS; result == ISC_R_SUCCESS; i++) {
+ addr = NULL;
+ fname = NULL;
+ result = isc_backtrace_getsymbolfromindex(i, &addr, &fname);
+ if (result == ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_DEBUG(99),
+ "[%d] %p %s", i, addr, fname);
+ }
+ }
+}
+
+#ifdef HAVE_LIBSECCOMP
+static void
+setup_seccomp() {
+ scmp_filter_ctx ctx;
+ unsigned int i;
+ int ret;
+
+ /* Make sure the lists are in sync */
+ INSIST((sizeof(scmp_syscalls) / sizeof(int)) ==
+ (sizeof(scmp_syscall_names) / sizeof(const char *)));
+
+ ctx = seccomp_init(SCMP_ACT_KILL);
+ if (ctx == NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_WARNING,
+ "libseccomp activation failed");
+ return;
+ }
+
+ for (i = 0 ; i < sizeof(scmp_syscalls)/sizeof(*(scmp_syscalls)); i++) {
+ ret = seccomp_rule_add(ctx, SCMP_ACT_ALLOW,
+ scmp_syscalls[i], 0);
+ if (ret < 0)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_WARNING,
+ "libseccomp rule failed: %s",
+ scmp_syscall_names[i]);
+
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_DEBUG(9),
+ "added libseccomp rule: %s",
+ scmp_syscall_names[i]);
+ }
+
+ ret = seccomp_load(ctx);
+ if (ret < 0) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_WARNING,
+ "libseccomp unable to load filter");
+ } else {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "libseccomp sandboxing active");
+ }
+
+ /*
+ * Release filter in ctx. Filters already loaded are not
+ * affected.
+ */
+ seccomp_release(ctx);
+}
+#endif /* HAVE_LIBSECCOMP */
+
+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((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 %s %s%s%s <id:%s>",
+ ns_g_product, ns_g_version,
+ *ns_g_description ? " " : "", ns_g_description,
+ ns_g_srcid);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE, "running on %s", ns_os_uname());
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE, "built with %s", ns_g_configargs);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE, "running as: %s%s",
+ program_name, saved_command_line);
+#ifdef __clang__
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by CLANG %s", __VERSION__);
+#else
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by ICC %s", __VERSION__);
+#else
+#ifdef __GNUC__
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by GCC %s", __VERSION__);
+#endif
+#endif
+#endif
+#ifdef _MSC_VER
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by MSVC %d", _MSC_VER);
+#endif
+#ifdef __SUNPRO_C
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled by Solaris Studio %x", __SUNPRO_C);
+#endif
+#ifdef OPENSSL
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled with OpenSSL version: %s",
+ OPENSSL_VERSION_TEXT);
+#if !defined(LIBRESSL_VERSION_NUMBER) && \
+ OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 or higher */
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to OpenSSL version: %s",
+ OpenSSL_version(OPENSSL_VERSION));
+#else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to OpenSSL version: %s",
+ SSLeay_version(SSLEAY_VERSION));
+#endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
+#endif
+#ifdef HAVE_LIBXML2
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled with libxml2 version: %s",
+ LIBXML_DOTTED_VERSION);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to libxml2 version: %s", xmlParserVersion);
+#endif
+#if defined(HAVE_JSON) && defined(JSON_C_VERSION)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled with libjson-c version: %s", JSON_C_VERSION);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to libjson-c version: %s", json_c_version());
+#endif
+#if defined(HAVE_ZLIB) && defined(ZLIB_VERSION)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "compiled with zlib version: %s", ZLIB_VERSION);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "linked to zlib version: %s", zlibVersion());
+#endif
+#ifdef ISC_PLATFORM_USETHREADS
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "threads support is enabled");
+#else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_MAIN, ISC_LOG_NOTICE,
+ "threads support is disabled");
+#endif
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE,
+ "----------------------------------------------------");
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE,
+ "BIND 9 is maintained by Internet Systems Consortium,");
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE,
+ "Inc. (ISC), a non-profit 501(c)(3) public-benefit ");
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE,
+ "corporation. Support and training for BIND 9 are ");
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE,
+ "available at https://www.isc.org/support");
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
+ ISC_LOG_NOTICE,
+ "----------------------------------------------------");
+
+ dump_symboltable();
+
+ /*
+ * 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 "
+ "%" PRIu64 " to "
+ "%" PRIu64,
+ 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 ISC_DLZ_DLOPEN
+ /*
+ * Register the DLZ "dlopen" driver.
+ */
+ result = dlz_dlopen_init(ns_g_mctx);
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("dlz_dlopen_init() failed: %s",
+ isc_result_totext(result));
+#endif
+
+#if CONTRIB_DLZ
+ /*
+ * Register any other contributed 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);
+
+#ifdef HAVE_LIBSECCOMP
+ setup_seccomp();
+#endif /* HAVE_LIBSECCOMP */
+}
+
+static void
+cleanup(void) {
+ destroy_managers();
+
+ if (ns_g_mapped != NULL)
+ dns_acl_detach(&ns_g_mapped);
+
+ ns_server_destroy(&ns_g_server);
+
+ isc_entropy_detach(&ns_g_entropy);
+ if (ns_g_fallbackentropy != NULL)
+ isc_entropy_detach(&ns_g_fallbackentropy);
+
+ ns_builtin_deinit();
+
+ /*
+ * Add calls to unregister sdb drivers here.
+ */
+ /* xxdb_clear(); */
+
+#ifdef CONTRIB_DLZ
+ /*
+ * Unregister contributed DLZ drivers.
+ */
+ dlz_drivers_clear();
+#endif
+#ifdef ISC_DLZ_DLOPEN
+ /*
+ * Unregister "dlopen" DLZ driver.
+ */
+ dlz_dlopen_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 = strdup(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 */
+
+/* main entry point, possibly hooked */
+
+int
+main(int argc, char *argv[]) {
+ isc_result_t result;
+#ifdef HAVE_LIBSCF
+ char *instance = NULL;
+#endif
+
+#ifdef HAVE_GPERFTOOLS_PROFILER
+ (void) ProfilerStart(NULL);
+#endif
+
+ /*
+ * Record version in core image.
+ * strings named.core | grep "named version:"
+ */
+ strlcat(version,
+#if defined(NO_VERSION_DATE) || !defined(__DATE__)
+ "named version: BIND " VERSION " <" SRCID ">",
+#else
+ "named version: BIND " VERSION " <" SRCID "> (" __DATE__ ")",
+#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 = true;
+
+ if (result != ISC_R_SUCCESS)
+ ns_main_earlyfatal("failed to build internal symbol table");
+
+ 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();
+#ifdef PKCS11CRYPTO
+ pk11_result_register();
+#endif
+
+ parse_command_line(argc, argv);
+
+#ifdef ENABLE_AFL
+ if (ns_g_fuzz_type != ns_fuzz_none) {
+ named_fuzz_setup();
+ }
+
+ if (ns_g_fuzz_type == ns_fuzz_resolver) {
+ dns_resolver_setfuzzing();
+ } else if (ns_g_fuzz_type == ns_fuzz_http) {
+ isc_httpd_setfinishhook(named_fuzz_notify);
+ }
+#endif
+ /*
+ * 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);
+ (void) 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();
+
+#ifdef HAVE_GPERFTOOLS_PROFILER
+ ProfilerStop();
+#endif
+
+ return (0);
+}
diff --git a/bin/named/named.8 b/bin/named/named.8
new file mode 100644
index 0000000..341580d
--- /dev/null
+++ b/bin/named/named.8
@@ -0,0 +1,379 @@
+.\" Copyright (C) 2000, 2001, 2003-2009, 2011, 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+.\"
+.\" This Source Code Form is subject to the terms of the Mozilla Public
+.\" License, v. 2.0. If a copy of the MPL was not distributed with this
+.\" file, You can obtain one at http://mozilla.org/MPL/2.0/.
+.\"
+.hy 0
+.ad l
+'\" t
+.\" Title: named
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
+.\" Date: 2014-02-19
+.\" Manual: BIND9
+.\" Source: ISC
+.\" Language: English
+.\"
+.TH "NAMED" "8" "2014\-02\-19" "ISC" "BIND9"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+named \- Internet domain name server
+.SH "SYNOPSIS"
+.HP \w'\fBnamed\fR\ 'u
+\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\-D\ \fR\fB\fIstring\fR\fR] [\fB\-E\ \fR\fB\fIengine\-name\fR\fR] [\fB\-f\fR] [\fB\-g\fR] [\fB\-L\ \fR\fB\fIlogfile\fR\fR] [\fB\-M\ \fR\fB\fIoption\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\-s\fR] [\fB\-S\ \fR\fB\fI#max\-socks\fR\fR] [\fB\-t\ \fR\fB\fIdirectory\fR\fR] [\fB\-U\ \fR\fB\fI#listeners\fR\fR] [\fB\-u\ \fR\fB\fIuser\fR\fR] [\fB\-v\fR] [\fB\-V\fR] [\fB\-X\ \fR\fB\fIlock\-file\fR\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
+/etc/named\&.conf, 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,
+/etc/named\&.conf\&. 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\*(Aqs debug level to
+\fIdebug\-level\fR\&. Debugging traces from
+\fBnamed\fR
+become more verbose as the debug level increases\&.
+.RE
+.PP
+\-D \fIstring\fR
+.RS 4
+Specifies a string that is used to identify a instance of
+\fBnamed\fR
+in a process listing\&. The contents of
+\fIstring\fR
+are not examined\&.
+.RE
+.PP
+\-E \fIengine\-name\fR
+.RS 4
+When applicable, specifies the hardware to use for cryptographic operations, such as a secure key store used for signing\&.
+.sp
+When BIND is built with OpenSSL PKCS#11 support, this defaults to the string "pkcs11", which identifies an OpenSSL engine that can drive a cryptographic accelerator or hardware service module\&. When BIND is built with native PKCS#11 cryptography (\-\-enable\-native\-pkcs11), it defaults to the path of the PKCS#11 provider library specified via "\-\-with\-pkcs11"\&.
+.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
+stderr\&.
+.RE
+.PP
+\-L \fIlogfile\fR
+.RS 4
+Log to the file
+\fBlogfile\fR
+by default instead of the system log\&.
+.RE
+.PP
+\-M \fIoption\fR
+.RS 4
+Sets the default memory context options\&. Currently the only supported option is
+\fIexternal\fR, which causes the internal memory manager to be bypassed in favor of system\-provided memory allocation functions\&.
+.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
+<isc/mem\&.h>\&.
+.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
+stdout
+on exit\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+This option is mainly of interest to BIND 9 developers and may be removed or changed in a future release\&.
+.sp .5v
+.RE
+.RE
+.PP
+\-S \fI#max\-socks\fR
+.RS 4
+Allow
+\fBnamed\fR
+to use up to
+\fI#max\-socks\fR
+sockets\&. The default value is 4096 on systems built with default configuration options, and 21000 on systems built with "configure \-\-with\-tuning=large"\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBWarning\fR
+.ps -1
+.br
+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\&.
+.sp .5v
+.RE
+.RE
+.PP
+\-t \fIdirectory\fR
+.RS 4
+Chroot to
+\fIdirectory\fR
+after processing the command line arguments, but before reading the configuration file\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBWarning\fR
+.ps -1
+.br
+This option should be used in conjunction with the
+\fB\-u\fR
+option, as chrooting a process running as root doesn\*(Aqt enhance security on most systems; the way
+\fBchroot(2)\fR
+is defined allows a process with root privileges to escape a chroot jail\&.
+.sp .5v
+.RE
+.RE
+.PP
+\-U \fI#listeners\fR
+.RS 4
+Use
+\fI#listeners\fR
+worker threads to listen for incoming UDP packets on each address\&. If not specified,
+\fBnamed\fR
+will calculate a default value based on the number of detected CPUs: 1 for 1 CPU, and the number of detected CPUs minus one for machines with more than 1 CPU\&. This cannot be increased to a value higher than the number of CPUs\&. If
+\fB\-n\fR
+has been set to a higher value than the number of detected CPUs, then
+\fB\-U\fR
+may be increased as high as that value, but no higher\&. On Windows, the number of UDP listeners is hardwired to 1 and this option has no effect\&.
+.RE
+.PP
+\-u \fIuser\fR
+.RS 4
+Setuid to
+\fIuser\fR
+after completing privileged operations, such as creating sockets that listen on privileged ports\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBNote\fR
+.ps -1
+.br
+On Linux,
+\fBnamed\fR
+uses the kernel\*(Aqs 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\&.
+.sp .5v
+.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 \fIlock\-file\fR
+.RS 4
+Acquire a lock on the specified file at runtime; this helps to prevent duplicate
+\fBnamed\fR
+instances from running simultaneously\&. Use of this option overrides the
+\fBlock\-file\fR
+option in
+named\&.conf\&. If set to
+none, the lock file check is disabled\&.
+.RE
+.PP
+\-x \fIcache\-file\fR
+.RS 4
+Load data from
+\fIcache\-file\fR
+into the cache of the default view\&.
+.if n \{\
+.sp
+.\}
+.RS 4
+.it 1 an-trap
+.nr an-no-space-flag 1
+.nr an-break-flag 1
+.br
+.ps +1
+\fBWarning\fR
+.ps -1
+.br
+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\&.
+.sp .5v
+.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\&.
+.PP
+\fBnamed\fR
+inherits the
+\fBumask\fR
+(file creation mode mask) from the parent process\&. If files created by
+\fBnamed\fR, such as journal files, need to have custom permissions, the
+\fBumask\fR
+should be set explicitly in the script used to start the
+\fBnamed\fR
+process\&.
+.SH "FILES"
+.PP
+/etc/named\&.conf
+.RS 4
+The default configuration file\&.
+.RE
+.PP
+/var/run/named/named\&.pid
+.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
+\fBInternet Systems Consortium, Inc\&.\fR
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2000, 2001, 2003-2009, 2011, 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+.br
diff --git a/bin/named/named.conf.5 b/bin/named/named.conf.5
new file mode 100644
index 0000000..471f0f5
--- /dev/null
+++ b/bin/named/named.conf.5
@@ -0,0 +1,1028 @@
+.\" Copyright (C) 2004-2019 Internet Systems Consortium, Inc. ("ISC")
+.\"
+.\" This Source Code Form is subject to the terms of the Mozilla Public
+.\" License, v. 2.0. If a copy of the MPL was not distributed with this
+.\" file, You can obtain one at http://mozilla.org/MPL/2.0/.
+.\"
+.hy 0
+.ad l
+'\" t
+.\" Title: named.conf
+.\" Author:
+.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
+.\" Date: 2018-06-21
+.\" Manual: BIND9
+.\" Source: ISC
+.\" Language: English
+.\"
+.TH "NAMED\&.CONF" "5" "2018\-06\-21" "ISC" "BIND9"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+named.conf \- configuration file for \fBnamed\fR
+.SH "SYNOPSIS"
+.HP \w'\fBnamed\&.conf\fR\ 'u
+\fBnamed\&.conf\fR
+.SH "DESCRIPTION"
+.PP
+named\&.conf
+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
+.if n \{\
+.RS 4
+.\}
+.nf
+acl \fIstring\fR { \fIaddress_match_element\fR; \&.\&.\&. };
+.fi
+.if n \{\
+.RE
+.\}
+.SH "CONTROLS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+controls {
+ inet ( \fIipv4_address\fR | \fIipv6_address\fR |
+ * ) [ port ( \fIinteger\fR | * ) ] allow
+ { \fIaddress_match_element\fR; \&.\&.\&. } [
+ keys { \fIstring\fR; \&.\&.\&. } ] [ read\-only
+ \fIboolean\fR ];
+ unix \fIquoted_string\fR perm \fIinteger\fR
+ owner \fIinteger\fR group \fIinteger\fR [
+ keys { \fIstring\fR; \&.\&.\&. } ] [ read\-only
+ \fIboolean\fR ];
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "DLZ"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+dlz \fIstring\fR {
+ database \fIstring\fR;
+ search \fIboolean\fR;
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "DYNDB"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+dyndb \fIstring\fR \fIquoted_string\fR {
+ \fIunspecified\-text\fR };
+.fi
+.if n \{\
+.RE
+.\}
+.SH "KEY"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+key \fIstring\fR {
+ algorithm \fIstring\fR;
+ secret \fIstring\fR;
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "LOGGING"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+logging {
+ category \fIstring\fR { \fIstring\fR; \&.\&.\&. };
+ channel \fIstring\fR {
+ buffered \fIboolean\fR;
+ file \fIquoted_string\fR [ versions ( "unlimited" | \fIinteger\fR )
+ ] [ size \fIsize\fR ];
+ null;
+ print\-category \fIboolean\fR;
+ print\-severity \fIboolean\fR;
+ print\-time \fIboolean\fR;
+ severity \fIlog_severity\fR;
+ stderr;
+ syslog [ \fIsyslog_facility\fR ];
+ };
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "LWRES"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+lwres {
+ listen\-on [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fIipv4_address\fR
+ | \fIipv6_address\fR ) [ port \fIinteger\fR ] [ dscp \fIinteger\fR ]; \&.\&.\&. };
+ lwres\-clients \fIinteger\fR;
+ lwres\-tasks \fIinteger\fR;
+ ndots \fIinteger\fR;
+ search { \fIstring\fR; \&.\&.\&. };
+ view \fIstring\fR [ \fIclass\fR ];
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "MANAGED-KEYS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+managed\-keys { \fIstring\fR \fIstring\fR \fIinteger\fR
+ \fIinteger\fR \fIinteger\fR \fIquoted_string\fR; \&.\&.\&. };
+.fi
+.if n \{\
+.RE
+.\}
+.SH "MASTERS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+masters \fIstring\fR [ port \fIinteger\fR ] [ dscp
+ \fIinteger\fR ] { ( \fImasters\fR | \fIipv4_address\fR [
+ port \fIinteger\fR ] | \fIipv6_address\fR [ port
+ \fIinteger\fR ] ) [ key \fIstring\fR ]; \&.\&.\&. };
+.fi
+.if n \{\
+.RE
+.\}
+.SH "OPTIONS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+options {
+ acache\-cleaning\-interval \fIinteger\fR;
+ acache\-enable \fIboolean\fR;
+ additional\-from\-auth \fIboolean\fR;
+ additional\-from\-cache \fIboolean\fR;
+ allow\-new\-zones \fIboolean\fR;
+ allow\-notify { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-query { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-query\-cache { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-query\-cache\-on { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-query\-on { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-recursion { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-recursion\-on { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-transfer { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-update { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-update\-forwarding { \fIaddress_match_element\fR; \&.\&.\&. };
+ also\-notify [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fImasters\fR |
+ \fIipv4_address\fR [ port \fIinteger\fR ] | \fIipv6_address\fR [ port
+ \fIinteger\fR ] ) [ key \fIstring\fR ]; \&.\&.\&. };
+ alt\-transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * )
+ ] [ dscp \fIinteger\fR ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR |
+ * ) ] [ dscp \fIinteger\fR ];
+ answer\-cookie \fIboolean\fR;
+ attach\-cache \fIstring\fR;
+ auth\-nxdomain \fIboolean\fR; // default changed
+ auto\-dnssec ( allow | maintain | off );
+ automatic\-interface\-scan \fIboolean\fR;
+ avoid\-v4\-udp\-ports { \fIportrange\fR; \&.\&.\&. };
+ avoid\-v6\-udp\-ports { \fIportrange\fR; \&.\&.\&. };
+ bindkeys\-file \fIquoted_string\fR;
+ blackhole { \fIaddress_match_element\fR; \&.\&.\&. };
+ cache\-file \fIquoted_string\fR;
+ catalog\-zones { zone \fIquoted_string\fR [ default\-masters [ port
+ \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fImasters\fR | \fIipv4_address\fR [
+ port \fIinteger\fR ] | \fIipv6_address\fR [ port \fIinteger\fR ] ) [ key
+ \fIstring\fR ]; \&.\&.\&. } ] [ zone\-directory \fIquoted_string\fR ] [
+ in\-memory \fIboolean\fR ] [ min\-update\-interval \fIinteger\fR ]; \&.\&.\&. };
+ check\-dup\-records ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx ( fail | warn | ignore );
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-names ( master | slave | response
+ ) ( fail | warn | ignore );
+ check\-sibling \fIboolean\fR;
+ check\-spf ( warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ check\-wildcard \fIboolean\fR;
+ cleaning\-interval \fIinteger\fR;
+ clients\-per\-query \fIinteger\fR;
+ cookie\-algorithm ( aes | sha1 | sha256 );
+ cookie\-secret \fIstring\fR;
+ coresize ( default | unlimited | \fIsizeval\fR );
+ datasize ( default | unlimited | \fIsizeval\fR );
+ deny\-answer\-addresses { \fIaddress_match_element\fR; \&.\&.\&. } [
+ except\-from { \fIquoted_string\fR; \&.\&.\&. } ];
+ deny\-answer\-aliases { \fIquoted_string\fR; \&.\&.\&. } [ except\-from {
+ \fIquoted_string\fR; \&.\&.\&. } ];
+ dialup ( notify | notify\-passive | passive | refresh | \fIboolean\fR );
+ directory \fIquoted_string\fR;
+ disable\-algorithms \fIstring\fR { \fIstring\fR;
+ \&.\&.\&. };
+ disable\-ds\-digests \fIstring\fR { \fIstring\fR;
+ \&.\&.\&. };
+ disable\-empty\-zone \fIstring\fR;
+ dns64 \fInetprefix\fR {
+ break\-dnssec \fIboolean\fR;
+ clients { \fIaddress_match_element\fR; \&.\&.\&. };
+ exclude { \fIaddress_match_element\fR; \&.\&.\&. };
+ mapped { \fIaddress_match_element\fR; \&.\&.\&. };
+ recursive\-only \fIboolean\fR;
+ suffix \fIipv6_address\fR;
+ };
+ dns64\-contact \fIstring\fR;
+ dns64\-server \fIstring\fR;
+ dnssec\-accept\-expired \fIboolean\fR;
+ dnssec\-dnskey\-kskonly \fIboolean\fR;
+ dnssec\-enable \fIboolean\fR;
+ dnssec\-loadkeys\-interval \fIinteger\fR;
+ dnssec\-lookaside ( \fIstring\fR trust\-anchor
+ \fIstring\fR | auto | no );
+ dnssec\-must\-be\-secure \fIstring\fR \fIboolean\fR;
+ dnssec\-secure\-to\-insecure \fIboolean\fR;
+ dnssec\-update\-mode ( maintain | no\-resign );
+ dnssec\-validation ( yes | no | auto );
+ dnstap { ( all | auth | client | forwarder |
+ resolver ) [ ( query | response ) ]; \&.\&.\&. };
+ dnstap\-identity ( \fIquoted_string\fR | none |
+ hostname );
+ dnstap\-output ( file | unix ) \fIquoted_string\fR;
+ dnstap\-version ( \fIquoted_string\fR | none );
+ dscp \fIinteger\fR;
+ dual\-stack\-servers [ port \fIinteger\fR ] { ( \fIquoted_string\fR [ port
+ \fIinteger\fR ] [ dscp \fIinteger\fR ] | \fIipv4_address\fR [ port
+ \fIinteger\fR ] [ dscp \fIinteger\fR ] | \fIipv6_address\fR [ port
+ \fIinteger\fR ] [ dscp \fIinteger\fR ] ); \&.\&.\&. };
+ dump\-file \fIquoted_string\fR;
+ edns\-udp\-size \fIinteger\fR;
+ empty\-contact \fIstring\fR;
+ empty\-server \fIstring\fR;
+ empty\-zones\-enable \fIboolean\fR;
+ fetch\-quota\-params \fIinteger\fR \fIfixedpoint\fR \fIfixedpoint\fR \fIfixedpoint\fR;
+ fetches\-per\-server \fIinteger\fR [ ( drop | fail ) ];
+ fetches\-per\-zone \fIinteger\fR [ ( drop | fail ) ];
+ files ( default | unlimited | \fIsizeval\fR );
+ filter\-aaaa { \fIaddress_match_element\fR; \&.\&.\&. };
+ filter\-aaaa\-on\-v4 ( break\-dnssec | \fIboolean\fR );
+ filter\-aaaa\-on\-v6 ( break\-dnssec | \fIboolean\fR );
+ flush\-zones\-on\-shutdown \fIboolean\fR;
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fIipv4_address\fR
+ | \fIipv6_address\fR ) [ port \fIinteger\fR ] [ dscp \fIinteger\fR ]; \&.\&.\&. };
+ fstrm\-set\-buffer\-hint \fIinteger\fR;
+ fstrm\-set\-flush\-timeout \fIinteger\fR;
+ fstrm\-set\-input\-queue\-size \fIinteger\fR;
+ fstrm\-set\-output\-notify\-threshold \fIinteger\fR;
+ fstrm\-set\-output\-queue\-model ( mpsc | spsc );
+ fstrm\-set\-output\-queue\-size \fIinteger\fR;
+ fstrm\-set\-reopen\-interval \fIinteger\fR;
+ geoip\-directory ( \fIquoted_string\fR | none );
+ geoip\-use\-ecs \fIboolean\fR;
+ heartbeat\-interval \fIinteger\fR;
+ hostname ( \fIquoted_string\fR | none );
+ inline\-signing \fIboolean\fR;
+ interface\-interval \fIinteger\fR;
+ ixfr\-from\-differences ( master | slave | \fIboolean\fR );
+ keep\-response\-order { \fIaddress_match_element\fR; \&.\&.\&. };
+ key\-directory \fIquoted_string\fR;
+ lame\-ttl \fIttlval\fR;
+ listen\-on [ port \fIinteger\fR ] [ dscp
+ \fIinteger\fR ] {
+ \fIaddress_match_element\fR; \&.\&.\&. };
+ listen\-on\-v6 [ port \fIinteger\fR ] [ dscp
+ \fIinteger\fR ] {
+ \fIaddress_match_element\fR; \&.\&.\&. };
+ lmdb\-mapsize \fIsizeval\fR;
+ lock\-file ( \fIquoted_string\fR | none );
+ managed\-keys\-directory \fIquoted_string\fR;
+ masterfile\-format ( map | raw | text );
+ masterfile\-style ( full | relative );
+ match\-mapped\-addresses \fIboolean\fR;
+ max\-acache\-size ( unlimited | \fIsizeval\fR );
+ max\-cache\-size ( default | unlimited | \fIsizeval\fR | \fIpercentage\fR );
+ max\-cache\-ttl \fIinteger\fR;
+ max\-clients\-per\-query \fIinteger\fR;
+ max\-journal\-size ( unlimited | \fIsizeval\fR );
+ max\-ncache\-ttl \fIinteger\fR;
+ max\-records \fIinteger\fR;
+ max\-recursion\-depth \fIinteger\fR;
+ max\-recursion\-queries \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ max\-rsa\-exponent\-size \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-udp\-size \fIinteger\fR;
+ max\-zone\-ttl ( unlimited | \fIttlval\fR );
+ memstatistics \fIboolean\fR;
+ memstatistics\-file \fIquoted_string\fR;
+ message\-compression \fIboolean\fR;
+ min\-refresh\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ minimal\-any \fIboolean\fR;
+ minimal\-responses ( no\-auth | no\-auth\-recursive | \fIboolean\fR );
+ multi\-master \fIboolean\fR;
+ no\-case\-compress { \fIaddress_match_element\fR; \&.\&.\&. };
+ nocookie\-udp\-size \fIinteger\fR;
+ notify ( explicit | master\-only | \fIboolean\fR );
+ notify\-delay \fIinteger\fR;
+ notify\-rate \fIinteger\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ] [
+ dscp \fIinteger\fR ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ]
+ [ dscp \fIinteger\fR ];
+ notify\-to\-soa \fIboolean\fR;
+ nta\-lifetime \fIttlval\fR;
+ nta\-recheck \fIttlval\fR;
+ nxdomain\-redirect \fIstring\fR;
+ pid\-file ( \fIquoted_string\fR | none );
+ port \fIinteger\fR;
+ preferred\-glue \fIstring\fR;
+ prefetch \fIinteger\fR [ \fIinteger\fR ];
+ provide\-ixfr \fIboolean\fR;
+ query\-source ( ( [ address ] ( \fIipv4_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] ) | ( [ [ address ] ( \fIipv4_address\fR | * ) ]
+ port ( \fIinteger\fR | * ) ) ) [ dscp \fIinteger\fR ];
+ query\-source\-v6 ( ( [ address ] ( \fIipv6_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] ) | ( [ [ address ] ( \fIipv6_address\fR | * ) ]
+ port ( \fIinteger\fR | * ) ) ) [ dscp \fIinteger\fR ];
+ querylog \fIboolean\fR;
+ random\-device \fIquoted_string\fR;
+ rate\-limit {
+ all\-per\-second \fIinteger\fR;
+ errors\-per\-second \fIinteger\fR;
+ exempt\-clients { \fIaddress_match_element\fR; \&.\&.\&. };
+ ipv4\-prefix\-length \fIinteger\fR;
+ ipv6\-prefix\-length \fIinteger\fR;
+ log\-only \fIboolean\fR;
+ max\-table\-size \fIinteger\fR;
+ min\-table\-size \fIinteger\fR;
+ nodata\-per\-second \fIinteger\fR;
+ nxdomains\-per\-second \fIinteger\fR;
+ qps\-scale \fIinteger\fR;
+ referrals\-per\-second \fIinteger\fR;
+ responses\-per\-second \fIinteger\fR;
+ slip \fIinteger\fR;
+ window \fIinteger\fR;
+ };
+ recursing\-file \fIquoted_string\fR;
+ recursion \fIboolean\fR;
+ recursive\-clients \fIinteger\fR;
+ request\-expire \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ request\-nsid \fIboolean\fR;
+ require\-server\-cookie \fIboolean\fR;
+ reserved\-sockets \fIinteger\fR;
+ resolver\-query\-timeout \fIinteger\fR;
+ response\-policy { zone \fIquoted_string\fR [ log \fIboolean\fR ] [
+ max\-policy\-ttl \fIinteger\fR ] [ policy ( cname | disabled | drop |
+ given | no\-op | nodata | nxdomain | passthru | tcp\-only
+ \fIquoted_string\fR ) ] [ recursive\-only \fIboolean\fR ]; \&.\&.\&. } [
+ break\-dnssec \fIboolean\fR ] [ max\-policy\-ttl \fIinteger\fR ] [
+ min\-ns\-dots \fIinteger\fR ] [ nsip\-wait\-recurse \fIboolean\fR ] [
+ qname\-wait\-recurse \fIboolean\fR ] [ recursive\-only \fIboolean\fR ];
+ root\-delegation\-only [ exclude { \fIquoted_string\fR; \&.\&.\&. } ];
+ root\-key\-sentinel \fIboolean\fR;
+ rrset\-order { [ class \fIstring\fR ] [ type \fIstring\fR ] [ name
+ \fIquoted_string\fR ] \fIstring\fR \fIstring\fR; \&.\&.\&. };
+ secroots\-file \fIquoted_string\fR;
+ send\-cookie \fIboolean\fR;
+ serial\-query\-rate \fIinteger\fR;
+ serial\-update\-method ( date | increment | unixtime );
+ server\-id ( \fIquoted_string\fR | none | hostname );
+ servfail\-ttl \fIttlval\fR;
+ session\-keyalg \fIstring\fR;
+ session\-keyfile ( \fIquoted_string\fR | none );
+ session\-keyname \fIstring\fR;
+ sig\-signing\-nodes \fIinteger\fR;
+ sig\-signing\-signatures \fIinteger\fR;
+ sig\-signing\-type \fIinteger\fR;
+ sig\-validity\-interval \fIinteger\fR [ \fIinteger\fR ];
+ sortlist { \fIaddress_match_element\fR; \&.\&.\&. };
+ stacksize ( default | unlimited | \fIsizeval\fR );
+ startup\-notify\-rate \fIinteger\fR;
+ statistics\-file \fIquoted_string\fR;
+ tcp\-clients \fIinteger\fR;
+ tcp\-listen\-queue \fIinteger\fR;
+ tkey\-dhkey \fIquoted_string\fR \fIinteger\fR;
+ tkey\-domain \fIquoted_string\fR;
+ tkey\-gssapi\-credential \fIquoted_string\fR;
+ tkey\-gssapi\-keytab \fIquoted_string\fR;
+ transfer\-format ( many\-answers | one\-answer );
+ transfer\-message\-size \fIinteger\fR;
+ transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ] [
+ dscp \fIinteger\fR ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * )
+ ] [ dscp \fIinteger\fR ];
+ transfers\-in \fIinteger\fR;
+ transfers\-out \fIinteger\fR;
+ transfers\-per\-ns \fIinteger\fR;
+ trust\-anchor\-telemetry \fIboolean\fR; // experimental
+ try\-tcp\-refresh \fIboolean\fR;
+ update\-check\-ksk \fIboolean\fR;
+ use\-alt\-transfer\-source \fIboolean\fR;
+ use\-v4\-udp\-ports { \fIportrange\fR; \&.\&.\&. };
+ use\-v6\-udp\-ports { \fIportrange\fR; \&.\&.\&. };
+ v6\-bias \fIinteger\fR;
+ version ( \fIquoted_string\fR | none );
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ zero\-no\-soa\-ttl\-cache \fIboolean\fR;
+ zone\-statistics ( full | terse | none | \fIboolean\fR );
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "SERVER"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+server \fInetprefix\fR {
+ bogus \fIboolean\fR;
+ edns \fIboolean\fR;
+ edns\-udp\-size \fIinteger\fR;
+ edns\-version \fIinteger\fR;
+ keys \fIserver_key\fR;
+ max\-udp\-size \fIinteger\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ] [
+ dscp \fIinteger\fR ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ]
+ [ dscp \fIinteger\fR ];
+ provide\-ixfr \fIboolean\fR;
+ query\-source ( ( [ address ] ( \fIipv4_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] ) | ( [ [ address ] ( \fIipv4_address\fR | * ) ]
+ port ( \fIinteger\fR | * ) ) ) [ dscp \fIinteger\fR ];
+ query\-source\-v6 ( ( [ address ] ( \fIipv6_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] ) | ( [ [ address ] ( \fIipv6_address\fR | * ) ]
+ port ( \fIinteger\fR | * ) ) ) [ dscp \fIinteger\fR ];
+ request\-expire \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ request\-nsid \fIboolean\fR;
+ send\-cookie \fIboolean\fR;
+ tcp\-only \fIboolean\fR;
+ transfer\-format ( many\-answers | one\-answer );
+ transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ] [
+ dscp \fIinteger\fR ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * )
+ ] [ dscp \fIinteger\fR ];
+ transfers \fIinteger\fR;
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "STATISTICS-CHANNELS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+statistics\-channels {
+ inet ( \fIipv4_address\fR | \fIipv6_address\fR |
+ * ) [ port ( \fIinteger\fR | * ) ] [
+ allow { \fIaddress_match_element\fR; \&.\&.\&.
+ } ];
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "TRUSTED-KEYS"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+trusted\-keys { \fIstring\fR \fIinteger\fR \fIinteger\fR
+ \fIinteger\fR \fIquoted_string\fR; \&.\&.\&. };
+.fi
+.if n \{\
+.RE
+.\}
+.SH "VIEW"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+view \fIstring\fR [ \fIclass\fR ] {
+ acache\-cleaning\-interval \fIinteger\fR;
+ acache\-enable \fIboolean\fR;
+ additional\-from\-auth \fIboolean\fR;
+ additional\-from\-cache \fIboolean\fR;
+ allow\-new\-zones \fIboolean\fR;
+ allow\-notify { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-query { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-query\-cache { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-query\-cache\-on { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-query\-on { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-recursion { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-recursion\-on { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-transfer { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-update { \fIaddress_match_element\fR; \&.\&.\&. };
+ allow\-update\-forwarding { \fIaddress_match_element\fR; \&.\&.\&. };
+ also\-notify [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fImasters\fR |
+ \fIipv4_address\fR [ port \fIinteger\fR ] | \fIipv6_address\fR [ port
+ \fIinteger\fR ] ) [ key \fIstring\fR ]; \&.\&.\&. };
+ alt\-transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * )
+ ] [ dscp \fIinteger\fR ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR |
+ * ) ] [ dscp \fIinteger\fR ];
+ attach\-cache \fIstring\fR;
+ auth\-nxdomain \fIboolean\fR; // default changed
+ auto\-dnssec ( allow | maintain | off );
+ cache\-file \fIquoted_string\fR;
+ catalog\-zones { zone \fIquoted_string\fR [ default\-masters [ port
+ \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fImasters\fR | \fIipv4_address\fR [
+ port \fIinteger\fR ] | \fIipv6_address\fR [ port \fIinteger\fR ] ) [ key
+ \fIstring\fR ]; \&.\&.\&. } ] [ zone\-directory \fIquoted_string\fR ] [
+ in\-memory \fIboolean\fR ] [ min\-update\-interval \fIinteger\fR ]; \&.\&.\&. };
+ check\-dup\-records ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx ( fail | warn | ignore );
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-names ( master | slave | response
+ ) ( fail | warn | ignore );
+ check\-sibling \fIboolean\fR;
+ check\-spf ( warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ check\-wildcard \fIboolean\fR;
+ cleaning\-interval \fIinteger\fR;
+ clients\-per\-query \fIinteger\fR;
+ deny\-answer\-addresses { \fIaddress_match_element\fR; \&.\&.\&. } [
+ except\-from { \fIquoted_string\fR; \&.\&.\&. } ];
+ deny\-answer\-aliases { \fIquoted_string\fR; \&.\&.\&. } [ except\-from {
+ \fIquoted_string\fR; \&.\&.\&. } ];
+ dialup ( notify | notify\-passive | passive | refresh | \fIboolean\fR );
+ disable\-algorithms \fIstring\fR { \fIstring\fR;
+ \&.\&.\&. };
+ disable\-ds\-digests \fIstring\fR { \fIstring\fR;
+ \&.\&.\&. };
+ disable\-empty\-zone \fIstring\fR;
+ dlz \fIstring\fR {
+ database \fIstring\fR;
+ search \fIboolean\fR;
+ };
+ dns64 \fInetprefix\fR {
+ break\-dnssec \fIboolean\fR;
+ clients { \fIaddress_match_element\fR; \&.\&.\&. };
+ exclude { \fIaddress_match_element\fR; \&.\&.\&. };
+ mapped { \fIaddress_match_element\fR; \&.\&.\&. };
+ recursive\-only \fIboolean\fR;
+ suffix \fIipv6_address\fR;
+ };
+ dns64\-contact \fIstring\fR;
+ dns64\-server \fIstring\fR;
+ dnssec\-accept\-expired \fIboolean\fR;
+ dnssec\-dnskey\-kskonly \fIboolean\fR;
+ dnssec\-enable \fIboolean\fR;
+ dnssec\-loadkeys\-interval \fIinteger\fR;
+ dnssec\-lookaside ( \fIstring\fR trust\-anchor
+ \fIstring\fR | auto | no );
+ dnssec\-must\-be\-secure \fIstring\fR \fIboolean\fR;
+ dnssec\-secure\-to\-insecure \fIboolean\fR;
+ dnssec\-update\-mode ( maintain | no\-resign );
+ dnssec\-validation ( yes | no | auto );
+ dnstap { ( all | auth | client | forwarder |
+ resolver ) [ ( query | response ) ]; \&.\&.\&. };
+ dual\-stack\-servers [ port \fIinteger\fR ] { ( \fIquoted_string\fR [ port
+ \fIinteger\fR ] [ dscp \fIinteger\fR ] | \fIipv4_address\fR [ port
+ \fIinteger\fR ] [ dscp \fIinteger\fR ] | \fIipv6_address\fR [ port
+ \fIinteger\fR ] [ dscp \fIinteger\fR ] ); \&.\&.\&. };
+ dyndb \fIstring\fR \fIquoted_string\fR {
+ \fIunspecified\-text\fR };
+ edns\-udp\-size \fIinteger\fR;
+ empty\-contact \fIstring\fR;
+ empty\-server \fIstring\fR;
+ empty\-zones\-enable \fIboolean\fR;
+ fetch\-quota\-params \fIinteger\fR \fIfixedpoint\fR \fIfixedpoint\fR \fIfixedpoint\fR;
+ fetches\-per\-server \fIinteger\fR [ ( drop | fail ) ];
+ fetches\-per\-zone \fIinteger\fR [ ( drop | fail ) ];
+ filter\-aaaa { \fIaddress_match_element\fR; \&.\&.\&. };
+ filter\-aaaa\-on\-v4 ( break\-dnssec | \fIboolean\fR );
+ filter\-aaaa\-on\-v6 ( break\-dnssec | \fIboolean\fR );
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fIipv4_address\fR
+ | \fIipv6_address\fR ) [ port \fIinteger\fR ] [ dscp \fIinteger\fR ]; \&.\&.\&. };
+ inline\-signing \fIboolean\fR;
+ ixfr\-from\-differences ( master | slave | \fIboolean\fR );
+ key \fIstring\fR {
+ algorithm \fIstring\fR;
+ secret \fIstring\fR;
+ };
+ key\-directory \fIquoted_string\fR;
+ lame\-ttl \fIttlval\fR;
+ lmdb\-mapsize \fIsizeval\fR;
+ managed\-keys { \fIstring\fR \fIstring\fR
+ \fIinteger\fR \fIinteger\fR \fIinteger\fR
+ \fIquoted_string\fR; \&.\&.\&. };
+ masterfile\-format ( map | raw | text );
+ masterfile\-style ( full | relative );
+ match\-clients { \fIaddress_match_element\fR; \&.\&.\&. };
+ match\-destinations { \fIaddress_match_element\fR; \&.\&.\&. };
+ match\-recursive\-only \fIboolean\fR;
+ max\-acache\-size ( unlimited | \fIsizeval\fR );
+ max\-cache\-size ( default | unlimited | \fIsizeval\fR | \fIpercentage\fR );
+ max\-cache\-ttl \fIinteger\fR;
+ max\-clients\-per\-query \fIinteger\fR;
+ max\-journal\-size ( unlimited | \fIsizeval\fR );
+ max\-ncache\-ttl \fIinteger\fR;
+ max\-records \fIinteger\fR;
+ max\-recursion\-depth \fIinteger\fR;
+ max\-recursion\-queries \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-udp\-size \fIinteger\fR;
+ max\-zone\-ttl ( unlimited | \fIttlval\fR );
+ message\-compression \fIboolean\fR;
+ min\-refresh\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ minimal\-any \fIboolean\fR;
+ minimal\-responses ( no\-auth | no\-auth\-recursive | \fIboolean\fR );
+ multi\-master \fIboolean\fR;
+ no\-case\-compress { \fIaddress_match_element\fR; \&.\&.\&. };
+ nocookie\-udp\-size \fIinteger\fR;
+ notify ( explicit | master\-only | \fIboolean\fR );
+ notify\-delay \fIinteger\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ] [
+ dscp \fIinteger\fR ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ]
+ [ dscp \fIinteger\fR ];
+ notify\-to\-soa \fIboolean\fR;
+ nta\-lifetime \fIttlval\fR;
+ nta\-recheck \fIttlval\fR;
+ nxdomain\-redirect \fIstring\fR;
+ preferred\-glue \fIstring\fR;
+ prefetch \fIinteger\fR [ \fIinteger\fR ];
+ provide\-ixfr \fIboolean\fR;
+ query\-source ( ( [ address ] ( \fIipv4_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] ) | ( [ [ address ] ( \fIipv4_address\fR | * ) ]
+ port ( \fIinteger\fR | * ) ) ) [ dscp \fIinteger\fR ];
+ query\-source\-v6 ( ( [ address ] ( \fIipv6_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] ) | ( [ [ address ] ( \fIipv6_address\fR | * ) ]
+ port ( \fIinteger\fR | * ) ) ) [ dscp \fIinteger\fR ];
+ rate\-limit {
+ all\-per\-second \fIinteger\fR;
+ errors\-per\-second \fIinteger\fR;
+ exempt\-clients { \fIaddress_match_element\fR; \&.\&.\&. };
+ ipv4\-prefix\-length \fIinteger\fR;
+ ipv6\-prefix\-length \fIinteger\fR;
+ log\-only \fIboolean\fR;
+ max\-table\-size \fIinteger\fR;
+ min\-table\-size \fIinteger\fR;
+ nodata\-per\-second \fIinteger\fR;
+ nxdomains\-per\-second \fIinteger\fR;
+ qps\-scale \fIinteger\fR;
+ referrals\-per\-second \fIinteger\fR;
+ responses\-per\-second \fIinteger\fR;
+ slip \fIinteger\fR;
+ window \fIinteger\fR;
+ };
+ recursion \fIboolean\fR;
+ request\-expire \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ request\-nsid \fIboolean\fR;
+ require\-server\-cookie \fIboolean\fR;
+ resolver\-query\-timeout \fIinteger\fR;
+ response\-policy { zone \fIquoted_string\fR [ log \fIboolean\fR ] [
+ max\-policy\-ttl \fIinteger\fR ] [ policy ( cname | disabled | drop |
+ given | no\-op | nodata | nxdomain | passthru | tcp\-only
+ \fIquoted_string\fR ) ] [ recursive\-only \fIboolean\fR ]; \&.\&.\&. } [
+ break\-dnssec \fIboolean\fR ] [ max\-policy\-ttl \fIinteger\fR ] [
+ min\-ns\-dots \fIinteger\fR ] [ nsip\-wait\-recurse \fIboolean\fR ] [
+ qname\-wait\-recurse \fIboolean\fR ] [ recursive\-only \fIboolean\fR ];
+ root\-delegation\-only [ exclude { \fIquoted_string\fR; \&.\&.\&. } ];
+ root\-key\-sentinel \fIboolean\fR;
+ rrset\-order { [ class \fIstring\fR ] [ type \fIstring\fR ] [ name
+ \fIquoted_string\fR ] \fIstring\fR \fIstring\fR; \&.\&.\&. };
+ send\-cookie \fIboolean\fR;
+ serial\-update\-method ( date | increment | unixtime );
+ server \fInetprefix\fR {
+ bogus \fIboolean\fR;
+ edns \fIboolean\fR;
+ edns\-udp\-size \fIinteger\fR;
+ edns\-version \fIinteger\fR;
+ keys \fIserver_key\fR;
+ max\-udp\-size \fIinteger\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | *
+ ) ] [ dscp \fIinteger\fR ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR
+ | * ) ] [ dscp \fIinteger\fR ];
+ provide\-ixfr \fIboolean\fR;
+ query\-source ( ( [ address ] ( \fIipv4_address\fR | * ) [ port
+ ( \fIinteger\fR | * ) ] ) | ( [ [ address ] (
+ \fIipv4_address\fR | * ) ] port ( \fIinteger\fR | * ) ) ) [
+ dscp \fIinteger\fR ];
+ query\-source\-v6 ( ( [ address ] ( \fIipv6_address\fR | * ) [
+ port ( \fIinteger\fR | * ) ] ) | ( [ [ address ] (
+ \fIipv6_address\fR | * ) ] port ( \fIinteger\fR | * ) ) ) [
+ dscp \fIinteger\fR ];
+ request\-expire \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ request\-nsid \fIboolean\fR;
+ send\-cookie \fIboolean\fR;
+ tcp\-only \fIboolean\fR;
+ transfer\-format ( many\-answers | one\-answer );
+ transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR |
+ * ) ] [ dscp \fIinteger\fR ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] [ dscp \fIinteger\fR ];
+ transfers \fIinteger\fR;
+ };
+ servfail\-ttl \fIttlval\fR;
+ sig\-signing\-nodes \fIinteger\fR;
+ sig\-signing\-signatures \fIinteger\fR;
+ sig\-signing\-type \fIinteger\fR;
+ sig\-validity\-interval \fIinteger\fR [ \fIinteger\fR ];
+ sortlist { \fIaddress_match_element\fR; \&.\&.\&. };
+ transfer\-format ( many\-answers | one\-answer );
+ transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ] [
+ dscp \fIinteger\fR ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * )
+ ] [ dscp \fIinteger\fR ];
+ trust\-anchor\-telemetry \fIboolean\fR; // experimental
+ trusted\-keys { \fIstring\fR \fIinteger\fR
+ \fIinteger\fR \fIinteger\fR \fIquoted_string\fR;
+ \&.\&.\&. };
+ try\-tcp\-refresh \fIboolean\fR;
+ update\-check\-ksk \fIboolean\fR;
+ use\-alt\-transfer\-source \fIboolean\fR;
+ v6\-bias \fIinteger\fR;
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ zero\-no\-soa\-ttl\-cache \fIboolean\fR;
+ zone \fIstring\fR [ \fIclass\fR ] {
+ allow\-notify { \fIaddress_match_element\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; \&.\&.\&. };
+ also\-notify [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { (
+ \fImasters\fR | \fIipv4_address\fR [ port \fIinteger\fR ] |
+ \fIipv6_address\fR [ port \fIinteger\fR ] ) [ key \fIstring\fR ];
+ \&.\&.\&. };
+ alt\-transfer\-source ( \fIipv4_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] [ dscp \fIinteger\fR ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] [ dscp \fIinteger\fR ];
+ auto\-dnssec ( allow | maintain | off );
+ check\-dup\-records ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx ( fail | warn | ignore );
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-names ( fail | warn | ignore );
+ check\-sibling \fIboolean\fR;
+ check\-spf ( warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ check\-wildcard \fIboolean\fR;
+ database \fIstring\fR;
+ delegation\-only \fIboolean\fR;
+ dialup ( notify | notify\-passive | passive | refresh |
+ \fIboolean\fR );
+ dlz \fIstring\fR;
+ dnssec\-dnskey\-kskonly \fIboolean\fR;
+ dnssec\-loadkeys\-interval \fIinteger\fR;
+ dnssec\-secure\-to\-insecure \fIboolean\fR;
+ dnssec\-update\-mode ( maintain | no\-resign );
+ file \fIquoted_string\fR;
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { (
+ \fIipv4_address\fR | \fIipv6_address\fR ) [ port \fIinteger\fR ] [
+ dscp \fIinteger\fR ]; \&.\&.\&. };
+ in\-view \fIstring\fR;
+ inline\-signing \fIboolean\fR;
+ ixfr\-from\-differences \fIboolean\fR;
+ journal \fIquoted_string\fR;
+ key\-directory \fIquoted_string\fR;
+ masterfile\-format ( map | raw | text );
+ masterfile\-style ( full | relative );
+ masters [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fImasters\fR
+ | \fIipv4_address\fR [ port \fIinteger\fR ] | \fIipv6_address\fR [
+ port \fIinteger\fR ] ) [ key \fIstring\fR ]; \&.\&.\&. };
+ max\-ixfr\-log\-size ( default | unlimited |
+ max\-journal\-size ( unlimited | \fIsizeval\fR );
+ max\-records \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-zone\-ttl ( unlimited | \fIttlval\fR );
+ min\-refresh\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ multi\-master \fIboolean\fR;
+ notify ( explicit | master\-only | \fIboolean\fR );
+ notify\-delay \fIinteger\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | *
+ ) ] [ dscp \fIinteger\fR ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR
+ | * ) ] [ dscp \fIinteger\fR ];
+ notify\-to\-soa \fIboolean\fR;
+ pubkey \fIinteger\fR
+ \fIinteger\fR
+ \fIinteger\fR
+ request\-expire \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ serial\-update\-method ( date | increment | unixtime );
+ server\-addresses { ( \fIipv4_address\fR | \fIipv6_address\fR ) [
+ port \fIinteger\fR ]; \&.\&.\&. };
+ server\-names { \fIquoted_string\fR; \&.\&.\&. };
+ sig\-signing\-nodes \fIinteger\fR;
+ sig\-signing\-signatures \fIinteger\fR;
+ sig\-signing\-type \fIinteger\fR;
+ sig\-validity\-interval \fIinteger\fR [ \fIinteger\fR ];
+ transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR |
+ * ) ] [ dscp \fIinteger\fR ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port (
+ \fIinteger\fR | * ) ] [ dscp \fIinteger\fR ];
+ try\-tcp\-refresh \fIboolean\fR;
+ type ( delegation\-only | forward | hint | master | redirect
+ | slave | static\-stub | stub );
+ update\-check\-ksk \fIboolean\fR;
+ update\-policy ( local | { ( deny | grant ) \fIstring\fR (
+ 6to4\-self | external | krb5\-self | krb5\-selfsub |
+ krb5\-subdomain | ms\-self | ms\-selfsub | ms\-subdomain |
+ name | self | selfsub | selfwild | subdomain | tcp\-self
+ | wildcard | zonesub ) [ \fIstring\fR ] \fIrrtypelist\fR; \&.\&.\&. };
+ use\-alt\-transfer\-source \fIboolean\fR;
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ zone\-statistics ( full | terse | none | \fIboolean\fR );
+ };
+ zone\-statistics ( full | terse | none | \fIboolean\fR );
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "ZONE"
+.sp
+.if n \{\
+.RS 4
+.\}
+.nf
+zone \fIstring\fR [ \fIclass\fR ] {
+ allow\-notify { \fIaddress_match_element\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; \&.\&.\&. };
+ also\-notify [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fImasters\fR |
+ \fIipv4_address\fR [ port \fIinteger\fR ] | \fIipv6_address\fR [ port
+ \fIinteger\fR ] ) [ key \fIstring\fR ]; \&.\&.\&. };
+ alt\-transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * )
+ ] [ dscp \fIinteger\fR ];
+ alt\-transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR |
+ * ) ] [ dscp \fIinteger\fR ];
+ auto\-dnssec ( allow | maintain | off );
+ check\-dup\-records ( fail | warn | ignore );
+ check\-integrity \fIboolean\fR;
+ check\-mx ( fail | warn | ignore );
+ check\-mx\-cname ( fail | warn | ignore );
+ check\-names ( fail | warn | ignore );
+ check\-sibling \fIboolean\fR;
+ check\-spf ( warn | ignore );
+ check\-srv\-cname ( fail | warn | ignore );
+ check\-wildcard \fIboolean\fR;
+ database \fIstring\fR;
+ delegation\-only \fIboolean\fR;
+ dialup ( notify | notify\-passive | passive | refresh | \fIboolean\fR );
+ dlz \fIstring\fR;
+ dnssec\-dnskey\-kskonly \fIboolean\fR;
+ dnssec\-loadkeys\-interval \fIinteger\fR;
+ dnssec\-secure\-to\-insecure \fIboolean\fR;
+ dnssec\-update\-mode ( maintain | no\-resign );
+ file \fIquoted_string\fR;
+ forward ( first | only );
+ forwarders [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fIipv4_address\fR
+ | \fIipv6_address\fR ) [ port \fIinteger\fR ] [ dscp \fIinteger\fR ]; \&.\&.\&. };
+ in\-view \fIstring\fR;
+ inline\-signing \fIboolean\fR;
+ ixfr\-from\-differences \fIboolean\fR;
+ journal \fIquoted_string\fR;
+ key\-directory \fIquoted_string\fR;
+ masterfile\-format ( map | raw | text );
+ masterfile\-style ( full | relative );
+ masters [ port \fIinteger\fR ] [ dscp \fIinteger\fR ] { ( \fImasters\fR |
+ \fIipv4_address\fR [ port \fIinteger\fR ] | \fIipv6_address\fR [ port
+ \fIinteger\fR ] ) [ key \fIstring\fR ]; \&.\&.\&. };
+ max\-journal\-size ( unlimited | \fIsizeval\fR );
+ max\-records \fIinteger\fR;
+ max\-refresh\-time \fIinteger\fR;
+ max\-retry\-time \fIinteger\fR;
+ max\-transfer\-idle\-in \fIinteger\fR;
+ max\-transfer\-idle\-out \fIinteger\fR;
+ max\-transfer\-time\-in \fIinteger\fR;
+ max\-transfer\-time\-out \fIinteger\fR;
+ max\-zone\-ttl ( unlimited | \fIttlval\fR );
+ min\-refresh\-time \fIinteger\fR;
+ min\-retry\-time \fIinteger\fR;
+ multi\-master \fIboolean\fR;
+ notify ( explicit | master\-only | \fIboolean\fR );
+ notify\-delay \fIinteger\fR;
+ notify\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ] [
+ dscp \fIinteger\fR ];
+ notify\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * ) ]
+ [ dscp \fIinteger\fR ];
+ notify\-to\-soa \fIboolean\fR;
+ pubkey \fIinteger\fR \fIinteger\fR
+ request\-expire \fIboolean\fR;
+ request\-ixfr \fIboolean\fR;
+ serial\-update\-method ( date | increment | unixtime );
+ server\-addresses { ( \fIipv4_address\fR | \fIipv6_address\fR ) [ port
+ \fIinteger\fR ]; \&.\&.\&. };
+ server\-names { \fIquoted_string\fR; \&.\&.\&. };
+ sig\-signing\-nodes \fIinteger\fR;
+ sig\-signing\-signatures \fIinteger\fR;
+ sig\-signing\-type \fIinteger\fR;
+ sig\-validity\-interval \fIinteger\fR [ \fIinteger\fR ];
+ transfer\-source ( \fIipv4_address\fR | * ) [ port ( \fIinteger\fR | * ) ] [
+ dscp \fIinteger\fR ];
+ transfer\-source\-v6 ( \fIipv6_address\fR | * ) [ port ( \fIinteger\fR | * )
+ ] [ dscp \fIinteger\fR ];
+ try\-tcp\-refresh \fIboolean\fR;
+ type ( delegation\-only | forward | hint | master | redirect | slave
+ | static\-stub | stub );
+ update\-check\-ksk \fIboolean\fR;
+ update\-policy ( local | { ( deny | grant ) \fIstring\fR ( 6to4\-self |
+ external | krb5\-self | krb5\-selfsub | krb5\-subdomain | ms\-self
+ | ms\-selfsub | ms\-subdomain | name | self | selfsub | selfwild
+ | subdomain | tcp\-self | wildcard | zonesub ) [ \fIstring\fR ]
+ \fIrrtypelist\fR; \&.\&.\&. };
+ use\-alt\-transfer\-source \fIboolean\fR;
+ zero\-no\-soa\-ttl \fIboolean\fR;
+ zone\-statistics ( full | terse | none | \fIboolean\fR );
+};
+.fi
+.if n \{\
+.RE
+.\}
+.SH "FILES"
+.PP
+/etc/named\&.conf
+.SH "SEE ALSO"
+.PP
+\fBddns-confgen\fR(8),
+\fBnamed\fR(8),
+\fBnamed-checkconf\fR(8),
+\fBrndc\fR(8),
+\fBrndc-confgen\fR(8),
+BIND 9 Administrator Reference Manual\&.
+.SH "AUTHOR"
+.PP
+\fBInternet Systems Consortium, Inc\&.\fR
+.SH "COPYRIGHT"
+.br
+Copyright \(co 2004-2019 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..113b990
--- /dev/null
+++ b/bin/named/named.conf.docbook
@@ -0,0 +1,1006 @@
+<!--
+ - Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ -
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ -
+ - See the COPYRIGHT file distributed with this work for additional
+ - information regarding copyright ownership.
+-->
+
+<!-- Generated by doc/misc/docbook-options.pl -->
+
+<refentry xmlns:db="http://docbook.org/ns/docbook" version="5.0" xml:id="man.named.conf">
+ <info>
+ <date>2018-06-21</date>
+ </info>
+ <refentryinfo>
+ <corpname>ISC</corpname>
+ <corpauthor>Internet Systems Consortium, Inc.</corpauthor>
+ </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 <command>named</command></refpurpose>
+ </refnamediv>
+
+ <docinfo>
+ <copyright>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2006</year>
+ <year>2007</year>
+ <year>2008</year>
+ <year>2009</year>
+ <year>2010</year>
+ <year>2011</year>
+ <year>2012</year>
+ <year>2013</year>
+ <year>2014</year>
+ <year>2015</year>
+ <year>2016</year>
+ <year>2017</year>
+ <year>2018</year>
+ <year>2019</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis sepchar=" ">
+ <command>named.conf</command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsection><info><title>DESCRIPTION</title></info>
+
+ <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>
+ </refsection>
+
+ <refsection><info><title>ACL</title></info>
+
+ <literallayout class="normal">
+acl <replaceable>string</replaceable> { <replaceable>address_match_element</replaceable>; ... };
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>CONTROLS</title></info>
+
+ <literallayout class="normal">
+controls {
+ inet ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> |
+ * ) [ port ( <replaceable>integer</replaceable> | * ) ] allow
+ { <replaceable>address_match_element</replaceable>; ... } [
+ keys { <replaceable>string</replaceable>; ... } ] [ read-only
+ <replaceable>boolean</replaceable> ];
+ unix <replaceable>quoted_string</replaceable> perm <replaceable>integer</replaceable>
+ owner <replaceable>integer</replaceable> group <replaceable>integer</replaceable> [
+ keys { <replaceable>string</replaceable>; ... } ] [ read-only
+ <replaceable>boolean</replaceable> ];
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>DLZ</title></info>
+
+ <literallayout class="normal">
+dlz <replaceable>string</replaceable> {
+ database <replaceable>string</replaceable>;
+ search <replaceable>boolean</replaceable>;
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>DYNDB</title></info>
+
+ <literallayout class="normal">
+dyndb <replaceable>string</replaceable> <replaceable>quoted_string</replaceable> {
+ <replaceable>unspecified-text</replaceable> };
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>KEY</title></info>
+
+ <literallayout class="normal">
+key <replaceable>string</replaceable> {
+ algorithm <replaceable>string</replaceable>;
+ secret <replaceable>string</replaceable>;
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>LOGGING</title></info>
+
+ <literallayout class="normal">
+logging {
+ category <replaceable>string</replaceable> { <replaceable>string</replaceable>; ... };
+ channel <replaceable>string</replaceable> {
+ buffered <replaceable>boolean</replaceable>;
+ file <replaceable>quoted_string</replaceable> [ versions ( "unlimited" | <replaceable>integer</replaceable> )
+ ] [ size <replaceable>size</replaceable> ];
+ null;
+ print-category <replaceable>boolean</replaceable>;
+ print-severity <replaceable>boolean</replaceable>;
+ print-time <replaceable>boolean</replaceable>;
+ severity <replaceable>log_severity</replaceable>;
+ stderr;
+ syslog [ <replaceable>syslog_facility</replaceable> ];
+ };
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>LWRES</title></info>
+
+ <literallayout class="normal">
+lwres {
+ listen-on [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>ipv4_address</replaceable>
+ | <replaceable>ipv6_address</replaceable> ) [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ]; ... };
+ lwres-clients <replaceable>integer</replaceable>;
+ lwres-tasks <replaceable>integer</replaceable>;
+ ndots <replaceable>integer</replaceable>;
+ search { <replaceable>string</replaceable>; ... };
+ view <replaceable>string</replaceable> [ <replaceable>class</replaceable> ];
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>MANAGED-KEYS</title></info>
+
+ <literallayout class="normal">
+managed-keys { <replaceable>string</replaceable> <replaceable>string</replaceable> <replaceable>integer</replaceable>
+ <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>quoted_string</replaceable>; ... };
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>MASTERS</title></info>
+
+ <literallayout class="normal">
+masters <replaceable>string</replaceable> [ port <replaceable>integer</replaceable> ] [ dscp
+ <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> [
+ port <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port
+ <replaceable>integer</replaceable> ] ) [ key <replaceable>string</replaceable> ]; ... };
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>OPTIONS</title></info>
+
+ <literallayout class="normal">
+options {
+ acache-cleaning-interval <replaceable>integer</replaceable>;
+ acache-enable <replaceable>boolean</replaceable>;
+ additional-from-auth <replaceable>boolean</replaceable>;
+ additional-from-cache <replaceable>boolean</replaceable>;
+ allow-new-zones <replaceable>boolean</replaceable>;
+ allow-notify { <replaceable>address_match_element</replaceable>; ... };
+ allow-query { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache-on { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-on { <replaceable>address_match_element</replaceable>; ... };
+ allow-recursion { <replaceable>address_match_element</replaceable>; ... };
+ allow-recursion-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>; ... };
+ also-notify [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> |
+ <replaceable>ipv4_address</replaceable> [ port <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port
+ <replaceable>integer</replaceable> ] ) [ key <replaceable>string</replaceable> ]; ... };
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * )
+ ] [ dscp <replaceable>integer</replaceable> ];
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> |
+ * ) ] [ dscp <replaceable>integer</replaceable> ];
+ answer-cookie <replaceable>boolean</replaceable>;
+ attach-cache <replaceable>string</replaceable>;
+ auth-nxdomain <replaceable>boolean</replaceable>; // default changed
+ auto-dnssec ( allow | maintain | off );
+ automatic-interface-scan <replaceable>boolean</replaceable>;
+ avoid-v4-udp-ports { <replaceable>portrange</replaceable>; ... };
+ avoid-v6-udp-ports { <replaceable>portrange</replaceable>; ... };
+ bindkeys-file <replaceable>quoted_string</replaceable>;
+ blackhole { <replaceable>address_match_element</replaceable>; ... };
+ cache-file <replaceable>quoted_string</replaceable>;
+ catalog-zones { zone <replaceable>quoted_string</replaceable> [ default-masters [ port
+ <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> [
+ port <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port <replaceable>integer</replaceable> ] ) [ key
+ <replaceable>string</replaceable> ]; ... } ] [ zone-directory <replaceable>quoted_string</replaceable> ] [
+ in-memory <replaceable>boolean</replaceable> ] [ min-update-interval <replaceable>integer</replaceable> ]; ... };
+ check-dup-records ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx ( fail | warn | ignore );
+ check-mx-cname ( fail | warn | ignore );
+ check-names ( master | slave | response
+ ) ( fail | warn | ignore );
+ check-sibling <replaceable>boolean</replaceable>;
+ check-spf ( warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ check-wildcard <replaceable>boolean</replaceable>;
+ cleaning-interval <replaceable>integer</replaceable>;
+ clients-per-query <replaceable>integer</replaceable>;
+ cookie-algorithm ( aes | sha1 | sha256 );
+ cookie-secret <replaceable>string</replaceable>;
+ coresize ( default | unlimited | <replaceable>sizeval</replaceable> );
+ datasize ( default | unlimited | <replaceable>sizeval</replaceable> );
+ deny-answer-addresses { <replaceable>address_match_element</replaceable>; ... } [
+ except-from { <replaceable>quoted_string</replaceable>; ... } ];
+ deny-answer-aliases { <replaceable>quoted_string</replaceable>; ... } [ except-from {
+ <replaceable>quoted_string</replaceable>; ... } ];
+ dialup ( notify | notify-passive | passive | refresh | <replaceable>boolean</replaceable> );
+ directory <replaceable>quoted_string</replaceable>;
+ disable-algorithms <replaceable>string</replaceable> { <replaceable>string</replaceable>;
+ ... };
+ disable-ds-digests <replaceable>string</replaceable> { <replaceable>string</replaceable>;
+ ... };
+ disable-empty-zone <replaceable>string</replaceable>;
+ dns64 <replaceable>netprefix</replaceable> {
+ break-dnssec <replaceable>boolean</replaceable>;
+ clients { <replaceable>address_match_element</replaceable>; ... };
+ exclude { <replaceable>address_match_element</replaceable>; ... };
+ mapped { <replaceable>address_match_element</replaceable>; ... };
+ recursive-only <replaceable>boolean</replaceable>;
+ suffix <replaceable>ipv6_address</replaceable>;
+ };
+ dns64-contact <replaceable>string</replaceable>;
+ dns64-server <replaceable>string</replaceable>;
+ dnssec-accept-expired <replaceable>boolean</replaceable>;
+ dnssec-dnskey-kskonly <replaceable>boolean</replaceable>;
+ dnssec-enable <replaceable>boolean</replaceable>;
+ dnssec-loadkeys-interval <replaceable>integer</replaceable>;
+ dnssec-lookaside ( <replaceable>string</replaceable> trust-anchor
+ <replaceable>string</replaceable> | auto | no );
+ dnssec-must-be-secure <replaceable>string</replaceable> <replaceable>boolean</replaceable>;
+ dnssec-secure-to-insecure <replaceable>boolean</replaceable>;
+ dnssec-update-mode ( maintain | no-resign );
+ dnssec-validation ( yes | no | auto );
+ dnstap { ( all | auth | client | forwarder |
+ resolver ) [ ( query | response ) ]; ... };
+ dnstap-identity ( <replaceable>quoted_string</replaceable> | none |
+ hostname );
+ dnstap-output ( file | unix ) <replaceable>quoted_string</replaceable>;
+ dnstap-version ( <replaceable>quoted_string</replaceable> | none );
+ dscp <replaceable>integer</replaceable>;
+ dual-stack-servers [ port <replaceable>integer</replaceable> ] { ( <replaceable>quoted_string</replaceable> [ port
+ <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] | <replaceable>ipv4_address</replaceable> [ port
+ <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port
+ <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] ); ... };
+ dump-file <replaceable>quoted_string</replaceable>;
+ edns-udp-size <replaceable>integer</replaceable>;
+ empty-contact <replaceable>string</replaceable>;
+ empty-server <replaceable>string</replaceable>;
+ empty-zones-enable <replaceable>boolean</replaceable>;
+ fetch-quota-params <replaceable>integer</replaceable> <replaceable>fixedpoint</replaceable> <replaceable>fixedpoint</replaceable> <replaceable>fixedpoint</replaceable>;
+ fetches-per-server <replaceable>integer</replaceable> [ ( drop | fail ) ];
+ fetches-per-zone <replaceable>integer</replaceable> [ ( drop | fail ) ];
+ files ( default | unlimited | <replaceable>sizeval</replaceable> );
+ filter-aaaa { <replaceable>address_match_element</replaceable>; ... };
+ filter-aaaa-on-v4 ( break-dnssec | <replaceable>boolean</replaceable> );
+ filter-aaaa-on-v6 ( break-dnssec | <replaceable>boolean</replaceable> );
+ flush-zones-on-shutdown <replaceable>boolean</replaceable>;
+ forward ( first | only );
+ forwarders [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>ipv4_address</replaceable>
+ | <replaceable>ipv6_address</replaceable> ) [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ]; ... };
+ fstrm-set-buffer-hint <replaceable>integer</replaceable>;
+ fstrm-set-flush-timeout <replaceable>integer</replaceable>;
+ fstrm-set-input-queue-size <replaceable>integer</replaceable>;
+ fstrm-set-output-notify-threshold <replaceable>integer</replaceable>;
+ fstrm-set-output-queue-model ( mpsc | spsc );
+ fstrm-set-output-queue-size <replaceable>integer</replaceable>;
+ fstrm-set-reopen-interval <replaceable>integer</replaceable>;
+ geoip-directory ( <replaceable>quoted_string</replaceable> | none );
+ geoip-use-ecs <replaceable>boolean</replaceable>;
+ heartbeat-interval <replaceable>integer</replaceable>;
+ hostname ( <replaceable>quoted_string</replaceable> | none );
+ inline-signing <replaceable>boolean</replaceable>;
+ interface-interval <replaceable>integer</replaceable>;
+ ixfr-from-differences ( master | slave | <replaceable>boolean</replaceable> );
+ keep-response-order { <replaceable>address_match_element</replaceable>; ... };
+ key-directory <replaceable>quoted_string</replaceable>;
+ lame-ttl <replaceable>ttlval</replaceable>;
+ listen-on [ port <replaceable>integer</replaceable> ] [ dscp
+ <replaceable>integer</replaceable> ] {
+ <replaceable>address_match_element</replaceable>; ... };
+ listen-on-v6 [ port <replaceable>integer</replaceable> ] [ dscp
+ <replaceable>integer</replaceable> ] {
+ <replaceable>address_match_element</replaceable>; ... };
+ lmdb-mapsize <replaceable>sizeval</replaceable>;
+ lock-file ( <replaceable>quoted_string</replaceable> | none );
+ managed-keys-directory <replaceable>quoted_string</replaceable>;
+ masterfile-format ( map | raw | text );
+ masterfile-style ( full | relative );
+ match-mapped-addresses <replaceable>boolean</replaceable>;
+ max-acache-size ( unlimited | <replaceable>sizeval</replaceable> );
+ max-cache-size ( default | unlimited | <replaceable>sizeval</replaceable> | <replaceable>percentage</replaceable> );
+ max-cache-ttl <replaceable>integer</replaceable>;
+ max-clients-per-query <replaceable>integer</replaceable>;
+ max-journal-size ( unlimited | <replaceable>sizeval</replaceable> );
+ max-ncache-ttl <replaceable>integer</replaceable>;
+ max-records <replaceable>integer</replaceable>;
+ max-recursion-depth <replaceable>integer</replaceable>;
+ max-recursion-queries <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ max-rsa-exponent-size <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ max-zone-ttl ( unlimited | <replaceable>ttlval</replaceable> );
+ memstatistics <replaceable>boolean</replaceable>;
+ memstatistics-file <replaceable>quoted_string</replaceable>;
+ message-compression <replaceable>boolean</replaceable>;
+ min-refresh-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ minimal-any <replaceable>boolean</replaceable>;
+ minimal-responses ( no-auth | no-auth-recursive | <replaceable>boolean</replaceable> );
+ multi-master <replaceable>boolean</replaceable>;
+ no-case-compress { <replaceable>address_match_element</replaceable>; ... };
+ nocookie-udp-size <replaceable>integer</replaceable>;
+ notify ( explicit | master-only | <replaceable>boolean</replaceable> );
+ notify-delay <replaceable>integer</replaceable>;
+ notify-rate <replaceable>integer</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
+ dscp <replaceable>integer</replaceable> ];
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ]
+ [ dscp <replaceable>integer</replaceable> ];
+ notify-to-soa <replaceable>boolean</replaceable>;
+ nta-lifetime <replaceable>ttlval</replaceable>;
+ nta-recheck <replaceable>ttlval</replaceable>;
+ nxdomain-redirect <replaceable>string</replaceable>;
+ pid-file ( <replaceable>quoted_string</replaceable> | none );
+ port <replaceable>integer</replaceable>;
+ preferred-glue <replaceable>string</replaceable>;
+ prefetch <replaceable>integer</replaceable> [ <replaceable>integer</replaceable> ];
+ provide-ixfr <replaceable>boolean</replaceable>;
+ query-source ( ( [ address ] ( <replaceable>ipv4_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] ) | ( [ [ address ] ( <replaceable>ipv4_address</replaceable> | * ) ]
+ port ( <replaceable>integer</replaceable> | * ) ) ) [ dscp <replaceable>integer</replaceable> ];
+ query-source-v6 ( ( [ address ] ( <replaceable>ipv6_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] ) | ( [ [ address ] ( <replaceable>ipv6_address</replaceable> | * ) ]
+ port ( <replaceable>integer</replaceable> | * ) ) ) [ dscp <replaceable>integer</replaceable> ];
+ querylog <replaceable>boolean</replaceable>;
+ random-device <replaceable>quoted_string</replaceable>;
+ rate-limit {
+ all-per-second <replaceable>integer</replaceable>;
+ errors-per-second <replaceable>integer</replaceable>;
+ exempt-clients { <replaceable>address_match_element</replaceable>; ... };
+ ipv4-prefix-length <replaceable>integer</replaceable>;
+ ipv6-prefix-length <replaceable>integer</replaceable>;
+ log-only <replaceable>boolean</replaceable>;
+ max-table-size <replaceable>integer</replaceable>;
+ min-table-size <replaceable>integer</replaceable>;
+ nodata-per-second <replaceable>integer</replaceable>;
+ nxdomains-per-second <replaceable>integer</replaceable>;
+ qps-scale <replaceable>integer</replaceable>;
+ referrals-per-second <replaceable>integer</replaceable>;
+ responses-per-second <replaceable>integer</replaceable>;
+ slip <replaceable>integer</replaceable>;
+ window <replaceable>integer</replaceable>;
+ };
+ recursing-file <replaceable>quoted_string</replaceable>;
+ recursion <replaceable>boolean</replaceable>;
+ recursive-clients <replaceable>integer</replaceable>;
+ request-expire <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ request-nsid <replaceable>boolean</replaceable>;
+ require-server-cookie <replaceable>boolean</replaceable>;
+ reserved-sockets <replaceable>integer</replaceable>;
+ resolver-query-timeout <replaceable>integer</replaceable>;
+ response-policy { zone <replaceable>quoted_string</replaceable> [ log <replaceable>boolean</replaceable> ] [
+ max-policy-ttl <replaceable>integer</replaceable> ] [ policy ( cname | disabled | drop |
+ given | no-op | nodata | nxdomain | passthru | tcp-only
+ <replaceable>quoted_string</replaceable> ) ] [ recursive-only <replaceable>boolean</replaceable> ]; ... } [
+ break-dnssec <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>integer</replaceable> ] [
+ min-ns-dots <replaceable>integer</replaceable> ] [ nsip-wait-recurse <replaceable>boolean</replaceable> ] [
+ qname-wait-recurse <replaceable>boolean</replaceable> ] [ recursive-only <replaceable>boolean</replaceable> ];
+ root-delegation-only [ exclude { <replaceable>quoted_string</replaceable>; ... } ];
+ root-key-sentinel <replaceable>boolean</replaceable>;
+ rrset-order { [ class <replaceable>string</replaceable> ] [ type <replaceable>string</replaceable> ] [ name
+ <replaceable>quoted_string</replaceable> ] <replaceable>string</replaceable> <replaceable>string</replaceable>; ... };
+ secroots-file <replaceable>quoted_string</replaceable>;
+ send-cookie <replaceable>boolean</replaceable>;
+ serial-query-rate <replaceable>integer</replaceable>;
+ serial-update-method ( date | increment | unixtime );
+ server-id ( <replaceable>quoted_string</replaceable> | none | hostname );
+ servfail-ttl <replaceable>ttlval</replaceable>;
+ session-keyalg <replaceable>string</replaceable>;
+ session-keyfile ( <replaceable>quoted_string</replaceable> | none );
+ session-keyname <replaceable>string</replaceable>;
+ sig-signing-nodes <replaceable>integer</replaceable>;
+ sig-signing-signatures <replaceable>integer</replaceable>;
+ sig-signing-type <replaceable>integer</replaceable>;
+ sig-validity-interval <replaceable>integer</replaceable> [ <replaceable>integer</replaceable> ];
+ sortlist { <replaceable>address_match_element</replaceable>; ... };
+ stacksize ( default | unlimited | <replaceable>sizeval</replaceable> );
+ startup-notify-rate <replaceable>integer</replaceable>;
+ statistics-file <replaceable>quoted_string</replaceable>;
+ tcp-clients <replaceable>integer</replaceable>;
+ tcp-listen-queue <replaceable>integer</replaceable>;
+ tkey-dhkey <replaceable>quoted_string</replaceable> <replaceable>integer</replaceable>;
+ tkey-domain <replaceable>quoted_string</replaceable>;
+ tkey-gssapi-credential <replaceable>quoted_string</replaceable>;
+ tkey-gssapi-keytab <replaceable>quoted_string</replaceable>;
+ transfer-format ( many-answers | one-answer );
+ transfer-message-size <replaceable>integer</replaceable>;
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
+ dscp <replaceable>integer</replaceable> ];
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * )
+ ] [ dscp <replaceable>integer</replaceable> ];
+ transfers-in <replaceable>integer</replaceable>;
+ transfers-out <replaceable>integer</replaceable>;
+ transfers-per-ns <replaceable>integer</replaceable>;
+ trust-anchor-telemetry <replaceable>boolean</replaceable>; // experimental
+ try-tcp-refresh <replaceable>boolean</replaceable>;
+ update-check-ksk <replaceable>boolean</replaceable>;
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+ use-v4-udp-ports { <replaceable>portrange</replaceable>; ... };
+ use-v6-udp-ports { <replaceable>portrange</replaceable>; ... };
+ v6-bias <replaceable>integer</replaceable>;
+ version ( <replaceable>quoted_string</replaceable> | none );
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+ zero-no-soa-ttl-cache <replaceable>boolean</replaceable>;
+ zone-statistics ( full | terse | none | <replaceable>boolean</replaceable> );
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>SERVER</title></info>
+
+ <literallayout class="normal">
+server <replaceable>netprefix</replaceable> {
+ bogus <replaceable>boolean</replaceable>;
+ edns <replaceable>boolean</replaceable>;
+ edns-udp-size <replaceable>integer</replaceable>;
+ edns-version <replaceable>integer</replaceable>;
+ keys <replaceable>server_key</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
+ dscp <replaceable>integer</replaceable> ];
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ]
+ [ dscp <replaceable>integer</replaceable> ];
+ provide-ixfr <replaceable>boolean</replaceable>;
+ query-source ( ( [ address ] ( <replaceable>ipv4_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] ) | ( [ [ address ] ( <replaceable>ipv4_address</replaceable> | * ) ]
+ port ( <replaceable>integer</replaceable> | * ) ) ) [ dscp <replaceable>integer</replaceable> ];
+ query-source-v6 ( ( [ address ] ( <replaceable>ipv6_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] ) | ( [ [ address ] ( <replaceable>ipv6_address</replaceable> | * ) ]
+ port ( <replaceable>integer</replaceable> | * ) ) ) [ dscp <replaceable>integer</replaceable> ];
+ request-expire <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ request-nsid <replaceable>boolean</replaceable>;
+ send-cookie <replaceable>boolean</replaceable>;
+ tcp-only <replaceable>boolean</replaceable>;
+ transfer-format ( many-answers | one-answer );
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
+ dscp <replaceable>integer</replaceable> ];
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * )
+ ] [ dscp <replaceable>integer</replaceable> ];
+ transfers <replaceable>integer</replaceable>;
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>STATISTICS-CHANNELS</title></info>
+
+ <literallayout class="normal">
+statistics-channels {
+ inet ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> |
+ * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
+ allow { <replaceable>address_match_element</replaceable>; ...
+ } ];
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>TRUSTED-KEYS</title></info>
+
+ <literallayout class="normal">
+trusted-keys { <replaceable>string</replaceable> <replaceable>integer</replaceable> <replaceable>integer</replaceable>
+ <replaceable>integer</replaceable> <replaceable>quoted_string</replaceable>; ... };
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>VIEW</title></info>
+
+ <literallayout class="normal">
+view <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
+ acache-cleaning-interval <replaceable>integer</replaceable>;
+ acache-enable <replaceable>boolean</replaceable>;
+ additional-from-auth <replaceable>boolean</replaceable>;
+ additional-from-cache <replaceable>boolean</replaceable>;
+ allow-new-zones <replaceable>boolean</replaceable>;
+ allow-notify { <replaceable>address_match_element</replaceable>; ... };
+ allow-query { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-cache-on { <replaceable>address_match_element</replaceable>; ... };
+ allow-query-on { <replaceable>address_match_element</replaceable>; ... };
+ allow-recursion { <replaceable>address_match_element</replaceable>; ... };
+ allow-recursion-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>; ... };
+ also-notify [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> |
+ <replaceable>ipv4_address</replaceable> [ port <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port
+ <replaceable>integer</replaceable> ] ) [ key <replaceable>string</replaceable> ]; ... };
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * )
+ ] [ dscp <replaceable>integer</replaceable> ];
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> |
+ * ) ] [ dscp <replaceable>integer</replaceable> ];
+ attach-cache <replaceable>string</replaceable>;
+ auth-nxdomain <replaceable>boolean</replaceable>; // default changed
+ auto-dnssec ( allow | maintain | off );
+ cache-file <replaceable>quoted_string</replaceable>;
+ catalog-zones { zone <replaceable>quoted_string</replaceable> [ default-masters [ port
+ <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> [
+ port <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port <replaceable>integer</replaceable> ] ) [ key
+ <replaceable>string</replaceable> ]; ... } ] [ zone-directory <replaceable>quoted_string</replaceable> ] [
+ in-memory <replaceable>boolean</replaceable> ] [ min-update-interval <replaceable>integer</replaceable> ]; ... };
+ check-dup-records ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx ( fail | warn | ignore );
+ check-mx-cname ( fail | warn | ignore );
+ check-names ( master | slave | response
+ ) ( fail | warn | ignore );
+ check-sibling <replaceable>boolean</replaceable>;
+ check-spf ( warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ check-wildcard <replaceable>boolean</replaceable>;
+ cleaning-interval <replaceable>integer</replaceable>;
+ clients-per-query <replaceable>integer</replaceable>;
+ deny-answer-addresses { <replaceable>address_match_element</replaceable>; ... } [
+ except-from { <replaceable>quoted_string</replaceable>; ... } ];
+ deny-answer-aliases { <replaceable>quoted_string</replaceable>; ... } [ except-from {
+ <replaceable>quoted_string</replaceable>; ... } ];
+ dialup ( notify | notify-passive | passive | refresh | <replaceable>boolean</replaceable> );
+ disable-algorithms <replaceable>string</replaceable> { <replaceable>string</replaceable>;
+ ... };
+ disable-ds-digests <replaceable>string</replaceable> { <replaceable>string</replaceable>;
+ ... };
+ disable-empty-zone <replaceable>string</replaceable>;
+ dlz <replaceable>string</replaceable> {
+ database <replaceable>string</replaceable>;
+ search <replaceable>boolean</replaceable>;
+ };
+ dns64 <replaceable>netprefix</replaceable> {
+ break-dnssec <replaceable>boolean</replaceable>;
+ clients { <replaceable>address_match_element</replaceable>; ... };
+ exclude { <replaceable>address_match_element</replaceable>; ... };
+ mapped { <replaceable>address_match_element</replaceable>; ... };
+ recursive-only <replaceable>boolean</replaceable>;
+ suffix <replaceable>ipv6_address</replaceable>;
+ };
+ dns64-contact <replaceable>string</replaceable>;
+ dns64-server <replaceable>string</replaceable>;
+ dnssec-accept-expired <replaceable>boolean</replaceable>;
+ dnssec-dnskey-kskonly <replaceable>boolean</replaceable>;
+ dnssec-enable <replaceable>boolean</replaceable>;
+ dnssec-loadkeys-interval <replaceable>integer</replaceable>;
+ dnssec-lookaside ( <replaceable>string</replaceable> trust-anchor
+ <replaceable>string</replaceable> | auto | no );
+ dnssec-must-be-secure <replaceable>string</replaceable> <replaceable>boolean</replaceable>;
+ dnssec-secure-to-insecure <replaceable>boolean</replaceable>;
+ dnssec-update-mode ( maintain | no-resign );
+ dnssec-validation ( yes | no | auto );
+ dnstap { ( all | auth | client | forwarder |
+ resolver ) [ ( query | response ) ]; ... };
+ dual-stack-servers [ port <replaceable>integer</replaceable> ] { ( <replaceable>quoted_string</replaceable> [ port
+ <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] | <replaceable>ipv4_address</replaceable> [ port
+ <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port
+ <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] ); ... };
+ dyndb <replaceable>string</replaceable> <replaceable>quoted_string</replaceable> {
+ <replaceable>unspecified-text</replaceable> };
+ edns-udp-size <replaceable>integer</replaceable>;
+ empty-contact <replaceable>string</replaceable>;
+ empty-server <replaceable>string</replaceable>;
+ empty-zones-enable <replaceable>boolean</replaceable>;
+ fetch-quota-params <replaceable>integer</replaceable> <replaceable>fixedpoint</replaceable> <replaceable>fixedpoint</replaceable> <replaceable>fixedpoint</replaceable>;
+ fetches-per-server <replaceable>integer</replaceable> [ ( drop | fail ) ];
+ fetches-per-zone <replaceable>integer</replaceable> [ ( drop | fail ) ];
+ filter-aaaa { <replaceable>address_match_element</replaceable>; ... };
+ filter-aaaa-on-v4 ( break-dnssec | <replaceable>boolean</replaceable> );
+ filter-aaaa-on-v6 ( break-dnssec | <replaceable>boolean</replaceable> );
+ forward ( first | only );
+ forwarders [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>ipv4_address</replaceable>
+ | <replaceable>ipv6_address</replaceable> ) [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ]; ... };
+ inline-signing <replaceable>boolean</replaceable>;
+ ixfr-from-differences ( master | slave | <replaceable>boolean</replaceable> );
+ key <replaceable>string</replaceable> {
+ algorithm <replaceable>string</replaceable>;
+ secret <replaceable>string</replaceable>;
+ };
+ key-directory <replaceable>quoted_string</replaceable>;
+ lame-ttl <replaceable>ttlval</replaceable>;
+ lmdb-mapsize <replaceable>sizeval</replaceable>;
+ managed-keys { <replaceable>string</replaceable> <replaceable>string</replaceable>
+ <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>integer</replaceable>
+ <replaceable>quoted_string</replaceable>; ... };
+ masterfile-format ( map | raw | text );
+ masterfile-style ( full | relative );
+ match-clients { <replaceable>address_match_element</replaceable>; ... };
+ match-destinations { <replaceable>address_match_element</replaceable>; ... };
+ match-recursive-only <replaceable>boolean</replaceable>;
+ max-acache-size ( unlimited | <replaceable>sizeval</replaceable> );
+ max-cache-size ( default | unlimited | <replaceable>sizeval</replaceable> | <replaceable>percentage</replaceable> );
+ max-cache-ttl <replaceable>integer</replaceable>;
+ max-clients-per-query <replaceable>integer</replaceable>;
+ max-journal-size ( unlimited | <replaceable>sizeval</replaceable> );
+ max-ncache-ttl <replaceable>integer</replaceable>;
+ max-records <replaceable>integer</replaceable>;
+ max-recursion-depth <replaceable>integer</replaceable>;
+ max-recursion-queries <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ max-zone-ttl ( unlimited | <replaceable>ttlval</replaceable> );
+ message-compression <replaceable>boolean</replaceable>;
+ min-refresh-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ minimal-any <replaceable>boolean</replaceable>;
+ minimal-responses ( no-auth | no-auth-recursive | <replaceable>boolean</replaceable> );
+ multi-master <replaceable>boolean</replaceable>;
+ no-case-compress { <replaceable>address_match_element</replaceable>; ... };
+ nocookie-udp-size <replaceable>integer</replaceable>;
+ notify ( explicit | master-only | <replaceable>boolean</replaceable> );
+ notify-delay <replaceable>integer</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
+ dscp <replaceable>integer</replaceable> ];
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ]
+ [ dscp <replaceable>integer</replaceable> ];
+ notify-to-soa <replaceable>boolean</replaceable>;
+ nta-lifetime <replaceable>ttlval</replaceable>;
+ nta-recheck <replaceable>ttlval</replaceable>;
+ nxdomain-redirect <replaceable>string</replaceable>;
+ preferred-glue <replaceable>string</replaceable>;
+ prefetch <replaceable>integer</replaceable> [ <replaceable>integer</replaceable> ];
+ provide-ixfr <replaceable>boolean</replaceable>;
+ query-source ( ( [ address ] ( <replaceable>ipv4_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] ) | ( [ [ address ] ( <replaceable>ipv4_address</replaceable> | * ) ]
+ port ( <replaceable>integer</replaceable> | * ) ) ) [ dscp <replaceable>integer</replaceable> ];
+ query-source-v6 ( ( [ address ] ( <replaceable>ipv6_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] ) | ( [ [ address ] ( <replaceable>ipv6_address</replaceable> | * ) ]
+ port ( <replaceable>integer</replaceable> | * ) ) ) [ dscp <replaceable>integer</replaceable> ];
+ rate-limit {
+ all-per-second <replaceable>integer</replaceable>;
+ errors-per-second <replaceable>integer</replaceable>;
+ exempt-clients { <replaceable>address_match_element</replaceable>; ... };
+ ipv4-prefix-length <replaceable>integer</replaceable>;
+ ipv6-prefix-length <replaceable>integer</replaceable>;
+ log-only <replaceable>boolean</replaceable>;
+ max-table-size <replaceable>integer</replaceable>;
+ min-table-size <replaceable>integer</replaceable>;
+ nodata-per-second <replaceable>integer</replaceable>;
+ nxdomains-per-second <replaceable>integer</replaceable>;
+ qps-scale <replaceable>integer</replaceable>;
+ referrals-per-second <replaceable>integer</replaceable>;
+ responses-per-second <replaceable>integer</replaceable>;
+ slip <replaceable>integer</replaceable>;
+ window <replaceable>integer</replaceable>;
+ };
+ recursion <replaceable>boolean</replaceable>;
+ request-expire <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ request-nsid <replaceable>boolean</replaceable>;
+ require-server-cookie <replaceable>boolean</replaceable>;
+ resolver-query-timeout <replaceable>integer</replaceable>;
+ response-policy { zone <replaceable>quoted_string</replaceable> [ log <replaceable>boolean</replaceable> ] [
+ max-policy-ttl <replaceable>integer</replaceable> ] [ policy ( cname | disabled | drop |
+ given | no-op | nodata | nxdomain | passthru | tcp-only
+ <replaceable>quoted_string</replaceable> ) ] [ recursive-only <replaceable>boolean</replaceable> ]; ... } [
+ break-dnssec <replaceable>boolean</replaceable> ] [ max-policy-ttl <replaceable>integer</replaceable> ] [
+ min-ns-dots <replaceable>integer</replaceable> ] [ nsip-wait-recurse <replaceable>boolean</replaceable> ] [
+ qname-wait-recurse <replaceable>boolean</replaceable> ] [ recursive-only <replaceable>boolean</replaceable> ];
+ root-delegation-only [ exclude { <replaceable>quoted_string</replaceable>; ... } ];
+ root-key-sentinel <replaceable>boolean</replaceable>;
+ rrset-order { [ class <replaceable>string</replaceable> ] [ type <replaceable>string</replaceable> ] [ name
+ <replaceable>quoted_string</replaceable> ] <replaceable>string</replaceable> <replaceable>string</replaceable>; ... };
+ send-cookie <replaceable>boolean</replaceable>;
+ serial-update-method ( date | increment | unixtime );
+ server <replaceable>netprefix</replaceable> {
+ bogus <replaceable>boolean</replaceable>;
+ edns <replaceable>boolean</replaceable>;
+ edns-udp-size <replaceable>integer</replaceable>;
+ edns-version <replaceable>integer</replaceable>;
+ keys <replaceable>server_key</replaceable>;
+ max-udp-size <replaceable>integer</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | *
+ ) ] [ dscp <replaceable>integer</replaceable> ];
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable>
+ | * ) ] [ dscp <replaceable>integer</replaceable> ];
+ provide-ixfr <replaceable>boolean</replaceable>;
+ query-source ( ( [ address ] ( <replaceable>ipv4_address</replaceable> | * ) [ port
+ ( <replaceable>integer</replaceable> | * ) ] ) | ( [ [ address ] (
+ <replaceable>ipv4_address</replaceable> | * ) ] port ( <replaceable>integer</replaceable> | * ) ) ) [
+ dscp <replaceable>integer</replaceable> ];
+ query-source-v6 ( ( [ address ] ( <replaceable>ipv6_address</replaceable> | * ) [
+ port ( <replaceable>integer</replaceable> | * ) ] ) | ( [ [ address ] (
+ <replaceable>ipv6_address</replaceable> | * ) ] port ( <replaceable>integer</replaceable> | * ) ) ) [
+ dscp <replaceable>integer</replaceable> ];
+ request-expire <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ request-nsid <replaceable>boolean</replaceable>;
+ send-cookie <replaceable>boolean</replaceable>;
+ tcp-only <replaceable>boolean</replaceable>;
+ transfer-format ( many-answers | one-answer );
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> |
+ * ) ] [ dscp <replaceable>integer</replaceable> ];
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] [ dscp <replaceable>integer</replaceable> ];
+ transfers <replaceable>integer</replaceable>;
+ };
+ servfail-ttl <replaceable>ttlval</replaceable>;
+ sig-signing-nodes <replaceable>integer</replaceable>;
+ sig-signing-signatures <replaceable>integer</replaceable>;
+ sig-signing-type <replaceable>integer</replaceable>;
+ sig-validity-interval <replaceable>integer</replaceable> [ <replaceable>integer</replaceable> ];
+ sortlist { <replaceable>address_match_element</replaceable>; ... };
+ transfer-format ( many-answers | one-answer );
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
+ dscp <replaceable>integer</replaceable> ];
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * )
+ ] [ dscp <replaceable>integer</replaceable> ];
+ trust-anchor-telemetry <replaceable>boolean</replaceable>; // experimental
+ trusted-keys { <replaceable>string</replaceable> <replaceable>integer</replaceable>
+ <replaceable>integer</replaceable> <replaceable>integer</replaceable> <replaceable>quoted_string</replaceable>;
+ ... };
+ try-tcp-refresh <replaceable>boolean</replaceable>;
+ update-check-ksk <replaceable>boolean</replaceable>;
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+ v6-bias <replaceable>integer</replaceable>;
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+ zero-no-soa-ttl-cache <replaceable>boolean</replaceable>;
+ zone <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
+ allow-notify { <replaceable>address_match_element</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>; ... };
+ also-notify [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { (
+ <replaceable>masters</replaceable> | <replaceable>ipv4_address</replaceable> [ port <replaceable>integer</replaceable> ] |
+ <replaceable>ipv6_address</replaceable> [ port <replaceable>integer</replaceable> ] ) [ key <replaceable>string</replaceable> ];
+ ... };
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] [ dscp <replaceable>integer</replaceable> ];
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] [ dscp <replaceable>integer</replaceable> ];
+ auto-dnssec ( allow | maintain | off );
+ check-dup-records ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx ( fail | warn | ignore );
+ check-mx-cname ( fail | warn | ignore );
+ check-names ( fail | warn | ignore );
+ check-sibling <replaceable>boolean</replaceable>;
+ check-spf ( warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ check-wildcard <replaceable>boolean</replaceable>;
+ database <replaceable>string</replaceable>;
+ delegation-only <replaceable>boolean</replaceable>;
+ dialup ( notify | notify-passive | passive | refresh |
+ <replaceable>boolean</replaceable> );
+ dlz <replaceable>string</replaceable>;
+ dnssec-dnskey-kskonly <replaceable>boolean</replaceable>;
+ dnssec-loadkeys-interval <replaceable>integer</replaceable>;
+ dnssec-secure-to-insecure <replaceable>boolean</replaceable>;
+ dnssec-update-mode ( maintain | no-resign );
+ file <replaceable>quoted_string</replaceable>;
+ forward ( first | only );
+ forwarders [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { (
+ <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) [ port <replaceable>integer</replaceable> ] [
+ dscp <replaceable>integer</replaceable> ]; ... };
+ in-view <replaceable>string</replaceable>;
+ inline-signing <replaceable>boolean</replaceable>;
+ ixfr-from-differences <replaceable>boolean</replaceable>;
+ journal <replaceable>quoted_string</replaceable>;
+ key-directory <replaceable>quoted_string</replaceable>;
+ masterfile-format ( map | raw | text );
+ masterfile-style ( full | relative );
+ masters [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable>
+ | <replaceable>ipv4_address</replaceable> [ port <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [
+ port <replaceable>integer</replaceable> ] ) [ key <replaceable>string</replaceable> ]; ... };
+ max-ixfr-log-size ( default | unlimited |
+ max-journal-size ( unlimited | <replaceable>sizeval</replaceable> );
+ max-records <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-zone-ttl ( unlimited | <replaceable>ttlval</replaceable> );
+ min-refresh-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ multi-master <replaceable>boolean</replaceable>;
+ notify ( explicit | master-only | <replaceable>boolean</replaceable> );
+ notify-delay <replaceable>integer</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | *
+ ) ] [ dscp <replaceable>integer</replaceable> ];
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable>
+ | * ) ] [ dscp <replaceable>integer</replaceable> ];
+ notify-to-soa <replaceable>boolean</replaceable>;
+ pubkey <replaceable>integer</replaceable>
+ <replaceable>integer</replaceable>
+ <replaceable>integer</replaceable>
+ request-expire <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ serial-update-method ( date | increment | unixtime );
+ server-addresses { ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) [
+ port <replaceable>integer</replaceable> ]; ... };
+ server-names { <replaceable>quoted_string</replaceable>; ... };
+ sig-signing-nodes <replaceable>integer</replaceable>;
+ sig-signing-signatures <replaceable>integer</replaceable>;
+ sig-signing-type <replaceable>integer</replaceable>;
+ sig-validity-interval <replaceable>integer</replaceable> [ <replaceable>integer</replaceable> ];
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> |
+ * ) ] [ dscp <replaceable>integer</replaceable> ];
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port (
+ <replaceable>integer</replaceable> | * ) ] [ dscp <replaceable>integer</replaceable> ];
+ try-tcp-refresh <replaceable>boolean</replaceable>;
+ type ( delegation-only | forward | hint | master | redirect
+ | slave | static-stub | stub );
+ update-check-ksk <replaceable>boolean</replaceable>;
+ update-policy ( local | { ( deny | grant ) <replaceable>string</replaceable> (
+ 6to4-self | external | krb5-self | krb5-selfsub |
+ krb5-subdomain | ms-self | ms-selfsub | ms-subdomain |
+ name | self | selfsub | selfwild | subdomain | tcp-self
+ | wildcard | zonesub ) [ <replaceable>string</replaceable> ] <replaceable>rrtypelist</replaceable>; ... };
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+ zone-statistics ( full | terse | none | <replaceable>boolean</replaceable> );
+ };
+ zone-statistics ( full | terse | none | <replaceable>boolean</replaceable> );
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>ZONE</title></info>
+
+ <literallayout class="normal">
+zone <replaceable>string</replaceable> [ <replaceable>class</replaceable> ] {
+ allow-notify { <replaceable>address_match_element</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>; ... };
+ also-notify [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> |
+ <replaceable>ipv4_address</replaceable> [ port <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port
+ <replaceable>integer</replaceable> ] ) [ key <replaceable>string</replaceable> ]; ... };
+ alt-transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * )
+ ] [ dscp <replaceable>integer</replaceable> ];
+ alt-transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> |
+ * ) ] [ dscp <replaceable>integer</replaceable> ];
+ auto-dnssec ( allow | maintain | off );
+ check-dup-records ( fail | warn | ignore );
+ check-integrity <replaceable>boolean</replaceable>;
+ check-mx ( fail | warn | ignore );
+ check-mx-cname ( fail | warn | ignore );
+ check-names ( fail | warn | ignore );
+ check-sibling <replaceable>boolean</replaceable>;
+ check-spf ( warn | ignore );
+ check-srv-cname ( fail | warn | ignore );
+ check-wildcard <replaceable>boolean</replaceable>;
+ database <replaceable>string</replaceable>;
+ delegation-only <replaceable>boolean</replaceable>;
+ dialup ( notify | notify-passive | passive | refresh | <replaceable>boolean</replaceable> );
+ dlz <replaceable>string</replaceable>;
+ dnssec-dnskey-kskonly <replaceable>boolean</replaceable>;
+ dnssec-loadkeys-interval <replaceable>integer</replaceable>;
+ dnssec-secure-to-insecure <replaceable>boolean</replaceable>;
+ dnssec-update-mode ( maintain | no-resign );
+ file <replaceable>quoted_string</replaceable>;
+ forward ( first | only );
+ forwarders [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>ipv4_address</replaceable>
+ | <replaceable>ipv6_address</replaceable> ) [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ]; ... };
+ in-view <replaceable>string</replaceable>;
+ inline-signing <replaceable>boolean</replaceable>;
+ ixfr-from-differences <replaceable>boolean</replaceable>;
+ journal <replaceable>quoted_string</replaceable>;
+ key-directory <replaceable>quoted_string</replaceable>;
+ masterfile-format ( map | raw | text );
+ masterfile-style ( full | relative );
+ masters [ port <replaceable>integer</replaceable> ] [ dscp <replaceable>integer</replaceable> ] { ( <replaceable>masters</replaceable> |
+ <replaceable>ipv4_address</replaceable> [ port <replaceable>integer</replaceable> ] | <replaceable>ipv6_address</replaceable> [ port
+ <replaceable>integer</replaceable> ] ) [ key <replaceable>string</replaceable> ]; ... };
+ max-journal-size ( unlimited | <replaceable>sizeval</replaceable> );
+ max-records <replaceable>integer</replaceable>;
+ max-refresh-time <replaceable>integer</replaceable>;
+ max-retry-time <replaceable>integer</replaceable>;
+ max-transfer-idle-in <replaceable>integer</replaceable>;
+ max-transfer-idle-out <replaceable>integer</replaceable>;
+ max-transfer-time-in <replaceable>integer</replaceable>;
+ max-transfer-time-out <replaceable>integer</replaceable>;
+ max-zone-ttl ( unlimited | <replaceable>ttlval</replaceable> );
+ min-refresh-time <replaceable>integer</replaceable>;
+ min-retry-time <replaceable>integer</replaceable>;
+ multi-master <replaceable>boolean</replaceable>;
+ notify ( explicit | master-only | <replaceable>boolean</replaceable> );
+ notify-delay <replaceable>integer</replaceable>;
+ notify-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
+ dscp <replaceable>integer</replaceable> ];
+ notify-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ]
+ [ dscp <replaceable>integer</replaceable> ];
+ notify-to-soa <replaceable>boolean</replaceable>;
+ pubkey <replaceable>integer</replaceable> <replaceable>integer</replaceable>
+ request-expire <replaceable>boolean</replaceable>;
+ request-ixfr <replaceable>boolean</replaceable>;
+ serial-update-method ( date | increment | unixtime );
+ server-addresses { ( <replaceable>ipv4_address</replaceable> | <replaceable>ipv6_address</replaceable> ) [ port
+ <replaceable>integer</replaceable> ]; ... };
+ server-names { <replaceable>quoted_string</replaceable>; ... };
+ sig-signing-nodes <replaceable>integer</replaceable>;
+ sig-signing-signatures <replaceable>integer</replaceable>;
+ sig-signing-type <replaceable>integer</replaceable>;
+ sig-validity-interval <replaceable>integer</replaceable> [ <replaceable>integer</replaceable> ];
+ transfer-source ( <replaceable>ipv4_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * ) ] [
+ dscp <replaceable>integer</replaceable> ];
+ transfer-source-v6 ( <replaceable>ipv6_address</replaceable> | * ) [ port ( <replaceable>integer</replaceable> | * )
+ ] [ dscp <replaceable>integer</replaceable> ];
+ try-tcp-refresh <replaceable>boolean</replaceable>;
+ type ( delegation-only | forward | hint | master | redirect | slave
+ | static-stub | stub );
+ update-check-ksk <replaceable>boolean</replaceable>;
+ update-policy ( local | { ( deny | grant ) <replaceable>string</replaceable> ( 6to4-self |
+ external | krb5-self | krb5-selfsub | krb5-subdomain | ms-self
+ | ms-selfsub | ms-subdomain | name | self | selfsub | selfwild
+ | subdomain | tcp-self | wildcard | zonesub ) [ <replaceable>string</replaceable> ]
+ <replaceable>rrtypelist</replaceable>; ... };
+ use-alt-transfer-source <replaceable>boolean</replaceable>;
+ zero-no-soa-ttl <replaceable>boolean</replaceable>;
+ zone-statistics ( full | terse | none | <replaceable>boolean</replaceable> );
+};
+</literallayout>
+ </refsection>
+
+ <refsection><info><title>FILES</title></info>
+
+ <para><filename>/etc/named.conf</filename>
+ </para>
+ </refsection>
+
+ <refsection><info><title>SEE ALSO</title></info>
+
+ <para><citerefentry>
+ <refentrytitle>ddns-confgen</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <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>,
+ <citerefentry>
+ <refentrytitle>rndc-confgen</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>,
+ <citetitle>BIND 9 Administrator Reference Manual</citetitle>.
+ </para>
+ </refsection>
+
+</refentry>
diff --git a/bin/named/named.conf.html b/bin/named/named.conf.html
new file mode 100644
index 0000000..537eafc
--- /dev/null
+++ b/bin/named/named.conf.html
@@ -0,0 +1,1002 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!--
+ - Copyright (C) 2004-2019 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+<html lang="en">
+<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.78.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry">
+<a name="man.named.conf"></a><div class="titlepage"></div>
+
+
+
+
+
+ <div class="refnamediv">
+<h2>Name</h2>
+<p>
+ <code class="filename">named.conf</code>
+ &#8212; configuration file for <span class="command"><strong>named</strong></span>
+ </p>
+</div>
+
+
+
+ <div class="refsynopsisdiv">
+<h2>Synopsis</h2>
+ <div class="cmdsynopsis"><p>
+ <code class="command">named.conf</code>
+ </p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.7"></a><h2>DESCRIPTION</h2>
+
+ <p><code class="filename">named.conf</code> is the configuration file
+ for
+ <span class="command"><strong>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="refsection">
+<a name="id-1.8"></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>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.9"></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>
+     * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] allow<br>
+     { <em class="replaceable"><code>address_match_element</code></em>; ... } [<br>
+     keys { <em class="replaceable"><code>string</code></em>; ... } ] [ read-only<br>
+     <em class="replaceable"><code>boolean</code></em> ];<br>
+ unix <em class="replaceable"><code>quoted_string</code></em> perm <em class="replaceable"><code>integer</code></em><br>
+     owner <em class="replaceable"><code>integer</code></em> group <em class="replaceable"><code>integer</code></em> [<br>
+     keys { <em class="replaceable"><code>string</code></em>; ... } ] [ read-only<br>
+     <em class="replaceable"><code>boolean</code></em> ];<br>
+};<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.10"></a><h2>DLZ</h2>
+
+ <div class="literallayout"><p><br>
+dlz <em class="replaceable"><code>string</code></em> {<br>
+ database <em class="replaceable"><code>string</code></em>;<br>
+ search <em class="replaceable"><code>boolean</code></em>;<br>
+};<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.11"></a><h2>DYNDB</h2>
+
+ <div class="literallayout"><p><br>
+dyndb <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>quoted_string</code></em> {<br>
+    <em class="replaceable"><code>unspecified-text</code></em> };<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.12"></a><h2>KEY</h2>
+
+ <div class="literallayout"><p><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>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.13"></a><h2>LOGGING</h2>
+
+ <div class="literallayout"><p><br>
+logging {<br>
+ category <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>; ... };<br>
+ channel <em class="replaceable"><code>string</code></em> {<br>
+ buffered <em class="replaceable"><code>boolean</code></em>;<br>
+ file <em class="replaceable"><code>quoted_string</code></em> [ versions ( "unlimited" | <em class="replaceable"><code>integer</code></em> )<br>
+     ] [ size <em class="replaceable"><code>size</code></em> ];<br>
+ null;<br>
+ print-category <em class="replaceable"><code>boolean</code></em>;<br>
+ print-severity <em class="replaceable"><code>boolean</code></em>;<br>
+ print-time <em class="replaceable"><code>boolean</code></em>;<br>
+ severity <em class="replaceable"><code>log_severity</code></em>;<br>
+ stderr;<br>
+ syslog [ <em class="replaceable"><code>syslog_facility</code></em> ];<br>
+ };<br>
+};<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.14"></a><h2>LWRES</h2>
+
+ <div class="literallayout"><p><br>
+lwres {<br>
+ listen-on [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>ipv4_address</code></em><br>
+     | <em class="replaceable"><code>ipv6_address</code></em> ) [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ]; ... };<br>
+ lwres-clients <em class="replaceable"><code>integer</code></em>;<br>
+ lwres-tasks <em class="replaceable"><code>integer</code></em>;<br>
+ ndots <em class="replaceable"><code>integer</code></em>;<br>
+ search { <em class="replaceable"><code>string</code></em>; ... };<br>
+ view <em class="replaceable"><code>string</code></em> [ <em class="replaceable"><code>class</code></em> ];<br>
+};<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.15"></a><h2>MANAGED-KEYS</h2>
+
+ <div class="literallayout"><p><br>
+managed-keys { <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>integer</code></em><br>
+    <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>quoted_string</code></em>; ... };<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.16"></a><h2>MASTERS</h2>
+
+ <div class="literallayout"><p><br>
+masters <em class="replaceable"><code>string</code></em> [ port <em class="replaceable"><code>integer</code></em> ] [ dscp<br>
+    <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>masters</code></em> | <em class="replaceable"><code>ipv4_address</code></em> [<br>
+    port <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [ port<br>
+    <em class="replaceable"><code>integer</code></em> ] ) [ key <em class="replaceable"><code>string</code></em> ]; ... };<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.17"></a><h2>OPTIONS</h2>
+
+ <div class="literallayout"><p><br>
+options {<br>
+ acache-cleaning-interval <em class="replaceable"><code>integer</code></em>;<br>
+ acache-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ additional-from-auth <em class="replaceable"><code>boolean</code></em>;<br>
+ additional-from-cache <em class="replaceable"><code>boolean</code></em>;<br>
+ allow-new-zones <em class="replaceable"><code>boolean</code></em>;<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query { <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-query-on { <em class="replaceable"><code>address_match_element</code></em>; ... };<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>
+ 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>
+ also-notify [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>masters</code></em> |<br>
+     <em class="replaceable"><code>ipv4_address</code></em> [ port <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] ) [ key <em class="replaceable"><code>string</code></em> ]; ... };<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * )<br>
+     ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> |<br>
+     * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ answer-cookie <em class="replaceable"><code>boolean</code></em>;<br>
+ attach-cache <em class="replaceable"><code>string</code></em>;<br>
+ auth-nxdomain <em class="replaceable"><code>boolean</code></em>; // default changed<br>
+ auto-dnssec ( allow | maintain | off );<br>
+ automatic-interface-scan <em class="replaceable"><code>boolean</code></em>;<br>
+ avoid-v4-udp-ports { <em class="replaceable"><code>portrange</code></em>; ... };<br>
+ avoid-v6-udp-ports { <em class="replaceable"><code>portrange</code></em>; ... };<br>
+ bindkeys-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ blackhole { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ cache-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ catalog-zones { zone <em class="replaceable"><code>quoted_string</code></em> [ default-masters [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>masters</code></em> | <em class="replaceable"><code>ipv4_address</code></em> [<br>
+     port <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [ port <em class="replaceable"><code>integer</code></em> ] ) [ key<br>
+     <em class="replaceable"><code>string</code></em> ]; ... } ] [ zone-directory <em class="replaceable"><code>quoted_string</code></em> ] [<br>
+     in-memory <em class="replaceable"><code>boolean</code></em> ] [ min-update-interval <em class="replaceable"><code>integer</code></em> ]; ... };<br>
+ check-dup-records ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-names ( master | slave | response<br>
+     ) ( fail | warn | ignore );<br>
+ check-sibling <em class="replaceable"><code>boolean</code></em>;<br>
+ check-spf ( warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ check-wildcard <em class="replaceable"><code>boolean</code></em>;<br>
+ cleaning-interval <em class="replaceable"><code>integer</code></em>;<br>
+ clients-per-query <em class="replaceable"><code>integer</code></em>;<br>
+ cookie-algorithm ( aes | sha1 | sha256 );<br>
+ cookie-secret <em class="replaceable"><code>string</code></em>;<br>
+ coresize ( default | unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ datasize ( default | unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ deny-answer-addresses { <em class="replaceable"><code>address_match_element</code></em>; ... } [<br>
+     except-from { <em class="replaceable"><code>quoted_string</code></em>; ... } ];<br>
+ deny-answer-aliases { <em class="replaceable"><code>quoted_string</code></em>; ... } [ except-from {<br>
+     <em class="replaceable"><code>quoted_string</code></em>; ... } ];<br>
+ dialup ( notify | notify-passive | passive | refresh | <em class="replaceable"><code>boolean</code></em> );<br>
+ directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ disable-algorithms <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>;<br>
+     ... };<br>
+ disable-ds-digests <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>;<br>
+     ... };<br>
+ disable-empty-zone <em class="replaceable"><code>string</code></em>;<br>
+ dns64 <em class="replaceable"><code>netprefix</code></em> {<br>
+ break-dnssec <em class="replaceable"><code>boolean</code></em>;<br>
+ clients { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ exclude { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ mapped { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ recursive-only <em class="replaceable"><code>boolean</code></em>;<br>
+ suffix <em class="replaceable"><code>ipv6_address</code></em>;<br>
+ };<br>
+ dns64-contact <em class="replaceable"><code>string</code></em>;<br>
+ dns64-server <em class="replaceable"><code>string</code></em>;<br>
+ dnssec-accept-expired <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-dnskey-kskonly <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-loadkeys-interval <em class="replaceable"><code>integer</code></em>;<br>
+ dnssec-lookaside ( <em class="replaceable"><code>string</code></em> trust-anchor<br>
+     <em class="replaceable"><code>string</code></em> | auto | no );<br>
+ dnssec-must-be-secure <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-secure-to-insecure <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-update-mode ( maintain | no-resign );<br>
+ dnssec-validation ( yes | no | auto );<br>
+ dnstap { ( all | auth | client | forwarder |<br>
+     resolver ) [ ( query | response ) ]; ... };<br>
+ dnstap-identity ( <em class="replaceable"><code>quoted_string</code></em> | none |<br>
+     hostname );<br>
+ dnstap-output ( file | unix ) <em class="replaceable"><code>quoted_string</code></em>;<br>
+ dnstap-version ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ dscp <em class="replaceable"><code>integer</code></em>;<br>
+ dual-stack-servers [ port <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>quoted_string</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv4_address</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] ); ... };<br>
+ dump-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ edns-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ empty-contact <em class="replaceable"><code>string</code></em>;<br>
+ empty-server <em class="replaceable"><code>string</code></em>;<br>
+ empty-zones-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ fetch-quota-params <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>fixedpoint</code></em> <em class="replaceable"><code>fixedpoint</code></em> <em class="replaceable"><code>fixedpoint</code></em>;<br>
+ fetches-per-server <em class="replaceable"><code>integer</code></em> [ ( drop | fail ) ];<br>
+ fetches-per-zone <em class="replaceable"><code>integer</code></em> [ ( drop | fail ) ];<br>
+ files ( default | unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ filter-aaaa { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ filter-aaaa-on-v4 ( break-dnssec | <em class="replaceable"><code>boolean</code></em> );<br>
+ filter-aaaa-on-v6 ( break-dnssec | <em class="replaceable"><code>boolean</code></em> );<br>
+ flush-zones-on-shutdown <em class="replaceable"><code>boolean</code></em>;<br>
+ forward ( first | only );<br>
+ forwarders [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>ipv4_address</code></em><br>
+     | <em class="replaceable"><code>ipv6_address</code></em> ) [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ]; ... };<br>
+ fstrm-set-buffer-hint <em class="replaceable"><code>integer</code></em>;<br>
+ fstrm-set-flush-timeout <em class="replaceable"><code>integer</code></em>;<br>
+ fstrm-set-input-queue-size <em class="replaceable"><code>integer</code></em>;<br>
+ fstrm-set-output-notify-threshold <em class="replaceable"><code>integer</code></em>;<br>
+ fstrm-set-output-queue-model ( mpsc | spsc );<br>
+ fstrm-set-output-queue-size <em class="replaceable"><code>integer</code></em>;<br>
+ fstrm-set-reopen-interval <em class="replaceable"><code>integer</code></em>;<br>
+ geoip-directory ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ geoip-use-ecs <em class="replaceable"><code>boolean</code></em>;<br>
+ heartbeat-interval <em class="replaceable"><code>integer</code></em>;<br>
+ hostname ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ inline-signing <em class="replaceable"><code>boolean</code></em>;<br>
+ interface-interval <em class="replaceable"><code>integer</code></em>;<br>
+ ixfr-from-differences ( master | slave | <em class="replaceable"><code>boolean</code></em> );<br>
+ keep-response-order { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ lame-ttl <em class="replaceable"><code>ttlval</code></em>;<br>
+ listen-on [ port <em class="replaceable"><code>integer</code></em> ] [ dscp<br>
+     <em class="replaceable"><code>integer</code></em> ] {<br>
+     <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ listen-on-v6 [ port <em class="replaceable"><code>integer</code></em> ] [ dscp<br>
+     <em class="replaceable"><code>integer</code></em> ] {<br>
+     <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ lmdb-mapsize <em class="replaceable"><code>sizeval</code></em>;<br>
+ lock-file ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ managed-keys-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ masterfile-format ( map | raw | text );<br>
+ masterfile-style ( full | relative );<br>
+ match-mapped-addresses <em class="replaceable"><code>boolean</code></em>;<br>
+ max-acache-size ( unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ max-cache-size ( default | unlimited | <em class="replaceable"><code>sizeval</code></em> | <em class="replaceable"><code>percentage</code></em> );<br>
+ max-cache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-clients-per-query <em class="replaceable"><code>integer</code></em>;<br>
+ max-journal-size ( unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ max-ncache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-records <em class="replaceable"><code>integer</code></em>;<br>
+ max-recursion-depth <em class="replaceable"><code>integer</code></em>;<br>
+ max-recursion-queries <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-rsa-exponent-size <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-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ max-zone-ttl ( unlimited | <em class="replaceable"><code>ttlval</code></em> );<br>
+ memstatistics <em class="replaceable"><code>boolean</code></em>;<br>
+ memstatistics-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ message-compression <em class="replaceable"><code>boolean</code></em>;<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ minimal-any <em class="replaceable"><code>boolean</code></em>;<br>
+ minimal-responses ( no-auth | no-auth-recursive | <em class="replaceable"><code>boolean</code></em> );<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+ no-case-compress { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ nocookie-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ notify ( explicit | master-only | <em class="replaceable"><code>boolean</code></em> );<br>
+ notify-delay <em class="replaceable"><code>integer</code></em>;<br>
+ notify-rate <em class="replaceable"><code>integer</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ]<br>
+     [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-to-soa <em class="replaceable"><code>boolean</code></em>;<br>
+ nta-lifetime <em class="replaceable"><code>ttlval</code></em>;<br>
+ nta-recheck <em class="replaceable"><code>ttlval</code></em>;<br>
+ nxdomain-redirect <em class="replaceable"><code>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>
+ preferred-glue <em class="replaceable"><code>string</code></em>;<br>
+ prefetch <em class="replaceable"><code>integer</code></em> [ <em class="replaceable"><code>integer</code></em> ];<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ query-source ( ( [ address ] ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] ) | ( [ [ address ] ( <em class="replaceable"><code>ipv4_address</code></em> | * ) ]<br>
+     port ( <em class="replaceable"><code>integer</code></em> | * ) ) ) [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ query-source-v6 ( ( [ address ] ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] ) | ( [ [ address ] ( <em class="replaceable"><code>ipv6_address</code></em> | * ) ]<br>
+     port ( <em class="replaceable"><code>integer</code></em> | * ) ) ) [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ querylog <em class="replaceable"><code>boolean</code></em>;<br>
+ random-device <em class="replaceable"><code>quoted_string</code></em>;<br>
+ rate-limit {<br>
+ all-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ errors-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ exempt-clients { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ ipv4-prefix-length <em class="replaceable"><code>integer</code></em>;<br>
+ ipv6-prefix-length <em class="replaceable"><code>integer</code></em>;<br>
+ log-only <em class="replaceable"><code>boolean</code></em>;<br>
+ max-table-size <em class="replaceable"><code>integer</code></em>;<br>
+ min-table-size <em class="replaceable"><code>integer</code></em>;<br>
+ nodata-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ nxdomains-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ qps-scale <em class="replaceable"><code>integer</code></em>;<br>
+ referrals-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ responses-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ slip <em class="replaceable"><code>integer</code></em>;<br>
+ window <em class="replaceable"><code>integer</code></em>;<br>
+ };<br>
+ recursing-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ recursion <em class="replaceable"><code>boolean</code></em>;<br>
+ recursive-clients <em class="replaceable"><code>integer</code></em>;<br>
+ request-expire <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-nsid <em class="replaceable"><code>boolean</code></em>;<br>
+ require-server-cookie <em class="replaceable"><code>boolean</code></em>;<br>
+ reserved-sockets <em class="replaceable"><code>integer</code></em>;<br>
+ resolver-query-timeout <em class="replaceable"><code>integer</code></em>;<br>
+ response-policy { zone <em class="replaceable"><code>quoted_string</code></em> [ log <em class="replaceable"><code>boolean</code></em> ] [<br>
+     max-policy-ttl <em class="replaceable"><code>integer</code></em> ] [ policy ( cname | disabled | drop |<br>
+     given | no-op | nodata | nxdomain | passthru | tcp-only<br>
+     <em class="replaceable"><code>quoted_string</code></em> ) ] [ recursive-only <em class="replaceable"><code>boolean</code></em> ]; ... } [<br>
+     break-dnssec <em class="replaceable"><code>boolean</code></em> ] [ max-policy-ttl <em class="replaceable"><code>integer</code></em> ] [<br>
+     min-ns-dots <em class="replaceable"><code>integer</code></em> ] [ nsip-wait-recurse <em class="replaceable"><code>boolean</code></em> ] [<br>
+     qname-wait-recurse <em class="replaceable"><code>boolean</code></em> ] [ recursive-only <em class="replaceable"><code>boolean</code></em> ];<br>
+ root-delegation-only [ exclude { <em class="replaceable"><code>quoted_string</code></em>; ... } ];<br>
+ root-key-sentinel <em class="replaceable"><code>boolean</code></em>;<br>
+ rrset-order { [ class <em class="replaceable"><code>string</code></em> ] [ type <em class="replaceable"><code>string</code></em> ] [ name<br>
+     <em class="replaceable"><code>quoted_string</code></em> ] <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>string</code></em>; ... };<br>
+ secroots-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ send-cookie <em class="replaceable"><code>boolean</code></em>;<br>
+ serial-query-rate <em class="replaceable"><code>integer</code></em>;<br>
+ serial-update-method ( date | increment | unixtime );<br>
+ server-id ( <em class="replaceable"><code>quoted_string</code></em> | none | hostname );<br>
+ servfail-ttl <em class="replaceable"><code>ttlval</code></em>;<br>
+ session-keyalg <em class="replaceable"><code>string</code></em>;<br>
+ session-keyfile ( <em class="replaceable"><code>quoted_string</code></em> | none );<br>
+ session-keyname <em class="replaceable"><code>string</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>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em> [ <em class="replaceable"><code>integer</code></em> ];<br>
+ sortlist { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ stacksize ( default | unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ startup-notify-rate <em class="replaceable"><code>integer</code></em>;<br>
+ statistics-file <em class="replaceable"><code>quoted_string</code></em>;<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-domain <em class="replaceable"><code>quoted_string</code></em>;<br>
+ tkey-gssapi-credential <em class="replaceable"><code>quoted_string</code></em>;<br>
+ tkey-gssapi-keytab <em class="replaceable"><code>quoted_string</code></em>;<br>
+ transfer-format ( many-answers | one-answer );<br>
+ transfer-message-size <em class="replaceable"><code>integer</code></em>;<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * )<br>
+     ] [ dscp <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>
+ transfers-per-ns <em class="replaceable"><code>integer</code></em>;<br>
+ trust-anchor-telemetry <em class="replaceable"><code>boolean</code></em>; // experimental<br>
+ try-tcp-refresh <em class="replaceable"><code>boolean</code></em>;<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+ use-v4-udp-ports { <em class="replaceable"><code>portrange</code></em>; ... };<br>
+ use-v6-udp-ports { <em class="replaceable"><code>portrange</code></em>; ... };<br>
+ v6-bias <em class="replaceable"><code>integer</code></em>;<br>
+ version ( <em class="replaceable"><code>quoted_string</code></em> | none );<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>
+ zone-statistics ( full | terse | none | <em class="replaceable"><code>boolean</code></em> );<br>
+};<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.18"></a><h2>SERVER</h2>
+
+ <div class="literallayout"><p><br>
+server <em class="replaceable"><code>netprefix</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>
+ edns-version <em class="replaceable"><code>integer</code></em>;<br>
+ keys <em class="replaceable"><code>server_key</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ]<br>
+     [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ query-source ( ( [ address ] ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] ) | ( [ [ address ] ( <em class="replaceable"><code>ipv4_address</code></em> | * ) ]<br>
+     port ( <em class="replaceable"><code>integer</code></em> | * ) ) ) [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ query-source-v6 ( ( [ address ] ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] ) | ( [ [ address ] ( <em class="replaceable"><code>ipv6_address</code></em> | * ) ]<br>
+     port ( <em class="replaceable"><code>integer</code></em> | * ) ) ) [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ request-expire <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-nsid <em class="replaceable"><code>boolean</code></em>;<br>
+ send-cookie <em class="replaceable"><code>boolean</code></em>;<br>
+ tcp-only <em class="replaceable"><code>boolean</code></em>;<br>
+ transfer-format ( many-answers | one-answer );<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * )<br>
+     ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ transfers <em class="replaceable"><code>integer</code></em>;<br>
+};<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.19"></a><h2>STATISTICS-CHANNELS</h2>
+
+ <div class="literallayout"><p><br>
+statistics-channels {<br>
+ inet ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> |<br>
+     * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] [<br>
+     allow { <em class="replaceable"><code>address_match_element</code></em>; ...<br>
+     } ];<br>
+};<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.20"></a><h2>TRUSTED-KEYS</h2>
+
+ <div class="literallayout"><p><br>
+trusted-keys { <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em><br>
+    <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>quoted_string</code></em>; ... };<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.21"></a><h2>VIEW</h2>
+
+ <div class="literallayout"><p><br>
+view <em class="replaceable"><code>string</code></em> [ <em class="replaceable"><code>class</code></em> ] {<br>
+ acache-cleaning-interval <em class="replaceable"><code>integer</code></em>;<br>
+ acache-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ additional-from-auth <em class="replaceable"><code>boolean</code></em>;<br>
+ additional-from-cache <em class="replaceable"><code>boolean</code></em>;<br>
+ allow-new-zones <em class="replaceable"><code>boolean</code></em>;<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ allow-query { <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-query-on { <em class="replaceable"><code>address_match_element</code></em>; ... };<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>
+ 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>
+ also-notify [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>masters</code></em> |<br>
+     <em class="replaceable"><code>ipv4_address</code></em> [ port <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] ) [ key <em class="replaceable"><code>string</code></em> ]; ... };<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * )<br>
+     ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> |<br>
+     * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ attach-cache <em class="replaceable"><code>string</code></em>;<br>
+ auth-nxdomain <em class="replaceable"><code>boolean</code></em>; // default changed<br>
+ auto-dnssec ( allow | maintain | off );<br>
+ cache-file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ catalog-zones { zone <em class="replaceable"><code>quoted_string</code></em> [ default-masters [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>masters</code></em> | <em class="replaceable"><code>ipv4_address</code></em> [<br>
+     port <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [ port <em class="replaceable"><code>integer</code></em> ] ) [ key<br>
+     <em class="replaceable"><code>string</code></em> ]; ... } ] [ zone-directory <em class="replaceable"><code>quoted_string</code></em> ] [<br>
+     in-memory <em class="replaceable"><code>boolean</code></em> ] [ min-update-interval <em class="replaceable"><code>integer</code></em> ]; ... };<br>
+ check-dup-records ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-names ( master | slave | response<br>
+     ) ( fail | warn | ignore );<br>
+ check-sibling <em class="replaceable"><code>boolean</code></em>;<br>
+ check-spf ( warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ check-wildcard <em class="replaceable"><code>boolean</code></em>;<br>
+ cleaning-interval <em class="replaceable"><code>integer</code></em>;<br>
+ clients-per-query <em class="replaceable"><code>integer</code></em>;<br>
+ deny-answer-addresses { <em class="replaceable"><code>address_match_element</code></em>; ... } [<br>
+     except-from { <em class="replaceable"><code>quoted_string</code></em>; ... } ];<br>
+ deny-answer-aliases { <em class="replaceable"><code>quoted_string</code></em>; ... } [ except-from {<br>
+     <em class="replaceable"><code>quoted_string</code></em>; ... } ];<br>
+ dialup ( notify | notify-passive | passive | refresh | <em class="replaceable"><code>boolean</code></em> );<br>
+ disable-algorithms <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>;<br>
+     ... };<br>
+ disable-ds-digests <em class="replaceable"><code>string</code></em> { <em class="replaceable"><code>string</code></em>;<br>
+     ... };<br>
+ disable-empty-zone <em class="replaceable"><code>string</code></em>;<br>
+ dlz <em class="replaceable"><code>string</code></em> {<br>
+ database <em class="replaceable"><code>string</code></em>;<br>
+ search <em class="replaceable"><code>boolean</code></em>;<br>
+ };<br>
+ dns64 <em class="replaceable"><code>netprefix</code></em> {<br>
+ break-dnssec <em class="replaceable"><code>boolean</code></em>;<br>
+ clients { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ exclude { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ mapped { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ recursive-only <em class="replaceable"><code>boolean</code></em>;<br>
+ suffix <em class="replaceable"><code>ipv6_address</code></em>;<br>
+ };<br>
+ dns64-contact <em class="replaceable"><code>string</code></em>;<br>
+ dns64-server <em class="replaceable"><code>string</code></em>;<br>
+ dnssec-accept-expired <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-dnskey-kskonly <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-loadkeys-interval <em class="replaceable"><code>integer</code></em>;<br>
+ dnssec-lookaside ( <em class="replaceable"><code>string</code></em> trust-anchor<br>
+     <em class="replaceable"><code>string</code></em> | auto | no );<br>
+ dnssec-must-be-secure <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-secure-to-insecure <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-update-mode ( maintain | no-resign );<br>
+ dnssec-validation ( yes | no | auto );<br>
+ dnstap { ( all | auth | client | forwarder |<br>
+     resolver ) [ ( query | response ) ]; ... };<br>
+ dual-stack-servers [ port <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>quoted_string</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv4_address</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] ); ... };<br>
+ dyndb <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>quoted_string</code></em> {<br>
+     <em class="replaceable"><code>unspecified-text</code></em> };<br>
+ edns-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ empty-contact <em class="replaceable"><code>string</code></em>;<br>
+ empty-server <em class="replaceable"><code>string</code></em>;<br>
+ empty-zones-enable <em class="replaceable"><code>boolean</code></em>;<br>
+ fetch-quota-params <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>fixedpoint</code></em> <em class="replaceable"><code>fixedpoint</code></em> <em class="replaceable"><code>fixedpoint</code></em>;<br>
+ fetches-per-server <em class="replaceable"><code>integer</code></em> [ ( drop | fail ) ];<br>
+ fetches-per-zone <em class="replaceable"><code>integer</code></em> [ ( drop | fail ) ];<br>
+ filter-aaaa { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ filter-aaaa-on-v4 ( break-dnssec | <em class="replaceable"><code>boolean</code></em> );<br>
+ filter-aaaa-on-v6 ( break-dnssec | <em class="replaceable"><code>boolean</code></em> );<br>
+ forward ( first | only );<br>
+ forwarders [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>ipv4_address</code></em><br>
+     | <em class="replaceable"><code>ipv6_address</code></em> ) [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ]; ... };<br>
+ inline-signing <em class="replaceable"><code>boolean</code></em>;<br>
+ ixfr-from-differences ( master | slave | <em class="replaceable"><code>boolean</code></em> );<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>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ lame-ttl <em class="replaceable"><code>ttlval</code></em>;<br>
+ lmdb-mapsize <em class="replaceable"><code>sizeval</code></em>;<br>
+ managed-keys { <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>string</code></em><br>
+     <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em><br>
+     <em class="replaceable"><code>quoted_string</code></em>; ... };<br>
+ masterfile-format ( map | raw | text );<br>
+ masterfile-style ( full | relative );<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>
+ max-acache-size ( unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ max-cache-size ( default | unlimited | <em class="replaceable"><code>sizeval</code></em> | <em class="replaceable"><code>percentage</code></em> );<br>
+ max-cache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-clients-per-query <em class="replaceable"><code>integer</code></em>;<br>
+ max-journal-size ( unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ max-ncache-ttl <em class="replaceable"><code>integer</code></em>;<br>
+ max-records <em class="replaceable"><code>integer</code></em>;<br>
+ max-recursion-depth <em class="replaceable"><code>integer</code></em>;<br>
+ max-recursion-queries <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <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-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ max-zone-ttl ( unlimited | <em class="replaceable"><code>ttlval</code></em> );<br>
+ message-compression <em class="replaceable"><code>boolean</code></em>;<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ minimal-any <em class="replaceable"><code>boolean</code></em>;<br>
+ minimal-responses ( no-auth | no-auth-recursive | <em class="replaceable"><code>boolean</code></em> );<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+ no-case-compress { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ nocookie-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ notify ( explicit | master-only | <em class="replaceable"><code>boolean</code></em> );<br>
+ notify-delay <em class="replaceable"><code>integer</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ]<br>
+     [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-to-soa <em class="replaceable"><code>boolean</code></em>;<br>
+ nta-lifetime <em class="replaceable"><code>ttlval</code></em>;<br>
+ nta-recheck <em class="replaceable"><code>ttlval</code></em>;<br>
+ nxdomain-redirect <em class="replaceable"><code>string</code></em>;<br>
+ preferred-glue <em class="replaceable"><code>string</code></em>;<br>
+ prefetch <em class="replaceable"><code>integer</code></em> [ <em class="replaceable"><code>integer</code></em> ];<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ query-source ( ( [ address ] ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] ) | ( [ [ address ] ( <em class="replaceable"><code>ipv4_address</code></em> | * ) ]<br>
+     port ( <em class="replaceable"><code>integer</code></em> | * ) ) ) [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ query-source-v6 ( ( [ address ] ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] ) | ( [ [ address ] ( <em class="replaceable"><code>ipv6_address</code></em> | * ) ]<br>
+     port ( <em class="replaceable"><code>integer</code></em> | * ) ) ) [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ rate-limit {<br>
+ all-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ errors-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ exempt-clients { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ ipv4-prefix-length <em class="replaceable"><code>integer</code></em>;<br>
+ ipv6-prefix-length <em class="replaceable"><code>integer</code></em>;<br>
+ log-only <em class="replaceable"><code>boolean</code></em>;<br>
+ max-table-size <em class="replaceable"><code>integer</code></em>;<br>
+ min-table-size <em class="replaceable"><code>integer</code></em>;<br>
+ nodata-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ nxdomains-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ qps-scale <em class="replaceable"><code>integer</code></em>;<br>
+ referrals-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ responses-per-second <em class="replaceable"><code>integer</code></em>;<br>
+ slip <em class="replaceable"><code>integer</code></em>;<br>
+ window <em class="replaceable"><code>integer</code></em>;<br>
+ };<br>
+ recursion <em class="replaceable"><code>boolean</code></em>;<br>
+ request-expire <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-nsid <em class="replaceable"><code>boolean</code></em>;<br>
+ require-server-cookie <em class="replaceable"><code>boolean</code></em>;<br>
+ resolver-query-timeout <em class="replaceable"><code>integer</code></em>;<br>
+ response-policy { zone <em class="replaceable"><code>quoted_string</code></em> [ log <em class="replaceable"><code>boolean</code></em> ] [<br>
+     max-policy-ttl <em class="replaceable"><code>integer</code></em> ] [ policy ( cname | disabled | drop |<br>
+     given | no-op | nodata | nxdomain | passthru | tcp-only<br>
+     <em class="replaceable"><code>quoted_string</code></em> ) ] [ recursive-only <em class="replaceable"><code>boolean</code></em> ]; ... } [<br>
+     break-dnssec <em class="replaceable"><code>boolean</code></em> ] [ max-policy-ttl <em class="replaceable"><code>integer</code></em> ] [<br>
+     min-ns-dots <em class="replaceable"><code>integer</code></em> ] [ nsip-wait-recurse <em class="replaceable"><code>boolean</code></em> ] [<br>
+     qname-wait-recurse <em class="replaceable"><code>boolean</code></em> ] [ recursive-only <em class="replaceable"><code>boolean</code></em> ];<br>
+ root-delegation-only [ exclude { <em class="replaceable"><code>quoted_string</code></em>; ... } ];<br>
+ root-key-sentinel <em class="replaceable"><code>boolean</code></em>;<br>
+ rrset-order { [ class <em class="replaceable"><code>string</code></em> ] [ type <em class="replaceable"><code>string</code></em> ] [ name<br>
+     <em class="replaceable"><code>quoted_string</code></em> ] <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>string</code></em>; ... };<br>
+ send-cookie <em class="replaceable"><code>boolean</code></em>;<br>
+ serial-update-method ( date | increment | unixtime );<br>
+ server <em class="replaceable"><code>netprefix</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>
+ edns-version <em class="replaceable"><code>integer</code></em>;<br>
+ keys <em class="replaceable"><code>server_key</code></em>;<br>
+ max-udp-size <em class="replaceable"><code>integer</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | *<br>
+     ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em><br>
+     | * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ provide-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ query-source ( ( [ address ] ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port<br>
+     ( <em class="replaceable"><code>integer</code></em> | * ) ] ) | ( [ [ address ] (<br>
+     <em class="replaceable"><code>ipv4_address</code></em> | * ) ] port ( <em class="replaceable"><code>integer</code></em> | * ) ) ) [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ query-source-v6 ( ( [ address ] ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [<br>
+     port ( <em class="replaceable"><code>integer</code></em> | * ) ] ) | ( [ [ address ] (<br>
+     <em class="replaceable"><code>ipv6_address</code></em> | * ) ] port ( <em class="replaceable"><code>integer</code></em> | * ) ) ) [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ request-expire <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ request-nsid <em class="replaceable"><code>boolean</code></em>;<br>
+ send-cookie <em class="replaceable"><code>boolean</code></em>;<br>
+ tcp-only <em class="replaceable"><code>boolean</code></em>;<br>
+ transfer-format ( many-answers | one-answer );<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> |<br>
+     * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ transfers <em class="replaceable"><code>integer</code></em>;<br>
+ };<br>
+ servfail-ttl <em class="replaceable"><code>ttlval</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>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em> [ <em class="replaceable"><code>integer</code></em> ];<br>
+ sortlist { <em class="replaceable"><code>address_match_element</code></em>; ... };<br>
+ transfer-format ( many-answers | one-answer );<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * )<br>
+     ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ trust-anchor-telemetry <em class="replaceable"><code>boolean</code></em>; // experimental<br>
+ trusted-keys { <em class="replaceable"><code>string</code></em> <em class="replaceable"><code>integer</code></em><br>
+     <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>quoted_string</code></em>;<br>
+     ... };<br>
+ try-tcp-refresh <em class="replaceable"><code>boolean</code></em>;<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+ v6-bias <em class="replaceable"><code>integer</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>
+ zone <em class="replaceable"><code>string</code></em> [ <em class="replaceable"><code>class</code></em> ] {<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<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>
+ also-notify [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { (<br>
+     <em class="replaceable"><code>masters</code></em> | <em class="replaceable"><code>ipv4_address</code></em> [ port <em class="replaceable"><code>integer</code></em> ] |<br>
+     <em class="replaceable"><code>ipv6_address</code></em> [ port <em class="replaceable"><code>integer</code></em> ] ) [ key <em class="replaceable"><code>string</code></em> ];<br>
+     ... };<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ auto-dnssec ( allow | maintain | off );<br>
+ check-dup-records ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-names ( fail | warn | ignore );<br>
+ check-sibling <em class="replaceable"><code>boolean</code></em>;<br>
+ check-spf ( warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ check-wildcard <em class="replaceable"><code>boolean</code></em>;<br>
+ database <em class="replaceable"><code>string</code></em>;<br>
+ delegation-only <em class="replaceable"><code>boolean</code></em>;<br>
+ dialup ( notify | notify-passive | passive | refresh |<br>
+     <em class="replaceable"><code>boolean</code></em> );<br>
+ dlz <em class="replaceable"><code>string</code></em>;<br>
+ dnssec-dnskey-kskonly <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-loadkeys-interval <em class="replaceable"><code>integer</code></em>;<br>
+ dnssec-secure-to-insecure <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-update-mode ( maintain | no-resign );<br>
+ file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ forward ( first | only );<br>
+ forwarders [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { (<br>
+     <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [ port <em class="replaceable"><code>integer</code></em> ] [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ]; ... };<br>
+ in-view <em class="replaceable"><code>string</code></em>;<br>
+ inline-signing <em class="replaceable"><code>boolean</code></em>;<br>
+ ixfr-from-differences <em class="replaceable"><code>boolean</code></em>;<br>
+ journal <em class="replaceable"><code>quoted_string</code></em>;<br>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ masterfile-format ( map | raw | text );<br>
+ masterfile-style ( full | relative );<br>
+ masters [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>masters</code></em><br>
+     | <em class="replaceable"><code>ipv4_address</code></em> [ port <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [<br>
+     port <em class="replaceable"><code>integer</code></em> ] ) [ key <em class="replaceable"><code>string</code></em> ]; ... };<br>
+ max-ixfr-log-size ( default | unlimited |<br>
+ max-journal-size ( unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ max-records <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <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-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-zone-ttl ( unlimited | <em class="replaceable"><code>ttlval</code></em> );<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+ notify ( explicit | master-only | <em class="replaceable"><code>boolean</code></em> );<br>
+ notify-delay <em class="replaceable"><code>integer</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | *<br>
+     ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em><br>
+     | * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-to-soa <em class="replaceable"><code>boolean</code></em>;<br>
+ pubkey <em class="replaceable"><code>integer</code></em><br>
+     <em class="replaceable"><code>integer</code></em><br>
+     <em class="replaceable"><code>integer</code></em><br>
+ request-expire <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ serial-update-method ( date | increment | unixtime );<br>
+ server-addresses { ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [<br>
+     port <em class="replaceable"><code>integer</code></em> ]; ... };<br>
+ server-names { <em class="replaceable"><code>quoted_string</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>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em> [ <em class="replaceable"><code>integer</code></em> ];<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> |<br>
+     * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port (<br>
+     <em class="replaceable"><code>integer</code></em> | * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ try-tcp-refresh <em class="replaceable"><code>boolean</code></em>;<br>
+ type ( delegation-only | forward | hint | master | redirect<br>
+     | slave | static-stub | stub );<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+ update-policy ( local | { ( deny | grant ) <em class="replaceable"><code>string</code></em> (<br>
+     6to4-self | external | krb5-self | krb5-selfsub |<br>
+     krb5-subdomain | ms-self | ms-selfsub | ms-subdomain |<br>
+     name | self | selfsub | selfwild | subdomain | tcp-self<br>
+     | wildcard | zonesub ) [ <em class="replaceable"><code>string</code></em> ] <em class="replaceable"><code>rrtypelist</code></em>; ... };<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+ zero-no-soa-ttl <em class="replaceable"><code>boolean</code></em>;<br>
+ zone-statistics ( full | terse | none | <em class="replaceable"><code>boolean</code></em> );<br>
+ };<br>
+ zone-statistics ( full | terse | none | <em class="replaceable"><code>boolean</code></em> );<br>
+};<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.22"></a><h2>ZONE</h2>
+
+ <div class="literallayout"><p><br>
+zone <em class="replaceable"><code>string</code></em> [ <em class="replaceable"><code>class</code></em> ] {<br>
+ allow-notify { <em class="replaceable"><code>address_match_element</code></em>; ... };<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>
+ also-notify [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>masters</code></em> |<br>
+     <em class="replaceable"><code>ipv4_address</code></em> [ port <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] ) [ key <em class="replaceable"><code>string</code></em> ]; ... };<br>
+ alt-transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * )<br>
+     ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ alt-transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> |<br>
+     * ) ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ auto-dnssec ( allow | maintain | off );<br>
+ check-dup-records ( fail | warn | ignore );<br>
+ check-integrity <em class="replaceable"><code>boolean</code></em>;<br>
+ check-mx ( fail | warn | ignore );<br>
+ check-mx-cname ( fail | warn | ignore );<br>
+ check-names ( fail | warn | ignore );<br>
+ check-sibling <em class="replaceable"><code>boolean</code></em>;<br>
+ check-spf ( warn | ignore );<br>
+ check-srv-cname ( fail | warn | ignore );<br>
+ check-wildcard <em class="replaceable"><code>boolean</code></em>;<br>
+ database <em class="replaceable"><code>string</code></em>;<br>
+ delegation-only <em class="replaceable"><code>boolean</code></em>;<br>
+ dialup ( notify | notify-passive | passive | refresh | <em class="replaceable"><code>boolean</code></em> );<br>
+ dlz <em class="replaceable"><code>string</code></em>;<br>
+ dnssec-dnskey-kskonly <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-loadkeys-interval <em class="replaceable"><code>integer</code></em>;<br>
+ dnssec-secure-to-insecure <em class="replaceable"><code>boolean</code></em>;<br>
+ dnssec-update-mode ( maintain | no-resign );<br>
+ file <em class="replaceable"><code>quoted_string</code></em>;<br>
+ forward ( first | only );<br>
+ forwarders [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>ipv4_address</code></em><br>
+     | <em class="replaceable"><code>ipv6_address</code></em> ) [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ]; ... };<br>
+ in-view <em class="replaceable"><code>string</code></em>;<br>
+ inline-signing <em class="replaceable"><code>boolean</code></em>;<br>
+ ixfr-from-differences <em class="replaceable"><code>boolean</code></em>;<br>
+ journal <em class="replaceable"><code>quoted_string</code></em>;<br>
+ key-directory <em class="replaceable"><code>quoted_string</code></em>;<br>
+ masterfile-format ( map | raw | text );<br>
+ masterfile-style ( full | relative );<br>
+ masters [ port <em class="replaceable"><code>integer</code></em> ] [ dscp <em class="replaceable"><code>integer</code></em> ] { ( <em class="replaceable"><code>masters</code></em> |<br>
+     <em class="replaceable"><code>ipv4_address</code></em> [ port <em class="replaceable"><code>integer</code></em> ] | <em class="replaceable"><code>ipv6_address</code></em> [ port<br>
+     <em class="replaceable"><code>integer</code></em> ] ) [ key <em class="replaceable"><code>string</code></em> ]; ... };<br>
+ max-journal-size ( unlimited | <em class="replaceable"><code>sizeval</code></em> );<br>
+ max-records <em class="replaceable"><code>integer</code></em>;<br>
+ max-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ max-retry-time <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-transfer-time-in <em class="replaceable"><code>integer</code></em>;<br>
+ max-transfer-time-out <em class="replaceable"><code>integer</code></em>;<br>
+ max-zone-ttl ( unlimited | <em class="replaceable"><code>ttlval</code></em> );<br>
+ min-refresh-time <em class="replaceable"><code>integer</code></em>;<br>
+ min-retry-time <em class="replaceable"><code>integer</code></em>;<br>
+ multi-master <em class="replaceable"><code>boolean</code></em>;<br>
+ notify ( explicit | master-only | <em class="replaceable"><code>boolean</code></em> );<br>
+ notify-delay <em class="replaceable"><code>integer</code></em>;<br>
+ notify-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ]<br>
+     [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ notify-to-soa <em class="replaceable"><code>boolean</code></em>;<br>
+ pubkey <em class="replaceable"><code>integer</code></em> <em class="replaceable"><code>integer</code></em><br>
+ request-expire <em class="replaceable"><code>boolean</code></em>;<br>
+ request-ixfr <em class="replaceable"><code>boolean</code></em>;<br>
+ serial-update-method ( date | increment | unixtime );<br>
+ server-addresses { ( <em class="replaceable"><code>ipv4_address</code></em> | <em class="replaceable"><code>ipv6_address</code></em> ) [ port<br>
+     <em class="replaceable"><code>integer</code></em> ]; ... };<br>
+ server-names { <em class="replaceable"><code>quoted_string</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>
+ sig-validity-interval <em class="replaceable"><code>integer</code></em> [ <em class="replaceable"><code>integer</code></em> ];<br>
+ transfer-source ( <em class="replaceable"><code>ipv4_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * ) ] [<br>
+     dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ transfer-source-v6 ( <em class="replaceable"><code>ipv6_address</code></em> | * ) [ port ( <em class="replaceable"><code>integer</code></em> | * )<br>
+     ] [ dscp <em class="replaceable"><code>integer</code></em> ];<br>
+ try-tcp-refresh <em class="replaceable"><code>boolean</code></em>;<br>
+ type ( delegation-only | forward | hint | master | redirect | slave<br>
+     | static-stub | stub );<br>
+ update-check-ksk <em class="replaceable"><code>boolean</code></em>;<br>
+ update-policy ( local | { ( deny | grant ) <em class="replaceable"><code>string</code></em> ( 6to4-self |<br>
+     external | krb5-self | krb5-selfsub | krb5-subdomain | ms-self<br>
+     | ms-selfsub | ms-subdomain | name | self | selfsub | selfwild<br>
+     | subdomain | tcp-self | wildcard | zonesub ) [ <em class="replaceable"><code>string</code></em> ]<br>
+     <em class="replaceable"><code>rrtypelist</code></em>; ... };<br>
+ use-alt-transfer-source <em class="replaceable"><code>boolean</code></em>;<br>
+ zero-no-soa-ttl <em class="replaceable"><code>boolean</code></em>;<br>
+ zone-statistics ( full | terse | none | <em class="replaceable"><code>boolean</code></em> );<br>
+};<br>
+</p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.23"></a><h2>FILES</h2>
+
+ <p><code class="filename">/etc/named.conf</code>
+ </p>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.24"></a><h2>SEE ALSO</h2>
+
+ <p><span class="citerefentry">
+ <span class="refentrytitle">ddns-confgen</span>(8)
+ </span>,
+ <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>,
+ <span class="citerefentry">
+ <span class="refentrytitle">rndc-confgen</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..c869369
--- /dev/null
+++ b/bin/named/named.docbook
@@ -0,0 +1,547 @@
+<!--
+ - Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ -
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ -
+ - See the COPYRIGHT file distributed with this work for additional
+ - information regarding copyright ownership.
+-->
+
+<!-- Converted by db4-upgrade version 1.0 -->
+<refentry xmlns:db="http://docbook.org/ns/docbook" version="5.0" xml:id="man.named">
+ <info>
+ <date>2014-02-19</date>
+ </info>
+ <refentryinfo>
+ <corpname>ISC</corpname>
+ <corpauthor>Internet Systems Consortium, Inc.</corpauthor>
+ </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>2000</year>
+ <year>2001</year>
+ <year>2003</year>
+ <year>2004</year>
+ <year>2005</year>
+ <year>2006</year>
+ <year>2007</year>
+ <year>2008</year>
+ <year>2009</year>
+ <year>2011</year>
+ <year>2013</year>
+ <year>2014</year>
+ <year>2015</year>
+ <year>2016</year>
+ <year>2017</year>
+ <year>2018</year>
+ <year>2019</year>
+ <holder>Internet Systems Consortium, Inc. ("ISC")</holder>
+ </copyright>
+ </docinfo>
+
+ <refsynopsisdiv>
+ <cmdsynopsis sepchar=" ">
+ <command>named</command>
+ <group choice="opt" rep="norepeat">
+ <arg choice="opt" rep="norepeat"><option>-4</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-6</option></arg>
+ </group>
+ <arg choice="opt" rep="norepeat"><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-d <replaceable class="parameter">debug-level</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-D <replaceable class="parameter">string</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-E <replaceable class="parameter">engine-name</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-f</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-g</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-L <replaceable class="parameter">logfile</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-M <replaceable class="parameter">option</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-m <replaceable class="parameter">flag</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-n <replaceable class="parameter">#cpus</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-p <replaceable class="parameter">port</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-s</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-S <replaceable class="parameter">#max-socks</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-t <replaceable class="parameter">directory</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-U <replaceable class="parameter">#listeners</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-u <replaceable class="parameter">user</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-v</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-V</option></arg>
+ <arg choice="opt" rep="norepeat"><option>-X <replaceable class="parameter">lock-file</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-x <replaceable class="parameter">cache-file</replaceable></option></arg>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsection><info><title>DESCRIPTION</title></info>
+
+ <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>
+ </refsection>
+
+ <refsection><info><title>OPTIONS</title></info>
+
+
+ <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>-D <replaceable class="parameter">string</replaceable></term>
+ <listitem>
+ <para>
+ Specifies a string that is used to identify a instance of
+ <command>named</command> in a process listing. The contents
+ of <replaceable class="parameter">string</replaceable> are
+ not examined.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-E <replaceable class="parameter">engine-name</replaceable></term>
+ <listitem>
+ <para>
+ When applicable, specifies the hardware to use for
+ cryptographic operations, such as a secure key store used
+ for signing.
+ </para>
+ <para>
+ When BIND is built with OpenSSL PKCS#11 support, this defaults
+ to the string "pkcs11", which identifies an OpenSSL engine
+ that can drive a cryptographic accelerator or hardware service
+ module. When BIND is built with native PKCS#11 cryptography
+ (--enable-native-pkcs11), it defaults to the path of the PKCS#11
+ provider library specified via "--with-pkcs11".
+ </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>-L <replaceable class="parameter">logfile</replaceable></term>
+ <listitem>
+ <para>
+ Log to the file <option>logfile</option> by default
+ instead of the system log.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>-M <replaceable class="parameter">option</replaceable></term>
+ <listitem>
+ <para>
+ Sets the default memory context options. Currently
+ the only supported option is
+ <replaceable class="parameter">external</replaceable>,
+ which causes the internal memory manager to be bypassed
+ in favor of system-provided memory allocation functions.
+ </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.
+ The default value is 4096 on systems built with default
+ configuration options, and 21000 on systems built with
+ "configure --with-tuning=large".
+ </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">#listeners</replaceable></term>
+ <listitem>
+ <para>
+ Use <replaceable class="parameter">#listeners</replaceable>
+ worker threads to listen for incoming UDP packets on each
+ address. If not specified, <command>named</command> will
+ calculate a default value based on the number of detected
+ CPUs: 1 for 1 CPU, and the number of detected CPUs
+ minus one for machines with more than 1 CPU. This cannot
+ be increased to a value higher than the number of CPUs.
+ If <option>-n</option> has been set to a higher value than
+ the number of detected CPUs, then <option>-U</option> may
+ be increased as high as that value, but no higher.
+ On Windows, the number of UDP listeners is hardwired to 1
+ and this option has no effect.
+ </para>
+ </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">lock-file</replaceable></term>
+ <listitem>
+ <para>
+ Acquire a lock on the specified file at runtime; this
+ helps to prevent duplicate <command>named</command> instances
+ from running simultaneously.
+ Use of this option overrides the <command>lock-file</command>
+ option in <filename>named.conf</filename>.
+ If set to <literal>none</literal>, the lock file check
+ is disabled.
+ </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>
+
+ </refsection>
+
+ <refsection><info><title>SIGNALS</title></info>
+
+ <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>
+
+ </refsection>
+
+ <refsection><info><title>CONFIGURATION</title></info>
+
+ <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>
+
+ <para>
+ <command>named</command> inherits the <function>umask</function>
+ (file creation mode mask) from the parent process. If files
+ created by <command>named</command>, such as journal files,
+ need to have custom permissions, the <function>umask</function>
+ should be set explicitly in the script used to start the
+ <command>named</command> process.
+ </para>
+
+ </refsection>
+
+ <refsection><info><title>FILES</title></info>
+
+
+ <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>
+
+ </refsection>
+
+ <refsection><info><title>SEE ALSO</title></info>
+
+ <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>
+ </refsection>
+
+</refentry>
diff --git a/bin/named/named.html b/bin/named/named.html
new file mode 100644
index 0000000..b912196
--- /dev/null
+++ b/bin/named/named.html
@@ -0,0 +1,456 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!--
+ - Copyright (C) 2000, 2001, 2003-2009, 2011, 2013-2019 Internet Systems Consortium, Inc. ("ISC")
+ -
+ - This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-->
+<html lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>named</title>
+<meta name="generator" content="DocBook XSL Stylesheets V1.78.1">
+</head>
+<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="refentry">
+<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">-D <em class="replaceable"><code>string</code></em></code>]
+ [<code class="option">-E <em class="replaceable"><code>engine-name</code></em></code>]
+ [<code class="option">-f</code>]
+ [<code class="option">-g</code>]
+ [<code class="option">-L <em class="replaceable"><code>logfile</code></em></code>]
+ [<code class="option">-M <em class="replaceable"><code>option</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">-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>#listeners</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>lock-file</code></em></code>]
+ [<code class="option">-x <em class="replaceable"><code>cache-file</code></em></code>]
+ </p></div>
+ </div>
+
+ <div class="refsection">
+<a name="id-1.7"></a><h2>DESCRIPTION</h2>
+
+ <p><span class="command"><strong>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 class="command"><strong>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="refsection">
+<a name="id-1.8"></a><h2>OPTIONS</h2>
+
+
+ <div class="variablelist"><dl class="variablelist">
+<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 class="command"><strong>named</strong></span> become
+ more verbose as the debug level increases.
+ </p>
+ </dd>
+<dt><span class="term">-D <em class="replaceable"><code>string</code></em></span></dt>
+<dd>
+ <p>
+ Specifies a string that is used to identify a instance of
+ <span class="command"><strong>named</strong></span> in a process listing. The contents
+ of <em class="replaceable"><code>string</code></em> are
+ not examined.
+ </p>
+ </dd>
+<dt><span class="term">-E <em class="replaceable"><code>engine-name</code></em></span></dt>
+<dd>
+ <p>
+ When applicable, specifies the hardware to use for
+ cryptographic operations, such as a secure key store used
+ for signing.
+ </p>
+ <p>
+ When BIND is built with OpenSSL PKCS#11 support, this defaults
+ to the string "pkcs11", which identifies an OpenSSL engine
+ that can drive a cryptographic accelerator or hardware service
+ module. When BIND is built with native PKCS#11 cryptography
+ (--enable-native-pkcs11), it defaults to the path of the PKCS#11
+ provider library specified via "--with-pkcs11".
+ </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">-L <em class="replaceable"><code>logfile</code></em></span></dt>
+<dd>
+ <p>
+ Log to the file <code class="option">logfile</code> by default
+ instead of the system log.
+ </p>
+ </dd>
+<dt><span class="term">-M <em class="replaceable"><code>option</code></em></span></dt>
+<dd>
+ <p>
+ Sets the default memory context options. Currently
+ the only supported option is
+ <em class="replaceable"><code>external</code></em>,
+ which causes the internal memory manager to be bypassed
+ in favor of system-provided memory allocation functions.
+ </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 class="command"><strong>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 class="command"><strong>named</strong></span> to use up to
+ <em class="replaceable"><code>#max-socks</code></em> sockets.
+ The default value is 4096 on systems built with default
+ configuration options, and 21000 on systems built with
+ "configure --with-tuning=large".
+ </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 class="command"><strong>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>#listeners</code></em></span></dt>
+<dd>
+ <p>
+ Use <em class="replaceable"><code>#listeners</code></em>
+ worker threads to listen for incoming UDP packets on each
+ address. If not specified, <span class="command"><strong>named</strong></span> will
+ calculate a default value based on the number of detected
+ CPUs: 1 for 1 CPU, and the number of detected CPUs
+ minus one for machines with more than 1 CPU. This cannot
+ be increased to a value higher than the number of CPUs.
+ If <code class="option">-n</code> has been set to a higher value than
+ the number of detected CPUs, then <code class="option">-U</code> may
+ be increased as high as that value, but no higher.
+ On Windows, the number of UDP listeners is hardwired to 1
+ and this option has no effect.
+ </p>
+ </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 class="command"><strong>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 class="command"><strong>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>lock-file</code></em></span></dt>
+<dd>
+ <p>
+ Acquire a lock on the specified file at runtime; this
+ helps to prevent duplicate <span class="command"><strong>named</strong></span> instances
+ from running simultaneously.
+ Use of this option overrides the <span class="command"><strong>lock-file</strong></span>
+ option in <code class="filename">named.conf</code>.
+ If set to <code class="literal">none</code>, the lock file check
+ is disabled.
+ </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="refsection">
+<a name="id-1.9"></a><h2>SIGNALS</h2>
+
+ <p>
+ In routine operation, signals should not be used to control
+ the nameserver; <span class="command"><strong>rndc</strong></span> should be used
+ instead.
+ </p>
+
+ <div class="variablelist"><dl class="variablelist">
+<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="refsection">
+<a name="id-1.10"></a><h2>CONFIGURATION</h2>
+
+ <p>
+ The <span class="command"><strong>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>
+
+ <p>
+ <span class="command"><strong>named</strong></span> inherits the <code class="function">umask</code>
+ (file creation mode mask) from the parent process. If files
+ created by <span class="command"><strong>named</strong></span>, such as journal files,
+ need to have custom permissions, the <code class="function">umask</code>
+ should be set explicitly in the script used to start the
+ <span class="command"><strong>named</strong></span> process.
+ </p>
+
+ </div>
+
+ <div class="refsection">
+<a name="id-1.11"></a><h2>FILES</h2>
+
+
+ <div class="variablelist"><dl class="variablelist">
+<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="refsection">
+<a name="id-1.12"></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></body>
+</html>
diff --git a/bin/named/notify.c b/bin/named/notify.c
new file mode 100644
index 0000000..0ad5abc
--- /dev/null
+++ b/bin/named/notify.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $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, true);
+ if (msg_result != ISC_R_SUCCESS)
+ msg_result = dns_message_reply(message, 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 * 2 + 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");
+ result = DNS_R_FORMERR;
+ goto done;
+ }
+
+ /*
+ * 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");
+ result = DNS_R_FORMERR;
+ goto done;
+ }
+
+ /* 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");
+ result = DNS_R_FORMERR;
+ goto done;
+ }
+
+ /* 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");
+ result = DNS_R_FORMERR;
+ goto done;
+ }
+
+ 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) {
+ dns_zonetype_t zonetype = dns_zone_gettype(zone);
+
+ if ((zonetype == dns_zone_master) ||
+ (zonetype == dns_zone_slave) ||
+ (zonetype == dns_zone_stub))
+ {
+ isc_sockaddr_t *from = ns_client_getsockaddr(client);
+ isc_sockaddr_t *to = ns_client_getdestaddr(client);
+ notify_log(client, ISC_LOG_INFO,
+ "received notify for zone '%s'%s",
+ namebuf, tsigbuf);
+ result = dns_zone_notifyreceive2(zone, from, to,
+ request);
+ goto done;
+ }
+ }
+
+ notify_log(client, ISC_LOG_NOTICE,
+ "received notify for zone '%s'%s: not authoritative",
+ namebuf, tsigbuf);
+ result = DNS_R_NOTAUTH;
+
+ done:
+ 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..f8dbef2
--- /dev/null
+++ b/bin/named/query.c
@@ -0,0 +1,9615 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <isc/hex.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/rwlock.h>
+#include <isc/serial.h>
+#include <isc/stats.h>
+#include <isc/thread.h>
+#include <isc/util.h>
+
+#include <dns/adb.h>
+#include <dns/badcache.h>
+#include <dns/byaddr.h>
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/dlz.h>
+#include <dns/dns64.h>
+#include <dns/dnssec.h>
+#include <dns/events.h>
+#include <dns/keytable.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/globals.h>
+#include <named/log.h>
+#include <named/server.h>
+#include <named/sortlist.h>
+#include <named/xfrout.h>
+
+#if 0
+/*
+ * It has been recommended that DNS64 be changed to return excluded
+ * AAAA addresses if DNS64 synthesis does not occur. This minimises
+ * the impact on the lookup results. While most DNS AAAA lookups are
+ * done to send IP packets to a host, not all of them are and filtering
+ * excluded addresses has a negative impact on those uses.
+ */
+#define dns64_bis_return_excluded_addresses 1
+#endif
+
+/*% 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)
+/*% Is TCP? */
+#define TCP(c) (((c)->attributes & NS_CLIENTATTR_TCP) != 0)
+
+/*% Want DNSSEC? */
+#define WANTDNSSEC(c) (((c)->attributes & \
+ NS_CLIENTATTR_WANTDNSSEC) != 0)
+/*% Want WANTAD? */
+#define WANTAD(c) (((c)->attributes & \
+ NS_CLIENTATTR_WANTAD) != 0)
+/*% Client presented a valid COOKIE. */
+#define HAVECOOKIE(c) (((c)->attributes & \
+ NS_CLIENTATTR_HAVECOOKIE) != 0)
+/*% Client presented a COOKIE. */
+#define WANTCOOKIE(c) (((c)->attributes & \
+ NS_CLIENTATTR_WANTCOOKIE) != 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)
+/*% DNS64 A lookup? */
+#define DNS64(c) (((c)->query.attributes & \
+ NS_QUERYATTR_DNS64) != 0)
+
+#define DNS64EXCLUDE(c) (((c)->query.attributes & \
+ NS_QUERYATTR_DNS64EXCLUDE) != 0)
+
+#define REDIRECT(c) (((c)->query.attributes & \
+ NS_QUERYATTR_REDIRECT) != 0)
+
+/*% No QNAME Proof? */
+#define NOQNAME(r) (((r)->attributes & \
+ DNS_RDATASETATTR_NOQNAME) != 0)
+
+#ifdef WANT_QUERYTRACE
+static inline void
+client_trace(ns_client_t *client, int level, const char *message) {
+ if (client != NULL && client->query.qname != NULL) {
+ if (isc_log_wouldlog(ns_g_lctx, level)) {
+ char qbuf[DNS_NAME_FORMATSIZE];
+ char tbuf[DNS_RDATATYPE_FORMATSIZE];
+ dns_name_format(client->query.qname,
+ qbuf, sizeof(qbuf));
+ dns_rdatatype_format(client->query.qtype,
+ tbuf, sizeof(tbuf));
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY, level,
+ "query client=%p thread=0x%lx "
+ "(%s/%s): %s",
+ client,
+ (unsigned long) isc_thread_self(),
+ qbuf, tbuf, message);
+ }
+ } else {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY, level,
+ "query client=%p thread=0x%lx "
+ "(<unknown-query>): %s",
+ client,
+ (unsigned long) isc_thread_self(),
+ message);
+ }
+}
+#define CTRACE(l,m) client_trace(client, l, m)
+#else
+#define CTRACE(l,m) ((void)m)
+#endif /* WANT_QUERYTRACE */
+
+
+#define DNS_GETDB_NOEXACT 0x01U
+#define DNS_GETDB_NOLOG 0x02U
+#define DNS_GETDB_PARTIAL 0x04U
+#define DNS_GETDB_IGNOREACL 0x08U
+
+#define PENDINGOK(x) (((x) & DNS_DBFIND_PENDINGOK) != 0)
+
+#define SFCACHE_CDFLAG 0x1
+
+/*
+ * These have the same semantics as:
+ *
+ * foo_attach(b, a);
+ * foo_detach(&a);
+ *
+ * without the locking and magic testing.
+ *
+ * We use SAVE and RESTORE as that shows the operation being performed.
+ */
+#define SAVE(a, b) do { INSIST(a == NULL); a = b; b = NULL; } while (0)
+#define RESTORE(a, b) SAVE(a, b)
+
+typedef struct client_additionalctx {
+ ns_client_t *client;
+ dns_rdataset_t *rdataset;
+} client_additionalctx_t;
+
+static isc_result_t
+query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype);
+
+static bool
+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, bool exact,
+ dns_name_t *found);
+
+static inline void
+log_queryerror(ns_client_t *client, isc_result_t result, int line, int level);
+
+static void
+rpz_st_clear(ns_client_t *client);
+
+static bool
+rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset);
+
+/*%
+ * Increment query statistics counters.
+ */
+static inline void
+inc_stats(ns_client_t *client, isc_statscounter_t counter) {
+ dns_zone_t *zone = client->query.authzone;
+ dns_rdatatype_t qtype;
+ dns_rdataset_t *rdataset;
+ isc_stats_t *zonestats;
+ dns_stats_t *querystats = NULL;
+
+ isc_stats_increment(ns_g_server->nsstats, counter);
+
+ if (zone == NULL)
+ return;
+
+ /* Do regular response type stats */
+ zonestats = dns_zone_getrequeststats(zone);
+
+ if (zonestats != NULL)
+ isc_stats_increment(zonestats, counter);
+
+ /* Do query type statistics
+ *
+ * We only increment per-type if we're using the authoritative
+ * answer counter, preventing double-counting.
+ */
+ if (counter == dns_nsstatscounter_authans) {
+ querystats = dns_zone_getrcvquerystats(zone);
+ if (querystats != NULL) {
+ rdataset = ISC_LIST_HEAD(client->query.qname->list);
+ if (rdataset != NULL) {
+ qtype = rdataset->type;
+ dns_rdatatypestats_increment(querystats, qtype);
+ }
+ }
+ }
+}
+
+static void
+query_send(ns_client_t *client) {
+ isc_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) {
+ dns_section_t answer = DNS_SECTION_ANSWER;
+ if (ISC_LIST_EMPTY(client->message->sections[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 if (client->message->rcode == dns_rcode_badcookie)
+ counter = dns_nsstatscounter_badcookie;
+ 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, int line) {
+ int loglevel = ISC_LOG_DEBUG(3);
+
+ switch (result) {
+ case DNS_R_SERVFAIL:
+ loglevel = ISC_LOG_DEBUG(1);
+ 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;
+ }
+
+ if (ns_g_server->log_queries)
+ loglevel = ISC_LOG_INFO;
+
+ log_queryerror(client, result, line, loglevel);
+
+ 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, bool 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) {
+ REQUIRE(NS_CLIENT_VALID(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_putrdataset(ns_client_t *client, dns_rdataset_t **rdatasetp) {
+ dns_rdataset_t *rdataset = *rdatasetp;
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_putrdataset");
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, rdatasetp);
+ }
+ CTRACE(ISC_LOG_DEBUG(3), "query_putrdataset: done");
+}
+
+static inline void
+query_reset(ns_client_t *client, bool everything) {
+ isc_buffer_t *dbuf, *dbuf_next;
+ ns_dbversion_t *dbversion, *dbversion_next;
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_reset");
+
+ /*%
+ * 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,
+ 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);
+
+ if (client->query.dns64_aaaa != NULL)
+ query_putrdataset(client, &client->query.dns64_aaaa);
+ if (client->query.dns64_sigaaaa != NULL)
+ query_putrdataset(client, &client->query.dns64_sigaaaa);
+ if (client->query.dns64_aaaaok != NULL) {
+ isc_mem_put(client->mctx, client->query.dns64_aaaaok,
+ client->query.dns64_aaaaoklen *
+ sizeof(bool));
+ client->query.dns64_aaaaok = NULL;
+ client->query.dns64_aaaaoklen = 0;
+ }
+
+ query_putrdataset(client, &client->query.redirect.rdataset);
+ query_putrdataset(client, &client->query.redirect.sigrdataset);
+ if (client->query.redirect.db != NULL) {
+ if (client->query.redirect.node != NULL)
+ dns_db_detachnode(client->query.redirect.db,
+ &client->query.redirect.node);
+ dns_db_detach(&client->query.redirect.db);
+ }
+ if (client->query.redirect.zone != NULL)
+ dns_zone_detach(&client->query.redirect.zone);
+
+ 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 = false;
+ if (client->query.rpz_st != NULL) {
+ rpz_st_clear(client);
+ if (everything) {
+ isc_mem_put(client->mctx, client->query.rpz_st,
+ sizeof(*client->query.rpz_st));
+ client->query.rpz_st = NULL;
+ }
+ }
+ client->query.origqname = NULL;
+ client->query.dboptions = 0;
+ client->query.fetchoptions = 0;
+ client->query.gluedb = NULL;
+ client->query.authdbset = false;
+ client->query.isreferral = false;
+ client->query.dns64_options = 0;
+ client->query.dns64_ttl = UINT32_MAX;
+ client->query.root_key_sentinel_keyid = 0;
+ client->query.root_key_sentinel_is_ta = false;
+ client->query.root_key_sentinel_not_ta = false;
+}
+
+static void
+query_next_callback(ns_client_t *client) {
+ query_reset(client, false);
+}
+
+void
+ns_query_free(ns_client_t *client) {
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ query_reset(client, true);
+}
+
+static inline isc_result_t
+query_newnamebuf(ns_client_t *client) {
+ isc_buffer_t *dbuf;
+ isc_result_t result;
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_newnamebuf");
+ /*%
+ * Allocate a name buffer.
+ */
+
+ dbuf = NULL;
+ result = isc_buffer_allocate(client->mctx, &dbuf, 1024);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "query_newnamebuf: isc_buffer_allocate failed: done");
+ return (result);
+ }
+ ISC_LIST_APPEND(client->query.namebufs, dbuf, link);
+
+ CTRACE(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3),
+ "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 < DNS_NAME_MAXWIRE) {
+ result = query_newnamebuf(client);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "query_newname");
+ name = NULL;
+ result = dns_message_gettempname(client->message, &name);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "query_newrdataset");
+ rdataset = NULL;
+ result = dns_message_gettemprdataset(client->message, &rdataset);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "query_newrdataset: "
+ "dns_message_gettemprdataset failed: done");
+ return (NULL);
+ }
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_newrdataset: done");
+ return (rdataset);
+}
+
+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;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ 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 = false;
+ client->query.rpz_st = NULL;
+ client->query.qname = NULL;
+ /*
+ * This mutex is destroyed when the client is destroyed in
+ * exit_check().
+ */
+ result = isc_mutex_init(&client->query.fetchlock);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ client->query.fetch = NULL;
+ client->query.prefetch = NULL;
+ client->query.authdb = NULL;
+ client->query.authzone = NULL;
+ client->query.authdbset = false;
+ client->query.isreferral = false;
+ client->query.dns64_aaaa = NULL;
+ client->query.dns64_sigaaaa = NULL;
+ client->query.dns64_aaaaok = NULL;
+ client->query.dns64_aaaaoklen = 0;
+ client->query.redirect.db = NULL;
+ client->query.redirect.node = NULL;
+ client->query.redirect.zone = NULL;
+ client->query.redirect.qtype = dns_rdatatype_none;
+ client->query.redirect.result = ISC_R_SUCCESS;
+ client->query.redirect.rdataset = NULL;
+ client->query.redirect.sigrdataset = NULL;
+ client->query.redirect.authoritative = false;
+ client->query.redirect.is_zone = false;
+ client->query.redirect.fname =
+ dns_fixedname_initname(&client->query.redirect.fixed);
+ query_reset(client, 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, true);
+ DESTROYLOCK(&client->query.fetchlock);
+ }
+
+ return (result);
+}
+
+static ns_dbversion_t *
+query_findversion(ns_client_t *client, dns_db_t *db) {
+ 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->acl_checked = false;
+ dbversion->queryok = false;
+ ISC_LIST_APPEND(client->query.activeversions,
+ dbversion, link);
+ }
+
+ 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;
+ dns_acl_t *queryacl, *queryonacl;
+ 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)
+ return (DNS_R_REFUSED);
+
+ /*
+ * Non recursive query to a static-stub zone is prohibited; its
+ * zone content is not public data, but a part of local configuration
+ * and should not be disclosed.
+ */
+ if (dns_zone_gettype(zone) == dns_zone_staticstub &&
+ !RECURSIONOK(client)) {
+ return (DNS_R_REFUSED);
+ }
+
+ /*
+ * 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.
+ */
+
+ /*
+ * Get the current version of this database.
+ */
+ dbversion = query_findversion(client, db);
+ if (dbversion == NULL) {
+ CTRACE(ISC_LOG_ERROR, "unable to get db version");
+ return (DNS_R_SERVFAIL);
+ }
+
+ if ((options & DNS_GETDB_IGNOREACL) != 0)
+ goto approved;
+ if (dbversion->acl_checked) {
+ if (!dbversion->queryok)
+ return (DNS_R_REFUSED);
+ goto approved;
+ }
+
+ 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.
+ */
+ dbversion->acl_checked = true;
+ if ((client->query.attributes &
+ NS_QUERYATTR_QUERYOK) == 0) {
+ dbversion->queryok = false;
+ return (DNS_R_REFUSED);
+ }
+ dbversion->queryok = true;
+ goto approved;
+ }
+ }
+
+ result = ns_client_checkaclsilent(client, NULL, queryacl, true);
+ if ((options & DNS_GETDB_NOLOG) == 0) {
+ 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 and only if we've gotten this far, check allow-query-on too */
+ if (result == ISC_R_SUCCESS) {
+ queryonacl = dns_zone_getqueryonacl(zone);
+ if (queryonacl == NULL)
+ queryonacl = client->view->queryonacl;
+
+ result = ns_client_checkaclsilent(client, &client->destaddr,
+ queryonacl, true);
+ if ((options & DNS_GETDB_NOLOG) == 0 &&
+ result != ISC_R_SUCCESS)
+ ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "query-on denied");
+ }
+
+ dbversion->acl_checked = true;
+ if (result != ISC_R_SUCCESS) {
+ dbversion->queryok = false;
+ return (DNS_R_REFUSED);
+ }
+ dbversion->queryok = true;
+
+ approved:
+ /* Transfer ownership, if necessary. */
+ if (versionp != NULL)
+ *versionp = dbversion->version;
+ return (ISC_R_SUCCESS);
+}
+
+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;
+ bool partial = 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 = 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 void
+rpz_log_rewrite(ns_client_t *client, bool disabled,
+ dns_rpz_policy_t policy, dns_rpz_type_t type,
+ dns_zone_t *p_zone, dns_name_t *p_name,
+ dns_name_t *cname, dns_rpz_num_t rpz_num)
+{
+ isc_stats_t *zonestats;
+ char qname_buf[DNS_NAME_FORMATSIZE];
+ char p_name_buf[DNS_NAME_FORMATSIZE];
+ char cname_buf[DNS_NAME_FORMATSIZE] = { 0 };
+ const char *s1 = cname_buf, *s2 = cname_buf;
+ dns_rpz_st_t *st;
+
+ /*
+ * Count enabled rewrites in the global counter.
+ * Count both enabled and disabled rewrites for each zone.
+ */
+ if (!disabled && policy != DNS_RPZ_POLICY_PASSTHRU) {
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_rpz_rewrites);
+ }
+ if (p_zone != NULL) {
+ zonestats = dns_zone_getrequeststats(p_zone);
+ if (zonestats != NULL)
+ isc_stats_increment(zonestats,
+ dns_nsstatscounter_rpz_rewrites);
+ }
+
+ if (!isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
+ return;
+
+ st = client->query.rpz_st;
+ if ((st->popt.no_log & DNS_RPZ_ZBIT(rpz_num)) != 0)
+ return;
+
+ dns_name_format(client->query.qname, qname_buf, sizeof(qname_buf));
+ dns_name_format(p_name, p_name_buf, sizeof(p_name_buf));
+ if (cname != NULL) {
+ s1 = " (CNAME to: ";
+ dns_name_format(cname, cname_buf, sizeof(cname_buf));
+ s2 = ")";
+ }
+
+ ns_client_log(client, DNS_LOGCATEGORY_RPZ, NS_LOGMODULE_QUERY,
+ DNS_RPZ_INFO_LEVEL, "%srpz %s %s rewrite %s via %s%s%s%s",
+ disabled ? "disabled " : "",
+ dns_rpz_type2str(type), dns_rpz_policy2str(policy),
+ qname_buf, p_name_buf, s1, cname_buf, s2);
+}
+
+static void
+rpz_log_fail_helper(ns_client_t *client, int level, dns_name_t *p_name,
+ dns_rpz_type_t rpz_type1, dns_rpz_type_t rpz_type2,
+ const char *str, isc_result_t result)
+{
+ char qnamebuf[DNS_NAME_FORMATSIZE];
+ char p_namebuf[DNS_NAME_FORMATSIZE];
+ const char *failed;
+ const char *slash;
+ const char *via;
+ const char *str_blank;
+ const char *rpztypestr1;
+ const char *rpztypestr2;
+
+ if (!isc_log_wouldlog(ns_g_lctx, level))
+ return;
+
+ /*
+ * bin/tests/system/rpz/tests.sh looks for "rpz.*failed" for problems.
+ */
+ if (level <= DNS_RPZ_DEBUG_LEVEL1)
+ failed = "failed: ";
+ else
+ failed = ": ";
+
+ rpztypestr1 = dns_rpz_type2str(rpz_type1);
+ if (rpz_type2 != DNS_RPZ_TYPE_BAD) {
+ slash = "/";
+ rpztypestr2 = dns_rpz_type2str(rpz_type2);
+ } else {
+ slash = "";
+ rpztypestr2 = "";
+ }
+
+ str_blank = (*str != ' ' && *str != '\0') ? " " : "";
+ dns_name_format(client->query.qname, qnamebuf, sizeof(qnamebuf));
+ if (p_name != NULL) {
+ via = " via ";
+ dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
+ } else {
+ via = "";
+ p_namebuf[0] = '\0';
+ }
+
+ ns_client_log(client, NS_LOGCATEGORY_QUERY_ERRORS,
+ NS_LOGMODULE_QUERY, level,
+ "rpz %s%s%s rewrite %s%s%s%s%s%s : %s",
+ rpztypestr1, slash, rpztypestr2,
+ qnamebuf, via, p_namebuf, str_blank,
+ str, failed, isc_result_totext(result));
+}
+
+static void
+rpz_log_fail(ns_client_t *client, int level, dns_name_t *p_name,
+ dns_rpz_type_t rpz_type, const char *str, isc_result_t result)
+{
+ rpz_log_fail_helper(client, level, p_name,
+ rpz_type, DNS_RPZ_TYPE_BAD, str, result);
+}
+
+/*
+ * Get a policy rewrite zone database.
+ */
+static isc_result_t
+rpz_getdb(ns_client_t *client, dns_name_t *p_name, dns_rpz_type_t rpz_type,
+ dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp)
+{
+ char qnamebuf[DNS_NAME_FORMATSIZE];
+ char p_namebuf[DNS_NAME_FORMATSIZE];
+ dns_dbversion_t *rpz_version = NULL;
+ isc_result_t result;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_getdb");
+
+ result = query_getzonedb(client, p_name, dns_rdatatype_any,
+ DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version);
+ if (result == ISC_R_SUCCESS) {
+ dns_rpz_st_t *st = client->query.rpz_st;
+
+ /*
+ * It isn't meaningful to log this message when
+ * logging is disabled for some policy zones.
+ */
+ if (st->popt.no_log == 0 &&
+ isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2))
+ {
+ dns_name_format(client->query.qname, qnamebuf,
+ sizeof(qnamebuf));
+ dns_name_format(p_name, p_namebuf, sizeof(p_namebuf));
+ ns_client_log(client, DNS_LOGCATEGORY_RPZ,
+ NS_LOGMODULE_QUERY, DNS_RPZ_DEBUG_LEVEL2,
+ "try rpz %s rewrite %s via %s",
+ dns_rpz_type2str(rpz_type),
+ qnamebuf, p_namebuf);
+ }
+ *versionp = rpz_version;
+ return (ISC_R_SUCCESS);
+ }
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
+ " query_getzonedb()", result);
+ 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;
+ bool 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_CACHEACLOKVALID) != 0) {
+ /*
+ * We've evaluated the view's cacheacl already. If
+ * NS_QUERYATTR_CACHEACLOK is set, then the client is
+ * allowed to make queries, otherwise the query should
+ * be refused.
+ */
+ check_acl = false;
+ if ((client->query.attributes & NS_QUERYATTR_CACHEACLOK) == 0)
+ goto refuse;
+ } else {
+ /*
+ * We haven't evaluated the view's queryacl yet.
+ */
+ check_acl = true;
+ }
+
+ if (check_acl) {
+ bool log = !(options & DNS_GETDB_NOLOG);
+ char msg[NS_CLIENT_ACLMSGSIZE("query (cache)")];
+
+ result = ns_client_checkaclsilent(client, NULL,
+ client->view->cacheacl,
+ true);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We were allowed by the "allow-query-cache" ACL.
+ * Remember this so we don't have to check again.
+ */
+ client->query.attributes |=
+ NS_QUERYATTR_CACHEACLOK;
+ 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_CACHEACLOKVALID attribute is now valid.
+ */
+ client->query.attributes |= NS_QUERYATTR_CACHEACLOKVALID;
+
+ 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, bool *is_zonep)
+{
+ isc_result_t result;
+
+ isc_result_t tresult;
+ unsigned int namelabels;
+ unsigned int zonelabels;
+ dns_zone_t *zone = NULL;
+
+ REQUIRE(zonep != NULL && *zonep == 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 DLZ drivers are loaded for this view
+ */
+ if (ISC_UNLIKELY(zonelabels < namelabels &&
+ !ISC_LIST_EMPTY(client->view->dlz_searched)))
+ {
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+ dns_db_t *tdbp;
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ tdbp = NULL;
+ tresult = dns_view_searchdlz(client->view, name,
+ zonelabels, &cm, &ci, &tdbp);
+ /* If we successful, we found a better match. */
+ if (tresult == ISC_R_SUCCESS) {
+ ns_dbversion_t *dbversion;
+
+ /*
+ * 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;
+
+ dbversion = query_findversion(client, tdbp);
+ if (dbversion == NULL) {
+ tresult = ISC_R_NOMEMORY;
+ } else {
+ /*
+ * Be sure to return our database.
+ */
+ *dbp = tdbp;
+ *versionp = dbversion->version;
+ }
+
+ /*
+ * We return a null zone, No stats for DLZ zones.
+ */
+ zone = NULL;
+ result = tresult;
+ }
+ }
+
+ /* If successful, Transfer ownership of zone. */
+ if (result == ISC_R_SUCCESS) {
+ *zonep = zone;
+ /*
+ * If neither attempt above succeeded, return the cache instead
+ */
+ *is_zonep = true;
+ } else if (result == ISC_R_NOTFOUND) {
+ result = query_getcachedb(client, name, qtype, dbp, options);
+ *is_zonep = false;
+ }
+ return (result);
+}
+
+static inline bool
+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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3),
+ "query_isduplicate: true: done");
+ return (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 (mnamep != NULL)
+ *mnamep = mname;
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_isduplicate: false: done");
+ return (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;
+ bool added_something, need_addname;
+ dns_zone_t *zone;
+ dns_rdatatype_t type;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+ dns_rdatasetadditional_t additionaltype;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(qtype != dns_rdatatype_any);
+
+ if (!WANTDNSSEC(client) && dns_rdatatype_isdnssec(qtype))
+ return (ISC_R_SUCCESS);
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_addadditional");
+
+ /*
+ * Initialization.
+ */
+ eresult = ISC_R_SUCCESS;
+ fname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ trdataset = NULL;
+ db = NULL;
+ version = NULL;
+ node = NULL;
+ added_something = false;
+ need_addname = false;
+ zone = NULL;
+ additionaltype = dns_rdatasetadditional_fromauth;
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, 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(ISC_LOG_DEBUG(3), "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.
+ */
+ result = dns_db_findext(db, name, version, type,
+ client->query.dboptions,
+ client->now, &node, fname, &cm, &ci,
+ 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:
+ 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;
+ /*
+ * Attempt to validate glue.
+ */
+ if (sigrdataset == NULL) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+ }
+ result = dns_db_findext(db, name, version, type,
+ client->query.dboptions |
+ DNS_DBFIND_GLUEOK | DNS_DBFIND_ADDITIONALOK,
+ client->now, &node, fname, &cm, &ci,
+ rdataset, sigrdataset);
+
+ dns_cache_updatestats(client->view->cache, result);
+ 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 poison 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);
+
+ additionaltype = dns_rdatasetadditional_fromglue;
+ result = dns_db_findext(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, &cm, &ci,
+ 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) {
+ INSIST(mname != fname);
+ query_releasename(client, &fname);
+ fname = mname;
+ } else
+ need_addname = true;
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ trdataset = rdataset;
+ rdataset = NULL;
+ added_something = 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) {
+#ifdef ALLOW_FILTER_AAAA
+ bool have_a = false;
+#endif
+
+ /*
+ * 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;
+ }
+ if (query_isduplicate(client, fname, dns_rdatatype_a, NULL))
+ goto aaaa_lookup;
+ 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);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ }
+ if (result == ISC_R_SUCCESS) {
+ bool invalid = false;
+ mname = NULL;
+#ifdef ALLOW_FILTER_AAAA
+ have_a = true;
+#endif
+ if (additionaltype ==
+ dns_rdatasetadditional_fromcache &&
+ (DNS_TRUST_PENDING(rdataset->trust) ||
+ DNS_TRUST_GLUE(rdataset->trust)))
+ {
+ /* validate() may change rdataset->trust */
+ invalid = !validate(client, db, fname,
+ rdataset, sigrdataset);
+ }
+ if (invalid && DNS_TRUST_PENDING(rdataset->trust)) {
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ } else if (!query_isduplicate(client, fname,
+ dns_rdatatype_a, &mname))
+ {
+ if (mname != fname) {
+ if (mname != NULL) {
+ query_releasename(client,
+ &fname);
+ fname = mname;
+ } else
+ need_addname = true;
+ }
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ added_something = 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);
+ }
+ }
+ aaaa_lookup:
+ if (query_isduplicate(client, fname, dns_rdatatype_aaaa, NULL))
+ goto addname;
+ 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);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ }
+ if (result == ISC_R_SUCCESS) {
+ bool invalid = false;
+ mname = NULL;
+ /*
+ * There's an A; check whether we're filtering AAAA
+ */
+#ifdef ALLOW_FILTER_AAAA
+ if (have_a &&
+ (client->filter_aaaa == dns_aaaa_break_dnssec ||
+ (client->filter_aaaa == dns_aaaa_filter &&
+ (!WANTDNSSEC(client) || sigrdataset == NULL ||
+ !dns_rdataset_isassociated(sigrdataset)))))
+ goto addname;
+#endif
+ if (additionaltype ==
+ dns_rdatasetadditional_fromcache &&
+ (DNS_TRUST_PENDING(rdataset->trust) ||
+ DNS_TRUST_GLUE(rdataset->trust)))
+ {
+ /* validate() may change rdataset->trust */
+ invalid = !validate(client, db, fname,
+ rdataset, sigrdataset);
+ }
+ if (invalid && DNS_TRUST_PENDING(rdataset->trust)) {
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ } else if (!query_isduplicate(client, fname,
+ dns_rdatatype_aaaa, &mname))
+ {
+ if (mname != fname) {
+ if (mname != NULL) {
+ query_releasename(client,
+ &fname);
+ fname = mname;
+ } else
+ need_addname = true;
+ }
+ ISC_LIST_APPEND(fname->list, rdataset, link);
+ added_something = true;
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ {
+ ISC_LIST_APPEND(fname->list,
+ sigrdataset, link);
+ sigrdataset = NULL;
+ }
+ rdataset = NULL;
+ }
+ }
+ }
+
+ addname:
+ CTRACE(ISC_LOG_DEBUG(3), "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 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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "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, 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, 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;
+ bool added_something, need_addname, needadditionalcache;
+ bool need_sigrrset;
+ dns_zone_t *zone;
+ dns_rdatatype_t type;
+ dns_rdatasetadditional_t additionaltype;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+ bool invalid;
+
+ /*
+ * If we don't have an additional cache call query_addadditional.
+ */
+ client = additionalctx->client;
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ if (qtype != dns_rdatatype_a || client->view->acache == NULL) {
+ /*
+ * 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;
+ eresult = ISC_R_SUCCESS;
+ fname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ db = NULL;
+ cdb = NULL;
+ version = NULL;
+ cversion = NULL;
+ node = NULL;
+ cnode = NULL;
+ added_something = false;
+ need_addname = false;
+ zone = NULL;
+ needadditionalcache = false;
+ POST(needadditionalcache);
+ additionaltype = dns_rdatasetadditional_fromauth;
+ dns_name_init(&cfname, NULL);
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ CTRACE(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3),
+ "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(ISC_LOG_DEBUG(3),
+ "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(ISC_LOG_DEBUG(3),
+ "query_addadditional2: negative auth additional cache");
+ dns_db_closeversion(cdb, &cversion, 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(ISC_LOG_DEBUG(3),
+ "query_addadditional2: auth additional cache");
+ dns_db_closeversion(cdb, &cversion, 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(ISC_LOG_DEBUG(3), "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_findext(db, name, version, type,
+ client->query.dboptions,
+ client->now, &node, fname, &cm, &ci,
+ 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_findext(db, name, version, type,
+ client->query.dboptions |
+ DNS_DBFIND_GLUEOK | DNS_DBFIND_ADDITIONALOK,
+ client->now, &node, fname, &cm, &ci,
+ 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 poison 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(ISC_LOG_DEBUG(3),
+ "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(ISC_LOG_DEBUG(3),
+ "query_addadditional2: negative glue additional cache");
+ dns_db_closeversion(cdb, &cversion, false);
+ dns_db_detach(&cdb);
+ goto cleanup;
+ }
+
+ /* Cache hit. */
+ CTRACE(ISC_LOG_DEBUG(3), "query_addadditional2: glue additional cache");
+ dns_db_closeversion(cdb, &cversion, 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_findext(db, name, version, type,
+ client->query.dboptions | DNS_DBFIND_GLUEOK,
+ client->now, &node, fname, &cm, &ci,
+ 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 = true;
+
+ rdataset = query_newrdataset(client);
+ if (rdataset == NULL)
+ goto cleanup;
+
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL)
+ goto cleanup;
+
+ if (additionaltype == dns_rdatasetadditional_fromcache &&
+ query_isduplicate(client, fname, dns_rdatatype_a, NULL))
+ goto aaaa_lookup;
+ /*
+ * 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);
+
+ /*
+ * Try to promote pending/glue from the cache to secure.
+ * If unable to do so, drop it from the response unless
+ * it's glue, in which case it may still be needed.
+ */
+ invalid = false;
+ if (result == ISC_R_SUCCESS &&
+ additionaltype == dns_rdatasetadditional_fromcache &&
+ (DNS_TRUST_PENDING(rdataset->trust) ||
+ DNS_TRUST_GLUE(rdataset->trust)))
+ {
+ invalid = !validate(client, db, fname,
+ rdataset, sigrdataset);
+ }
+ if (invalid && DNS_TRUST_PENDING(rdataset->trust)) {
+ 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);
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(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;
+ }
+ }
+
+ aaaa_lookup:
+ if (additionaltype == dns_rdatasetadditional_fromcache &&
+ query_isduplicate(client, fname, dns_rdatatype_aaaa, NULL))
+ goto foundcache;
+ /* Find AAAA RRset with sig RRset */
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_aaaa,
+ 0, client->now, rdataset, sigrdataset);
+ /*
+ * Try to promote pending/glue from the cache to secure.
+ * If unable to do so, drop it from the response unless
+ * it's glue, in which case it may still be needed.
+ */
+ invalid = false;
+ if (result == ISC_R_SUCCESS &&
+ additionaltype == dns_rdatasetadditional_fromcache &&
+ (DNS_TRUST_PENDING(rdataset->trust) ||
+ DNS_TRUST_GLUE(rdataset->trust)))
+ {
+ invalid = !validate(client, db, fname,
+ rdataset, sigrdataset);
+ }
+ if (invalid && DNS_TRUST_PENDING(rdataset->trust)) {
+ 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 == 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 = 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 != fname) {
+ 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 = true;
+ }
+ ISC_LIST_UNLINK(cfname.list, crdataset, link);
+ ISC_LIST_APPEND(fname->list, crdataset, link);
+ added_something = true;
+ need_sigrrset = true;
+ } else
+ need_sigrrset = 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 = true; /* just in case */
+ need_sigrrset = false;
+ }
+ }
+
+ CTRACE(ISC_LOG_DEBUG(3), "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 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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "query_addrdataset: done");
+}
+
+static isc_result_t
+query_dns64(ns_client_t *client, dns_name_t **namep, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset, isc_buffer_t *dbuf,
+ dns_section_t section)
+{
+ dns_name_t *name, *mname;
+ dns_rdata_t *dns64_rdata;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t *dns64_rdatalist;
+ dns_rdataset_t *dns64_rdataset;
+ dns_rdataset_t *mrdataset;
+ isc_buffer_t *buffer;
+ isc_region_t r;
+ isc_result_t result;
+ dns_view_t *view = client->view;
+ isc_netaddr_t netaddr;
+ dns_dns64_t *dns64;
+ unsigned int flags = 0;
+
+ /*%
+ * 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(ISC_LOG_DEBUG(3), "query_dns64");
+ name = *namep;
+ mname = NULL;
+ mrdataset = NULL;
+ buffer = NULL;
+ dns64_rdata = NULL;
+ dns64_rdataset = NULL;
+ dns64_rdatalist = NULL;
+ result = dns_message_findname(client->message, section,
+ name, dns_rdatatype_aaaa,
+ 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(ISC_LOG_DEBUG(3),
+ "query_dns64: dns_message_findname succeeded: done");
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ return (ISC_R_SUCCESS);
+ } 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;
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+ result = isc_buffer_allocate(client->mctx, &buffer, view->dns64cnt *
+ 16 * dns_rdataset_count(rdataset));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdataset(client->message, &dns64_rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdatalist(client->message,
+ &dns64_rdatalist);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_rdatalist_init(dns64_rdatalist);
+ dns64_rdatalist->rdclass = dns_rdataclass_in;
+ dns64_rdatalist->type = dns_rdatatype_aaaa;
+ if (client->query.dns64_ttl != UINT32_MAX)
+ dns64_rdatalist->ttl = ISC_MIN(rdataset->ttl,
+ client->query.dns64_ttl);
+ else
+ dns64_rdatalist->ttl = ISC_MIN(rdataset->ttl, 600);
+
+ if (RECURSIONOK(client))
+ flags |= DNS_DNS64_RECURSIVE;
+
+ /*
+ * We use the signatures from the A lookup to set DNS_DNS64_DNSSEC
+ * as this provides a easy way to see if the answer was signed.
+ */
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
+ flags |= DNS_DNS64_DNSSEC;
+
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ for (dns64 = ISC_LIST_HEAD(client->view->dns64);
+ dns64 != NULL; dns64 = dns_dns64_next(dns64)) {
+
+ dns_rdataset_current(rdataset, &rdata);
+ isc_buffer_availableregion(buffer, &r);
+ INSIST(r.length >= 16);
+ result = dns_dns64_aaaafroma(dns64, &netaddr,
+ client->signer,
+ &ns_g_server->aclenv,
+ flags, rdata.data, r.base);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdata_reset(&rdata);
+ continue;
+ }
+ isc_buffer_add(buffer, 16);
+ isc_buffer_remainingregion(buffer, &r);
+ isc_buffer_forward(buffer, 16);
+ result = dns_message_gettemprdata(client->message,
+ &dns64_rdata);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdata_init(dns64_rdata);
+ dns_rdata_fromregion(dns64_rdata, dns_rdataclass_in,
+ dns_rdatatype_aaaa, &r);
+ ISC_LIST_APPEND(dns64_rdatalist->rdata, dns64_rdata,
+ link);
+ dns64_rdata = NULL;
+ dns_rdata_reset(&rdata);
+ }
+ }
+ if (result != ISC_R_NOMORE)
+ goto cleanup;
+
+ if (ISC_LIST_EMPTY(dns64_rdatalist->rdata))
+ goto cleanup;
+
+ result = dns_rdatalist_tordataset(dns64_rdatalist, dns64_rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdataset_setownercase(dns64_rdataset, mname);
+ client->query.attributes |= NS_QUERYATTR_NOADDITIONAL;
+ dns64_rdataset->trust = rdataset->trust;
+ query_addrdataset(client, mname, dns64_rdataset);
+ dns64_rdataset = NULL;
+ dns64_rdatalist = NULL;
+ dns_message_takebuffer(client->message, &buffer);
+ inc_stats(client, dns_nsstatscounter_dns64);
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (buffer != NULL)
+ isc_buffer_free(&buffer);
+
+ if (dns64_rdata != NULL)
+ dns_message_puttemprdata(client->message, &dns64_rdata);
+
+ if (dns64_rdataset != NULL)
+ dns_message_puttemprdataset(client->message, &dns64_rdataset);
+
+ if (dns64_rdatalist != NULL) {
+ for (dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata);
+ dns64_rdata != NULL;
+ dns64_rdata = ISC_LIST_HEAD(dns64_rdatalist->rdata))
+ {
+ ISC_LIST_UNLINK(dns64_rdatalist->rdata,
+ dns64_rdata, link);
+ dns_message_puttemprdata(client->message, &dns64_rdata);
+ }
+ dns_message_puttemprdatalist(client->message, &dns64_rdatalist);
+ }
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_dns64: done");
+ return (result);
+}
+
+static void
+query_filter64(ns_client_t *client, dns_name_t **namep,
+ dns_rdataset_t *rdataset, isc_buffer_t *dbuf,
+ dns_section_t section)
+{
+ dns_name_t *name, *mname;
+ dns_rdata_t *myrdata;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t *myrdatalist;
+ dns_rdataset_t *myrdataset;
+ isc_buffer_t *buffer;
+ isc_region_t r;
+ isc_result_t result;
+ unsigned int i;
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_filter64");
+
+ INSIST(client->query.dns64_aaaaok != NULL);
+ INSIST(client->query.dns64_aaaaoklen == dns_rdataset_count(rdataset));
+
+ name = *namep;
+ mname = NULL;
+ buffer = NULL;
+ myrdata = NULL;
+ myrdataset = NULL;
+ myrdatalist = NULL;
+ result = dns_message_findname(client->message, section,
+ name, dns_rdatatype_aaaa,
+ rdataset->covers,
+ &mname, &myrdataset);
+ if (result == ISC_R_SUCCESS) {
+ /*
+ * We've already got an RRset of the given name and type.
+ * There's nothing else to do;
+ */
+ CTRACE(ISC_LOG_DEBUG(3),
+ "query_filter64: dns_message_findname succeeded: done");
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ return;
+ } else if (result == DNS_R_NXDOMAIN) {
+ mname = name;
+ *namep = NULL;
+ } else {
+ RUNTIME_CHECK(result == DNS_R_NXRRSET);
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ dbuf = NULL;
+ }
+
+ if (rdataset->trust != dns_trust_secure &&
+ (section == DNS_SECTION_ANSWER ||
+ section == DNS_SECTION_AUTHORITY))
+ client->query.attributes &= ~NS_QUERYATTR_SECURE;
+
+ result = isc_buffer_allocate(client->mctx, &buffer,
+ 16 * dns_rdataset_count(rdataset));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdataset(client->message, &myrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_message_gettemprdatalist(client->message, &myrdatalist);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_rdatalist_init(myrdatalist);
+ myrdatalist->rdclass = dns_rdataclass_in;
+ myrdatalist->type = dns_rdatatype_aaaa;
+ myrdatalist->ttl = rdataset->ttl;
+
+ i = 0;
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ if (!client->query.dns64_aaaaok[i++])
+ continue;
+ dns_rdataset_current(rdataset, &rdata);
+ INSIST(rdata.length == 16);
+ isc_buffer_putmem(buffer, rdata.data, rdata.length);
+ isc_buffer_remainingregion(buffer, &r);
+ isc_buffer_forward(buffer, rdata.length);
+ result = dns_message_gettemprdata(client->message, &myrdata);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdata_init(myrdata);
+ dns_rdata_fromregion(myrdata, dns_rdataclass_in,
+ dns_rdatatype_aaaa, &r);
+ ISC_LIST_APPEND(myrdatalist->rdata, myrdata, link);
+ myrdata = NULL;
+ dns_rdata_reset(&rdata);
+ }
+ if (result != ISC_R_NOMORE)
+ goto cleanup;
+
+ result = dns_rdatalist_tordataset(myrdatalist, myrdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_rdataset_setownercase(myrdataset, name);
+ client->query.attributes |= NS_QUERYATTR_NOADDITIONAL;
+ if (mname == name) {
+ if (dbuf != NULL)
+ query_keepname(client, name, dbuf);
+ dns_message_addname(client->message, name, section);
+ dbuf = NULL;
+ }
+ myrdataset->trust = rdataset->trust;
+ query_addrdataset(client, mname, myrdataset);
+ myrdataset = NULL;
+ myrdatalist = NULL;
+ dns_message_takebuffer(client->message, &buffer);
+
+ cleanup:
+ if (buffer != NULL)
+ isc_buffer_free(&buffer);
+
+ if (myrdata != NULL)
+ dns_message_puttemprdata(client->message, &myrdata);
+
+ if (myrdataset != NULL)
+ dns_message_puttemprdataset(client->message, &myrdataset);
+
+ if (myrdatalist != NULL) {
+ for (myrdata = ISC_LIST_HEAD(myrdatalist->rdata);
+ myrdata != NULL;
+ myrdata = ISC_LIST_HEAD(myrdatalist->rdata))
+ {
+ ISC_LIST_UNLINK(myrdatalist->rdata, myrdata, link);
+ dns_message_puttemprdata(client->message, &myrdata);
+ }
+ dns_message_puttemprdatalist(client->message, &myrdatalist);
+ }
+ if (dbuf != NULL)
+ query_releasename(client, &name);
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_filter64: 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(ISC_LOG_DEBUG(3), "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.
+ */
+ CTRACE(ISC_LOG_DEBUG(3),
+ "query_addrrset: dns_message_findname succeeded: done");
+ if (dbuf != NULL)
+ query_releasename(client, namep);
+ if ((rdataset->attributes & DNS_RDATASETATTR_REQUIRED) != 0)
+ mrdataset->attributes |= DNS_RDATASETATTR_REQUIRED;
+ 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(ISC_LOG_DEBUG(3), "query_addrrset: done");
+}
+
+static inline isc_result_t
+query_addsoa(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version,
+ unsigned int override_ttl, bool isassociated,
+ dns_section_t section)
+{
+ 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;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_addsoa");
+ /*
+ * Initialization.
+ */
+ eresult = ISC_R_SUCCESS;
+ name = NULL;
+ rdataset = NULL;
+ node = NULL;
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ /*
+ * Don't add the SOA record for test which set "-T nosoa".
+ */
+ if (ns_g_nosoa && (!WANTDNSSEC(client) || !isassociated))
+ return (ISC_R_SUCCESS);
+
+ /*
+ * 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) {
+ CTRACE(ISC_LOG_ERROR, "unable to allocate rdataset");
+ eresult = DNS_R_SERVFAIL;
+ goto cleanup;
+ }
+ if (WANTDNSSEC(client) && dns_db_issecure(db)) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ CTRACE(ISC_LOG_ERROR, "unable to allocate sigrdataset");
+ 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;
+
+ fname = dns_fixedname_initname(&foundname);
+
+ result = dns_db_findext(db, name, version, dns_rdatatype_soa,
+ client->query.dboptions, 0, &node,
+ fname, &cm, &ci, 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!
+ */
+ CTRACE(ISC_LOG_ERROR, "unable to find SOA RR at zone apex");
+ 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 (override_ttl != UINT32_MAX &&
+ override_ttl < rdataset->ttl) {
+ rdataset->ttl = override_ttl;
+ if (sigrdataset != NULL)
+ sigrdataset->ttl = override_ttl;
+ }
+
+ /*
+ * 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;
+
+ if (section == DNS_SECTION_ADDITIONAL)
+ rdataset->attributes |= DNS_RDATASETATTR_REQUIRED;
+ query_addrrset(client, &name, &rdataset, sigrdatasetp, NULL,
+ section);
+ }
+
+ 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;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_addns");
+ /*
+ * Initialization.
+ */
+ eresult = ISC_R_SUCCESS;
+ name = NULL;
+ rdataset = NULL;
+ node = NULL;
+ fname = dns_fixedname_initname(&foundname);
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ /*
+ * Get resources and make 'name' be the database origin.
+ */
+ result = dns_message_gettempname(client->message, &name);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "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(ISC_LOG_ERROR,
+ "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(ISC_LOG_ERROR,
+ "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(ISC_LOG_DEBUG(3), "query_addns: calling dns_db_find");
+ result = dns_db_findext(db, name, NULL, dns_rdatatype_ns,
+ client->query.dboptions, 0, &node,
+ fname, &cm, &ci, rdataset, sigrdataset);
+ CTRACE(ISC_LOG_DEBUG(3), "query_addns: dns_db_find complete");
+ }
+ if (result != ISC_R_SUCCESS) {
+ CTRACE(ISC_LOG_ERROR,
+ "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(ISC_LOG_DEBUG(3), "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(ISC_LOG_DEBUG(3), "query_addns: done");
+ return (eresult);
+}
+static isc_result_t
+query_add_cname(ns_client_t *client, dns_name_t *qname, dns_name_t *tname,
+ dns_trust_t trust, dns_ttl_t ttl)
+{
+ dns_rdataset_t *rdataset;
+ dns_rdatalist_t *rdatalist;
+ dns_rdata_t *rdata;
+ isc_region_t r;
+ dns_name_t *aname;
+ isc_result_t result;
+
+ /*
+ * We assume the name data referred to by tname won't go away.
+ */
+
+ aname = NULL;
+ result = dns_message_gettempname(client->message, &aname);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_name_dup(qname, client->mctx, aname);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &aname);
+ return (result);
+ }
+
+ rdatalist = NULL;
+ result = dns_message_gettemprdatalist(client->message, &rdatalist);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &aname);
+ return (result);
+ }
+ rdata = NULL;
+ result = dns_message_gettemprdata(client->message, &rdata);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &aname);
+ dns_message_puttemprdatalist(client->message, &rdatalist);
+ return (result);
+ }
+ rdataset = NULL;
+ result = dns_message_gettemprdataset(client->message, &rdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_message_puttempname(client->message, &aname);
+ dns_message_puttemprdatalist(client->message, &rdatalist);
+ dns_message_puttemprdata(client->message, &rdata);
+ return (result);
+ }
+ rdatalist->type = dns_rdatatype_cname;
+ rdatalist->rdclass = client->message->rdclass;
+ rdatalist->ttl = ttl;
+
+ dns_name_toregion(tname, &r);
+ rdata->data = r.base;
+ rdata->length = r.length;
+ rdata->rdclass = client->message->rdclass;
+ rdata->type = dns_rdatatype_cname;
+
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset)
+ == ISC_R_SUCCESS);
+ rdataset->trust = trust;
+ dns_rdataset_setownercase(rdataset, aname);
+
+ query_addrrset(client, &aname, &rdataset, NULL, NULL,
+ DNS_SECTION_ANSWER);
+ if (rdataset != NULL) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, &rdataset);
+ }
+ if (aname != NULL)
+ dns_message_puttempname(client->message, &aname);
+
+ return (ISC_R_SUCCESS);
+}
+
+static bool
+get_root_key_sentinel_id(ns_client_t *client, const char *ndata) {
+ unsigned int v = 0;
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ if (ndata[i] < '0' || ndata[i] > '9') {
+ return (false);
+ }
+ v *= 10;
+ v += ndata[i] - '0';
+ }
+ if (v > 65535U) {
+ return (false);
+ }
+ client->query.root_key_sentinel_keyid = v;
+ return (true);
+}
+
+/*%
+ * Find out if the query is for a root key sentinel and if so, record the type
+ * of root key sentinel query and the key id that is being checked for.
+ *
+ * The code is assuming a zero padded decimal field of width 5.
+ */
+static void
+root_key_sentinel_detect(ns_client_t *client) {
+ const char *ndata = (const char *)client->query.qname->ndata;
+
+ if (client->query.qname->length > 30 && ndata[0] == 29 &&
+ strncasecmp(ndata + 1, "root-key-sentinel-is-ta-", 24) == 0)
+ {
+ if (!get_root_key_sentinel_id(client, ndata + 25)) {
+ return;
+ }
+ client->query.root_key_sentinel_is_ta = true;
+ ns_client_log(client, NS_LOGCATEGORY_TAT,
+ NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "root-key-sentinel-is-ta query label found");
+ } else if (client->query.qname->length > 31 && ndata[0] == 30 &&
+ strncasecmp(ndata + 1, "root-key-sentinel-not-ta-", 25) == 0)
+ {
+ if (!get_root_key_sentinel_id(client, ndata + 26)) {
+ return;
+ }
+ client->query.root_key_sentinel_not_ta = true;
+ ns_client_log(client, NS_LOGCATEGORY_TAT,
+ NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+ "root-key-sentinel-not-ta query label found");
+ }
+}
+
+/*
+ * 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,
+ dns_rdata_rrsig_t *rrsig, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ isc_result_t result;
+ dns_dbnode_t *node = NULL;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+ isc_stdtime_t now;
+
+ rdataset->trust = dns_trust_secure;
+ sigrdataset->trust = dns_trust_secure;
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ /*
+ * Save the updated secure state. Ignore failures.
+ */
+ result = dns_db_findnodeext(db, name, true, &cm, &ci, &node);
+ if (result != ISC_R_SUCCESS)
+ return;
+
+ isc_stdtime_get(&now);
+ dns_rdataset_trimttl(rdataset, sigrdataset, rrsig, now,
+ client->view->acceptexpired);
+
+ (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 successive calls,
+ * there may be multiple keys with the same keyid.
+ * Return false if we have exhausted all the possible keys.
+ */
+static bool
+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;
+ bool secure = false;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ if (!dns_rdataset_isassociated(keyrdataset)) {
+ result = dns_db_findnodeext(db, &rrsig->signer, false,
+ &cm, &ci, &node);
+ if (result != ISC_R_SUCCESS)
+ return (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 (false);
+
+ if (keyrdataset->trust != dns_trust_secure)
+ return (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 = true;
+ break;
+ }
+ dst_key_free(keyp);
+ }
+ return (secure);
+}
+
+static bool
+verify(dst_key_t *key, dns_name_t *name, dns_rdataset_t *rdataset,
+ dns_rdata_t *rdata, ns_client_t *client)
+{
+ isc_result_t result;
+ dns_fixedname_t fixed;
+ bool ignore = false;
+
+ dns_fixedname_init(&fixed);
+
+again:
+ result = dns_dnssec_verify3(name, rdataset, key, ignore,
+ client->view->maxbits, client->mctx,
+ rdata, NULL);
+ if (result == DNS_R_SIGEXPIRED && client->view->acceptexpired) {
+ ignore = true;
+ goto again;
+ }
+ if (result == ISC_R_SUCCESS || result == DNS_R_FROMWILDCARD)
+ return (true);
+ return (false);
+}
+
+/*
+ * Validate the rdataset if possible with available records.
+ */
+static bool
+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 (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 (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)) {
+ dst_key_free(&key);
+ dns_rdataset_disassociate(&keyrdataset);
+ mark_secure(client, db, name, &rrsig,
+ rdataset, sigrdataset);
+ return (true);
+ }
+ dst_key_free(&key);
+ } while (1);
+ if (dns_rdataset_isassociated(&keyrdataset))
+ dns_rdataset_disassociate(&keyrdataset);
+ }
+ return (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;
+ bool is_zone, use_zone;
+ isc_buffer_t *dbuf;
+ isc_result_t result;
+ dns_dbversion_t *version;
+ dns_zone_t *zone;
+ isc_buffer_t b;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+
+ CTRACE(ISC_LOG_DEBUG(3), "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 = false;
+ use_zone = false;
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ /*
+ * 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_findext(db, client->query.qname, version,
+ dns_rdatatype_ns,
+ client->query.dboptions,
+ client->now, &node, fname,
+ &cm, &ci, rdataset, sigrdataset);
+ if (result != DNS_R_DELEGATION)
+ goto cleanup;
+ if (USECACHE(client)) {
+ query_keepname(client, fname, dbuf);
+ dns_db_detachnode(db, &node);
+ SAVE(zdb, db);
+ SAVE(zfname, fname);
+ SAVE(zrdataset, rdataset);
+ SAVE(zsigrdataset, sigrdataset);
+ version = NULL;
+ dns_db_attach(client->view->cachedb, &db);
+ is_zone = 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 = 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 = true;
+ } else
+ goto cleanup;
+ }
+
+ if (use_zone) {
+ query_releasename(client, &fname);
+ /*
+ * 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);
+
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+
+ RESTORE(db, zdb);
+ RESTORE(fname, zfname);
+ RESTORE(rdataset, zrdataset);
+ RESTORE(sigrdataset, zsigrdataset);
+ }
+
+ /*
+ * Attempt to validate RRsets that are pending or that are glue.
+ */
+ if ((DNS_TRUST_PENDING(rdataset->trust) ||
+ (sigrdataset != NULL && DNS_TRUST_PENDING(sigrdataset->trust)))
+ && !validate(client, db, fname, rdataset, sigrdataset) &&
+ !PENDINGOK(client->query.dboptions))
+ goto cleanup;
+
+ if ((DNS_TRUST_GLUE(rdataset->trust) ||
+ (sigrdataset != NULL && DNS_TRUST_GLUE(sigrdataset->trust))) &&
+ !validate(client, db, fname, rdataset, sigrdataset) &&
+ SECURE(client) && WANTDNSSEC(client))
+ goto cleanup;
+
+ /*
+ * If the answer is secure only add NS records if they are secure
+ * when the client may be looking for AD in the response.
+ */
+ if (SECURE(client) && (WANTDNSSEC(client) || WANTAD(client)) &&
+ ((rdataset->trust != dns_trust_secure) ||
+ (sigrdataset != NULL && sigrdataset->trust != dns_trust_secure)))
+ 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(ISC_LOG_DEBUG(3), "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_iszone(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);
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ query_findclosestnsec3(name, db, version, client, rdataset,
+ sigrdataset, fname, 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,
+ 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,
+ bool ispositive, bool nodata)
+{
+ 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;
+ bool have_wname;
+ int order;
+ dns_fixedname_t cfixed;
+ dns_name_t *cname;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+
+ CTRACE(ISC_LOG_DEBUG(3), "query_addwildcardproof");
+ fname = NULL;
+ rdataset = NULL;
+ sigrdataset = NULL;
+ node = NULL;
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ /*
+ * Get the NOQNAME proof then if !ispositive
+ * 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 *.example
+ */
+ options = client->query.dboptions | DNS_DBFIND_NOWILD;
+ wname = dns_fixedname_initname(&wfixed);
+ again:
+ have_wname = 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_findext(db, name, version, dns_rdatatype_nsec,
+ options, 0, &node, fname, &cm, &ci,
+ rdataset, sigrdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+
+ if (!dns_rdataset_isassociated(rdataset)) {
+ /*
+ * No NSEC proof available, return NSEC3 proofs instead.
+ */
+ cname = dns_fixedname_initname(&cfixed);
+ /*
+ * Find the closest encloser.
+ */
+ dns_name_copy(name, cname, NULL);
+ while (result == DNS_R_NXDOMAIN) {
+ labels = dns_name_countlabels(cname) - 1;
+ /*
+ * Sanity check.
+ */
+ if (labels == 0U)
+ goto cleanup;
+ dns_name_split(cname, labels, NULL, cname);
+ result = dns_db_findext(db, cname, version,
+ dns_rdatatype_nsec,
+ options, 0, NULL, fname,
+ &cm, &ci, NULL, NULL);
+ }
+ /*
+ * Add closest (provable) encloser NSEC3.
+ */
+ query_findclosestnsec3(cname, db, version, client, rdataset,
+ sigrdataset, fname, true, cname);
+ if (!dns_rdataset_isassociated(rdataset))
+ goto cleanup;
+ if (!ispositive)
+ 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, version, client, rdataset,
+ sigrdataset, fname, 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, version, client, rdataset,
+ sigrdataset, fname, nodata, 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 = 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 = 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,
+ true, false);
+
+ /*
+ * 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
+free_devent(ns_client_t *client, isc_event_t **eventp,
+ dns_fetchevent_t **deventp)
+{
+ dns_fetchevent_t *devent = *deventp;
+
+ REQUIRE((void*)(*eventp) == (void *)(*deventp));
+
+ if (devent->fetch != NULL)
+ dns_resolver_destroyfetch(&devent->fetch);
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ if (devent->rdataset != NULL)
+ query_putrdataset(client, &devent->rdataset);
+ if (devent->sigrdataset != NULL)
+ query_putrdataset(client, &devent->sigrdataset);
+ /*
+ * If the two pointers are the same then leave the setting of
+ * (*deventp) to NULL to isc_event_free.
+ */
+ if ((void *)eventp != (void *)deventp)
+ (*deventp) = NULL;
+ isc_event_free(eventp);
+}
+
+/*%
+ * Check the configured trust anchors for a root zone trust anchor
+ * with a key id that matches client->query.root_key_sentinel_keyid.
+ *
+ * Return true when found, otherwise return false.
+ */
+static bool
+has_ta(ns_client_t *client) {
+ dns_keytable_t *keytable = NULL;
+ dns_keynode_t *keynode = NULL;
+ isc_result_t result;
+
+ result = dns_view_getsecroots(client->view, &keytable);
+ if (result != ISC_R_SUCCESS) {
+ return (false);
+ }
+
+ result = dns_keytable_find(keytable, dns_rootname, &keynode);
+ while (result == ISC_R_SUCCESS) {
+ dns_keynode_t *nextnode = NULL;
+ dns_keytag_t keyid = dst_key_id(dns_keynode_key(keynode));
+ if (keyid == client->query.root_key_sentinel_keyid) {
+ dns_keytable_detachkeynode(keytable, &keynode);
+ dns_keytable_detach(&keytable);
+ return (true);
+ }
+ result = dns_keytable_nextkeynode(keytable, keynode, &nextnode);
+ dns_keytable_detachkeynode(keytable, &keynode);
+ keynode = nextnode;
+ }
+ dns_keytable_detach(&keytable);
+
+ return (false);
+}
+
+/*%
+ * Check if a root key sentinel SERVFAIL should be returned.
+ */
+static bool
+root_key_sentinel_return_servfail(ns_client_t *client, bool is_zone,
+ dns_rdataset_t *rdataset, isc_result_t result)
+{
+ /*
+ * Are we looking at a "root-key-sentinel" query?
+ */
+ if (!client->query.root_key_sentinel_is_ta &&
+ !client->query.root_key_sentinel_not_ta)
+ {
+ return (false);
+ }
+
+ /*
+ * We only care about the query if 'result' indicates we have a cached
+ * answer.
+ */
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_CNAME:
+ case DNS_R_DNAME:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ break;
+ default:
+ return (false);
+ }
+
+ /*
+ * Do we meet the specified conditions to return SERVFAIL?
+ */
+ if (!is_zone && rdataset->trust == dns_trust_secure &&
+ ((client->query.root_key_sentinel_is_ta && !has_ta(client)) ||
+ (client->query.root_key_sentinel_not_ta && has_ta(client))))
+ {
+ return (true);
+ }
+
+ /*
+ * As special processing may only be triggered by the original QNAME,
+ * disable it after following a CNAME/DNAME.
+ */
+ client->query.root_key_sentinel_is_ta = false;
+ client->query.root_key_sentinel_not_ta = false;
+
+ return (false);
+}
+
+static void
+query_resume(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
+ dns_fetch_t *fetch = NULL;
+ ns_client_t *client;
+ bool fetch_canceled, client_shuttingdown;
+ isc_result_t result;
+ isc_logcategory_t *logcategory = NS_LOGCATEGORY_QUERY_ERRORS;
+ int errorloglevel;
+
+ /*
+ * 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_canceled = false;
+ /*
+ * Update client->now.
+ */
+ isc_stdtime_get(&client->now);
+ } else {
+ /*
+ * This is a fetch completion event for a canceled fetch.
+ * Clean up and don't resume the find.
+ */
+ fetch_canceled = true;
+ }
+ UNLOCK(&client->query.fetchlock);
+ INSIST(client->query.fetch == NULL);
+
+ client->query.attributes &= ~NS_QUERYATTR_RECURSING;
+ SAVE(fetch, 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_canceled || client_shuttingdown) {
+ free_devent(client, &event, &devent);
+ if (fetch_canceled) {
+ CTRACE(ISC_LOG_ERROR, "fetch cancelled");
+ query_error(client, DNS_R_SERVFAIL, __LINE__);
+ } else
+ query_next(client, ISC_R_CANCELED);
+ /*
+ * This may destroy the client.
+ */
+ ns_client_detach(&client);
+ } else {
+ result = query_find(client, devent, 0);
+ if (result != ISC_R_SUCCESS) {
+ if (result == DNS_R_SERVFAIL)
+ errorloglevel = ISC_LOG_DEBUG(2);
+ else
+ errorloglevel = ISC_LOG_DEBUG(4);
+ if (isc_log_wouldlog(ns_g_lctx, errorloglevel)) {
+ dns_resolver_logfetch(fetch, ns_g_lctx,
+ logcategory,
+ NS_LOGMODULE_QUERY,
+ errorloglevel, false);
+ }
+ }
+ }
+
+ dns_resolver_destroyfetch(&fetch);
+}
+
+static void
+prefetch_done(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent = (dns_fetchevent_t *)event;
+ ns_client_t *client;
+
+ UNUSED(task);
+
+ REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+ client = devent->ev_arg;
+ REQUIRE(NS_CLIENT_VALID(client));
+ REQUIRE(task == client->task);
+
+ LOCK(&client->query.fetchlock);
+ if (client->query.prefetch != NULL) {
+ INSIST(devent->fetch == client->query.prefetch);
+ client->query.prefetch = NULL;
+ }
+ UNLOCK(&client->query.fetchlock);
+ free_devent(client, &event, &devent);
+ ns_client_detach(&client);
+}
+
+static void
+query_prefetch(ns_client_t *client, dns_name_t *qname,
+ dns_rdataset_t *rdataset)
+{
+ isc_result_t result;
+ isc_sockaddr_t *peeraddr;
+ dns_rdataset_t *tmprdataset;
+ ns_client_t *dummy = NULL;
+ unsigned int options;
+
+ if (client->query.prefetch != NULL ||
+ client->view->prefetch_trigger == 0U ||
+ rdataset->ttl > client->view->prefetch_trigger ||
+ (rdataset->attributes & DNS_RDATASETATTR_PREFETCH) == 0)
+ return;
+
+ if (client->recursionquota == NULL) {
+ result = isc_quota_attach(&ns_g_server->recursionquota,
+ &client->recursionquota);
+ if (result == ISC_R_SUCCESS && !client->mortal && !TCP(client))
+ result = ns_client_replace(client);
+ if (result != ISC_R_SUCCESS)
+ return;
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_recursclients);
+ }
+
+ tmprdataset = query_newrdataset(client);
+ if (tmprdataset == NULL)
+ return;
+ if (!TCP(client))
+ peeraddr = &client->peeraddr;
+ else
+ peeraddr = NULL;
+ ns_client_attach(client, &dummy);
+ options = client->query.fetchoptions | DNS_FETCHOPT_PREFETCH;
+ result = dns_resolver_createfetch3(client->view->resolver,
+ qname, rdataset->type, NULL, NULL,
+ NULL, peeraddr, client->message->id,
+ options, 0, NULL, client->task,
+ prefetch_done, client,
+ tmprdataset, NULL,
+ &client->query.prefetch);
+ if (result != ISC_R_SUCCESS) {
+ query_putrdataset(client, &tmprdataset);
+ ns_client_detach(&dummy);
+ }
+ dns_rdataset_clearprefetch(rdataset);
+}
+
+static isc_result_t
+query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
+ dns_name_t *qdomain, dns_rdataset_t *nameservers,
+ bool 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);
+
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_recursclients);
+
+ 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 (%d/%d/%d), "
+ "aborting oldest query",
+ client->recursionquota->used,
+ client->recursionquota->soft,
+ client->recursionquota->max);
+ }
+ 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 "
+ "(%d/%d/%d): %s",
+ ns_g_server->recursionquota.used,
+ ns_g_server->recursionquota.soft,
+ ns_g_server->recursionquota.max,
+ isc_result_totext(result));
+ }
+ ns_client_killoldestquery(client);
+ }
+ if (result == ISC_R_SUCCESS && !client->mortal &&
+ !TCP(client)) {
+ 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);
+ isc_stats_decrement(ns_g_server->nsstats,
+ dns_nsstatscounter_recursclients);
+ }
+ }
+ 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 == false)
+ ns_client_settimeout(client, 60);
+ if (!TCP(client))
+ peeraddr = &client->peeraddr;
+ else
+ peeraddr = NULL;
+ result = dns_resolver_createfetch3(client->view->resolver,
+ qname, qtype, qdomain, nameservers,
+ NULL, peeraddr, client->message->id,
+ client->query.fetchoptions, 0, NULL,
+ 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);
+}
+
+static inline void
+rpz_clean(dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep,
+ dns_rdataset_t **rdatasetp)
+{
+ if (nodep != NULL && *nodep != NULL) {
+ REQUIRE(dbp != NULL && *dbp != NULL);
+ dns_db_detachnode(*dbp, nodep);
+ }
+ if (dbp != NULL && *dbp != NULL)
+ dns_db_detach(dbp);
+ if (zonep != NULL && *zonep != NULL)
+ dns_zone_detach(zonep);
+ if (rdatasetp != NULL && *rdatasetp != NULL &&
+ dns_rdataset_isassociated(*rdatasetp))
+ dns_rdataset_disassociate(*rdatasetp);
+}
+
+static inline void
+rpz_match_clear(dns_rpz_st_t *st) {
+ rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset);
+ st->m.version = NULL;
+}
+
+static inline isc_result_t
+rpz_ready(ns_client_t *client, dns_rdataset_t **rdatasetp) {
+ REQUIRE(rdatasetp != NULL);
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_ready");
+
+ if (*rdatasetp == NULL) {
+ *rdatasetp = query_newrdataset(client);
+ if (*rdatasetp == NULL) {
+ CTRACE(ISC_LOG_ERROR,
+ "rpz_ready: query_newrdataset failed");
+ return (DNS_R_SERVFAIL);
+ }
+ } else if (dns_rdataset_isassociated(*rdatasetp)) {
+ dns_rdataset_disassociate(*rdatasetp);
+ }
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rpz_st_clear(ns_client_t *client) {
+ dns_rpz_st_t *st = client->query.rpz_st;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_st_clear");
+
+ if (st->m.rdataset != NULL)
+ query_putrdataset(client, &st->m.rdataset);
+ rpz_match_clear(st);
+
+ rpz_clean(NULL, &st->r.db, NULL, NULL);
+ if (st->r.ns_rdataset != NULL)
+ query_putrdataset(client, &st->r.ns_rdataset);
+ if (st->r.r_rdataset != NULL)
+ query_putrdataset(client, &st->r.r_rdataset);
+
+ rpz_clean(&st->q.zone, &st->q.db, &st->q.node, NULL);
+ if (st->q.rdataset != NULL)
+ query_putrdataset(client, &st->q.rdataset);
+ if (st->q.sigrdataset != NULL)
+ query_putrdataset(client, &st->q.sigrdataset);
+ st->state = 0;
+ st->m.type = DNS_RPZ_TYPE_BAD;
+ st->m.policy = DNS_RPZ_POLICY_MISS;
+}
+
+static dns_rpz_zbits_t
+rpz_get_zbits(ns_client_t *client,
+ dns_rdatatype_t ip_type, dns_rpz_type_t rpz_type)
+{
+ dns_rpz_st_t *st;
+ dns_rpz_zbits_t zbits;
+
+ REQUIRE(client != NULL);
+ REQUIRE(client->query.rpz_st != NULL);
+
+ st = client->query.rpz_st;
+
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ zbits = st->have.client_ip;
+ break;
+ case DNS_RPZ_TYPE_QNAME:
+ zbits = st->have.qname;
+ break;
+ case DNS_RPZ_TYPE_IP:
+ if (ip_type == dns_rdatatype_a) {
+ zbits = st->have.ipv4;
+ } else if (ip_type == dns_rdatatype_aaaa) {
+ zbits = st->have.ipv6;
+ } else {
+ zbits = st->have.ip;
+ }
+ break;
+ case DNS_RPZ_TYPE_NSDNAME:
+ zbits = st->have.nsdname;
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ if (ip_type == dns_rdatatype_a) {
+ zbits = st->have.nsipv4;
+ } else if (ip_type == dns_rdatatype_aaaa) {
+ zbits = st->have.nsipv6;
+ } else {
+ zbits = st->have.nsip;
+ }
+ break;
+ default:
+ INSIST(0);
+ break;
+ }
+
+ /*
+ * Choose
+ * the earliest configured policy zone (rpz->num)
+ * QNAME over IP over NSDNAME over NSIP (rpz_type)
+ * the smallest name,
+ * the longest IP address prefix,
+ * the lexically smallest address.
+ */
+ if (st->m.policy != DNS_RPZ_POLICY_MISS) {
+ if (st->m.type >= rpz_type) {
+ zbits &= DNS_RPZ_ZMASK(st->m.rpz->num);
+ } else{
+ zbits &= DNS_RPZ_ZMASK(st->m.rpz->num) >> 1;
+ }
+ }
+
+ /*
+ * If the client wants recursion, allow only compatible policies.
+ */
+ if (!RECURSIONOK(client))
+ zbits &= st->popt.no_rd_ok;
+
+ return (zbits);
+}
+
+static void
+query_rpzfetch(ns_client_t *client, dns_name_t *qname, dns_rdatatype_t type) {
+ isc_result_t result;
+ isc_sockaddr_t *peeraddr;
+ dns_rdataset_t *tmprdataset;
+ ns_client_t *dummy = NULL;
+ unsigned int options;
+
+ if (client->query.prefetch != NULL)
+ return;
+
+ if (client->recursionquota == NULL) {
+ result = isc_quota_attach(&ns_g_server->recursionquota,
+ &client->recursionquota);
+ if (result == ISC_R_SUCCESS && !client->mortal && !TCP(client))
+ result = ns_client_replace(client);
+ if (result != ISC_R_SUCCESS)
+ return;
+ isc_stats_increment(ns_g_server->nsstats,
+ dns_nsstatscounter_recursclients);
+ }
+
+ tmprdataset = query_newrdataset(client);
+ if (tmprdataset == NULL)
+ return;
+ if (!TCP(client))
+ peeraddr = &client->peeraddr;
+ else
+ peeraddr = NULL;
+ ns_client_attach(client, &dummy);
+ options = client->query.fetchoptions;
+ result = dns_resolver_createfetch3(client->view->resolver, qname, type,
+ NULL, NULL, NULL, peeraddr,
+ client->message->id, options, 0,
+ NULL, client->task, prefetch_done,
+ client, tmprdataset, NULL,
+ &client->query.prefetch);
+ if (result != ISC_R_SUCCESS) {
+ query_putrdataset(client, &tmprdataset);
+ ns_client_detach(&dummy);
+ }
+}
+
+/*
+ * Get an NS, A, or AAAA rrset related to the response for the client
+ * to check the contents of that rrset for hits by eligible policy zones.
+ */
+static isc_result_t
+rpz_rrset_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
+ dns_rpz_type_t rpz_type, dns_db_t **dbp,
+ dns_dbversion_t *version, dns_rdataset_t **rdatasetp,
+ bool resuming)
+{
+ dns_rpz_st_t *st;
+ bool is_zone;
+ dns_dbnode_t *node;
+ dns_fixedname_t fixed;
+ dns_name_t *found;
+ isc_result_t result;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rrset_find");
+
+ st = client->query.rpz_st;
+ if ((st->state & DNS_RPZ_RECURSING) != 0) {
+ INSIST(st->r.r_type == type);
+ INSIST(dns_name_equal(name, st->r_name));
+ INSIST(*rdatasetp == NULL ||
+ !dns_rdataset_isassociated(*rdatasetp));
+ st->state &= ~DNS_RPZ_RECURSING;
+ RESTORE(*dbp, st->r.db);
+ if (*rdatasetp != NULL)
+ query_putrdataset(client, rdatasetp);
+ RESTORE(*rdatasetp, st->r.r_rdataset);
+ result = st->r.r_result;
+ if (result == DNS_R_DELEGATION) {
+ CTRACE(ISC_LOG_ERROR, "RPZ recursing");
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
+ rpz_type, " rpz_rrset_find(1)", result);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ result = DNS_R_SERVFAIL;
+ }
+ return (result);
+ }
+
+ result = rpz_ready(client, rdatasetp);
+ if (result != ISC_R_SUCCESS) {
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (result);
+ }
+ if (*dbp != NULL) {
+ is_zone = false;
+ } else {
+ dns_zone_t *zone;
+
+ version = NULL;
+ zone = NULL;
+ result = query_getdb(client, name, type, 0, &zone, dbp,
+ &version, &is_zone);
+ if (result != ISC_R_SUCCESS) {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
+ rpz_type, " rpz_rrset_find(2)", result);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ return (result);
+ }
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ }
+
+ node = NULL;
+ found = dns_fixedname_initname(&fixed);
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+ result = dns_db_findext(*dbp, name, version, type, DNS_DBFIND_GLUEOK,
+ client->now, &node, found,
+ &cm, &ci, *rdatasetp, NULL);
+ if (result == DNS_R_DELEGATION && is_zone && USECACHE(client)) {
+ /*
+ * Try the cache if we're authoritative for an
+ * ancestor but not the domain itself.
+ */
+ rpz_clean(NULL, dbp, &node, rdatasetp);
+ version = NULL;
+ dns_db_attach(client->view->cachedb, dbp);
+ result = dns_db_findext(*dbp, name, version, type,
+ 0, client->now, &node, found,
+ &cm, &ci, *rdatasetp, NULL);
+ }
+ rpz_clean(NULL, dbp, &node, NULL);
+ if (result == DNS_R_DELEGATION) {
+ rpz_clean(NULL, NULL, NULL, rdatasetp);
+ /*
+ * Recurse for NS rrset or A or AAAA rrset for an NS.
+ * Do not recurse for addresses for the query name.
+ */
+ if (rpz_type == DNS_RPZ_TYPE_IP) {
+ result = DNS_R_NXRRSET;
+ } else if (!client->view->rpzs->p.nsip_wait_recurse) {
+ query_rpzfetch(client, name, type);
+ result = DNS_R_NXRRSET;
+ } else {
+ dns_name_copy(name, st->r_name, NULL);
+ result = query_recurse(client, type, st->r_name,
+ NULL, NULL, resuming);
+ if (result == ISC_R_SUCCESS) {
+ st->state |= DNS_RPZ_RECURSING;
+ result = DNS_R_DELEGATION;
+ }
+ }
+ }
+ return (result);
+}
+
+/*
+ * Compute a policy owner name, p_name, in a policy zone given the needed
+ * policy type and the trigger name.
+ */
+static isc_result_t
+rpz_get_p_name(ns_client_t *client, dns_name_t *p_name,
+ dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+ dns_name_t *trig_name)
+{
+ dns_offsets_t prefix_offsets;
+ dns_name_t prefix, *suffix;
+ unsigned int first, labels;
+ isc_result_t result;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_get_p_name");
+
+ /*
+ * The policy owner name consists of a suffix depending on the type
+ * and policy zone and a prefix that is the longest possible string
+ * from the trigger name that keesp the resulting policy owner name
+ * from being too long.
+ */
+ switch (rpz_type) {
+ case DNS_RPZ_TYPE_CLIENT_IP:
+ suffix = &rpz->client_ip;
+ break;
+ case DNS_RPZ_TYPE_QNAME:
+ suffix = &rpz->origin;
+ break;
+ case DNS_RPZ_TYPE_IP:
+ suffix = &rpz->ip;
+ break;
+ case DNS_RPZ_TYPE_NSDNAME:
+ suffix = &rpz->nsdname;
+ break;
+ case DNS_RPZ_TYPE_NSIP:
+ suffix = &rpz->nsip;
+ break;
+ default:
+ INSIST(0);
+ }
+
+ /*
+ * Start with relative version of the full trigger name,
+ * and trim enough allow the addition of the suffix.
+ */
+ dns_name_init(&prefix, prefix_offsets);
+ labels = dns_name_countlabels(trig_name);
+ first = 0;
+ for (;;) {
+ dns_name_getlabelsequence(trig_name, first, labels-first-1,
+ &prefix);
+ result = dns_name_concatenate(&prefix, suffix, p_name, NULL);
+ if (result == ISC_R_SUCCESS)
+ break;
+ INSIST(result == DNS_R_NAMETOOLONG);
+ /*
+ * Trim the trigger name until the combination is not too long.
+ */
+ if (labels-first < 2) {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, suffix,
+ rpz_type, " concatentate()", result);
+ return (ISC_R_FAILURE);
+ }
+ /*
+ * Complain once about trimming the trigger name.
+ */
+ if (first == 0) {
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, suffix,
+ rpz_type, " concatentate()", result);
+ }
+ ++first;
+ }
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Look in policy zone rpz for a policy of rpz_type by p_name.
+ * The self-name (usually the client qname or an NS name) is compared with
+ * the target of a CNAME policy for the old style passthru encoding.
+ * If found, the policy is recorded in *zonep, *dbp, *versionp, *nodep,
+ * *rdatasetp, and *policyp.
+ * The target DNS type, qtype, chooses the best rdataset for *rdatasetp.
+ * The caller must decide if the found policy is most suitable, including
+ * better than a previously found policy.
+ * If it is best, the caller records it in client->query.rpz_st->m.
+ */
+static isc_result_t
+rpz_find_p(ns_client_t *client, dns_name_t *self_name, dns_rdatatype_t qtype,
+ dns_name_t *p_name, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+ dns_zone_t **zonep, dns_db_t **dbp, dns_dbversion_t **versionp,
+ dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
+ dns_rpz_policy_t *policyp)
+{
+ dns_fixedname_t foundf;
+ dns_name_t *found;
+ isc_result_t result;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+
+ REQUIRE(nodep != NULL);
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_find_p");
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ /*
+ * Try to find either a CNAME or the type of record demanded by the
+ * request from the policy zone.
+ */
+ rpz_clean(zonep, dbp, nodep, rdatasetp);
+ result = rpz_ready(client, rdatasetp);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE(ISC_LOG_ERROR, "rpz_ready() failed");
+ return (DNS_R_SERVFAIL);
+ }
+ *versionp = NULL;
+ result = rpz_getdb(client, p_name, rpz_type, zonep, dbp, versionp);
+ if (result != ISC_R_SUCCESS)
+ return (DNS_R_NXDOMAIN);
+ found = dns_fixedname_initname(&foundf);
+
+ result = dns_db_findext(*dbp, p_name, *versionp, dns_rdatatype_any, 0,
+ client->now, nodep, found, &cm, &ci,
+ *rdatasetp, NULL);
+ /*
+ * Choose the best rdataset if we found something.
+ */
+ if (result == ISC_R_SUCCESS) {
+ dns_rdatasetiter_t *rdsiter;
+
+ rdsiter = NULL;
+ result = dns_db_allrdatasets(*dbp, *nodep, *versionp, 0,
+ &rdsiter);
+ if (result != ISC_R_SUCCESS) {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name,
+ rpz_type, " allrdatasets()", result);
+ CTRACE(ISC_LOG_ERROR,
+ "rpz_find_p: allrdatasets failed");
+ return (DNS_R_SERVFAIL);
+ }
+ for (result = dns_rdatasetiter_first(rdsiter);
+ result == ISC_R_SUCCESS;
+ result = dns_rdatasetiter_next(rdsiter)) {
+ dns_rdatasetiter_current(rdsiter, *rdatasetp);
+ if ((*rdatasetp)->type == dns_rdatatype_cname ||
+ (*rdatasetp)->type == qtype)
+ break;
+ dns_rdataset_disassociate(*rdatasetp);
+ }
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_SUCCESS) {
+ if (result != ISC_R_NOMORE) {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
+ p_name, rpz_type,
+ " rdatasetiter", result);
+ CTRACE(ISC_LOG_ERROR,
+ "rpz_find_p: rdatasetiter failed");
+ return (DNS_R_SERVFAIL);
+ }
+ /*
+ * Ask again to get the right DNS_R_DNAME/NXRRSET/...
+ * result if there is neither a CNAME nor target type.
+ */
+ if (dns_rdataset_isassociated(*rdatasetp))
+ dns_rdataset_disassociate(*rdatasetp);
+ dns_db_detachnode(*dbp, nodep);
+
+ if (qtype == dns_rdatatype_rrsig ||
+ qtype == dns_rdatatype_sig)
+ result = DNS_R_NXRRSET;
+ else
+ result = dns_db_findext(*dbp, p_name, *versionp,
+ qtype, 0, client->now,
+ nodep, found, &cm, &ci,
+ *rdatasetp, NULL);
+ }
+ }
+ switch (result) {
+ case ISC_R_SUCCESS:
+ if ((*rdatasetp)->type != dns_rdatatype_cname) {
+ *policyp = DNS_RPZ_POLICY_RECORD;
+ } else {
+ *policyp = dns_rpz_decode_cname(rpz, *rdatasetp,
+ self_name);
+ if ((*policyp == DNS_RPZ_POLICY_RECORD ||
+ *policyp == DNS_RPZ_POLICY_WILDCNAME) &&
+ qtype != dns_rdatatype_cname &&
+ qtype != dns_rdatatype_any)
+ return (DNS_R_CNAME);
+ }
+ return (ISC_R_SUCCESS);
+ case DNS_R_NXRRSET:
+ *policyp = DNS_RPZ_POLICY_NODATA;
+ return (result);
+ case DNS_R_DNAME:
+ /*
+ * DNAME policy RRs have very few if any uses that are not
+ * better served with simple wildcards. Making them work would
+ * require complications to get the number of labels matched
+ * in the name or the found name to the main DNS_R_DNAME case
+ * in query_find(). The domain also does not appear in the
+ * summary database at the right level, so this happens only
+ * with a single policy zone when we have no summary database.
+ * Treat it as a miss.
+ */
+ case DNS_R_NXDOMAIN:
+ case DNS_R_EMPTYNAME:
+ return (DNS_R_NXDOMAIN);
+ default:
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, p_name, rpz_type,
+ "", result);
+ CTRACE(ISC_LOG_ERROR,
+ "rpz_find_p: unexpected result");
+ return (DNS_R_SERVFAIL);
+ }
+}
+
+static void
+rpz_save_p(dns_rpz_st_t *st, dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+ dns_rpz_policy_t policy, dns_name_t *p_name, dns_rpz_prefix_t prefix,
+ isc_result_t result, dns_zone_t **zonep, dns_db_t **dbp,
+ dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
+ dns_dbversion_t *version)
+{
+ dns_rdataset_t *trdataset = NULL;
+
+ rpz_match_clear(st);
+ st->m.rpz = rpz;
+ st->m.type = rpz_type;
+ st->m.policy = policy;
+ dns_name_copy(p_name, st->p_name, NULL);
+ st->m.prefix = prefix;
+ st->m.result = result;
+ SAVE(st->m.zone, *zonep);
+ SAVE(st->m.db, *dbp);
+ SAVE(st->m.node, *nodep);
+ if (*rdatasetp != NULL && dns_rdataset_isassociated(*rdatasetp)) {
+ /*
+ * Save the replacement rdataset from the policy
+ * and make the previous replacement rdataset scratch.
+ */
+ SAVE(trdataset, st->m.rdataset);
+ SAVE(st->m.rdataset, *rdatasetp);
+ SAVE(*rdatasetp, trdataset);
+ st->m.ttl = ISC_MIN(st->m.rdataset->ttl, rpz->max_policy_ttl);
+ } else {
+ st->m.ttl = ISC_MIN(DNS_RPZ_TTL_DEFAULT, rpz->max_policy_ttl);
+ }
+ SAVE(st->m.version, version);
+}
+
+/*
+ * Check this address in every eligible policy zone.
+ */
+static isc_result_t
+rpz_rewrite_ip(ns_client_t *client, const isc_netaddr_t *netaddr,
+ dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t zbits, dns_rdataset_t **p_rdatasetp)
+{
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_st_t *st;
+ dns_rpz_zone_t *rpz;
+ dns_rpz_prefix_t prefix;
+ dns_rpz_num_t rpz_num;
+ dns_fixedname_t ip_namef, p_namef;
+ dns_name_t *ip_name, *p_name;
+ dns_zone_t *p_zone;
+ dns_db_t *p_db;
+ dns_dbversion_t *p_version;
+ dns_dbnode_t *p_node;
+ dns_rpz_policy_t policy;
+ isc_result_t result;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ip");
+
+ ip_name = dns_fixedname_initname(&ip_namef);
+
+ p_zone = NULL;
+ p_db = NULL;
+ p_node = NULL;
+
+ rpzs = client->view->rpzs;
+ st = client->query.rpz_st;
+ while (zbits != 0) {
+ rpz_num = dns_rpz_find_ip(rpzs, rpz_type, zbits, netaddr,
+ ip_name, &prefix);
+ if (rpz_num == DNS_RPZ_INVALID_NUM)
+ break;
+ zbits &= (DNS_RPZ_ZMASK(rpz_num) >> 1);
+
+ /*
+ * Do not try applying policy zones that cannot replace a
+ * previously found policy zone.
+ * Stop looking if the next best choice cannot
+ * replace what we already have.
+ */
+ rpz = rpzs->zones[rpz_num];
+ if (st->m.policy != DNS_RPZ_POLICY_MISS) {
+ if (st->m.rpz->num < rpz->num)
+ break;
+ if (st->m.rpz->num == rpz->num &&
+ (st->m.type < rpz_type ||
+ st->m.prefix > prefix))
+ break;
+ }
+
+ /*
+ * Get the policy for a prefix at least as long
+ * as the prefix of the entry we had before.
+ */
+ p_name = dns_fixedname_initname(&p_namef);
+ result = rpz_get_p_name(client, p_name, rpz, rpz_type, ip_name);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ result = rpz_find_p(client, ip_name, qtype,
+ p_name, rpz, rpz_type,
+ &p_zone, &p_db, &p_version, &p_node,
+ p_rdatasetp, &policy);
+ switch (result) {
+ case DNS_R_NXDOMAIN:
+ /*
+ * Continue after a policy record that is missing
+ * contrary to the summary data. The summary
+ * data can out of date during races with and among
+ * policy zone updates.
+ */
+ CTRACE(ISC_LOG_ERROR,
+ "rpz_rewrite_ip: mismatched summary data; "
+ "continuing");
+ continue;
+ case DNS_R_SERVFAIL:
+ rpz_clean(&p_zone, &p_db, &p_node, p_rdatasetp);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (DNS_R_SERVFAIL);
+ default:
+ /*
+ * Forget this policy if it is not preferable
+ * to the previously found policy.
+ * If this policy is not good, then stop looking
+ * because none of the later policy zones would work.
+ *
+ * With more than one applicable policy, prefer
+ * the earliest configured policy,
+ * client-IP over QNAME over IP over NSDNAME over NSIP,
+ * the longest prefix
+ * the lexically smallest address.
+ * dns_rpz_find_ip() ensures st->m.rpz->num >= rpz->num.
+ * We can compare new and current p_name because
+ * both are of the same type and in the same zone.
+ * The tests above eliminate other reasons to
+ * reject this policy. If this policy can't work,
+ * then neither can later zones.
+ */
+ if (st->m.policy != DNS_RPZ_POLICY_MISS &&
+ rpz->num == st->m.rpz->num &&
+ (st->m.type == rpz_type &&
+ st->m.prefix == prefix &&
+ 0 > dns_name_rdatacompare(st->p_name, p_name)))
+ break;
+
+ /*
+ * Stop checking after saving an enabled hit in this
+ * policy zone. The radix tree in the policy zone
+ * ensures that we found the longest match.
+ */
+ if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "rpz_rewrite_ip: rpz_save_p");
+ rpz_save_p(st, rpz, rpz_type,
+ policy, p_name, prefix, result,
+ &p_zone, &p_db, &p_node,
+ p_rdatasetp, p_version);
+ break;
+ }
+
+ /*
+ * Log DNS_RPZ_POLICY_DISABLED zones
+ * and try the next eligible policy zone.
+ */
+ rpz_log_rewrite(client, true, policy, rpz_type,
+ p_zone, p_name, NULL, rpz_num);
+ }
+ }
+
+ rpz_clean(&p_zone, &p_db, &p_node, p_rdatasetp);
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Check the IP addresses in the A or AAAA rrsets for name against
+ * all eligible rpz_type (IP or NSIP) response policy rewrite rules.
+ */
+static isc_result_t
+rpz_rewrite_ip_rrset(ns_client_t *client,
+ dns_name_t *name, dns_rdatatype_t qtype,
+ dns_rpz_type_t rpz_type, dns_rdatatype_t ip_type,
+ dns_db_t **ip_dbp, dns_dbversion_t *ip_version,
+ dns_rdataset_t **ip_rdatasetp,
+ dns_rdataset_t **p_rdatasetp, bool resuming)
+{
+ dns_rpz_zbits_t zbits;
+ isc_netaddr_t netaddr;
+ struct in_addr ina;
+ struct in6_addr in6a;
+ isc_result_t result;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ip_rrset");
+
+ zbits = rpz_get_zbits(client, ip_type, rpz_type);
+ if (zbits == 0)
+ return (ISC_R_SUCCESS);
+
+ /*
+ * Get the A or AAAA rdataset.
+ */
+ result = rpz_rrset_find(client, name, ip_type, rpz_type, ip_dbp,
+ ip_version, ip_rdatasetp, resuming);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_GLUE:
+ case DNS_R_ZONECUT:
+ break;
+ case DNS_R_EMPTYNAME:
+ case DNS_R_EMPTYWILD:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NXRRSET:
+ case DNS_R_NCACHENXRRSET:
+ case ISC_R_NOTFOUND:
+ return (ISC_R_SUCCESS);
+ case DNS_R_DELEGATION:
+ case DNS_R_DUPLICATE:
+ case DNS_R_DROP:
+ return (result);
+ case DNS_R_CNAME:
+ case DNS_R_DNAME:
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, name, rpz_type,
+ " NS address rewrite rrset", result);
+ return (ISC_R_SUCCESS);
+ default:
+ if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
+ client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR;
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL, name,
+ rpz_type, " NS address rewrite rrset",
+ result);
+ }
+ CTRACE(ISC_LOG_ERROR,
+ "rpz_rewrite_ip_rrset: unexpected result");
+ return (DNS_R_SERVFAIL);
+ }
+
+ /*
+ * Check all of the IP addresses in the rdataset.
+ */
+ for (result = dns_rdataset_first(*ip_rdatasetp);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(*ip_rdatasetp)) {
+
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_current(*ip_rdatasetp, &rdata);
+ switch (rdata.type) {
+ case dns_rdatatype_a:
+ INSIST(rdata.length == 4);
+ memmove(&ina.s_addr, rdata.data, 4);
+ isc_netaddr_fromin(&netaddr, &ina);
+ break;
+ case dns_rdatatype_aaaa:
+ INSIST(rdata.length == 16);
+ memmove(in6a.s6_addr, rdata.data, 16);
+ isc_netaddr_fromin6(&netaddr, &in6a);
+ break;
+ default:
+ continue;
+ }
+
+ result = rpz_rewrite_ip(client, &netaddr, qtype, rpz_type,
+ zbits, p_rdatasetp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Look for IP addresses in A and AAAA rdatasets
+ * that trigger all eligible IP or NSIP policy rules.
+ */
+static isc_result_t
+rpz_rewrite_ip_rrsets(ns_client_t *client, dns_name_t *name,
+ dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
+ dns_rdataset_t **ip_rdatasetp, bool resuming)
+{
+ dns_rpz_st_t *st;
+ dns_dbversion_t *ip_version;
+ dns_db_t *ip_db;
+ dns_rdataset_t *p_rdataset;
+ isc_result_t result;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ip_rrsets");
+
+ st = client->query.rpz_st;
+ ip_version = NULL;
+ ip_db = NULL;
+ p_rdataset = NULL;
+ if ((st->state & DNS_RPZ_DONE_IPv4) == 0 &&
+ (qtype == dns_rdatatype_a ||
+ qtype == dns_rdatatype_any ||
+ rpz_type == DNS_RPZ_TYPE_NSIP)) {
+ /*
+ * Rewrite based on an IPv4 address that will appear
+ * in the ANSWER section or if we are checking IP addresses.
+ */
+ result = rpz_rewrite_ip_rrset(client, name, qtype,
+ rpz_type, dns_rdatatype_a,
+ &ip_db, ip_version, ip_rdatasetp,
+ &p_rdataset, resuming);
+ if (result == ISC_R_SUCCESS)
+ st->state |= DNS_RPZ_DONE_IPv4;
+ } else {
+ result = ISC_R_SUCCESS;
+ }
+ if (result == ISC_R_SUCCESS &&
+ (qtype == dns_rdatatype_aaaa ||
+ qtype == dns_rdatatype_any ||
+ rpz_type == DNS_RPZ_TYPE_NSIP)) {
+ /*
+ * Rewrite based on IPv6 addresses that will appear
+ * in the ANSWER section or if we are checking IP addresses.
+ */
+ result = rpz_rewrite_ip_rrset(client, name, qtype,
+ rpz_type, dns_rdatatype_aaaa,
+ &ip_db, ip_version, ip_rdatasetp,
+ &p_rdataset, resuming);
+ }
+ if (ip_db != NULL)
+ dns_db_detach(&ip_db);
+ query_putrdataset(client, &p_rdataset);
+ return (result);
+}
+
+/*
+ * Try to rewrite a request for a qtype rdataset based on the trigger name
+ * trig_name and rpz_type (DNS_RPZ_TYPE_QNAME or DNS_RPZ_TYPE_NSDNAME).
+ * Record the results including the replacement rdataset if any
+ * in client->query.rpz_st.
+ * *rdatasetp is a scratch rdataset.
+ */
+static isc_result_t
+rpz_rewrite_name(ns_client_t *client, dns_name_t *trig_name,
+ dns_rdatatype_t qtype, dns_rpz_type_t rpz_type,
+ dns_rpz_zbits_t allowed_zbits, dns_rdataset_t **rdatasetp)
+{
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_zone_t *rpz;
+ dns_rpz_st_t *st;
+ dns_fixedname_t p_namef;
+ dns_name_t *p_name;
+ dns_rpz_zbits_t zbits;
+ dns_rpz_num_t rpz_num;
+ dns_zone_t *p_zone;
+ dns_db_t *p_db;
+ dns_dbversion_t *p_version;
+ dns_dbnode_t *p_node;
+ dns_rpz_policy_t policy;
+ isc_result_t result;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_name");
+
+ zbits = rpz_get_zbits(client, qtype, rpz_type);
+ zbits &= allowed_zbits;
+ if (zbits == 0)
+ return (ISC_R_SUCCESS);
+
+ rpzs = client->view->rpzs;
+
+ /*
+ * Use the summary database to find the bit mask of policy zones
+ * with policies for this trigger name. We do this even if there
+ * is only one eligible policy zone so that wildcard triggers
+ * are matched correctly, and not into their parent.
+ */
+ zbits = dns_rpz_find_name(rpzs, rpz_type, zbits, trig_name);
+ if (zbits == 0)
+ return (ISC_R_SUCCESS);
+
+ p_name = dns_fixedname_initname(&p_namef);
+
+ p_zone = NULL;
+ p_db = NULL;
+ p_node = NULL;
+
+ st = client->query.rpz_st;
+
+ /*
+ * Check the trigger name in every policy zone that the summary data
+ * says has a hit for the trigger name.
+ * Most of the time there are no eligible zones and the summary data
+ * keeps us from getting this far.
+ * We check the most eligible zone first and so usually check only
+ * one policy zone.
+ */
+ for (rpz_num = 0; zbits != 0; ++rpz_num, zbits >>= 1) {
+ if ((zbits & 1) == 0)
+ continue;
+
+ /*
+ * Do not check policy zones that cannot replace a previously
+ * found policy.
+ */
+ rpz = rpzs->zones[rpz_num];
+ if (st->m.policy != DNS_RPZ_POLICY_MISS) {
+ if (st->m.rpz->num < rpz->num)
+ break;
+ if (st->m.rpz->num == rpz->num &&
+ st->m.type < rpz_type)
+ break;
+ }
+
+ /*
+ * Get the next policy zone's record for this trigger name.
+ */
+ result = rpz_get_p_name(client, p_name, rpz, rpz_type,
+ trig_name);
+ if (result != ISC_R_SUCCESS)
+ continue;
+ result = rpz_find_p(client, trig_name, qtype, p_name,
+ rpz, rpz_type,
+ &p_zone, &p_db, &p_version, &p_node,
+ rdatasetp, &policy);
+ switch (result) {
+ case DNS_R_NXDOMAIN:
+ /*
+ * Continue after a missing policy record
+ * contrary to the summary data. The summary
+ * data can out of date during races with and among
+ * policy zone updates.
+ */
+ CTRACE(ISC_LOG_ERROR,
+ "rpz_rewrite_name: mismatched summary data; "
+ "continuing");
+ continue;
+ case DNS_R_SERVFAIL:
+ rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ return (DNS_R_SERVFAIL);
+ default:
+ /*
+ * With more than one applicable policy, prefer
+ * the earliest configured policy,
+ * client-IP over QNAME over IP over NSDNAME over NSIP,
+ * and the smallest name.
+ * We known st->m.rpz->num >= rpz->num and either
+ * st->m.rpz->num > rpz->num or st->m.type >= rpz_type
+ */
+ if (st->m.policy != DNS_RPZ_POLICY_MISS &&
+ rpz->num == st->m.rpz->num &&
+ (st->m.type < rpz_type ||
+ (st->m.type == rpz_type &&
+ 0 >= dns_name_compare(p_name, st->p_name))))
+ continue;
+#if 0
+ /*
+ * This code would block a customer reported information
+ * leak of rpz rules by rewriting requests in the
+ * rpz-ip, rpz-nsip, rpz-nsdname,and rpz-passthru TLDs.
+ * Without this code, a bad guy could request
+ * 24.0.3.2.10.rpz-ip. to find the policy rule for
+ * 10.2.3.0/14. It is an insignificant leak and this
+ * code is not worth its cost, because the bad guy
+ * could publish "evil.com A 10.2.3.4" and request
+ * evil.com to get the same information.
+ * Keep code with "#if 0" in case customer demand
+ * is irresistible.
+ *
+ * We have the less frequent case of a triggered
+ * policy. Check that we have not trigger on one
+ * of the pretend RPZ TLDs.
+ * This test would make it impossible to rewrite
+ * names in TLDs that start with "rpz-" should
+ * ICANN ever allow such TLDs.
+ */
+ unsigned int labels;
+ labels = dns_name_countlabels(trig_name);
+ if (labels >= 2) {
+ dns_label_t label;
+
+ dns_name_getlabel(trig_name, labels-2, &label);
+ if (label.length >= sizeof(DNS_RPZ_PREFIX)-1 &&
+ strncasecmp((const char *)label.base+1,
+ DNS_RPZ_PREFIX,
+ sizeof(DNS_RPZ_PREFIX)-1) == 0)
+ continue;
+ }
+#endif
+ if (rpz->policy != DNS_RPZ_POLICY_DISABLED) {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "rpz_rewrite_name: rpz_save_p");
+ rpz_save_p(st, rpz, rpz_type,
+ policy, p_name, 0, result,
+ &p_zone, &p_db, &p_node,
+ rdatasetp, p_version);
+ /*
+ * After a hit, higher numbered policy zones
+ * are irrelevant
+ */
+ rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
+ return (ISC_R_SUCCESS);
+ }
+ /*
+ * Log DNS_RPZ_POLICY_DISABLED zones
+ * and try the next eligible policy zone.
+ */
+ rpz_log_rewrite(client, true, policy, rpz_type,
+ p_zone, p_name, NULL, rpz_num);
+ break;
+ }
+ }
+
+ rpz_clean(&p_zone, &p_db, &p_node, rdatasetp);
+ return (ISC_R_SUCCESS);
+}
+
+static void
+rpz_rewrite_ns_skip(ns_client_t *client, dns_name_t *nsname,
+ isc_result_t result, int level, const char *str)
+{
+ dns_rpz_st_t *st;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite_ns_skip");
+
+ st = client->query.rpz_st;
+
+ if (str != NULL)
+ rpz_log_fail_helper(client, level, nsname,
+ DNS_RPZ_TYPE_NSIP, DNS_RPZ_TYPE_NSDNAME,
+ str, result);
+ if (st->r.ns_rdataset != NULL &&
+ dns_rdataset_isassociated(st->r.ns_rdataset))
+ dns_rdataset_disassociate(st->r.ns_rdataset);
+
+ st->r.label--;
+}
+
+/*
+ * RPZ query result types
+ */
+typedef enum {
+ RPZ_QRESULT_TYPE_DONE,
+ RPZ_QRESULT_TYPE_RESTART,
+ RPZ_QRESULT_TYPE_RECURSE,
+} rpz_qresult_type_t;
+
+/*
+ * Look for response policy zone QNAME, NSIP, and NSDNAME rewriting.
+ */
+static isc_result_t
+rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
+ isc_result_t qresult, bool resuming,
+ dns_rdataset_t *ordataset, dns_rdataset_t *osigset)
+{
+ dns_rpz_zones_t *rpzs;
+ dns_rpz_st_t *st;
+ dns_rdataset_t *rdataset;
+ dns_fixedname_t nsnamef;
+ dns_name_t *nsname;
+ rpz_qresult_type_t qresult_type;
+ dns_rpz_zbits_t zbits;
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_rpz_have_t have;
+ dns_rpz_popt_t popt;
+ int rpz_ver;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_rewrite");
+
+ rpzs = client->view->rpzs;
+ st = client->query.rpz_st;
+
+ if (rpzs == NULL ||
+ (st != NULL && (st->state & DNS_RPZ_REWRITTEN) != 0))
+ return (DNS_R_DISALLOWED);
+
+ RWLOCK(&rpzs->search_lock, isc_rwlocktype_read);
+ if (rpzs->p.num_zones == 0 ||
+ (!RECURSIONOK(client) && rpzs->p.no_rd_ok == 0) ||
+ !rpz_ck_dnssec(client, qresult, ordataset, osigset))
+ {
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_read);
+ return (DNS_R_DISALLOWED);
+ }
+ have = rpzs->have;
+ popt = rpzs->p;
+ rpz_ver = rpzs->rpz_ver;
+ RWUNLOCK(&rpzs->search_lock, isc_rwlocktype_read);
+
+ if (st == NULL) {
+ st = isc_mem_get(client->mctx, sizeof(*st));
+ if (st == NULL)
+ return (ISC_R_NOMEMORY);
+ st->state = 0;
+ }
+ if (st->state == 0) {
+ st->state |= DNS_RPZ_ACTIVE;
+ memset(&st->m, 0, sizeof(st->m));
+ st->m.type = DNS_RPZ_TYPE_BAD;
+ st->m.policy = DNS_RPZ_POLICY_MISS;
+ st->m.ttl = ~0;
+ memset(&st->r, 0, sizeof(st->r));
+ memset(&st->q, 0, sizeof(st->q));
+ st->p_name = dns_fixedname_initname(&st->_p_namef);
+ st->r_name = dns_fixedname_initname(&st->_r_namef);
+ st->fname = dns_fixedname_initname(&st->_fnamef);
+ st->have = have;
+ st->popt = popt;
+ st->rpz_ver = rpz_ver;
+ client->query.rpz_st = st;
+ }
+
+ /*
+ * There is nothing to rewrite if the main query failed.
+ */
+ switch (qresult) {
+ case ISC_R_SUCCESS:
+ case DNS_R_GLUE:
+ case DNS_R_ZONECUT:
+ qresult_type = RPZ_QRESULT_TYPE_DONE;
+ break;
+ case DNS_R_EMPTYNAME:
+ case DNS_R_NXRRSET:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_EMPTYWILD:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ case DNS_R_CNAME:
+ case DNS_R_DNAME:
+ qresult_type = RPZ_QRESULT_TYPE_RESTART;
+ break;
+ case DNS_R_DELEGATION:
+ case ISC_R_NOTFOUND:
+ /*
+ * If recursion is on, do only tentative rewriting.
+ * If recursion is off, this the normal and only time we
+ * can rewrite.
+ */
+ if (RECURSIONOK(client))
+ qresult_type = RPZ_QRESULT_TYPE_RECURSE;
+ else
+ qresult_type = RPZ_QRESULT_TYPE_RESTART;
+ break;
+ case ISC_R_FAILURE:
+ case ISC_R_TIMEDOUT:
+ case DNS_R_BROKENCHAIN:
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL3, client->query.qname,
+ DNS_RPZ_TYPE_QNAME,
+ " stop on qresult in rpz_rewrite()", qresult);
+ return (ISC_R_SUCCESS);
+ default:
+ rpz_log_fail(client, DNS_RPZ_DEBUG_LEVEL1, client->query.qname,
+ DNS_RPZ_TYPE_QNAME,
+ " stop on unrecognized qresult in rpz_rewrite()",
+ qresult);
+ return (ISC_R_SUCCESS);
+ }
+
+ rdataset = NULL;
+
+ if ((st->state & (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) !=
+ (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME)) {
+ isc_netaddr_t netaddr;
+ dns_rpz_zbits_t allowed;
+
+ if (qresult_type == RPZ_QRESULT_TYPE_RECURSE) {
+ /*
+ * This request needs recursion that has not been done.
+ * Get bits for the policy zones that do not need
+ * to wait for the results of recursion.
+ */
+ allowed = st->have.qname_skip_recurse;
+ if (allowed == 0)
+ return (ISC_R_SUCCESS);
+ } else {
+ allowed = DNS_RPZ_ALL_ZBITS;
+ }
+
+ /*
+ * Check once for triggers for the client IP address.
+ */
+ if ((st->state & DNS_RPZ_DONE_CLIENT_IP) == 0) {
+ zbits = rpz_get_zbits(client, dns_rdatatype_none,
+ DNS_RPZ_TYPE_CLIENT_IP);
+ zbits &= allowed;
+ if (zbits != 0) {
+ isc_netaddr_fromsockaddr(&netaddr,
+ &client->peeraddr);
+ result = rpz_rewrite_ip(client, &netaddr, qtype,
+ DNS_RPZ_TYPE_CLIENT_IP,
+ zbits, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Check triggers for the query name if this is the first time
+ * for the current qname.
+ * There is a first time for each name in a CNAME chain
+ */
+ if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
+ result = rpz_rewrite_name(client, client->query.qname,
+ qtype, DNS_RPZ_TYPE_QNAME,
+ allowed, &rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * Check IPv4 addresses in A RRs next.
+ * Reset to the start of the NS names.
+ */
+ st->r.label = dns_name_countlabels(client->query.qname);
+ st->state &= ~(DNS_RPZ_DONE_QNAME_IP |
+ DNS_RPZ_DONE_IPv4);
+
+ }
+
+ /*
+ * Quit if this was an attempt to find a qname or
+ * client-IP trigger before recursion.
+ * We will be back if no pre-recursion triggers hit.
+ * For example, consider 2 policy zones, both with qname and
+ * IP address triggers. If the qname misses the 1st zone,
+ * then we cannot know whether a hit for the qname in the
+ * 2nd zone matters until after recursing to get the A RRs and
+ * testing them in the first zone.
+ * Do not bother saving the work from this attempt,
+ * because recusion is so slow.
+ */
+ if (qresult_type == RPZ_QRESULT_TYPE_RECURSE)
+ goto cleanup;
+
+ /*
+ * DNS_RPZ_DONE_QNAME but not DNS_RPZ_DONE_CLIENT_IP
+ * is reset at the end of dealing with each CNAME.
+ */
+ st->state |= (DNS_RPZ_DONE_CLIENT_IP | DNS_RPZ_DONE_QNAME);
+ }
+
+ /*
+ * Check known IP addresses for the query name if the database
+ * lookup resulted in some addresses (qresult_type ==
+ * RPZ_QRESULT_TYPE_DONE) and if we have not already checked them.
+ * Any recursion required for the query has already happened.
+ * Do not check addresses that will not be in the ANSWER section.
+ */
+ if ((st->state & DNS_RPZ_DONE_QNAME_IP) == 0 &&
+ qresult_type == RPZ_QRESULT_TYPE_DONE &&
+ rpz_get_zbits(client, qtype, DNS_RPZ_TYPE_IP) != 0) {
+ result = rpz_rewrite_ip_rrsets(client,
+ client->query.qname, qtype,
+ DNS_RPZ_TYPE_IP,
+ &rdataset, resuming);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ /*
+ * We are finished checking the IP addresses for the qname.
+ * Start with IPv4 if we will check NS IP addesses.
+ */
+ st->state |= DNS_RPZ_DONE_QNAME_IP;
+ st->state &= ~DNS_RPZ_DONE_IPv4;
+ }
+
+ /*
+ * Stop looking for rules if there are none of the other kinds
+ * that could override what we already have.
+ */
+ if (rpz_get_zbits(client, dns_rdatatype_any,
+ DNS_RPZ_TYPE_NSDNAME) == 0 &&
+ rpz_get_zbits(client, dns_rdatatype_any,
+ DNS_RPZ_TYPE_NSIP) == 0) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ dns_fixedname_init(&nsnamef);
+ dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef));
+ while (st->r.label > st->popt.min_ns_labels) {
+ /*
+ * Get NS rrset for each domain in the current qname.
+ */
+ if (st->r.label == dns_name_countlabels(client->query.qname)) {
+ nsname = client->query.qname;
+ } else {
+ nsname = dns_fixedname_name(&nsnamef);
+ dns_name_split(client->query.qname, st->r.label,
+ NULL, nsname);
+ }
+ if (st->r.ns_rdataset == NULL ||
+ !dns_rdataset_isassociated(st->r.ns_rdataset))
+ {
+ dns_db_t *db = NULL;
+ result = rpz_rrset_find(client, nsname,
+ dns_rdatatype_ns,
+ DNS_RPZ_TYPE_NSDNAME,
+ &db, NULL, &st->r.ns_rdataset,
+ resuming);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (st->m.policy == DNS_RPZ_POLICY_ERROR)
+ goto cleanup;
+ switch (result) {
+ case ISC_R_SUCCESS:
+ result = dns_rdataset_first(st->r.ns_rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ st->state &= ~(DNS_RPZ_DONE_NSDNAME |
+ DNS_RPZ_DONE_IPv4);
+ break;
+ case DNS_R_DELEGATION:
+ case DNS_R_DUPLICATE:
+ case DNS_R_DROP:
+ goto cleanup;
+ case DNS_R_EMPTYNAME:
+ case DNS_R_NXRRSET:
+ case DNS_R_EMPTYWILD:
+ case DNS_R_NXDOMAIN:
+ case DNS_R_NCACHENXDOMAIN:
+ case DNS_R_NCACHENXRRSET:
+ case ISC_R_NOTFOUND:
+ case DNS_R_CNAME:
+ case DNS_R_DNAME:
+ rpz_rewrite_ns_skip(client, nsname, result,
+ 0, NULL);
+ continue;
+ case ISC_R_TIMEDOUT:
+ case DNS_R_BROKENCHAIN:
+ case ISC_R_FAILURE:
+ rpz_rewrite_ns_skip(client, nsname, result,
+ DNS_RPZ_DEBUG_LEVEL3,
+ " NS rpz_rrset_find()");
+ continue;
+ default:
+ rpz_rewrite_ns_skip(client, nsname, result,
+ DNS_RPZ_INFO_LEVEL,
+ " unrecognized NS"
+ " rpz_rrset_find()");
+ continue;
+ }
+ }
+ /*
+ * Check all NS names.
+ */
+ do {
+ dns_rdata_ns_t ns;
+ dns_rdata_t nsrdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(st->r.ns_rdataset, &nsrdata);
+ result = dns_rdata_tostruct(&nsrdata, &ns, NULL);
+ dns_rdata_reset(&nsrdata);
+ if (result != ISC_R_SUCCESS) {
+ rpz_log_fail(client, DNS_RPZ_ERROR_LEVEL,
+ nsname, DNS_RPZ_TYPE_NSIP,
+ " rdata_tostruct()", result);
+ st->m.policy = DNS_RPZ_POLICY_ERROR;
+ goto cleanup;
+ }
+ /*
+ * Do nothing about "NS ."
+ */
+ if (dns_name_equal(&ns.name, dns_rootname)) {
+ dns_rdata_freestruct(&ns);
+ result = dns_rdataset_next(st->r.ns_rdataset);
+ continue;
+ }
+ /*
+ * Check this NS name if we did not handle it
+ * during a previous recursion.
+ */
+ if ((st->state & DNS_RPZ_DONE_NSDNAME) == 0) {
+ result = rpz_rewrite_name(client, &ns.name,
+ qtype,
+ DNS_RPZ_TYPE_NSDNAME,
+ DNS_RPZ_ALL_ZBITS,
+ &rdataset);
+ if (result != ISC_R_SUCCESS) {
+ dns_rdata_freestruct(&ns);
+ goto cleanup;
+ }
+ st->state |= DNS_RPZ_DONE_NSDNAME;
+ }
+ /*
+ * Check all IP addresses for this NS name.
+ */
+ result = rpz_rewrite_ip_rrsets(client, &ns.name, qtype,
+ DNS_RPZ_TYPE_NSIP,
+ &rdataset, resuming);
+ dns_rdata_freestruct(&ns);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ st->state &= ~(DNS_RPZ_DONE_NSDNAME |
+ DNS_RPZ_DONE_IPv4);
+ result = dns_rdataset_next(st->r.ns_rdataset);
+ } while (result == ISC_R_SUCCESS);
+ dns_rdataset_disassociate(st->r.ns_rdataset);
+ st->r.label--;
+
+ if (rpz_get_zbits(client, dns_rdatatype_any,
+ DNS_RPZ_TYPE_NSDNAME) == 0 &&
+ rpz_get_zbits(client, dns_rdatatype_any,
+ DNS_RPZ_TYPE_NSIP) == 0)
+ break;
+ }
+
+ /*
+ * Use the best hit, if any.
+ */
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (st->m.policy != DNS_RPZ_POLICY_MISS &&
+ st->m.policy != DNS_RPZ_POLICY_ERROR &&
+ st->m.rpz->policy != DNS_RPZ_POLICY_GIVEN)
+ st->m.policy = st->m.rpz->policy;
+ if (st->m.policy == DNS_RPZ_POLICY_MISS ||
+ st->m.policy == DNS_RPZ_POLICY_PASSTHRU ||
+ st->m.policy == DNS_RPZ_POLICY_ERROR) {
+ if (st->m.policy == DNS_RPZ_POLICY_PASSTHRU &&
+ result != DNS_R_DELEGATION)
+ rpz_log_rewrite(client, false, st->m.policy,
+ st->m.type, st->m.zone, st->p_name,
+ NULL, st->m.rpz->num);
+ rpz_match_clear(st);
+ }
+ if (st->m.policy == DNS_RPZ_POLICY_ERROR) {
+ CTRACE(ISC_LOG_ERROR, "SERVFAIL due to RPZ policy");
+ st->m.type = DNS_RPZ_TYPE_BAD;
+ result = DNS_R_SERVFAIL;
+ }
+ query_putrdataset(client, &rdataset);
+ if ((st->state & DNS_RPZ_RECURSING) == 0)
+ rpz_clean(NULL, &st->r.db, NULL, &st->r.ns_rdataset);
+
+ return (result);
+}
+
+/*
+ * See if response policy zone rewriting is allowed by a lack of interest
+ * by the client in DNSSEC or a lack of signatures.
+ */
+static bool
+rpz_ck_dnssec(ns_client_t *client, isc_result_t qresult,
+ dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+ dns_fixedname_t fixed;
+ dns_name_t *found;
+ dns_rdataset_t trdataset;
+ dns_rdatatype_t type;
+ isc_result_t result;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_ck_dnssec");
+
+ if (client->view->rpzs->p.break_dnssec || !WANTDNSSEC(client))
+ return (true);
+
+ /*
+ * We do not know if there are signatures if we have not recursed
+ * for them.
+ */
+ if (qresult == DNS_R_DELEGATION || qresult == ISC_R_NOTFOUND)
+ return (false);
+
+ if (sigrdataset == NULL)
+ return (true);
+ if (dns_rdataset_isassociated(sigrdataset))
+ return (false);
+
+ /*
+ * We are happy to rewrite nothing.
+ */
+ if (rdataset == NULL || !dns_rdataset_isassociated(rdataset))
+ return (true);
+ /*
+ * Do not rewrite if there is any sign of signatures.
+ */
+ if (rdataset->type == dns_rdatatype_nsec ||
+ rdataset->type == dns_rdatatype_nsec3 ||
+ rdataset->type == dns_rdatatype_rrsig)
+ return (false);
+
+ /*
+ * Look for a signature in a negative cache rdataset.
+ */
+ if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) == 0)
+ return (true);
+ found = dns_fixedname_initname(&fixed);
+ dns_rdataset_init(&trdataset);
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_ncache_current(rdataset, found, &trdataset);
+ type = trdataset.type;
+ dns_rdataset_disassociate(&trdataset);
+ if (type == dns_rdatatype_nsec ||
+ type == dns_rdatatype_nsec3 ||
+ type == dns_rdatatype_rrsig)
+ return (false);
+ }
+ return (true);
+}
+
+/*
+ * Add a CNAME to the query response, including translating foo.evil.com and
+ * *.evil.com CNAME *.example.com
+ * to
+ * foo.evil.com CNAME foo.evil.com.example.com
+ */
+static isc_result_t
+rpz_add_cname(ns_client_t *client, dns_rpz_st_t *st,
+ dns_name_t *cname, dns_name_t *fname, isc_buffer_t *dbuf)
+{
+ dns_fixedname_t prefix, suffix;
+ unsigned int labels;
+ isc_result_t result;
+
+ CTRACE(ISC_LOG_DEBUG(3), "rpz_add_cname");
+
+ labels = dns_name_countlabels(cname);
+ if (labels > 2 && dns_name_iswildcard(cname)) {
+ dns_fixedname_init(&prefix);
+ dns_name_split(client->query.qname, 1,
+ dns_fixedname_name(&prefix), NULL);
+ dns_fixedname_init(&suffix);
+ dns_name_split(cname, labels-1,
+ NULL, dns_fixedname_name(&suffix));
+ result = dns_name_concatenate(dns_fixedname_name(&prefix),
+ dns_fixedname_name(&suffix),
+ fname, NULL);
+ if (result == DNS_R_NAMETOOLONG)
+ client->message->rcode = dns_rcode_yxdomain;
+ } else {
+ result = dns_name_copy(cname, fname, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ query_keepname(client, fname, dbuf);
+ result = query_add_cname(client, client->query.qname,
+ fname, dns_trust_authanswer, st->m.ttl);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ rpz_log_rewrite(client, false, st->m.policy,
+ st->m.type, st->m.zone, st->p_name, fname,
+ st->m.rpz->num);
+ ns_client_qnamereplace(client, fname);
+ /*
+ * Turn off DNSSEC because the results of a
+ * response policy zone cannot verify.
+ */
+ client->attributes &= ~(NS_CLIENTATTR_WANTDNSSEC |
+ NS_CLIENTATTR_WANTAD);
+ return (ISC_R_SUCCESS);
+}
+
+#define MAX_RESTARTS 16
+
+#define QUERY_ERROR(r) \
+do { \
+ eresult = r; \
+ want_restart = false; \
+ line = __LINE__; \
+} while (0)
+
+#define RECURSE_ERROR(r) \
+do { \
+ if ((r) == DNS_R_DUPLICATE || (r) == DNS_R_DROP) \
+ QUERY_ERROR(r); \
+ else \
+ QUERY_ERROR(DNS_R_SERVFAIL); \
+} 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);
+ memmove(&ina.s_addr, rdata->data, 4);
+ isc_netaddr_fromin(netaddr, &ina);
+ return (ISC_R_SUCCESS);
+ case dns_rdatatype_aaaa:
+ INSIST(rdata->length == 16);
+ memmove(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(ISC_LOG_DEBUG(3), "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_REQUIRED;
+ }
+}
+
+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[] = {
+ DNS_NAME_INITABSOLUTE(inaddr10, inaddr10_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr16172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr17172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr18172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr19172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr20172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr21172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr22172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr23172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr24172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr25172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr26172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr27172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr28172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr29172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr30172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(inaddr31172, inaddr172_offsets),
+ DNS_NAME_INITABSOLUTE(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 =
+ DNS_NAME_INITABSOLUTE(prisoner_data, prisoner_offsets);
+static dns_name_t hostmaster =
+ DNS_NAME_INITABSOLUTE(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);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ 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, bool exact,
+ dns_name_t *found)
+{
+ unsigned char salt[256];
+ size_t salt_length;
+ uint16_t iterations;
+ isc_result_t result;
+ unsigned int dboptions;
+ dns_fixedname_t fixed;
+ dns_hash_t hash;
+ dns_name_t name;
+ unsigned int skip = 0, labels;
+ dns_rdata_nsec3_t nsec3;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ bool optout;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+
+ 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);
+ labels = dns_name_countlabels(&name);
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ /*
+ * 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_findext(db, dns_fixedname_name(&fixed), version,
+ dns_rdatatype_nsec3, dboptions, client->now,
+ NULL, fname, &cm, &ci, 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 = (nsec3.flags & DNS_NSEC3FLAG_OPTOUT);
+ if (found != NULL && optout &&
+ dns_name_issubdomain(&name, dns_db_origin(db)))
+ {
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ skip++;
+ dns_name_getlabelsequence(qname, skip, labels - skip,
+ &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 == qname) {
+ if (skip != 0U)
+ dns_name_getlabelsequence(qname, skip, labels - skip,
+ found);
+ } else if (found != NULL)
+ dns_name_copy(&name, found, NULL);
+ return;
+}
+
+#ifdef ALLOW_FILTER_AAAA
+static bool
+is_v4_client(ns_client_t *client) {
+ if (isc_sockaddr_pf(&client->peeraddr) == AF_INET)
+ return (true);
+ if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr))
+ return (true);
+ return (false);
+}
+
+static bool
+is_v6_client(ns_client_t *client) {
+ if (isc_sockaddr_pf(&client->peeraddr) == AF_INET6 &&
+ !IN6_IS_ADDR_V4MAPPED(&client->peeraddr.type.sin6.sin6_addr))
+ return (true);
+ return (false);
+}
+#endif
+
+static uint32_t
+dns64_ttl(dns_db_t *db, dns_dbversion_t *version) {
+ dns_dbnode_t *node = NULL;
+ dns_rdata_soa_t soa;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ uint32_t ttl = UINT32_MAX;
+
+ dns_rdataset_init(&rdataset);
+
+ result = dns_db_getoriginnode(db, &node);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ result = dns_db_findrdataset(db, node, version, dns_rdatatype_soa,
+ 0, 0, &rdataset, NULL);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ result = dns_rdataset_first(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ ttl = ISC_MIN(rdataset.ttl, soa.minimum);
+
+cleanup:
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (ttl);
+}
+
+static bool
+dns64_aaaaok(ns_client_t *client, dns_rdataset_t *rdataset,
+ dns_rdataset_t *sigrdataset)
+{
+ isc_netaddr_t netaddr;
+ dns_dns64_t *dns64 = ISC_LIST_HEAD(client->view->dns64);
+ unsigned int flags = 0;
+ unsigned int i, count;
+ bool *aaaaok;
+
+ INSIST(client->query.dns64_aaaaok == NULL);
+ INSIST(client->query.dns64_aaaaoklen == 0);
+ INSIST(client->query.dns64_aaaa == NULL);
+ INSIST(client->query.dns64_sigaaaa == NULL);
+
+ if (dns64 == NULL)
+ return (true);
+
+ if (RECURSIONOK(client))
+ flags |= DNS_DNS64_RECURSIVE;
+
+ if (sigrdataset != NULL && dns_rdataset_isassociated(sigrdataset))
+ flags |= DNS_DNS64_DNSSEC;
+
+ count = dns_rdataset_count(rdataset);
+ aaaaok = isc_mem_get(client->mctx, sizeof(bool) * count);
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ if (dns_dns64_aaaaok(dns64, &netaddr, client->signer,
+ &ns_g_server->aclenv, flags, rdataset,
+ aaaaok, count)) {
+ for (i = 0; i < count; i++) {
+ if (aaaaok != NULL && !aaaaok[i]) {
+ SAVE(client->query.dns64_aaaaok, aaaaok);
+ client->query.dns64_aaaaoklen = count;
+ break;
+ }
+ }
+ if (aaaaok != NULL)
+ isc_mem_put(client->mctx, aaaaok,
+ sizeof(bool) * count);
+ return (true);
+ }
+ if (aaaaok != NULL)
+ isc_mem_put(client->mctx, aaaaok,
+ sizeof(bool) * count);
+ return (false);
+}
+
+/*
+ * Look for the name and type in the redirection zone. If found update
+ * the arguments as appropriate. Return true if a update was
+ * performed.
+ *
+ * Only perform the update if the client is in the allow query acl and
+ * returning the update would not cause a DNSSEC validation failure.
+ */
+static isc_result_t
+redirect(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
+ dns_dbnode_t **nodep, dns_db_t **dbp, dns_dbversion_t **versionp,
+ dns_rdatatype_t qtype)
+{
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_fixedname_t fixed;
+ dns_name_t *found;
+ dns_rdataset_t trdataset;
+ isc_result_t result;
+ dns_rdatatype_t type;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+ ns_dbversion_t *dbversion;
+
+ CTRACE(ISC_LOG_DEBUG(3), "redirect");
+
+ if (client->view->redirect == NULL)
+ return (ISC_R_NOTFOUND);
+
+ found = dns_fixedname_initname(&fixed);
+ dns_rdataset_init(&trdataset);
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ if (WANTDNSSEC(client) && dns_db_iszone(*dbp) && dns_db_issecure(*dbp))
+ return (ISC_R_NOTFOUND);
+
+ if (WANTDNSSEC(client) && dns_rdataset_isassociated(rdataset)) {
+ if (rdataset->trust == dns_trust_secure)
+ return (ISC_R_NOTFOUND);
+ if (rdataset->trust == dns_trust_ultimate &&
+ (rdataset->type == dns_rdatatype_nsec ||
+ rdataset->type == dns_rdatatype_nsec3))
+ return (ISC_R_NOTFOUND);
+ if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_ncache_current(rdataset, found, &trdataset);
+ type = trdataset.type;
+ dns_rdataset_disassociate(&trdataset);
+ if (type == dns_rdatatype_nsec ||
+ type == dns_rdatatype_nsec3 ||
+ type == dns_rdatatype_rrsig)
+ return (ISC_R_NOTFOUND);
+ }
+ }
+ }
+
+ result = ns_client_checkaclsilent(client, NULL,
+ dns_zone_getqueryacl(client->view->redirect),
+ true);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_NOTFOUND);
+
+ result = dns_zone_getdb(client->view->redirect, &db);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_NOTFOUND);
+
+ dbversion = query_findversion(client, db);
+ if (dbversion == NULL) {
+ dns_db_detach(&db);
+ return (ISC_R_NOTFOUND);
+ }
+
+ /*
+ * Lookup the requested data in the redirect zone.
+ */
+ result = dns_db_findext(db, client->query.qname, dbversion->version,
+ qtype, DNS_DBFIND_NOZONECUT, client->now,
+ &node, found, &cm, &ci, &trdataset, NULL);
+ if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ goto nxrrset;
+ } else if (result != ISC_R_SUCCESS) {
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+ return (ISC_R_NOTFOUND);
+ }
+
+ CTRACE(ISC_LOG_DEBUG(3), "redirect: found data: done");
+ dns_name_copy(found, name, NULL);
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(&trdataset)) {
+ dns_rdataset_clone(&trdataset, rdataset);
+ dns_rdataset_disassociate(&trdataset);
+ }
+ nxrrset:
+ if (*nodep != NULL)
+ dns_db_detachnode(*dbp, nodep);
+ dns_db_detach(dbp);
+ dns_db_attachnode(db, node, nodep);
+ dns_db_attach(db, dbp);
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+ *versionp = dbversion->version;
+
+ client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
+ NS_QUERYATTR_NOADDITIONAL);
+
+ return (result);
+}
+
+static isc_result_t
+redirect2(ns_client_t *client, dns_name_t *name, dns_rdataset_t *rdataset,
+ dns_dbnode_t **nodep, dns_db_t **dbp, dns_dbversion_t **versionp,
+ dns_rdatatype_t qtype, bool *is_zonep)
+{
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_fixedname_t fixed;
+ dns_fixedname_t fixedredirect;
+ dns_name_t *found, *redirectname;
+ dns_rdataset_t trdataset;
+ isc_result_t result;
+ dns_rdatatype_t type;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+ dns_dbversion_t *version = NULL;
+ dns_zone_t *zone = NULL;
+ bool is_zone;
+ unsigned int options;
+
+ CTRACE(ISC_LOG_DEBUG(3), "redirect2");
+
+ if (client->view->redirectzone == NULL)
+ return (ISC_R_NOTFOUND);
+
+ if (dns_name_issubdomain(name, client->view->redirectzone))
+ return (ISC_R_NOTFOUND);
+
+ found = dns_fixedname_initname(&fixed);
+ dns_rdataset_init(&trdataset);
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+ if (WANTDNSSEC(client) && dns_db_iszone(*dbp) && dns_db_issecure(*dbp))
+ return (ISC_R_NOTFOUND);
+
+ if (WANTDNSSEC(client) && dns_rdataset_isassociated(rdataset)) {
+ if (rdataset->trust == dns_trust_secure)
+ return (ISC_R_NOTFOUND);
+ if (rdataset->trust == dns_trust_ultimate &&
+ (rdataset->type == dns_rdatatype_nsec ||
+ rdataset->type == dns_rdatatype_nsec3))
+ return (ISC_R_NOTFOUND);
+ if ((rdataset->attributes & DNS_RDATASETATTR_NEGATIVE) != 0) {
+ for (result = dns_rdataset_first(rdataset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(rdataset)) {
+ dns_ncache_current(rdataset, found, &trdataset);
+ type = trdataset.type;
+ dns_rdataset_disassociate(&trdataset);
+ if (type == dns_rdatatype_nsec ||
+ type == dns_rdatatype_nsec3 ||
+ type == dns_rdatatype_rrsig)
+ return (ISC_R_NOTFOUND);
+ }
+ }
+ }
+
+ redirectname = dns_fixedname_initname(&fixedredirect);
+ if (dns_name_countlabels(name) > 1U) {
+ dns_name_t prefix;
+ unsigned int labels = dns_name_countlabels(name) - 1;
+
+ dns_name_init(&prefix, NULL);
+ dns_name_getlabelsequence(name, 0, labels, &prefix);
+ result = dns_name_concatenate(&prefix,
+ client->view->redirectzone,
+ redirectname, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_NOTFOUND);
+ } else
+ dns_name_copy(redirectname, client->view->redirectzone, NULL);
+
+ options = 0;
+ result = query_getdb(client, redirectname, qtype, options, &zone,
+ &db, &version, &is_zone);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_NOTFOUND);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ /*
+ * Lookup the requested data in the redirect zone.
+ */
+ result = dns_db_findext(db, redirectname, version,
+ qtype, 0, client->now,
+ &node, found, &cm, &ci, &trdataset, NULL);
+ if (result == DNS_R_NXRRSET || result == DNS_R_NCACHENXRRSET) {
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ goto nxrrset;
+ } else if (result == ISC_R_NOTFOUND || result == DNS_R_DELEGATION) {
+ /*
+ * Cleanup.
+ */
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+ /*
+ * Don't loop forever if the lookup failed last time.
+ */
+ if (!REDIRECT(client)) {
+ result = query_recurse(client, qtype, redirectname,
+ NULL, NULL, true);
+ if (result == ISC_R_SUCCESS) {
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ client->query.attributes |=
+ NS_QUERYATTR_REDIRECT;
+ return (DNS_R_CONTINUE);
+ }
+ }
+ return (ISC_R_NOTFOUND);
+ } else if (result != ISC_R_SUCCESS) {
+ if (dns_rdataset_isassociated(&trdataset))
+ dns_rdataset_disassociate(&trdataset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+ return (ISC_R_NOTFOUND);
+ }
+
+ CTRACE(ISC_LOG_DEBUG(3), "redirect2: found data: done");
+ /*
+ * Adjust the found name to not include the redirectzone suffix.
+ */
+ dns_name_split(found, dns_name_countlabels(client->view->redirectzone),
+ found, NULL);
+ /*
+ * Make the name absolute.
+ */
+ result = dns_name_concatenate(found, dns_rootname, found, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ dns_name_copy(found, name, NULL);
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ if (dns_rdataset_isassociated(&trdataset)) {
+ dns_rdataset_clone(&trdataset, rdataset);
+ dns_rdataset_disassociate(&trdataset);
+ }
+ nxrrset:
+ if (*nodep != NULL)
+ dns_db_detachnode(*dbp, nodep);
+ dns_db_detach(dbp);
+ dns_db_attachnode(db, node, nodep);
+ dns_db_attach(db, dbp);
+ dns_db_detachnode(db, &node);
+ dns_db_detach(&db);
+ *is_zonep = is_zone;
+ *versionp = version;
+
+ client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
+ NS_QUERYATTR_NOADDITIONAL);
+
+ return (result);
+}
+
+/*
+ * 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 isc_result_t
+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 = qtype;
+ 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;
+ bool want_restart, is_zone, need_wildcardproof;
+ bool is_staticstub_zone;
+ bool authoritative = false;
+ bool answer_has_ns = false;
+ unsigned int n, nlabels;
+ dns_namereln_t namereln;
+ int order;
+ isc_buffer_t *dbuf;
+ isc_buffer_t b;
+ isc_result_t result, eresult, tresult;
+ 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;
+ bool empty_wild;
+ dns_rdataset_t *noqname;
+ dns_rpz_st_t *rpz_st;
+ bool resuming;
+ int line = -1;
+ bool dns64_exclude, dns64, rpz;
+ bool nxrewrite = false;
+ bool redirected = false;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+ char errmsg[256];
+ bool associated;
+ dns_section_t section;
+ dns_ttl_t ttl;
+ bool failcache;
+ uint32_t flags;
+#ifdef WANT_QUERYTRACE
+ char mbuf[BUFSIZ];
+ char qbuf[DNS_NAME_FORMATSIZE];
+ char tbuf[DNS_RDATATYPE_FORMATSIZE];
+#endif
+ dns_name_t *rpzqname;
+
+ CTRACE(ISC_LOG_DEBUG(3), "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 = false;
+ empty_wild = false;
+ dns64_exclude = dns64 = rpz = false;
+ options = 0;
+ resuming = false;
+ is_zone = false;
+ is_staticstub_zone = false;
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+ dns_clientinfo_init(&ci, client, NULL);
+
+#ifdef WANT_QUERYTRACE
+ if (client->query.origqname != NULL)
+ dns_name_format(client->query.origqname, qbuf,
+ sizeof(qbuf));
+ else
+ snprintf(qbuf, sizeof(qbuf), "<unset>");
+
+ snprintf(mbuf, sizeof(mbuf) - 1,
+ "client attr:0x%x, query attr:0x%X, restarts:%d, "
+ "origqname:%s, timer:%d, authdb:%d, referral:%d",
+ client->attributes,
+ client->query.attributes,
+ client->query.restarts, qbuf,
+ (int) client->query.timerset,
+ (int) client->query.authdbset,
+ (int) client->query.isreferral);
+ CTRACE(ISC_LOG_DEBUG(3), mbuf);
+#endif
+
+ if (event != NULL) {
+ /*
+ * We're returning from recursion. Restore the query context
+ * and resume.
+ */
+ want_restart = false;
+
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL &&
+ (rpz_st->state & DNS_RPZ_RECURSING) != 0)
+ {
+ CTRACE(ISC_LOG_DEBUG(3), "resume from RPZ recursion");
+#ifdef WANT_QUERYTRACE
+ {
+ char pbuf[DNS_NAME_FORMATSIZE] = "<unset>";
+ char fbuf[DNS_NAME_FORMATSIZE] = "<unset>";
+ if (rpz_st->r_name != NULL)
+ dns_name_format(rpz_st->r_name,
+ qbuf, sizeof(qbuf));
+ else
+ snprintf(qbuf, sizeof(qbuf),
+ "<unset>");
+ if (rpz_st->p_name != NULL)
+ dns_name_format(rpz_st->p_name,
+ pbuf, sizeof(pbuf));
+ if (rpz_st->fname != NULL)
+ dns_name_format(rpz_st->fname,
+ fbuf, sizeof(fbuf));
+
+ snprintf(mbuf, sizeof(mbuf) - 1,
+ "rpz rname:%s, pname:%s, fname:%s",
+ qbuf, pbuf, fbuf);
+ CTRACE(ISC_LOG_DEBUG(3), mbuf);
+ }
+#endif
+
+ is_zone = rpz_st->q.is_zone;
+ authoritative = rpz_st->q.authoritative;
+ RESTORE(zone, rpz_st->q.zone);
+ RESTORE(node, rpz_st->q.node);
+ RESTORE(db, rpz_st->q.db);
+ RESTORE(rdataset, rpz_st->q.rdataset);
+ RESTORE(sigrdataset, rpz_st->q.sigrdataset);
+ qtype = rpz_st->q.qtype;
+
+ if (event->node != NULL)
+ dns_db_detachnode(event->db, &event->node);
+ SAVE(rpz_st->r.db, event->db);
+ rpz_st->r.r_type = event->qtype;
+ SAVE(rpz_st->r.r_rdataset, event->rdataset);
+ query_putrdataset(client, &event->sigrdataset);
+ } else if (REDIRECT(client)) {
+ /*
+ * Restore saved state.
+ */
+ CTRACE(ISC_LOG_DEBUG(3),
+ "resume from redirect recursion");
+#ifdef WANT_QUERYTRACE
+ dns_name_format(client->query.redirect.fname,
+ qbuf, sizeof(qbuf));
+ dns_rdatatype_format(client->query.redirect.qtype,
+ tbuf, sizeof(tbuf));
+ snprintf(mbuf, sizeof(mbuf) - 1,
+ "redirect fname:%s, qtype:%s, auth:%d",
+ qbuf, tbuf,
+ client->query.redirect.authoritative);
+ CTRACE(ISC_LOG_DEBUG(3), mbuf);
+#endif
+ qtype = client->query.redirect.qtype;
+ INSIST(client->query.redirect.rdataset != NULL);
+ RESTORE(rdataset, client->query.redirect.rdataset);
+ RESTORE(sigrdataset,
+ client->query.redirect.sigrdataset);
+ RESTORE(db, client->query.redirect.db);
+ RESTORE(node, client->query.redirect.node);
+ RESTORE(zone, client->query.redirect.zone);
+ authoritative = client->query.redirect.authoritative;
+ is_zone = client->query.redirect.is_zone;
+
+ /*
+ * Free resources used while recursing.
+ */
+ query_putrdataset(client, &event->rdataset);
+ query_putrdataset(client, &event->sigrdataset);
+ if (event->node != NULL)
+ dns_db_detachnode(event->db, &event->node);
+ if (event->db != NULL)
+ dns_db_detach(&event->db);
+ } else {
+ CTRACE(ISC_LOG_DEBUG(3),
+ "resume from normal recursion");
+ authoritative = false;
+
+ qtype = event->qtype;
+ SAVE(db, event->db);
+ SAVE(node, event->node);
+ SAVE(rdataset, event->rdataset);
+ SAVE(sigrdataset, event->sigrdataset);
+ }
+ INSIST(rdataset != NULL);
+
+ if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig)
+ type = dns_rdatatype_any;
+ else
+ type = qtype;
+
+ if (DNS64(client)) {
+ client->query.attributes &= ~NS_QUERYATTR_DNS64;
+ dns64 = true;
+ }
+ if (DNS64EXCLUDE(client)) {
+ client->query.attributes &= ~NS_QUERYATTR_DNS64EXCLUDE;
+ dns64_exclude = true;
+ }
+
+ if (rpz_st != NULL &&
+ (rpz_st->state & DNS_RPZ_RECURSING) != 0)
+ {
+ /*
+ * Has response policy changed out from under us?
+ */
+ if (rpz_st->rpz_ver != client->view->rpzs->rpz_ver) {
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY,
+ DNS_RPZ_INFO_LEVEL,
+ "query_find: RPZ settings "
+ "out of date "
+ "(rpz_ver %d, expected %d)",
+ client->view->rpzs->rpz_ver,
+ rpz_st->rpz_ver);
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: query_getnamebuf failed (1)");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: query_newname failed (1)");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ if (rpz_st != NULL &&
+ (rpz_st->state & DNS_RPZ_RECURSING) != 0) {
+ tname = rpz_st->fname;
+ } else if (REDIRECT(client)) {
+ tname = client->query.redirect.fname;
+ } else {
+ tname = dns_fixedname_name(&event->foundname);
+ }
+ result = dns_name_copy(tname, fname, NULL);
+ if (result != ISC_R_SUCCESS) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: dns_name_copy failed");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ if (rpz_st != NULL &&
+ (rpz_st->state & DNS_RPZ_RECURSING) != 0) {
+ rpz_st->r.r_result = event->result;
+ result = rpz_st->q.result;
+ free_devent(client, ISC_EVENT_PTR(&event), &event);
+ } else if (REDIRECT(client)) {
+ result = client->query.redirect.result;
+ is_zone = client->query.redirect.is_zone;
+ } else {
+ result = event->result;
+ }
+ resuming = true;
+ goto resume;
+ }
+
+ /*
+ * Not returning from recursion.
+ *
+ * First, check for a recent match in the view's SERVFAIL cache.
+ * If we find one, and it was from a query with CD=1, *or*
+ * if the current query has CD=0, then we can just return
+ * SERVFAIL now.
+ */
+ if (RECURSIONOK(client)) {
+ flags = 0;
+#ifdef ENABLE_AFL
+ if (ns_g_fuzz_type == ns_fuzz_resolver) {
+ failcache = false;
+ } else {
+ failcache = dns_badcache_find(client->view->failcache,
+ client->query.qname, qtype,
+ &flags, &client->tnow);
+ }
+#else
+ failcache = dns_badcache_find(client->view->failcache,
+ client->query.qname, qtype,
+ &flags, &client->tnow);
+#endif
+ if (failcache &&
+ (((flags & NS_FAILCACHE_CD) != 0) ||
+ ((client->message->flags & DNS_MESSAGEFLAG_CD) == 0)))
+ {
+ if (isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(1))) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typename[DNS_RDATATYPE_FORMATSIZE];
+
+ dns_name_format(client->query.qname,
+ namebuf, sizeof(namebuf));
+ dns_rdatatype_format(qtype, typename,
+ sizeof(typename));
+ ns_client_log(client, NS_LOGCATEGORY_CLIENT,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_DEBUG(1),
+ "servfail cache hit %s/%s (%s)",
+ namebuf, typename,
+ ((flags & NS_FAILCACHE_CD) != 0)
+ ? "CD=1"
+ : "CD=0");
+ }
+ client->attributes |= NS_CLIENTATTR_NOSETFC;
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * 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(ISC_LOG_DEBUG(3), "query_find: restart");
+ want_restart = false;
+ authoritative = false;
+ version = NULL;
+ zversion = NULL;
+ need_wildcardproof = false;
+ rpz = false;
+
+ if (client->view->checknames &&
+ !dns_rdata_checkowner(client->query.qname,
+ client->message->rdclass,
+ qtype, 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;
+ }
+
+ /*
+ * Setup for root key sentinel processing.
+ */
+ if (client->view->root_key_sentinel &&
+ client->query.restarts == 0 &&
+ (qtype == dns_rdatatype_a ||
+ qtype == dns_rdatatype_aaaa) &&
+ (client->message->flags & DNS_MESSAGEFLAG_CD) == 0)
+ {
+ root_key_sentinel_detect(client);
+ }
+
+ /*
+ * 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 (ISC_UNLIKELY((result != ISC_R_SUCCESS || !is_zone) &&
+ qtype == dns_rdatatype_ds &&
+ !RECURSIONOK(client) &&
+ (options & DNS_GETDB_NOEXACT) != 0))
+ {
+ /*
+ * If the query type is DS, look to see if we are
+ * authoritative for the child zone.
+ */
+ dns_db_t *tdb = NULL;
+ dns_zone_t *tzone = NULL;
+ dns_dbversion_t *tversion = NULL;
+
+ 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 = NULL;
+ RESTORE(version, tversion);
+ RESTORE(db, tdb);
+ RESTORE(zone, tzone);
+ is_zone = 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 {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: query_getdb failed");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ }
+ goto cleanup;
+ }
+
+ is_staticstub_zone = false;
+ if (is_zone) {
+ authoritative = true;
+ if (zone != NULL &&
+ dns_zone_gettype(zone) == dns_zone_staticstub)
+ is_staticstub_zone = true;
+ }
+
+ if (event == NULL && client->query.restarts == 0) {
+ if (is_zone) {
+ if (zone != NULL) {
+ /*
+ * if is_zone = true, zone = NULL then this is
+ * a DLZ zone. Don't attempt to attach zone.
+ */
+ dns_zone_attach(zone, &client->query.authzone);
+ }
+ dns_db_attach(db, &client->query.authdb);
+ }
+ client->query.authdbset = true;
+
+ /* Track TCP vs UDP stats per zone */
+ if (TCP(client))
+ inc_stats(client, dns_nsstatscounter_tcp);
+ else
+ inc_stats(client, dns_nsstatscounter_udp);
+ }
+
+ db_find:
+ CTRACE(ISC_LOG_DEBUG(3), "query_find: db_find");
+ /*
+ * We'll need some resources...
+ */
+ dbuf = query_getnamebuf(client);
+ if (ISC_UNLIKELY(dbuf == NULL)) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: query_getnamebuf failed (2)");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ rdataset = query_newrdataset(client);
+ if (ISC_UNLIKELY(fname == NULL || rdataset == NULL)) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: query_newname failed (2)");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ if (WANTDNSSEC(client) && (!is_zone || dns_db_issecure(db))) {
+ sigrdataset = query_newrdataset(client);
+ if (sigrdataset == NULL) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: query_newrdataset failed (2)");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Now look for an answer in the database. If this is a dns64
+ * AAAA lookup on a rpz database adjust the qname.
+ */
+ if (dns64 && rpz)
+ rpzqname = client->query.rpz_st->p_name;
+ else
+ rpzqname = client->query.qname;
+
+ result = dns_db_findext(db, rpzqname, version, type,
+ client->query.dboptions, client->now,
+ &node, fname, &cm, &ci, rdataset, sigrdataset);
+ /*
+ * Fixup fname and sigrdataset.
+ */
+ if (dns64 && rpz) {
+ isc_result_t rresult;
+
+ rresult = dns_name_copy(client->query.qname, fname, NULL);
+ RUNTIME_CHECK(rresult == ISC_R_SUCCESS);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ }
+
+ if (!is_zone)
+ dns_cache_updatestats(client->view->cache, result);
+
+ resume:
+ CTRACE(ISC_LOG_DEBUG(3), "query_find: resume");
+
+ /*
+ * Rate limit these responses to this client.
+ * Do not delay counting and handling obvious referrals,
+ * since those won't come here again.
+ * Delay handling delegations for which we are certain to recurse and
+ * return here (DNS_R_DELEGATION, not a child of one of our
+ * own zones, and recursion enabled)
+ * Don't mess with responses rewritten by RPZ
+ * Count each response at most once.
+ */
+ if (client->view->rrl != NULL && !HAVECOOKIE(client) &&
+ ((fname != NULL && dns_name_isabsolute(fname)) ||
+ (result == ISC_R_NOTFOUND && !RECURSIONOK(client))) &&
+ !(result == DNS_R_DELEGATION && !is_zone && RECURSIONOK(client)) &&
+ (client->query.rpz_st == NULL ||
+ (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0)&&
+ (client->query.attributes & NS_QUERYATTR_RRL_CHECKED) == 0)
+ {
+ dns_rdataset_t nc_rdataset;
+ bool wouldlog;
+ char log_buf[DNS_RRL_LOG_BUF_LEN];
+ isc_result_t nc_result, resp_result;
+ dns_rrl_result_t rrl_result;
+
+ client->query.attributes |= NS_QUERYATTR_RRL_CHECKED;
+
+ wouldlog = isc_log_wouldlog(ns_g_lctx, DNS_RRL_LOG_DROP);
+ tname = fname;
+ if (result == DNS_R_NXDOMAIN) {
+ /*
+ * Use the database origin name to rate limit NXDOMAIN
+ */
+ if (db != NULL)
+ tname = dns_db_origin(db);
+ resp_result = result;
+ } else if (result == DNS_R_NCACHENXDOMAIN &&
+ rdataset != NULL &&
+ dns_rdataset_isassociated(rdataset) &&
+ (rdataset->attributes &
+ DNS_RDATASETATTR_NEGATIVE) != 0) {
+ /*
+ * Try to use owner name in the negative cache SOA.
+ */
+ dns_fixedname_init(&fixed);
+ dns_rdataset_init(&nc_rdataset);
+ for (nc_result = dns_rdataset_first(rdataset);
+ nc_result == ISC_R_SUCCESS;
+ nc_result = dns_rdataset_next(rdataset)) {
+ dns_ncache_current(rdataset,
+ dns_fixedname_name(&fixed),
+ &nc_rdataset);
+ if (nc_rdataset.type == dns_rdatatype_soa) {
+ dns_rdataset_disassociate(&nc_rdataset);
+ tname = dns_fixedname_name(&fixed);
+ break;
+ }
+ dns_rdataset_disassociate(&nc_rdataset);
+ }
+ resp_result = DNS_R_NXDOMAIN;
+ } else if (result == DNS_R_NXRRSET ||
+ result == DNS_R_EMPTYNAME) {
+ resp_result = DNS_R_NXRRSET;
+ } else if (result == DNS_R_DELEGATION) {
+ resp_result = result;
+ } else if (result == ISC_R_NOTFOUND) {
+ /*
+ * Handle referral to ".", including when recursion
+ * is off or not requested and the hints have not
+ * been loaded or we have "additional-from-cache no".
+ */
+ tname = dns_rootname;
+ resp_result = DNS_R_DELEGATION;
+ } else {
+ resp_result = ISC_R_SUCCESS;
+ }
+ rrl_result = dns_rrl(client->view, &client->peeraddr,
+ TCP(client), client->message->rdclass,
+ qtype, tname, resp_result, client->now,
+ wouldlog, log_buf, sizeof(log_buf));
+ if (rrl_result != DNS_RRL_RESULT_OK) {
+ /*
+ * Log dropped or slipped responses in the query
+ * category so that requests are not silently lost.
+ * Starts of rate-limited bursts are logged in
+ * DNS_LOGCATEGORY_RRL.
+ *
+ * Dropped responses are counted with dropped queries
+ * in QryDropped while slipped responses are counted
+ * with other truncated responses in RespTruncated.
+ */
+ if (wouldlog) {
+ ns_client_log(client, DNS_LOGCATEGORY_RRL,
+ NS_LOGMODULE_QUERY,
+ DNS_RRL_LOG_DROP,
+ "%s", log_buf);
+ }
+ if (!client->view->rrl->log_only) {
+ if (rrl_result == DNS_RRL_RESULT_DROP) {
+ /*
+ * These will also be counted in
+ * dns_nsstatscounter_dropped
+ */
+ inc_stats(client,
+ dns_nsstatscounter_ratedropped);
+ QUERY_ERROR(DNS_R_DROP);
+ } else {
+ /*
+ * These will also be counted in
+ * dns_nsstatscounter_truncatedresp
+ */
+ inc_stats(client,
+ dns_nsstatscounter_rateslipped);
+ if (WANTCOOKIE(client)) {
+ client->message->flags &=
+ ~DNS_MESSAGEFLAG_AA;
+ client->message->flags &=
+ ~DNS_MESSAGEFLAG_AD;
+ client->message->rcode =
+ dns_rcode_badcookie;
+ } else {
+ client->message->flags |=
+ DNS_MESSAGEFLAG_TC;
+ if (resp_result ==
+ DNS_R_NXDOMAIN)
+ client->message->rcode =
+ dns_rcode_nxdomain;
+ }
+ }
+ goto cleanup;
+ }
+ }
+ } else if (!TCP(client) && client->view->requireservercookie &&
+ WANTCOOKIE(client) && !HAVECOOKIE(client)) {
+ client->message->flags &= ~DNS_MESSAGEFLAG_AA;
+ client->message->flags &= ~DNS_MESSAGEFLAG_AD;
+ client->message->rcode = dns_rcode_badcookie;
+ goto cleanup;
+ }
+
+ if (!RECURSING(client) &&
+ !dns_name_equal(client->query.qname, dns_rootname))
+ {
+ isc_result_t rresult;
+
+ rresult = rpz_rewrite(client, qtype, result, resuming,
+ rdataset, sigrdataset);
+ rpz_st = client->query.rpz_st;
+ switch (rresult) {
+ case ISC_R_SUCCESS:
+ break;
+ case DNS_R_DISALLOWED:
+ goto norpz;
+ case DNS_R_DELEGATION:
+ /*
+ * recursing for NS names or addresses,
+ * so save the main query state
+ */
+ rpz_st->q.qtype = qtype;
+ rpz_st->q.is_zone = is_zone;
+ rpz_st->q.authoritative = authoritative;
+ SAVE(rpz_st->q.zone, zone);
+ SAVE(rpz_st->q.db, db);
+ SAVE(rpz_st->q.node, node);
+ SAVE(rpz_st->q.rdataset, rdataset);
+ SAVE(rpz_st->q.sigrdataset, sigrdataset);
+ dns_name_copy(fname, rpz_st->fname, NULL);
+ rpz_st->q.result = result;
+ client->query.attributes |= NS_QUERYATTR_RECURSING;
+ goto cleanup;
+ default:
+ RECURSE_ERROR(rresult);
+ goto cleanup;
+ }
+
+ if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS)
+ rpz_st->state |= DNS_RPZ_REWRITTEN;
+ if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS &&
+ rpz_st->m.policy != DNS_RPZ_POLICY_PASSTHRU &&
+ (rpz_st->m.policy != DNS_RPZ_POLICY_TCP_ONLY ||
+ !TCP(client)) &&
+ rpz_st->m.policy != DNS_RPZ_POLICY_ERROR)
+ {
+ /*
+ * We got a hit and are going to answer with our
+ * fiction. Ensure that we answer with the name
+ * we looked up even if we were stopped short
+ * in recursion or for a deferral.
+ */
+ rresult = dns_name_copy(client->query.qname,
+ fname, NULL);
+ RUNTIME_CHECK(rresult == ISC_R_SUCCESS);
+ rpz_clean(&zone, &db, &node, NULL);
+ if (rpz_st->m.rdataset != NULL) {
+ query_putrdataset(client, &rdataset);
+ RESTORE(rdataset, rpz_st->m.rdataset);
+ } else if (rdataset != NULL &&
+ dns_rdataset_isassociated(rdataset)) {
+ dns_rdataset_disassociate(rdataset);
+ }
+ version = NULL;
+
+ RESTORE(node, rpz_st->m.node);
+ RESTORE(db, rpz_st->m.db);
+ RESTORE(version, rpz_st->m.version);
+ RESTORE(zone, rpz_st->m.zone);
+
+ switch (rpz_st->m.policy) {
+ case DNS_RPZ_POLICY_TCP_ONLY:
+ client->message->flags |= DNS_MESSAGEFLAG_TC;
+ if (result == DNS_R_NXDOMAIN ||
+ result == DNS_R_NCACHENXDOMAIN)
+ client->message->rcode =
+ dns_rcode_nxdomain;
+ rpz_log_rewrite(client, false,
+ rpz_st->m.policy,
+ rpz_st->m.type, zone,
+ rpz_st->p_name, NULL,
+ rpz_st->m.rpz->num);
+ goto cleanup;
+ case DNS_RPZ_POLICY_DROP:
+ QUERY_ERROR(DNS_R_DROP);
+ rpz_log_rewrite(client, false,
+ rpz_st->m.policy,
+ rpz_st->m.type, zone,
+ rpz_st->p_name, NULL,
+ rpz_st->m.rpz->num);
+ goto cleanup;
+ case DNS_RPZ_POLICY_NXDOMAIN:
+ result = DNS_R_NXDOMAIN;
+ nxrewrite = true;
+ rpz = true;
+ break;
+ case DNS_RPZ_POLICY_NODATA:
+ result = DNS_R_NXRRSET;
+ nxrewrite = true;
+ rpz = true;
+ break;
+ case DNS_RPZ_POLICY_RECORD:
+ result = rpz_st->m.result;
+ if (qtype == dns_rdatatype_any &&
+ result != DNS_R_CNAME) {
+ /*
+ * We will add all of the rdatasets of
+ * the node by iterating later,
+ * and set the TTL then.
+ */
+ if (dns_rdataset_isassociated(rdataset))
+ dns_rdataset_disassociate(rdataset);
+ } else {
+ /*
+ * We will add this rdataset.
+ */
+ rdataset->ttl = ISC_MIN(rdataset->ttl,
+ rpz_st->m.ttl);
+ }
+ rpz = true;
+ break;
+ case DNS_RPZ_POLICY_WILDCNAME:
+ result = dns_rdataset_first(rdataset);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &cname,
+ NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdata_reset(&rdata);
+ result = rpz_add_cname(client, rpz_st,
+ &cname.cname,
+ fname, dbuf);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ fname = NULL;
+ want_restart = true;
+ goto cleanup;
+ case DNS_RPZ_POLICY_CNAME:
+ /*
+ * Add overridding CNAME from a named.conf
+ * response-policy statement
+ */
+ result = rpz_add_cname(client, rpz_st,
+ &rpz_st->m.rpz->cname,
+ fname, dbuf);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ fname = NULL;
+ want_restart = true;
+ goto cleanup;
+ default:
+ INSIST(0);
+ }
+
+ /*
+ * Turn off DNSSEC because the results of a
+ * response policy zone cannot verify.
+ */
+ client->attributes &= ~(NS_CLIENTATTR_WANTDNSSEC |
+ NS_CLIENTATTR_WANTAD);
+ client->message->flags &= ~DNS_MESSAGEFLAG_AD;
+ query_putrdataset(client, &sigrdataset);
+ is_zone = true;
+ rpz_log_rewrite(client, false, rpz_st->m.policy,
+ rpz_st->m.type, zone, rpz_st->p_name,
+ NULL, rpz_st->m.rpz->num);
+ }
+ }
+
+ norpz:
+ /*
+ * If required, handle special "root-key-sentinel-is-ta-<keyid>" and
+ * "root-key-sentinel-not-ta-<keyid>" labels by returning SERVFAIL.
+ */
+ if (root_key_sentinel_return_servfail(client, is_zone,
+ rdataset, result))
+ {
+ /*
+ * Don't record this response in the SERVFAIL cache.
+ */
+ client->attributes |= NS_CLIENTATTR_NOSETFC;
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+
+ 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 = 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_findext(db, dns_rootname,
+ NULL, dns_rdatatype_ns,
+ 0, client->now, &node,
+ fname, &cm, &ci,
+ 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)) {
+ INSIST(!REDIRECT(client));
+ result = query_recurse(client, qtype,
+ client->query.qname,
+ NULL, NULL, resuming);
+ if (result == ISC_R_SUCCESS) {
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ if (dns64)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64;
+ if (dns64_exclude)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64EXCLUDE;
+ } else
+ RECURSE_ERROR(result);
+ goto cleanup;
+ } else {
+ /* Unable to give root server referral. */
+ CTRACE(ISC_LOG_ERROR,
+ "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 = 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 = NULL;
+ RESTORE(version, tversion);
+ RESTORE(db, tdb);
+ RESTORE(zone, tzone);
+ authoritative = 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)) {
+ bool detach = false;
+
+ 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.
+ */
+ if (db != NULL &&
+ client->query.gluedb == NULL) {
+ dns_db_attach(db,
+ &client->query.gluedb);
+ detach = true;
+ }
+ client->query.isreferral = 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);
+ if (detach) {
+ dns_db_detach(&client->query.gluedb);
+ }
+ 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);
+ dns_db_detachnode(db, &node);
+ SAVE(zdb, db);
+ SAVE(zfname, fname);
+ SAVE(zversion, version);
+ SAVE(zrdataset, rdataset);
+ SAVE(zsigrdataset, sigrdataset);
+ dns_db_attach(client->view->cachedb, &db);
+ is_zone = false;
+ goto db_find;
+ }
+ } else {
+ if (zfname != NULL &&
+ (!dns_name_issubdomain(fname, zfname) ||
+ (is_staticstub_zone &&
+ dns_name_equal(fname, zfname)))) {
+ /*
+ * In the following cases use "authoritative"
+ * data instead of the cache delegation:
+ * 1. We've already got a delegation from
+ * authoritative data, and it is better
+ * than what we found in the cache.
+ * 2. The query name matches the origin name
+ * of a static-stub zone. This needs to be
+ * considered for the case where the NS of
+ * the static-stub zone and the cached NS
+ * are different. We still need to contact
+ * the nameservers configured in the
+ * static-stub zone.
+ */
+ query_releasename(client, &fname);
+ /*
+ * 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);
+ version = NULL;
+
+ RESTORE(fname, zfname);
+ RESTORE(version, zversion);
+ RESTORE(rdataset, zrdataset);
+ RESTORE(sigrdataset, zsigrdataset);
+ /*
+ * 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!
+ */
+ INSIST(!REDIRECT(client));
+ if (dns_rdatatype_atparent(type))
+ result = query_recurse(client, qtype,
+ client->query.qname,
+ NULL, NULL, resuming);
+ else if (dns64)
+ result = query_recurse(client,
+ dns_rdatatype_a,
+ client->query.qname,
+ NULL, NULL, resuming);
+ else
+ result = query_recurse(client, qtype,
+ client->query.qname,
+ fname, rdataset,
+ resuming);
+
+ if (result == ISC_R_SUCCESS) {
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ if (dns64)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64;
+ if (dns64_exclude)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64EXCLUDE;
+ } else if (result == DNS_R_DUPLICATE ||
+ result == DNS_R_DROP)
+ QUERY_ERROR(result);
+ else
+ RECURSE_ERROR(result);
+ } else {
+ bool detach = false;
+
+ 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.isreferral = true;
+
+ if (zdb != NULL &&
+ client->query.gluedb == NULL) {
+ dns_db_attach(zdb,
+ &client->query.gluedb);
+ detach = 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.attributes &=
+ ~NS_QUERYATTR_CACHEGLUEOK;
+ if (detach) {
+ dns_db_detach(&client->query.gluedb);
+ }
+
+ if (WANTDNSSEC(client))
+ query_addds(client, db, node, version,
+ dns_fixedname_name(&fixed));
+ }
+ }
+ goto cleanup;
+
+ case DNS_R_EMPTYNAME:
+ case DNS_R_NXRRSET:
+ iszone_nxrrset:
+ INSIST(is_zone);
+
+#ifdef dns64_bis_return_excluded_addresses
+ if (dns64)
+#else
+ if (dns64 && !dns64_exclude)
+#endif
+ {
+ /*
+ * Restore the answers from the previous AAAA lookup.
+ */
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ RESTORE(rdataset, client->query.dns64_aaaa);
+ RESTORE(sigrdataset, client->query.dns64_sigaaaa);
+ if (fname == NULL) {
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: "
+ "query_getnamebuf failed (3)");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: "
+ "query_newname failed (3)");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+ dns_name_copy(client->query.qname, fname, NULL);
+ dns64 = false;
+#ifdef dns64_bis_return_excluded_addresses
+ /*
+ * Resume the diverted processing of the AAAA response?
+ */
+ if (dns64_excluded)
+ break;
+#endif
+ } else if (result == DNS_R_NXRRSET &&
+ !ISC_LIST_EMPTY(client->view->dns64) &&
+ client->message->rdclass == dns_rdataclass_in &&
+ qtype == dns_rdatatype_aaaa)
+ {
+ /*
+ * Look to see if there are A records for this
+ * name.
+ */
+ SAVE(client->query.dns64_aaaa, rdataset);
+ SAVE(client->query.dns64_sigaaaa, sigrdataset);
+ client->query.dns64_ttl = dns64_ttl(db, version);
+ query_releasename(client, &fname);
+ dns_db_detachnode(db, &node);
+ type = qtype = dns_rdatatype_a;
+ dns64 = true;
+ goto db_find;
+ }
+
+ /*
+ * Look for a NSEC3 record if we don't have a NSEC record.
+ */
+ nxrrset_rrsig:
+ if (redirected)
+ goto cleanup;
+ if (!dns_rdataset_isassociated(rdataset) &&
+ WANTDNSSEC(client)) {
+ if ((fname->attributes & DNS_NAMEATTR_WILDCARD) == 0) {
+ dns_name_t *found;
+ dns_name_t *qname;
+
+ found = dns_fixedname_initname(&fixed);
+ qname = client->query.qname;
+
+ query_findclosestnsec3(qname, db, version,
+ client, rdataset,
+ sigrdataset, fname,
+ true, found);
+ /*
+ * Did we find the closest provable encloser
+ * instead? If so add the nearest to the
+ * closest provable encloser.
+ */
+ if (dns_rdataset_isassociated(rdataset) &&
+ !dns_name_equal(qname, found) &&
+ !(ns_g_nonearest &&
+ qtype != dns_rdatatype_ds))
+ {
+ 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) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: "
+ "failure getting "
+ "closest encloser");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ /*
+ * 'nearest' doesn't exist so
+ * 'exist' is set to false.
+ */
+ query_findclosestnsec3(found, db,
+ version,
+ client,
+ rdataset,
+ sigrdataset,
+ fname,
+ false,
+ NULL);
+ }
+ } else {
+ query_releasename(client, &fname);
+ query_addwildcardproof(client, db, version,
+ client->query.qname,
+ false, true);
+ }
+ }
+ 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 to the additional section if generated by a RPZ
+ * rewrite.
+ */
+ associated = dns_rdataset_isassociated(rdataset);
+ section = nxrewrite ? DNS_SECTION_ADDITIONAL :
+ DNS_SECTION_AUTHORITY;
+
+ result = query_addsoa(client, db, version, UINT32_MAX,
+ associated, section);
+ 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 = true;
+ /* FALLTHROUGH */
+
+ case DNS_R_NXDOMAIN:
+ INSIST(is_zone || REDIRECT(client));
+ if (!empty_wild) {
+ tresult = redirect(client, fname, rdataset, &node,
+ &db, &version, type);
+ if (tresult == ISC_R_SUCCESS) {
+ inc_stats(client,
+ dns_nsstatscounter_nxdomainredirect);
+ break;
+ }
+ if (tresult == DNS_R_NXRRSET) {
+ redirected = true;
+ goto iszone_nxrrset;
+ }
+ if (tresult == DNS_R_NCACHENXRRSET) {
+ redirected = true;
+ is_zone = false;
+ goto ncache_nxrrset;
+ }
+ tresult = redirect2(client, fname, rdataset, &node,
+ &db, &version, type, &is_zone);
+ if (tresult == DNS_R_CONTINUE) {
+ inc_stats(client,
+ dns_nsstatscounter_nxdomainredirect_rlookup);
+ client->query.redirect.qtype = qtype;
+ INSIST(rdataset != NULL);
+ SAVE(client->query.redirect.rdataset, rdataset);
+ SAVE(client->query.redirect.sigrdataset,
+ sigrdataset);
+ SAVE(client->query.redirect.db, db);
+ SAVE(client->query.redirect.node, node);
+ SAVE(client->query.redirect.zone, zone);
+ client->query.redirect.result = DNS_R_NXDOMAIN;
+ dns_name_copy(fname,
+ client->query.redirect.fname,
+ NULL);
+ client->query.redirect.authoritative =
+ authoritative;
+ client->query.redirect.is_zone = is_zone;
+ goto cleanup;
+ }
+ if (tresult == ISC_R_SUCCESS) {
+ inc_stats(client,
+ dns_nsstatscounter_nxdomainredirect);
+ break;
+ }
+ if (tresult == DNS_R_NXRRSET) {
+ redirected = true;
+ goto iszone_nxrrset;
+ }
+ if (tresult == DNS_R_NCACHENXRRSET) {
+ redirected = true;
+ is_zone = false;
+ goto ncache_nxrrset;
+ }
+ }
+ 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 to the additional section if generated by a
+ * RPZ rewrite.
+ *
+ * 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.
+ */
+ associated = dns_rdataset_isassociated(rdataset);
+ section = nxrewrite ? DNS_SECTION_ADDITIONAL :
+ DNS_SECTION_AUTHORITY;
+ ttl = UINT32_MAX;
+ if (!nxrewrite && qtype == dns_rdatatype_soa &&
+ zone != NULL && dns_zone_getzeronosoattl(zone))
+ ttl = 0;
+ result = query_addsoa(client, db, version, ttl, associated,
+ section);
+ 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, false,
+ 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:
+ tresult = redirect(client, fname, rdataset, &node,
+ &db, &version, type);
+ if (tresult == ISC_R_SUCCESS) {
+ inc_stats(client,
+ dns_nsstatscounter_nxdomainredirect);
+ break;
+ }
+ if (tresult == DNS_R_NXRRSET) {
+ redirected = true;
+ is_zone = true;
+ goto iszone_nxrrset;
+ }
+ if (tresult == DNS_R_NCACHENXRRSET) {
+ redirected = true;
+ result = tresult;
+ goto ncache_nxrrset;
+ }
+ tresult = redirect2(client, fname, rdataset, &node,
+ &db, &version, type, &is_zone);
+ if (tresult == DNS_R_CONTINUE) {
+ inc_stats(client,
+ dns_nsstatscounter_nxdomainredirect_rlookup);
+ SAVE(client->query.redirect.db, db);
+ SAVE(client->query.redirect.node, node);
+ SAVE(client->query.redirect.zone, zone);
+ client->query.redirect.qtype = qtype;
+ INSIST(rdataset != NULL);
+ SAVE(client->query.redirect.rdataset, rdataset);
+ SAVE(client->query.redirect.sigrdataset, sigrdataset);
+ client->query.redirect.result = DNS_R_NCACHENXDOMAIN;
+ dns_name_copy(fname, client->query.redirect.fname,
+ NULL);
+ client->query.redirect.authoritative = authoritative;
+ client->query.redirect.is_zone = is_zone;
+ goto cleanup;
+ }
+ if (tresult == ISC_R_SUCCESS) {
+ inc_stats(client,
+ dns_nsstatscounter_nxdomainredirect);
+ break;
+ }
+ if (tresult == DNS_R_NXRRSET) {
+ redirected = true;
+ is_zone = true;
+ goto iszone_nxrrset;
+ }
+ if (tresult == DNS_R_NCACHENXRRSET) {
+ redirected = true;
+ result = tresult;
+ goto ncache_nxrrset;
+ }
+ /* FALLTHROUGH */
+
+ case DNS_R_NCACHENXRRSET:
+ ncache_nxrrset:
+ INSIST(!is_zone);
+ authoritative = 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);
+
+#ifdef dns64_bis_return_excluded_addresses
+ if (dns64)
+#else
+ if (dns64 && !dns64_exclude)
+#endif
+ {
+ /*
+ * Restore the answers from the previous AAAA lookup.
+ */
+ if (rdataset != NULL)
+ query_putrdataset(client, &rdataset);
+ if (sigrdataset != NULL)
+ query_putrdataset(client, &sigrdataset);
+ RESTORE(rdataset, client->query.dns64_aaaa);
+ RESTORE(sigrdataset, client->query.dns64_sigaaaa);
+ if (fname == NULL) {
+ dbuf = query_getnamebuf(client);
+ if (dbuf == NULL) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: "
+ "query_getnamebuf failed (4)");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ fname = query_newname(client, dbuf, &b);
+ if (fname == NULL) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: "
+ "query_newname failed (4)");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ }
+ dns_name_copy(client->query.qname, fname, NULL);
+ dns64 = false;
+#ifdef dns64_bis_return_excluded_addresses
+ if (dns64_excluded)
+ break;
+#endif
+ } else if (result == DNS_R_NCACHENXRRSET &&
+ !ISC_LIST_EMPTY(client->view->dns64) &&
+ client->message->rdclass == dns_rdataclass_in &&
+ qtype == dns_rdatatype_aaaa)
+ {
+ /*
+ * Look to see if there are A records for this
+ * name.
+ */
+
+ /*
+ * If the ttl is zero we need to workout if we have just
+ * decremented to zero or if there was no negative cache
+ * ttl in the answer.
+ */
+ if (rdataset->ttl != 0)
+ client->query.dns64_ttl = rdataset->ttl;
+ else if (dns_rdataset_first(rdataset) == ISC_R_SUCCESS)
+ client->query.dns64_ttl = 0;
+ SAVE(client->query.dns64_aaaa, rdataset);
+ SAVE(client->query.dns64_sigaaaa, sigrdataset);
+ query_releasename(client, &fname);
+ dns_db_detachnode(db, &node);
+ type = qtype = dns_rdatatype_a;
+ dns64 = true;
+ goto db_find;
+ }
+
+ /*
+ * We don't call query_addrrset() because we don't need any
+ * of its extra features (and things would probably break!).
+ */
+ if (dns_rdataset_isassociated(rdataset)) {
+ 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:
+ /*
+ * If we have a zero ttl from the cache refetch it.
+ */
+ if (!is_zone && !resuming && rdataset->ttl == 0 &&
+ RECURSIONOK(client))
+ {
+ 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);
+
+ INSIST(!REDIRECT(client));
+ result = query_recurse(client, qtype,
+ client->query.qname,
+ NULL, NULL, resuming);
+ if (result == ISC_R_SUCCESS) {
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ if (dns64)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64;
+ if (dns64_exclude)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64EXCLUDE;
+ } else
+ RECURSE_ERROR(result);
+ goto cleanup;
+ }
+
+ /*
+ * 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 = true;
+ }
+ if (NOQNAME(rdataset) && WANTDNSSEC(client))
+ noqname = rdataset;
+ else
+ noqname = NULL;
+ if (!is_zone && RECURSIONOK(client))
+ query_prefetch(client, fname, rdataset);
+ 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 = 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 = true;
+ }
+ if (!is_zone && RECURSIONOK(client))
+ query_prefetch(client, fname, rdataset);
+ 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_clone(&dname.dname, tname);
+ dns_rdata_freestruct(&dname);
+ /*
+ * Construct the new qname consisting of
+ * <found name prefix>.<dname target>
+ */
+ prefix = dns_fixedname_initname(&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);
+ dns_message_puttempname(client->message, &tname);
+
+ /*
+ * RFC2672, section 4.1, subsection 3c says
+ * we should return YXDOMAIN if the constructed
+ * name would be too long.
+ */
+ if (result == DNS_R_NAMETOOLONG)
+ client->message->rcode = dns_rcode_yxdomain;
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ query_keepname(client, fname, dbuf);
+ /*
+ * Synthesize a CNAME consisting of
+ * <old qname> <dname ttl> CNAME <new qname>
+ * with <dname trust value>
+ *
+ * Synthesize a CNAME so old old clients that don't understand
+ * DNAME can chain.
+ *
+ * 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.
+ */
+ result = query_add_cname(client, client->query.qname, fname,
+ trdataset->trust, trdataset->ttl);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ /*
+ * Switch to the new qname and restart.
+ */
+ ns_client_qnamereplace(client, fname);
+ fname = NULL;
+ want_restart = true;
+ if (!WANTRECURSION(client))
+ options |= DNS_GETDB_NOLOG;
+ goto addauth;
+ default:
+ /*
+ * Something has gone wrong.
+ */
+ snprintf(errmsg, sizeof(errmsg) - 1,
+ "query_find: unexpected error after resuming: %s",
+ isc_result_totext(result));
+ CTRACE(ISC_LOG_ERROR, errmsg);
+ 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 = true;
+ }
+
+#ifdef ALLOW_FILTER_AAAA
+ /*
+ * The filter-aaaa-on-v4 option should suppress AAAAs for IPv4
+ * clients if there is an A; filter-aaaa-on-v6 option does the same
+ * for IPv6 clients.
+ */
+ client->filter_aaaa = dns_aaaa_ok;
+ if (client->view->v4_aaaa != dns_aaaa_ok ||
+ client->view->v6_aaaa != dns_aaaa_ok)
+ {
+ result = ns_client_checkaclsilent(client, NULL,
+ client->view->aaaa_acl,
+ true);
+ if (result == ISC_R_SUCCESS &&
+ client->view->v4_aaaa != dns_aaaa_ok &&
+ is_v4_client(client))
+ client->filter_aaaa = client->view->v4_aaaa;
+ else if (result == ISC_R_SUCCESS &&
+ client->view->v6_aaaa != dns_aaaa_ok &&
+ is_v6_client(client))
+ client->filter_aaaa = client->view->v6_aaaa;
+ }
+
+#endif
+
+ if (type == dns_rdatatype_any) {
+ /*
+ * For minimal-any, we only add records that
+ * match this type or cover this type.
+ */
+ dns_rdatatype_t onetype = 0;
+#ifdef ALLOW_FILTER_AAAA
+ bool have_aaaa, have_a, have_sig;
+
+ /*
+ * If we are not authoritative, assume there is a A
+ * even in if it is not in our cache. This assumption could
+ * be wrong but it is a good bet.
+ */
+ have_aaaa = false;
+ have_a = !authoritative;
+ have_sig = false;
+#endif
+ /*
+ * 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) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: type any; allrdatasets failed");
+ 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);
+#ifdef ALLOW_FILTER_AAAA
+ /*
+ * Notice the presence of A and AAAAs so
+ * that AAAAs can be hidden from IPv4 clients.
+ */
+ if (client->filter_aaaa != dns_aaaa_ok) {
+ if (rdataset->type == dns_rdatatype_aaaa)
+ have_aaaa = true;
+ else if (rdataset->type == dns_rdatatype_a)
+ have_a = true;
+ }
+#endif
+ /*
+ * We found an NS RRset; no need to add one later.
+ */
+ if (qtype == dns_rdatatype_any &&
+ rdataset->type == dns_rdatatype_ns)
+ {
+ answer_has_ns = true;
+ }
+
+ 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 (client->view->minimal_any &&
+ !TCP(client) && !WANTDNSSEC(client) &&
+ qtype == dns_rdatatype_any &&
+ (rdataset->type == dns_rdatatype_sig ||
+ rdataset->type == dns_rdatatype_rrsig)) {
+ CTRACE(ISC_LOG_DEBUG(5), "query_find: "
+ "minimal-any skip signature");
+ dns_rdataset_disassociate(rdataset);
+ } else if (client->view->minimal_any &&
+ !TCP(client) && onetype != 0 &&
+ rdataset->type != onetype &&
+ rdataset->covers != onetype) {
+ CTRACE(ISC_LOG_DEBUG(5), "query_find: "
+ "minimal-any skip rdataset");
+ dns_rdataset_disassociate(rdataset);
+ } else if ((qtype == dns_rdatatype_any ||
+ rdataset->type == qtype) && rdataset->type != 0) {
+#ifdef ALLOW_FILTER_AAAA
+ if (dns_rdatatype_isdnssec(rdataset->type))
+ have_sig = true;
+#endif
+ if (NOQNAME(rdataset) && WANTDNSSEC(client))
+ noqname = rdataset;
+ else
+ noqname = NULL;
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL)
+ rdataset->ttl = ISC_MIN(rdataset->ttl,
+ rpz_st->m.ttl);
+ if (!is_zone && RECURSIONOK(client)) {
+ dns_name_t *name;
+ name = (fname != NULL) ? fname : tname;
+ query_prefetch(client, name, rdataset);
+ }
+ /*
+ * Remember the first RRtype we find so we
+ * can skip others with minimal-any.
+ */
+ if (rdataset->type == dns_rdatatype_sig ||
+ rdataset->type == dns_rdatatype_rrsig)
+ onetype = rdataset->covers;
+ else
+ onetype = rdataset->type;
+ 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);
+ }
+
+#ifdef ALLOW_FILTER_AAAA
+ /*
+ * Filter AAAAs if there is an A and there is no signature
+ * or we are supposed to break DNSSEC.
+ */
+ if (client->filter_aaaa == dns_aaaa_break_dnssec)
+ client->attributes |= NS_CLIENTATTR_FILTER_AAAA;
+ else if (client->filter_aaaa != dns_aaaa_ok &&
+ have_aaaa && have_a &&
+ (!have_sig || !WANTDNSSEC(client)))
+ client->attributes |= NS_CLIENTATTR_FILTER_AAAA;
+#endif
+ if (fname != NULL)
+ dns_message_puttempname(client->message, &fname);
+
+ if (n == 0) {
+ /*
+ * No matching rdatasets found in cache. If we were
+ * searching for RRSIG/SIG, that's probably okay;
+ * otherwise this is an error condition.
+ */
+ if ((qtype == dns_rdatatype_rrsig ||
+ qtype == dns_rdatatype_sig) &&
+ result == ISC_R_NOMORE) {
+ if (!is_zone) {
+ authoritative = false;
+ dns_rdatasetiter_destroy(&rdsiter);
+ client->attributes &= ~NS_CLIENTATTR_RA;
+ goto addauth;
+ }
+
+ if (qtype == dns_rdatatype_rrsig &&
+ dns_db_issecure(db)) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(client->query.qname,
+ namebuf,
+ sizeof(namebuf));
+ ns_client_log(client,
+ DNS_LOGCATEGORY_DNSSEC,
+ NS_LOGMODULE_QUERY,
+ ISC_LOG_WARNING,
+ "missing signature "
+ "for %s", namebuf);
+ }
+
+ dns_rdatasetiter_destroy(&rdsiter);
+ fname = query_newname(client, dbuf, &b);
+ goto nxrrset_rrsig;
+ } else {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: no matching rdatasets "
+ "in cache");
+ result = DNS_R_SERVFAIL;
+ }
+ }
+
+ dns_rdatasetiter_destroy(&rdsiter);
+ if (result != ISC_R_NOMORE) {
+ CTRACE(ISC_LOG_ERROR,
+ "query_find: dns_rdatasetiter_destroy failed");
+ QUERY_ERROR(DNS_R_SERVFAIL);
+ goto cleanup;
+ }
+ } else {
+ /*
+ * This is the "normal" case -- an ordinary question to which
+ * we know the answer.
+ */
+
+ /*
+ * If we have a zero ttl from the cache refetch it.
+ */
+ if (!is_zone && !resuming && rdataset->ttl == 0 &&
+ RECURSIONOK(client))
+ {
+ 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);
+
+ INSIST(!REDIRECT(client));
+ result = query_recurse(client, qtype,
+ client->query.qname,
+ NULL, NULL, resuming);
+ if (result == ISC_R_SUCCESS) {
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ if (dns64)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64;
+ if (dns64_exclude)
+ client->query.attributes |=
+ NS_QUERYATTR_DNS64EXCLUDE;
+ } else
+ RECURSE_ERROR(result);
+ goto cleanup;
+ }
+
+ /*
+ * Check to see if the AAAA RRset has non-excluded addresses
+ * in it. If not look for a A RRset.
+ *
+ * Note: the order of dns64_aaaaok() and filter_aaaa check is
+ * important. Both result is fetches being called but the
+ * dns64 case goes to db_find while the filter_aaaa case
+ * adds the records now for later potential exclusion.
+ */
+ INSIST(client->query.dns64_aaaaok == NULL);
+
+ if (qtype == dns_rdatatype_aaaa && !dns64_exclude &&
+ !ISC_LIST_EMPTY(client->view->dns64) &&
+ client->message->rdclass == dns_rdataclass_in &&
+ !dns64_aaaaok(client, rdataset, sigrdataset)) {
+ /*
+ * Look to see if there are A records for this
+ * name.
+ */
+ client->query.dns64_ttl = rdataset->ttl;
+ SAVE(client->query.dns64_aaaa, rdataset);
+ SAVE(client->query.dns64_sigaaaa, sigrdataset);
+ query_releasename(client, &fname);
+ dns_db_detachnode(db, &node);
+ type = qtype = dns_rdatatype_a;
+ dns64_exclude = dns64 = true;
+ goto db_find;
+ }
+
+#ifdef ALLOW_FILTER_AAAA
+ /*
+ * Optionally hide AAAAs from IPv4 clients if there is an A.
+ * We add the AAAAs now, but might refuse to render them later
+ * after DNSSEC is figured out.
+ * This could be more efficient, but the whole idea is
+ * so fundamentally wrong, unavoidably inaccurate, and
+ * unneeded that it is best to keep it as short as possible.
+ */
+ if (client->filter_aaaa == dns_aaaa_break_dnssec ||
+ (client->filter_aaaa == dns_aaaa_filter &&
+ (!WANTDNSSEC(client) || sigrdataset == NULL ||
+ !dns_rdataset_isassociated(sigrdataset))))
+ {
+ if (qtype == dns_rdatatype_aaaa) {
+ trdataset = query_newrdataset(client);
+ result = dns_db_findrdataset(db, node, version,
+ dns_rdatatype_a, 0,
+ client->now,
+ trdataset, NULL);
+ if (dns_rdataset_isassociated(trdataset))
+ dns_rdataset_disassociate(trdataset);
+ query_putrdataset(client, &trdataset);
+
+ /*
+ * We have an AAAA but the A is not in our cache.
+ * Assume any result other than DNS_R_DELEGATION
+ * or ISC_R_NOTFOUND means there is no A and
+ * so AAAAs are ok.
+ * Assume there is no A if we can't recurse
+ * for this client, although that could be
+ * the wrong answer. What else can we do?
+ * Besides, that we have the AAAA and are using
+ * this mechanism suggests that we care more
+ * about As than AAAAs and would have cached
+ * the A if it existed.
+ */
+ if (result == ISC_R_SUCCESS) {
+ client->attributes |=
+ NS_CLIENTATTR_FILTER_AAAA;
+
+ } else if (authoritative ||
+ !RECURSIONOK(client) ||
+ (result != DNS_R_DELEGATION &&
+ result != ISC_R_NOTFOUND)) {
+ client->attributes &=
+ ~NS_CLIENTATTR_FILTER_AAAA;
+ } else {
+ /*
+ * This is an ugly kludge to recurse
+ * for the A and discard the result.
+ *
+ * Continue to add the AAAA now.
+ * We'll make a note to not render it
+ * if the recursion for the A succeeds.
+ */
+ INSIST(!REDIRECT(client));
+ result = query_recurse(client,
+ dns_rdatatype_a,
+ client->query.qname,
+ NULL, NULL, resuming);
+ if (result == ISC_R_SUCCESS) {
+ client->attributes |=
+ NS_CLIENTATTR_FILTER_AAAA_RC;
+ client->query.attributes |=
+ NS_QUERYATTR_RECURSING;
+ }
+ }
+
+ } else if (qtype == dns_rdatatype_a &&
+ (client->attributes &
+ NS_CLIENTATTR_FILTER_AAAA_RC) != 0) {
+ client->attributes &=
+ ~NS_CLIENTATTR_FILTER_AAAA_RC;
+ client->attributes |=
+ NS_CLIENTATTR_FILTER_AAAA;
+ dns_rdataset_disassociate(rdataset);
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
+ goto cleanup;
+ }
+ }
+#endif
+
+ if (sigrdataset != NULL)
+ sigrdatasetp = &sigrdataset;
+ else
+ sigrdatasetp = NULL;
+ if (NOQNAME(rdataset) && WANTDNSSEC(client))
+ noqname = rdataset;
+ else
+ noqname = NULL;
+ /*
+ * Special case NS handling
+ */
+ if (is_zone && qtype == dns_rdatatype_ns) {
+ /*
+ * We've already got an NS, no need to add one in
+ * the authority section
+ */
+ if (dns_name_equal(client->query.qname,
+ dns_db_origin(db)))
+ {
+ answer_has_ns = true;
+ }
+
+ /*
+ * BIND 8 priming queries need the additional section.
+ */
+ if (dns_name_equal(client->query.qname, dns_rootname)) {
+ client->query.attributes &=
+ ~NS_QUERYATTR_NOADDITIONAL;
+ }
+ }
+
+ /*
+ * Return the time to expire for slave and master zones.
+ */
+ if (zone != NULL && is_zone && qtype == dns_rdatatype_soa &&
+ (client->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0 &&
+ client->query.restarts == 0) {
+ dns_zone_t *raw = NULL, *mayberaw;
+
+ dns_zone_getraw(zone, &raw);
+ mayberaw = (raw != NULL) ? raw : zone;
+
+ if (dns_zone_gettype(mayberaw) == dns_zone_slave) {
+ isc_time_t expiretime;
+ uint32_t secs;
+ dns_zone_getexpiretime(zone, &expiretime);
+ secs = isc_time_seconds(&expiretime);
+ if (secs >= client->now &&
+ result == ISC_R_SUCCESS) {
+ client->attributes |=
+ NS_CLIENTATTR_HAVEEXPIRE;
+ client->expire = secs - client->now;
+ }
+ }
+ if (dns_zone_gettype(mayberaw) == dns_zone_master) {
+ dns_rdata_soa_t soa;
+ result = dns_rdataset_first(rdataset);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_rdataset_current(rdataset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &soa, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ client->expire = soa.expire;
+ client->attributes |= NS_CLIENTATTR_HAVEEXPIRE;
+ }
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ }
+
+ if (dns64) {
+ qtype = type = dns_rdatatype_aaaa;
+ result = query_dns64(client, &fname, rdataset,
+ sigrdataset, dbuf,
+ DNS_SECTION_ANSWER);
+ noqname = NULL;
+ dns_rdataset_disassociate(rdataset);
+ dns_message_puttemprdataset(client->message, &rdataset);
+ if (result == ISC_R_NOMORE) {
+#ifndef dns64_bis_return_excluded_addresses
+ if (dns64_exclude) {
+ if (!is_zone)
+ goto cleanup;
+ /*
+ * Add a fake SOA record.
+ */
+ (void)query_addsoa(client, db, version,
+ 600, false,
+ DNS_SECTION_AUTHORITY);
+ goto cleanup;
+ }
+#endif
+ if (is_zone)
+ goto iszone_nxrrset;
+ else
+ goto ncache_nxrrset;
+ } else if (result != ISC_R_SUCCESS) {
+ eresult = result;
+ goto cleanup;
+ }
+ } else if (client->query.dns64_aaaaok != NULL) {
+ query_filter64(client, &fname, rdataset, dbuf,
+ DNS_SECTION_ANSWER);
+ query_putrdataset(client, &rdataset);
+ } else {
+ if (!is_zone && RECURSIONOK(client))
+ query_prefetch(client, fname, rdataset);
+ 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(ISC_LOG_DEBUG(3), "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 (!answer_has_ns) {
+ (void)query_addns(client, db, version);
+ }
+ } else if (!answer_has_ns && 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),
+ true, false);
+ cleanup:
+ CTRACE(ISC_LOG_DEBUG(3), "query_find: cleanup");
+ /*
+ * General cleanup.
+ */
+ rpz_st = client->query.rpz_st;
+ if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) == 0) {
+ rpz_match_clear(rpz_st);
+ rpz_st->state &= ~DNS_RPZ_DONE_QNAME;
+ }
+ 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) {
+ free_devent(client, ISC_EVENT_PTR(&event), &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)
+ || eresult == DNS_R_DROP)) {
+ if (eresult == DNS_R_DUPLICATE || eresult == DNS_R_DROP) {
+ /*
+ * This was a duplicate query that we are
+ * recursing on or the result of rate limiting.
+ * Don't send a response now for a duplicate query,
+ * because the original 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.
+ */
+ INSIST(line >= 0);
+ query_error(client, eresult, line);
+ }
+ 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 (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER]) &&
+ 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 == true)
+ client->message->flags |= DNS_MESSAGEFLAG_AA;
+
+ /*
+ * If the response is somehow unexpected for the client and this
+ * is a result of recursion, return an error to the caller
+ * to indicate it may need to be logged.
+ */
+ if (resuming &&
+ (ISC_LIST_EMPTY(client->message->sections[DNS_SECTION_ANSWER]) ||
+ client->message->rcode != dns_rcode_noerror))
+ eresult = ISC_R_FAILURE;
+
+ query_send(client);
+ ns_client_detach(&client);
+ }
+ CTRACE(ISC_LOG_DEBUG(3), "query_find: done");
+
+ return (eresult);
+}
+
+static inline void
+log_tat(ns_client_t *client) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char clientbuf[ISC_NETADDR_FORMATSIZE];
+ char classname[DNS_RDATACLASS_FORMATSIZE];
+ isc_netaddr_t netaddr;
+ char *tags = NULL;
+ size_t taglen = 0;
+
+ if (!isc_log_wouldlog(ns_g_lctx, ISC_LOG_INFO)) {
+ return;
+ }
+
+ if ((client->query.qtype != dns_rdatatype_null ||
+ !dns_name_istat(client->query.qname)) &&
+ (client->keytag == NULL ||
+ client->query.qtype != dns_rdatatype_dnskey))
+ {
+ return;
+ }
+
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+ dns_name_format(client->query.qname, namebuf, sizeof(namebuf));
+ isc_netaddr_format(&netaddr, clientbuf, sizeof(clientbuf));
+ dns_rdataclass_format(client->view->rdclass, classname,
+ sizeof(classname));
+
+ if (client->query.qtype == dns_rdatatype_dnskey) {
+ uint16_t keytags = client->keytag_len / 2;
+ size_t len = taglen = sizeof("65000") * keytags + 1;
+ char *cp = tags = isc_mem_get(client->mctx, taglen);
+ int i = 0;
+
+ INSIST(client->keytag != NULL);
+ if (tags != NULL) {
+ while (keytags-- > 0U) {
+ int n;
+ uint16_t keytag;
+ keytag = (client->keytag[i * 2] << 8) |
+ client->keytag[i * 2 + 1];
+ n = snprintf(cp, len, " %u", keytag);
+ if (n > 0 && (size_t)n <= len) {
+ cp += n;
+ len -= n;
+ i++;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_TAT, NS_LOGMODULE_QUERY,
+ ISC_LOG_INFO, "trust-anchor-telemetry '%s/%s' from %s%s",
+ namebuf, classname, clientbuf, tags != NULL? tags : "");
+ if (tags != NULL) {
+ isc_mem_put(client->mctx, tags, taglen);
+ }
+}
+
+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];
+ char onbuf[ISC_NETADDR_FORMATSIZE];
+ char ednsbuf[sizeof("E(65535)")] = { 0 };
+ 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));
+ isc_netaddr_format(&client->destaddr, onbuf, sizeof(onbuf));
+
+ if (client->ednsversion >= 0)
+ snprintf(ednsbuf, sizeof(ednsbuf), "E(%hd)",
+ client->ednsversion);
+
+ ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY,
+ level, "query: %s %s %s %s%s%s%s%s%s%s (%s)", namebuf,
+ classname, typename, WANTRECURSION(client) ? "+" : "-",
+ (client->signer != NULL) ? "S" : "", ednsbuf,
+ TCP(client) ? "T" : "",
+ ((extflags & DNS_MESSAGEEXTFLAG_DO) != 0) ? "D" : "",
+ ((flags & DNS_MESSAGEFLAG_CD) != 0) ? "C" : "",
+ HAVECOOKIE(client) ? "V" : WANTCOOKIE(client) ? "K" : "",
+ onbuf);
+}
+
+static inline void
+log_queryerror(ns_client_t *client, isc_result_t result, int line, int level) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typename[DNS_RDATATYPE_FORMATSIZE];
+ char classname[DNS_RDATACLASS_FORMATSIZE];
+ const char *namep, *typep, *classp, *sep1, *sep2;
+ dns_rdataset_t *rdataset;
+
+ if (!isc_log_wouldlog(ns_g_lctx, level))
+ return;
+
+ namep = typep = classp = sep1 = sep2 = "";
+
+ /*
+ * Query errors can happen for various reasons. In some cases we cannot
+ * even assume the query contains a valid question section, so we should
+ * expect exceptional cases.
+ */
+ if (client->query.origqname != NULL) {
+ dns_name_format(client->query.origqname, namebuf,
+ sizeof(namebuf));
+ namep = namebuf;
+ sep1 = " for ";
+
+ rdataset = ISC_LIST_HEAD(client->query.origqname->list);
+ if (rdataset != NULL) {
+ dns_rdataclass_format(rdataset->rdclass, classname,
+ sizeof(classname));
+ classp = classname;
+ dns_rdatatype_format(rdataset->type, typename,
+ sizeof(typename));
+ typep = typename;
+ sep2 = "/";
+ }
+ }
+
+ ns_client_log(client, NS_LOGCATEGORY_QUERY_ERRORS, NS_LOGMODULE_QUERY,
+ level, "query failed (%s)%s%s%s%s%s%s at %s:%d",
+ isc_result_totext(result), sep1, namep, sep2,
+ classp, sep2, typep, __FILE__, line);
+}
+
+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;
+
+ REQUIRE(NS_CLIENT_VALID(client));
+
+ CTRACE(ISC_LOG_DEBUG(3), "ns_query_start");
+
+ /*
+ * Test only.
+ */
+ if (ns_g_clienttest && !TCP(client)) {
+ result = ns_client_replace(client);
+ if (result == ISC_R_SHUTTINGDOWN) {
+ ns_client_next(client, result);
+ return;
+ } else if (result != ISC_R_SUCCESS) {
+ query_error(client, result, __LINE__);
+ return;
+ }
+ }
+
+ /*
+ * 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 ((message->flags & DNS_MESSAGEFLAG_RD) != 0)
+ client->query.attributes |= NS_QUERYATTR_WANTRECURSION;
+
+ if ((client->extflags & DNS_MESSAGEEXTFLAG_DO) != 0)
+ client->attributes |= NS_CLIENTATTR_WANTDNSSEC;
+
+ switch (client->view->minimalresponses) {
+ case dns_minimal_no:
+ break;
+ case dns_minimal_yes:
+ client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
+ NS_QUERYATTR_NOADDITIONAL);
+ break;
+ case dns_minimal_noauth:
+ client->query.attributes |= NS_QUERYATTR_NOAUTHORITY;
+ break;
+ case dns_minimal_noauthrec:
+ if ((message->flags & DNS_MESSAGEFLAG_RD) != 0)
+ client->query.attributes |= NS_QUERYATTR_NOAUTHORITY;
+ break;
+ }
+
+ 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);
+ client->attributes |= NS_CLIENTATTR_NOSETFC;
+ } 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;
+ client->attributes |= NS_CLIENTATTR_NOSETFC;
+ }
+
+ /*
+ * Check for multiple question queries, since edns1 is dead.
+ */
+ if (message->counts[DNS_SECTION_QUESTION] > 1) {
+ query_error(client, DNS_R_FORMERR, __LINE__);
+ return;
+ }
+
+ /*
+ * Get the question name.
+ */
+ result = dns_message_firstname(message, DNS_SECTION_QUESTION);
+ if (result != ISC_R_SUCCESS) {
+ query_error(client, result, __LINE__);
+ 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, __LINE__);
+ } else
+ query_error(client, result, __LINE__);
+ return;
+ }
+
+ if (ns_g_server->log_queries)
+ log_query(client, saved_flags, saved_extflags);
+
+ /*
+ * Check for meta-queries like IXFR and AXFR.
+ */
+ rdataset = ISC_LIST_HEAD(client->query.qname->list);
+ INSIST(rdataset != NULL);
+ client->query.qtype = qtype = rdataset->type;
+ dns_rdatatypestats_increment(ns_g_server->rcvquerystats, qtype);
+
+ log_tat(client);
+
+ 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, __LINE__);
+ 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, __LINE__);
+ return;
+ default: /* TSIG, etc. */
+ query_error(client, DNS_R_FORMERR, __LINE__);
+ return;
+ }
+ }
+
+ /*
+ * Turn on minimal response for (C)DNSKEY and (C)DS queries.
+ */
+ if (qtype == dns_rdatatype_dnskey || qtype == dns_rdatatype_ds ||
+ qtype == dns_rdatatype_cdnskey || qtype == dns_rdatatype_cds)
+ {
+ client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
+ NS_QUERYATTR_NOADDITIONAL);
+ }
+
+ /*
+ * Maybe turn on minimal responses for ANY queries.
+ */
+ if (qtype == dns_rdatatype_any &&
+ client->view->minimal_any && !TCP(client))
+ client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
+ NS_QUERYATTR_NOADDITIONAL);
+
+ /*
+ * Turn on minimal responses for EDNS/UDP bufsize 512 queries.
+ */
+ if (client->ednsversion >= 0 && client->udpsize <= 512U && !TCP(client))
+ 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 NS_CLIENTATTR_WANTAD 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)
+ client->attributes |= NS_CLIENTATTR_WANTAD;
+
+ /*
+ * This is an ordinary query.
+ */
+ result = dns_message_reply(message, true);
+ if (result != ISC_R_SUCCESS) {
+ query_next(client, result);
+ return;
+ }
+
+ /*
+ * Assume authoritative response until it is known to be
+ * otherwise.
+ *
+ * If "-T noaa" has been set on the command line don't set
+ * AA on authoritative answers.
+ */
+ if (!ns_g_noaa)
+ message->flags |= DNS_MESSAGEFLAG_AA;
+
+ /*
+ * Set AD. We must clear it if we add non-validated data to a
+ * response.
+ */
+ if (WANTDNSSEC(client) || WANTAD(client))
+ message->flags |= DNS_MESSAGEFLAG_AD;
+
+ qclient = NULL;
+ ns_client_attach(client, &qclient);
+ (void)query_find(qclient, NULL, qtype);
+}
diff --git a/bin/named/server.c b/bin/named/server.c
new file mode 100644
index 0000000..7f87ccf
--- /dev/null
+++ b/bin/named/server.c
@@ -0,0 +1,14277 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <isc/aes.h>
+#include <isc/app.h>
+#include <isc/base64.h>
+#include <isc/commandline.h>
+#include <isc/dir.h>
+#include <isc/entropy.h>
+#include <isc/file.h>
+#include <isc/hash.h>
+#include <isc/hex.h>
+#include <isc/hmacsha.h>
+#include <isc/httpd.h>
+#include <isc/lex.h>
+#include <isc/meminfo.h>
+#include <isc/parseint.h>
+#include <isc/portset.h>
+#include <isc/print.h>
+#include <isc/random.h>
+#include <isc/refcount.h>
+#include <isc/resource.h>
+#include <isc/sha2.h>
+#include <isc/socket.h>
+#include <isc/stat.h>
+#include <isc/stats.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/grammar.h>
+#include <isccfg/namedconf.h>
+
+#include <bind9/check.h>
+
+#include <dns/acache.h>
+#include <dns/adb.h>
+#include <dns/badcache.h>
+#include <dns/cache.h>
+#include <dns/catz.h>
+#include <dns/db.h>
+#include <dns/dispatch.h>
+#include <dns/dlz.h>
+#include <dns/dns64.h>
+#include <dns/dyndb.h>
+#include <dns/events.h>
+#include <dns/forward.h>
+#include <dns/fixedname.h>
+#include <dns/journal.h>
+#include <dns/keytable.h>
+#include <dns/keyvalues.h>
+#include <dns/lib.h>
+#include <dns/master.h>
+#include <dns/masterdump.h>
+#include <dns/nta.h>
+#include <dns/order.h>
+#include <dns/peer.h>
+#include <dns/portlist.h>
+#include <dns/private.h>
+#include <dns/rbt.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/resolver.h>
+#include <dns/rootns.h>
+#include <dns/rriterator.h>
+#include <dns/secalg.h>
+#include <dns/soa.h>
+#include <dns/stats.h>
+#include <dns/tkey.h>
+#include <dns/tsig.h>
+#include <dns/ttl.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>
+#ifdef HAVE_GEOIP
+#include <named/geoip.h>
+#endif /* HAVE_GEOIP */
+#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
+
+#ifdef HAVE_LMDB
+#include <lmdb.h>
+#define count_newzones count_newzones_db
+#define configure_newzones configure_newzones_db
+#define dumpzone dumpzone_db
+#else /* HAVE_LMDB */
+#define count_newzones count_newzones_file
+#define configure_newzones configure_newzones_file
+#define dumpzone dumpzone_file
+#endif /* HAVE_LMDB */
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)-1)
+#endif
+
+#ifndef SIZE_AS_PERCENT
+#define SIZE_AS_PERCENT ((size_t)-2)
+#endif
+
+#ifdef TUNE_LARGE
+#define RESOLVER_NTASKS 523
+#define UDPBUFFERS 32768
+#define EXCLBUFFERS 32768
+#else
+#define RESOLVER_NTASKS 31
+#define UDPBUFFERS 1000
+#define EXCLBUFFERS 4096
+#endif /* TUNE_LARGE */
+
+/*%
+ * 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 TCHECK(op) \
+ do { tresult = (op); \
+ if (tresult != ISC_R_SUCCESS) { \
+ isc_buffer_clear(*text); \
+ 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) \
+
+/*%
+ * Maximum ADB size for views that share a cache. Use this limit to suppress
+ * the total of memory footprint, which should be the main reason for sharing
+ * a cache. Only effective when a finite max-cache-size is specified.
+ * This is currently defined to be 8MB.
+ */
+#define MAX_ADB_SIZE_FOR_CACHESHARE 8388608U
+
+struct ns_dispatch {
+ isc_sockaddr_t addr;
+ unsigned int dispatchgen;
+ dns_dispatch_t *dispatch;
+ ISC_LINK(struct ns_dispatch) link;
+};
+
+struct ns_cache {
+ dns_cache_t *cache;
+ dns_view_t *primaryview;
+ bool needflush;
+ bool adbsizeadjusted;
+ dns_rdataclass_t rdclass;
+ ISC_LINK(ns_cache_t) link;
+};
+
+struct dumpcontext {
+ isc_mem_t *mctx;
+ bool dumpcache;
+ bool dumpzones;
+ bool dumpadb;
+ bool dumpbad;
+ bool dumpfail;
+ 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;
+};
+
+/*%
+ * Configuration context to retain for each view that allows
+ * new zones to be added at runtime.
+ */
+typedef struct ns_cfgctx {
+ isc_mem_t * mctx;
+ cfg_parser_t * conf_parser;
+ cfg_parser_t * add_parser;
+ cfg_obj_t * config;
+ cfg_obj_t * vconfig;
+ cfg_obj_t * nzf_config;
+ cfg_aclconfctx_t * actx;
+} ns_cfgctx_t;
+
+/*%
+ * A function to write out added-zone configuration to the new_zone_file
+ * specified in 'view'. Maybe called by delete_zoneconf().
+ */
+typedef isc_result_t (*nzfwriter_t)(const cfg_obj_t *config, dns_view_t *view);
+
+/*%
+ * Holds state information for the initial zone loading process.
+ * Uses the isc_refcount structure to count the number of views
+ * with pending zone loads, dereferencing as each view finishes.
+ */
+typedef struct {
+ ns_server_t *server;
+ bool reconfig;
+ isc_refcount_t refs;
+} ns_zoneload_t;
+
+typedef struct {
+ ns_server_t *server;
+} catz_cb_data_t;
+
+typedef struct catz_chgzone_event {
+ ISC_EVENT_COMMON(struct catz_chgzone_event);
+ dns_catz_entry_t *entry;
+ dns_catz_zone_t *origin;
+ dns_view_t *view;
+ catz_cb_data_t *cbd;
+ bool mod;
+} catz_chgzone_event_t;
+
+/*
+ * These zones should not leak onto the Internet.
+ */
+const char *empty_zones[] = {
+ /* RFC 1918 */
+ "10.IN-ADDR.ARPA",
+ "16.172.IN-ADDR.ARPA",
+ "17.172.IN-ADDR.ARPA",
+ "18.172.IN-ADDR.ARPA",
+ "19.172.IN-ADDR.ARPA",
+ "20.172.IN-ADDR.ARPA",
+ "21.172.IN-ADDR.ARPA",
+ "22.172.IN-ADDR.ARPA",
+ "23.172.IN-ADDR.ARPA",
+ "24.172.IN-ADDR.ARPA",
+ "25.172.IN-ADDR.ARPA",
+ "26.172.IN-ADDR.ARPA",
+ "27.172.IN-ADDR.ARPA",
+ "28.172.IN-ADDR.ARPA",
+ "29.172.IN-ADDR.ARPA",
+ "30.172.IN-ADDR.ARPA",
+ "31.172.IN-ADDR.ARPA",
+ "168.192.IN-ADDR.ARPA",
+
+ /* RFC 6598 */
+ "64.100.IN-ADDR.ARPA",
+ "65.100.IN-ADDR.ARPA",
+ "66.100.IN-ADDR.ARPA",
+ "67.100.IN-ADDR.ARPA",
+ "68.100.IN-ADDR.ARPA",
+ "69.100.IN-ADDR.ARPA",
+ "70.100.IN-ADDR.ARPA",
+ "71.100.IN-ADDR.ARPA",
+ "72.100.IN-ADDR.ARPA",
+ "73.100.IN-ADDR.ARPA",
+ "74.100.IN-ADDR.ARPA",
+ "75.100.IN-ADDR.ARPA",
+ "76.100.IN-ADDR.ARPA",
+ "77.100.IN-ADDR.ARPA",
+ "78.100.IN-ADDR.ARPA",
+ "79.100.IN-ADDR.ARPA",
+ "80.100.IN-ADDR.ARPA",
+ "81.100.IN-ADDR.ARPA",
+ "82.100.IN-ADDR.ARPA",
+ "83.100.IN-ADDR.ARPA",
+ "84.100.IN-ADDR.ARPA",
+ "85.100.IN-ADDR.ARPA",
+ "86.100.IN-ADDR.ARPA",
+ "87.100.IN-ADDR.ARPA",
+ "88.100.IN-ADDR.ARPA",
+ "89.100.IN-ADDR.ARPA",
+ "90.100.IN-ADDR.ARPA",
+ "91.100.IN-ADDR.ARPA",
+ "92.100.IN-ADDR.ARPA",
+ "93.100.IN-ADDR.ARPA",
+ "94.100.IN-ADDR.ARPA",
+ "95.100.IN-ADDR.ARPA",
+ "96.100.IN-ADDR.ARPA",
+ "97.100.IN-ADDR.ARPA",
+ "98.100.IN-ADDR.ARPA",
+ "99.100.IN-ADDR.ARPA",
+ "100.100.IN-ADDR.ARPA",
+ "101.100.IN-ADDR.ARPA",
+ "102.100.IN-ADDR.ARPA",
+ "103.100.IN-ADDR.ARPA",
+ "104.100.IN-ADDR.ARPA",
+ "105.100.IN-ADDR.ARPA",
+ "106.100.IN-ADDR.ARPA",
+ "107.100.IN-ADDR.ARPA",
+ "108.100.IN-ADDR.ARPA",
+ "109.100.IN-ADDR.ARPA",
+ "110.100.IN-ADDR.ARPA",
+ "111.100.IN-ADDR.ARPA",
+ "112.100.IN-ADDR.ARPA",
+ "113.100.IN-ADDR.ARPA",
+ "114.100.IN-ADDR.ARPA",
+ "115.100.IN-ADDR.ARPA",
+ "116.100.IN-ADDR.ARPA",
+ "117.100.IN-ADDR.ARPA",
+ "118.100.IN-ADDR.ARPA",
+ "119.100.IN-ADDR.ARPA",
+ "120.100.IN-ADDR.ARPA",
+ "121.100.IN-ADDR.ARPA",
+ "122.100.IN-ADDR.ARPA",
+ "123.100.IN-ADDR.ARPA",
+ "124.100.IN-ADDR.ARPA",
+ "125.100.IN-ADDR.ARPA",
+ "126.100.IN-ADDR.ARPA",
+ "127.100.IN-ADDR.ARPA",
+
+ /* RFC 5735 and RFC 5737 */
+ "0.IN-ADDR.ARPA", /* THIS NETWORK */
+ "127.IN-ADDR.ARPA", /* LOOPBACK */
+ "254.169.IN-ADDR.ARPA", /* LINK LOCAL */
+ "2.0.192.IN-ADDR.ARPA", /* TEST NET */
+ "100.51.198.IN-ADDR.ARPA", /* TEST NET 2 */
+ "113.0.203.IN-ADDR.ARPA", /* TEST NET 3 */
+ "255.255.255.255.IN-ADDR.ARPA", /* 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",
+ "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",
+ /* LOCALLY ASSIGNED LOCAL ADDRESS SCOPE */
+ "D.F.IP6.ARPA",
+ "8.E.F.IP6.ARPA", /* LINK LOCAL */
+ "9.E.F.IP6.ARPA", /* LINK LOCAL */
+ "A.E.F.IP6.ARPA", /* LINK LOCAL */
+ "B.E.F.IP6.ARPA", /* LINK LOCAL */
+
+ /* Example Prefix, RFC 3849. */
+ "8.B.D.0.1.0.0.2.IP6.ARPA",
+
+ /* RFC 7534 */
+ "EMPTY.AS112.ARPA",
+
+ /* RFC 8375 */
+ "HOME.ARPA",
+
+ NULL
+};
+
+ISC_PLATFORM_NORETURN_PRE static void
+fatal(const char *msg, isc_result_t result) ISC_PLATFORM_NORETURN_POST;
+
+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,
+ uint16_t family, 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,
+ uint16_t family, 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,
+ dns_viewlist_t *viewlist, cfg_aclconfctx_t *aclconf,
+ bool added, bool old_rpz_ok,
+ bool modify);
+
+static isc_result_t
+configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
+ isc_mem_t *mctx, cfg_aclconfctx_t *actx);
+
+static isc_result_t
+add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx);
+
+static void
+end_reserved_dispatches(ns_server_t *server, bool all);
+
+static void
+newzone_cfgctx_destroy(void **cfgp);
+
+static inline isc_result_t
+putstr(isc_buffer_t **b, const char *str);
+
+static isc_result_t
+putmem(isc_buffer_t **b, const char *str, size_t len);
+
+static isc_result_t
+putuint8(isc_buffer_t **b, uint8_t val);
+
+static inline isc_result_t
+putnull(isc_buffer_t **b);
+
+static int
+count_zones(const cfg_obj_t *conf);
+
+#ifdef HAVE_LMDB
+static isc_result_t
+migrate_nzf(dns_view_t *view);
+
+static isc_result_t
+nzd_writable(dns_view_t *view);
+
+static isc_result_t
+nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi);
+
+static isc_result_t
+nzd_env_reopen(dns_view_t *view);
+
+static void
+nzd_env_close(dns_view_t *view);
+
+static isc_result_t
+nzd_close(MDB_txn **txnp, bool commit);
+
+static isc_result_t
+nzd_count(dns_view_t *view, int *countp);
+#else
+static isc_result_t
+nzf_append(dns_view_t *view, const cfg_obj_t *zconfig);
+#endif
+
+/*%
+ * Configure a single view ACL at '*aclp'. Get its configuration from
+ * 'vconfig' (for per-view configuration) and maybe from 'config'
+ */
+static isc_result_t
+configure_view_acl(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ const cfg_obj_t *gconfig, const char *aclname,
+ const char *acltuplename, cfg_aclconfctx_t *actx,
+ isc_mem_t *mctx, dns_acl_t **aclp)
+{
+ isc_result_t result;
+ const cfg_obj_t *maps[4];
+ 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;
+ }
+ }
+ if (gconfig != NULL) {
+ const cfg_obj_t *options = NULL;
+ (void)cfg_map_get(gconfig, "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);
+ }
+
+ if (acltuplename != NULL) {
+ /*
+ * If the ACL is given in an optional tuple, retrieve it.
+ * The parser should have ensured that a valid object be
+ * returned.
+ */
+ aclobj = cfg_tuple_get(aclobj, acltuplename);
+ }
+
+ 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_nametable(const cfg_obj_t *vconfig, const cfg_obj_t *config,
+ const char *confname, const char *conftuplename,
+ isc_mem_t *mctx, dns_rbt_t **rbtp)
+{
+ isc_result_t result;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *obj = NULL;
+ const cfg_listelt_t *element;
+ int i = 0;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+ const char *str;
+ const cfg_obj_t *nameobj;
+
+ if (*rbtp != NULL)
+ dns_rbt_destroy(rbtp);
+ 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, confname, &obj);
+ if (obj == NULL)
+ /*
+ * No value available. *rbtp == NULL.
+ */
+ return (ISC_R_SUCCESS);
+
+ if (conftuplename != NULL) {
+ obj = cfg_tuple_get(obj, conftuplename);
+ if (cfg_obj_isvoid(obj))
+ return (ISC_R_SUCCESS);
+ }
+
+ result = dns_rbt_create(mctx, NULL, NULL, rbtp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ name = dns_fixedname_initname(&fixed);
+ for (element = cfg_list_first(obj);
+ element != NULL;
+ element = cfg_list_next(element)) {
+ nameobj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(nameobj);
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+ /*
+ * We don't need the node data, but need to set dummy data to
+ * avoid a partial match with an empty node. For example, if
+ * we have foo.example.com and bar.example.com, we'd get a match
+ * for baz.example.com, which is not the expected result.
+ * We simply use (void *)1 as the dummy data.
+ */
+ result = dns_rbt_addname(*rbtp, name, (void *)1);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(nameobj, ns_g_lctx, ISC_LOG_ERROR,
+ "failed to add %s for %s: %s",
+ str, confname, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ }
+
+ return (result);
+
+ cleanup:
+ dns_rbt_destroy(rbtp);
+ return (result);
+
+}
+
+static isc_result_t
+dstkey_fromconfig(const cfg_obj_t *vconfig, const cfg_obj_t *key,
+ bool managed, dst_key_t **target, isc_mem_t *mctx)
+{
+ dns_rdataclass_t viewclass;
+ dns_rdata_dnskey_t keystruct;
+ 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;
+
+ INSIST(target != NULL && *target == 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 (managed) {
+ const char *initmethod;
+ initmethod = cfg_obj_asstring(cfg_tuple_get(key, "init"));
+
+ if (strcasecmp(initmethod, "initial-key") != 0) {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
+ "managed key '%s': "
+ "invalid initialization method '%s'",
+ keynamestr, initmethod);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ 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 = (uint16_t)flags;
+ keystruct.protocol = (uint8_t)proto;
+ keystruct.algorithm = (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,
+ "%s key '%s' has a weak exponent",
+ managed ? "managed" : "trusted",
+ keynamestr);
+
+ CHECK(dns_rdata_fromstruct(NULL,
+ keystruct.common.rdclass,
+ keystruct.common.rdtype,
+ &keystruct, &rrdatabuf));
+ dns_fixedname_init(&fkeyname);
+ isc_buffer_constinit(&namebuf, keynamestr, strlen(keynamestr));
+ isc_buffer_add(&namebuf, strlen(keynamestr));
+ CHECK(dns_name_fromtext(keyname, &namebuf, dns_rootname, 0, NULL));
+ CHECK(dst_key_fromdns(keyname, viewclass, &rrdatabuf,
+ mctx, &dstkey));
+
+ *target = dstkey;
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (result == DST_R_NOCRYPTO) {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
+ "ignoring %s key for '%s': no crypto support",
+ managed ? "managed" : "trusted",
+ keynamestr);
+ } else if (result == DST_R_UNSUPPORTEDALG) {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_WARNING,
+ "skipping %s key for '%s': %s",
+ managed ? "managed" : "trusted",
+ keynamestr, isc_result_totext(result));
+ } else {
+ cfg_obj_log(key, ns_g_lctx, ISC_LOG_ERROR,
+ "configuring %s key for '%s': %s",
+ managed ? "managed" : "trusted",
+ keynamestr, isc_result_totext(result));
+ result = ISC_R_FAILURE;
+ }
+
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+
+ return (result);
+}
+
+static isc_result_t
+load_view_keys(const cfg_obj_t *keys, const cfg_obj_t *vconfig,
+ dns_view_t *view, bool managed,
+ dns_name_t *keyname, isc_mem_t *mctx)
+{
+ const cfg_listelt_t *elt, *elt2;
+ const cfg_obj_t *key, *keylist;
+ dst_key_t *dstkey = NULL;
+ isc_result_t result;
+ dns_keytable_t *secroots = NULL;
+
+ CHECK(dns_view_getsecroots(view, &secroots));
+
+ for (elt = cfg_list_first(keys);
+ elt != NULL;
+ elt = cfg_list_next(elt)) {
+ keylist = cfg_listelt_value(elt);
+
+ for (elt2 = cfg_list_first(keylist);
+ elt2 != NULL;
+ elt2 = cfg_list_next(elt2)) {
+ key = cfg_listelt_value(elt2);
+ result = dstkey_fromconfig(vconfig, key, managed,
+ &dstkey, mctx);
+ if (result == DST_R_UNSUPPORTEDALG) {
+ result = ISC_R_SUCCESS;
+ continue;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * If keyname was specified, we only add that key.
+ */
+ if (keyname != NULL &&
+ !dns_name_equal(keyname, dst_key_name(dstkey)))
+ {
+ dst_key_free(&dstkey);
+ continue;
+ }
+
+ CHECK(dns_keytable_add(secroots, managed, &dstkey));
+ }
+ }
+
+ cleanup:
+ if (dstkey != NULL)
+ dst_key_free(&dstkey);
+ if (secroots != NULL)
+ dns_keytable_detach(&secroots);
+ if (result == DST_R_NOCRYPTO)
+ result = ISC_R_SUCCESS;
+ return (result);
+}
+
+/*%
+ * Check whether a key has been successfully loaded.
+ */
+static bool
+keyloaded(dns_view_t *view, dns_name_t *name) {
+ isc_result_t result;
+ dns_keytable_t *secroots = NULL;
+ dns_keynode_t *keynode = NULL;
+
+ result = dns_view_getsecroots(view, &secroots);
+ if (result != ISC_R_SUCCESS)
+ return (false);
+
+ result = dns_keytable_find(secroots, name, &keynode);
+
+ if (keynode != NULL)
+ dns_keytable_detachkeynode(secroots, &keynode);
+ if (secroots != NULL)
+ dns_keytable_detach(&secroots);
+
+ return (result == ISC_R_SUCCESS);
+}
+
+/*%
+ * Configure DNSSEC keys for a view.
+ *
+ * The per-view configuration values and the server-global defaults are read
+ * from 'vconfig' and 'config'.
+ */
+static isc_result_t
+configure_view_dnsseckeys(dns_view_t *view, const cfg_obj_t *vconfig,
+ const cfg_obj_t *config, const cfg_obj_t *bindkeys,
+ bool auto_root, isc_mem_t *mctx)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ const cfg_obj_t *view_keys = NULL;
+ const cfg_obj_t *global_keys = NULL;
+ const cfg_obj_t *view_managed_keys = NULL;
+ const cfg_obj_t *global_managed_keys = NULL;
+ const cfg_obj_t *maps[4];
+ const cfg_obj_t *voptions = NULL;
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *obj = NULL;
+ const char *directory;
+ bool need_mkey_dir = false;
+ int i = 0;
+
+ /* We don't need trust anchors for the _bind view */
+ if (strcmp(view->name, "_bind") == 0 &&
+ view->rdclass == dns_rdataclass_chaos)
+ {
+ return (ISC_R_SUCCESS);
+ }
+
+ if (vconfig != NULL) {
+ voptions = cfg_tuple_get(vconfig, "options");
+ if (voptions != NULL) {
+ (void) cfg_map_get(voptions, "trusted-keys",
+ &view_keys);
+ (void) cfg_map_get(voptions, "managed-keys",
+ &view_managed_keys);
+ maps[i++] = voptions;
+ }
+ }
+
+ if (config != NULL) {
+ (void)cfg_map_get(config, "trusted-keys", &global_keys);
+ (void)cfg_map_get(config, "managed-keys", &global_managed_keys);
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL) {
+ maps[i++] = options;
+ }
+ }
+
+ maps[i++] = ns_g_defaults;
+ maps[i] = NULL;
+
+ result = dns_view_initsecroots(view, mctx);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "couldn't create keytable");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ result = dns_view_initntatable(view, ns_g_taskmgr, ns_g_timermgr);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "couldn't create NTA table");
+ return (ISC_R_UNEXPECTED);
+ }
+
+ if (auto_root && view->rdclass == dns_rdataclass_in) {
+ const cfg_obj_t *builtin_keys = NULL;
+ const cfg_obj_t *builtin_managed_keys = NULL;
+
+ /*
+ * If bind.keys exists and is populated, it overrides
+ * the managed-keys clause hard-coded in ns_g_config.
+ */
+ if (bindkeys != NULL) {
+ isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "obtaining root key for view %s "
+ "from '%s'",
+ view->name, ns_g_server->bindkeysfile);
+
+ (void)cfg_map_get(bindkeys, "trusted-keys",
+ &builtin_keys);
+ (void)cfg_map_get(bindkeys, "managed-keys",
+ &builtin_managed_keys);
+
+ if ((builtin_keys == NULL) &&
+ (builtin_managed_keys == NULL))
+ isc_log_write(ns_g_lctx,
+ DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING,
+ "dnssec-validation auto: "
+ "WARNING: root zone key "
+ "not found");
+ }
+
+ if ((builtin_keys == NULL) &&
+ (builtin_managed_keys == NULL))
+ {
+ isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "using built-in root key for view %s",
+ view->name);
+
+ (void)cfg_map_get(ns_g_config, "trusted-keys",
+ &builtin_keys);
+ (void)cfg_map_get(ns_g_config, "managed-keys",
+ &builtin_managed_keys);
+ }
+
+ if (builtin_keys != NULL)
+ CHECK(load_view_keys(builtin_keys, vconfig, view,
+ false, dns_rootname, mctx));
+ if (builtin_managed_keys != NULL)
+ CHECK(load_view_keys(builtin_managed_keys, vconfig,
+ view, true, dns_rootname,
+ mctx));
+
+ if (!keyloaded(view, dns_rootname)) {
+ isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "root key not loaded");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ }
+
+ CHECK(load_view_keys(view_keys, vconfig, view, false,
+ NULL, mctx));
+ CHECK(load_view_keys(view_managed_keys, vconfig, view, true,
+ NULL, mctx));
+
+ if (view->rdclass == dns_rdataclass_in) {
+ CHECK(load_view_keys(global_keys, vconfig, view, false,
+ NULL, mctx));
+ CHECK(load_view_keys(global_managed_keys, vconfig, view,
+ true, NULL, mctx));
+ }
+
+ /*
+ * Add key zone for managed-keys.
+ */
+ need_mkey_dir = (auto_root || view_managed_keys != NULL);
+
+ obj = NULL;
+ (void)ns_config_get(maps, "managed-keys-directory", &obj);
+ directory = (obj != NULL ? cfg_obj_asstring(obj) : NULL);
+ if (directory != NULL) {
+ result = isc_file_isdirectory(directory);
+ }
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "invalid managed-keys-directory %s: %s",
+ directory, isc_result_totext(result));
+ goto cleanup;
+
+ } else if (need_mkey_dir && directory != NULL) {
+ if (!isc_file_isdirwritable(directory)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "managed-keys-directory '%s' "
+ "is not writable", directory);
+ result = ISC_R_NOPERM;
+ goto cleanup;
+ }
+ } else if (need_mkey_dir) {
+ char cwd[PATH_MAX];
+
+ if (getcwd(cwd, sizeof(cwd)) == NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "unable to retrieve "
+ "current working directory");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ if (!isc_file_isdirwritable(cwd)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "working directory '%s' "
+ "is not writable", cwd);
+ result = ISC_R_NOPERM;
+ goto cleanup;
+ }
+ }
+
+ CHECK(add_keydata_zone(view, directory, ns_g_mctx));
+
+ 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;
+ bool value;
+ isc_result_t result;
+ isc_buffer_t b;
+
+ name = dns_fixedname_initname(&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_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, 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_dscp_t *dscpp,
+ bool is_firstview)
+{
+ isc_result_t result = ISC_R_FAILURE;
+ dns_dispatch_t *disp;
+ isc_sockaddr_t sa;
+ unsigned int attrs, attrmask;
+ const cfg_obj_t *obj = NULL;
+ unsigned int maxdispatchbuffers = UDPBUFFERS;
+ isc_dscp_t dscp = -1;
+
+ 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);
+
+ dscp = cfg_obj_getdscp(obj);
+ if (dscp != -1 && dscpp != NULL)
+ *dscpp = dscp;
+
+ /*
+ * 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 = EXCLBUFFERS;
+ } 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.");
+ }
+ }
+
+ 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;
+ bool 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 = (strcmp(str, "*") == 0);
+ isc_buffer_constinit(&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, 0, 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"))
+#if DNS_RDATASET_FIXED
+ mode = DNS_RDATASETATTR_FIXEDORDER;
+#else
+ mode = 0;
+#endif /* DNS_RDATASET_FIXED */
+ 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-expire", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setrequestexpire(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, "send-cookie", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setsendcookie(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) {
+ uint32_t udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ CHECK(dns_peer_setudpsize(peer, (uint16_t)udpsize));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "edns-version", &obj);
+ if (obj != NULL) {
+ uint32_t ednsversion = cfg_obj_asuint32(obj);
+ if (ednsversion > 255)
+ ednsversion = 255;
+ CHECK(dns_peer_setednsversion(peer, (uint8_t)ednsversion));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "max-udp-size", &obj);
+ if (obj != NULL) {
+ uint32_t udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 512)
+ udpsize = 512;
+ if (udpsize > 4096)
+ udpsize = 4096;
+ CHECK(dns_peer_setmaxudp(peer, (uint16_t)udpsize));
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(cpeer, "tcp-only", &obj);
+ if (obj != NULL)
+ CHECK(dns_peer_setforcetcp(peer, cfg_obj_asboolean(obj)));
+
+ 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;
+ result = dns_peer_settransferdscp(peer, cfg_obj_getdscp(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;
+ result = dns_peer_setnotifydscp(peer, cfg_obj_getdscp(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;
+ result = dns_peer_setquerydscp(peer, cfg_obj_getdscp(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);
+}
+
+#ifdef HAVE_DLOPEN
+static isc_result_t
+configure_dyndb(const cfg_obj_t *dyndb, isc_mem_t *mctx,
+ const dns_dyndbctx_t *dctx)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ const cfg_obj_t *obj;
+ const char *name, *library;
+
+ /* Get the name of the dyndb instance and the library path . */
+ name = cfg_obj_asstring(cfg_tuple_get(dyndb, "name"));
+ library = cfg_obj_asstring(cfg_tuple_get(dyndb, "library"));
+
+ obj = cfg_tuple_get(dyndb, "parameters");
+ if (obj != NULL)
+ result = dns_dyndb_load(library, name, cfg_obj_asstring(obj),
+ cfg_obj_file(obj), cfg_obj_line(obj),
+ mctx, dctx);
+
+ if (result != ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dynamic database '%s' configuration failed: %s",
+ name, isc_result_totext(result));
+ return (result);
+}
+#endif
+
+
+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;
+
+ name = dns_fixedname_initname(&fixed);
+ str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, 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) {
+ 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_result_t
+disable_ds_digests(const cfg_obj_t *disabled, dns_resolver_t *resolver) {
+ isc_result_t result;
+ const cfg_obj_t *digests;
+ const cfg_listelt_t *element;
+ const char *str;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t b;
+
+ name = dns_fixedname_initname(&fixed);
+ str = cfg_obj_asstring(cfg_tuple_get(disabled, "name"));
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+
+ digests = cfg_tuple_get(disabled, "digests");
+ for (element = cfg_list_first(digests);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ isc_textregion_t r;
+ dns_dsdigest_t digest;
+
+ DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
+ r.length = strlen(r.base);
+
+ /* disable_ds_digests handles numeric values. */
+ result = dns_dsdigest_fromtext(&digest, &r);
+ 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_ds_digest(resolver, name, digest));
+ }
+ cleanup:
+ return (result);
+}
+
+static bool
+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;
+
+ name = dns_fixedname_initname(&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_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(name, &b, dns_rootname,
+ 0, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ if (dns_name_equal(name, zonename))
+ return (true);
+ }
+ return (false);
+}
+
+static isc_result_t
+check_dbtype(dns_zone_t *zone, unsigned int dbtypec, const char **dbargv,
+ isc_mem_t *mctx)
+{
+ char **argv = NULL;
+ unsigned int i;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ CHECK(dns_zone_getdbtype(zone, &argv, mctx));
+
+ /*
+ * Check that all the arguments match.
+ */
+ for (i = 0; i < dbtypec; i++)
+ if (argv[i] == NULL || strcmp(argv[i], dbargv[i]) != 0)
+ CHECK(ISC_R_FAILURE);
+
+ /*
+ * Check that there are not extra arguments.
+ */
+ if (i == dbtypec && argv[i] != NULL)
+ result = ISC_R_FAILURE;
+
+ cleanup:
+ isc_mem_free(mctx, argv);
+ return (result);
+}
+
+static isc_result_t
+setquerystats(dns_zone_t *zone, isc_mem_t *mctx, dns_zonestat_level_t level) {
+ isc_result_t result;
+ isc_stats_t *zoneqrystats;
+
+ dns_zone_setstatlevel(zone, level);
+
+ zoneqrystats = NULL;
+ if (level == dns_zonestat_full) {
+ result = isc_stats_create(mctx, &zoneqrystats,
+ dns_nsstatscounter_max);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ dns_zone_setrequeststats(zone, zoneqrystats);
+ if (zoneqrystats != NULL)
+ isc_stats_detach(&zoneqrystats);
+
+ return (ISC_R_SUCCESS);
+}
+
+static ns_cache_t *
+cachelist_find(ns_cachelist_t *cachelist, const char *cachename,
+ dns_rdataclass_t rdclass)
+{
+ ns_cache_t *nsc;
+
+ for (nsc = ISC_LIST_HEAD(*cachelist);
+ nsc != NULL;
+ nsc = ISC_LIST_NEXT(nsc, link)) {
+ if (nsc->rdclass == rdclass &&
+ strcmp(dns_cache_getname(nsc->cache), cachename) == 0)
+ return (nsc);
+ }
+
+ return (NULL);
+}
+
+static bool
+cache_reusable(dns_view_t *originview, dns_view_t *view,
+ bool new_zero_no_soattl)
+{
+ if (originview->rdclass != view->rdclass ||
+ originview->checknames != view->checknames ||
+ dns_resolver_getzeronosoattl(originview->resolver) !=
+ new_zero_no_soattl ||
+ originview->acceptexpired != view->acceptexpired ||
+ originview->enablevalidation != view->enablevalidation ||
+ originview->maxcachettl != view->maxcachettl ||
+ originview->maxncachettl != view->maxncachettl) {
+ return (false);
+ }
+
+ return (true);
+}
+
+static bool
+cache_sharable(dns_view_t *originview, dns_view_t *view,
+ bool new_zero_no_soattl,
+ unsigned int new_cleaning_interval,
+ uint64_t new_max_cache_size)
+{
+ /*
+ * If the cache cannot even reused for the same view, it cannot be
+ * shared with other views.
+ */
+ if (!cache_reusable(originview, view, new_zero_no_soattl))
+ return (false);
+
+ /*
+ * Check other cache related parameters that must be consistent among
+ * the sharing views.
+ */
+ if (dns_cache_getcleaninginterval(originview->cache) !=
+ new_cleaning_interval ||
+ dns_cache_getcachesize(originview->cache) != new_max_cache_size) {
+ return (false);
+ }
+
+ return (true);
+}
+
+/*
+ * Callback from DLZ configure when the driver sets up a writeable zone
+ */
+static isc_result_t
+dlzconfigure_callback(dns_view_t *view, dns_dlzdb_t *dlzdb, dns_zone_t *zone) {
+ dns_name_t *origin = dns_zone_getorigin(zone);
+ dns_rdataclass_t zclass = view->rdclass;
+ isc_result_t result;
+
+ result = dns_zonemgr_managezone(ns_g_server->zonemgr, zone);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ dns_zone_setstats(zone, ns_g_server->zonestats);
+
+ return (ns_zone_configure_writeable_dlz(dlzdb, zone, zclass, origin));
+}
+
+static isc_result_t
+dns64_reverse(dns_view_t *view, isc_mem_t *mctx, isc_netaddr_t *na,
+ unsigned int prefixlen, const char *server,
+ const char *contact)
+{
+ char reverse[48+sizeof("ip6.arpa.")] = { 0 };
+ char buf[sizeof("x.x.")];
+ const char *dns64_dbtype[4] = { "_dns64", "dns64", ".", "." };
+ const char *sep = ": view ";
+ const char *viewname = view->name;
+ const unsigned char *s6;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ dns_zone_t *zone = NULL;
+ int dns64_dbtypec = 4;
+ isc_buffer_t b;
+ isc_result_t result;
+
+ REQUIRE(prefixlen == 32 || prefixlen == 40 || prefixlen == 48 ||
+ prefixlen == 56 || prefixlen == 64 || prefixlen == 96);
+
+ if (!strcmp(viewname, "_default")) {
+ sep = "";
+ viewname = "";
+ }
+
+ /*
+ * Construct the reverse name of the zone.
+ */
+ s6 = na->type.in6.s6_addr;
+ while (prefixlen > 0) {
+ prefixlen -= 8;
+ snprintf(buf, sizeof(buf), "%x.%x.", s6[prefixlen/8] & 0xf,
+ (s6[prefixlen/8] >> 4) & 0xf);
+ strlcat(reverse, buf, sizeof(reverse));
+ }
+ strlcat(reverse, "ip6.arpa.", sizeof(reverse));
+
+ /*
+ * Create the actual zone.
+ */
+ if (server != NULL)
+ dns64_dbtype[2] = server;
+ if (contact != NULL)
+ dns64_dbtype[3] = contact;
+ name = dns_fixedname_initname(&fixed);
+ isc_buffer_constinit(&b, reverse, strlen(reverse));
+ isc_buffer_add(&b, strlen(reverse));
+ CHECK(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+ 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, dns64_dbtypec, dns64_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, true);
+ CHECK(setquerystats(zone, mctx, dns_zonestat_none)); /* XXXMPA */
+ CHECK(dns_view_addzone(view, zone));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "dns64 reverse zone%s%s: %s", sep,
+ viewname, reverse);
+
+cleanup:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+static isc_result_t
+configure_rpz_name(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
+ const char *str, const char *msg)
+{
+ isc_result_t result;
+
+ result = dns_name_fromstring(name, str, DNS_NAME_DOWNCASE, view->mctx);
+ if (result != ISC_R_SUCCESS)
+ cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "invalid %s '%s'", msg, str);
+ return (result);
+}
+
+static isc_result_t
+configure_rpz_name2(dns_view_t *view, const cfg_obj_t *obj, dns_name_t *name,
+ const char *str, const dns_name_t *origin)
+{
+ isc_result_t result;
+
+ result = dns_name_fromstring2(name, str, origin, DNS_NAME_DOWNCASE,
+ view->mctx);
+ if (result != ISC_R_SUCCESS)
+ cfg_obj_log(obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "invalid zone '%s'", str);
+ return (result);
+}
+
+static isc_result_t
+configure_rpz_zone(dns_view_t *view, const cfg_listelt_t *element,
+ bool recursive_only_def, dns_ttl_t ttl_def,
+ const dns_rpz_zone_t *old, bool *old_rpz_okp)
+{
+ const cfg_obj_t *rpz_obj, *obj;
+ const char *str;
+ dns_rpz_zone_t *new;
+ isc_result_t result;
+ dns_rpz_num_t rpz_num;
+
+ REQUIRE(old != NULL || !*old_rpz_okp);
+
+ rpz_obj = cfg_listelt_value(element);
+
+ if (view->rpzs->p.num_zones >= DNS_RPZ_MAX_ZONES) {
+ cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "limit of %d response policy zones exceeded",
+ DNS_RPZ_MAX_ZONES);
+ return (ISC_R_FAILURE);
+ }
+
+ new = isc_mem_get(view->rpzs->mctx, sizeof(*new));
+ if (new == NULL) {
+ cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "no memory for response policy zones");
+ return (ISC_R_NOMEMORY);
+ }
+
+ memset(new, 0, sizeof(*new));
+ result = isc_refcount_init(&new->refs, 1);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_put(view->rpzs->mctx, new, sizeof(*new));
+ return (result);
+ }
+ dns_name_init(&new->origin, NULL);
+ dns_name_init(&new->client_ip, NULL);
+ dns_name_init(&new->ip, NULL);
+ dns_name_init(&new->nsdname, NULL);
+ dns_name_init(&new->nsip, NULL);
+ dns_name_init(&new->passthru, NULL);
+ dns_name_init(&new->drop, NULL);
+ dns_name_init(&new->tcp_only, NULL);
+ dns_name_init(&new->cname, NULL);
+ new->num = view->rpzs->p.num_zones++;
+ view->rpzs->zones[new->num] = new;
+
+ obj = cfg_tuple_get(rpz_obj, "recursive-only");
+ if (cfg_obj_isvoid(obj) ? recursive_only_def : cfg_obj_asboolean(obj)) {
+ view->rpzs->p.no_rd_ok &= ~DNS_RPZ_ZBIT(new->num);
+ } else {
+ view->rpzs->p.no_rd_ok |= DNS_RPZ_ZBIT(new->num);
+ }
+
+ obj = cfg_tuple_get(rpz_obj, "log");
+ if (!cfg_obj_isvoid(obj) && !cfg_obj_asboolean(obj)) {
+ view->rpzs->p.no_log |= DNS_RPZ_ZBIT(new->num);
+ } else {
+ view->rpzs->p.no_log &= ~DNS_RPZ_ZBIT(new->num);
+ }
+
+ obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
+ if (cfg_obj_isuint32(obj)) {
+ new->max_policy_ttl = cfg_obj_asuint32(obj);
+ } else {
+ new->max_policy_ttl = ttl_def;
+ }
+ if (*old_rpz_okp && new->max_policy_ttl != old->max_policy_ttl)
+ *old_rpz_okp = false;
+
+ str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "zone name"));
+ result = configure_rpz_name(view, rpz_obj, &new->origin, str, "zone");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (dns_name_equal(&new->origin, dns_rootname)) {
+ cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "invalid zone name '%s'", str);
+ return (DNS_R_EMPTYLABEL);
+ }
+ for (rpz_num = 0; rpz_num < view->rpzs->p.num_zones-1; ++rpz_num) {
+ if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin,
+ &new->origin)) {
+ cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+ "duplicate '%s'", str);
+ result = DNS_R_DUPLICATE;
+ return (result);
+ }
+ }
+ if (*old_rpz_okp && !dns_name_equal(&old->origin, &new->origin))
+ *old_rpz_okp = false;
+
+ result = configure_rpz_name2(view, rpz_obj, &new->client_ip,
+ DNS_RPZ_CLIENT_IP_ZONE, &new->origin);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = configure_rpz_name2(view, rpz_obj, &new->ip,
+ DNS_RPZ_IP_ZONE, &new->origin);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = configure_rpz_name2(view, rpz_obj, &new->nsdname,
+ DNS_RPZ_NSDNAME_ZONE, &new->origin);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = configure_rpz_name2(view, rpz_obj, &new->nsip,
+ DNS_RPZ_NSIP_ZONE, &new->origin);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = configure_rpz_name(view, rpz_obj, &new->passthru,
+ DNS_RPZ_PASSTHRU_NAME, "name");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = configure_rpz_name(view, rpz_obj, &new->drop,
+ DNS_RPZ_DROP_NAME, "name");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = configure_rpz_name(view, rpz_obj, &new->tcp_only,
+ DNS_RPZ_TCP_ONLY_NAME, "name");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = cfg_tuple_get(rpz_obj, "policy");
+ if (cfg_obj_isvoid(obj)) {
+ new->policy = DNS_RPZ_POLICY_GIVEN;
+ } else {
+ str = cfg_obj_asstring(cfg_tuple_get(obj, "policy name"));
+ new->policy = dns_rpz_str2policy(str);
+ INSIST(new->policy != DNS_RPZ_POLICY_ERROR);
+ if (new->policy == DNS_RPZ_POLICY_CNAME) {
+ str = cfg_obj_asstring(cfg_tuple_get(obj, "cname"));
+ result = configure_rpz_name(view, rpz_obj, &new->cname,
+ str, "cname");
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ }
+ }
+ if (*old_rpz_okp && (new->policy != old->policy ||
+ !dns_name_equal(&old->cname, &new->cname)))
+ *old_rpz_okp = false;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+configure_rpz(dns_view_t *view, const cfg_obj_t *rpz_obj,
+ bool *old_rpz_okp)
+{
+ const cfg_listelt_t *zone_element;
+ const cfg_obj_t *sub_obj;
+ bool recursive_only_def;
+ dns_ttl_t ttl_def;
+ dns_rpz_zones_t *new;
+ const dns_rpz_zones_t *old;
+ dns_view_t *pview;
+ const dns_rpz_zone_t *old_zone;
+ isc_result_t result;
+ int i;
+
+ *old_rpz_okp = false;
+
+ zone_element = cfg_list_first(cfg_tuple_get(rpz_obj, "zone list"));
+ if (zone_element == NULL)
+ return (ISC_R_SUCCESS);
+
+ result = dns_rpz_new_zones(&view->rpzs, view->mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ new = view->rpzs;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "recursive-only");
+ if (!cfg_obj_isvoid(sub_obj) &&
+ !cfg_obj_asboolean(sub_obj))
+ recursive_only_def = false;
+ else
+ recursive_only_def = true;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "break-dnssec");
+ if (!cfg_obj_isvoid(sub_obj) &&
+ cfg_obj_asboolean(sub_obj))
+ new->p.break_dnssec = true;
+ else
+ new->p.break_dnssec = false;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "max-policy-ttl");
+ if (cfg_obj_isuint32(sub_obj))
+ ttl_def = cfg_obj_asuint32(sub_obj);
+ else
+ ttl_def = DNS_RPZ_MAX_TTL_DEFAULT;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "min-ns-dots");
+ if (cfg_obj_isuint32(sub_obj))
+ new->p.min_ns_labels = cfg_obj_asuint32(sub_obj) + 1;
+ else
+ new->p.min_ns_labels = 2;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "qname-wait-recurse");
+ if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj))
+ new->p.qname_wait_recurse = true;
+ else
+ new->p.qname_wait_recurse = false;
+
+ sub_obj = cfg_tuple_get(rpz_obj, "nsip-wait-recurse");
+ if (cfg_obj_isvoid(sub_obj) || cfg_obj_asboolean(sub_obj))
+ new->p.nsip_wait_recurse = true;
+ else
+ new->p.nsip_wait_recurse = false;
+
+ pview = NULL;
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name, view->rdclass, &pview);
+ if (result == ISC_R_SUCCESS) {
+ old = pview->rpzs;
+ } else {
+ old = NULL;
+ }
+ if (old == NULL)
+ *old_rpz_okp = false;
+ else
+ *old_rpz_okp = true;
+
+ for (i = 0;
+ zone_element != NULL;
+ ++i, zone_element = cfg_list_next(zone_element)) {
+ INSIST(old != NULL || !*old_rpz_okp);
+ if (*old_rpz_okp && i < old->p.num_zones) {
+ old_zone = old->zones[i];
+ } else {
+ *old_rpz_okp = false;
+ old_zone = NULL;
+ }
+ result = configure_rpz_zone(view, zone_element,
+ recursive_only_def, ttl_def,
+ old_zone, old_rpz_okp);
+ if (result != ISC_R_SUCCESS) {
+ if (pview != NULL)
+ dns_view_detach(&pview);
+ return (result);
+ }
+ }
+
+ /*
+ * If this is a reloading and the parameters and list of policy
+ * zones are unchanged, then use the same policy data.
+ * Data for individual zones that must be reloaded will be merged.
+ */
+ if (old != NULL && memcmp(&old->p, &new->p, sizeof(new->p)) != 0)
+ *old_rpz_okp = false;
+ if (*old_rpz_okp) {
+ dns_rpz_detach_rpzs(&view->rpzs);
+ dns_rpz_attach_rpzs(pview->rpzs, &view->rpzs);
+ } else if (old != NULL && pview != NULL) {
+ pview->rpzs->rpz_ver += 1;
+ view->rpzs->rpz_ver = pview->rpzs->rpz_ver;
+ cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_DEBUG_LEVEL1,
+ "updated RPZ policy: version %d",
+ view->rpzs->rpz_ver);
+ }
+
+ if (pview != NULL)
+ dns_view_detach(&pview);
+
+ return (ISC_R_SUCCESS);
+}
+
+static void
+catz_addmodzone_taskaction(isc_task_t *task, isc_event_t *event0) {
+ catz_chgzone_event_t *ev = (catz_chgzone_event_t *) event0;
+ isc_result_t result;
+ isc_buffer_t namebuf;
+ isc_buffer_t *confbuf;
+ char nameb[DNS_NAME_FORMATSIZE];
+ const cfg_obj_t *zlist = NULL;
+ cfg_obj_t *zoneconf = NULL;
+ cfg_obj_t *zoneobj = NULL;
+ ns_cfgctx_t *cfg;
+ dns_zone_t *zone = NULL;
+
+ cfg = (ns_cfgctx_t *) ev->view->new_zone_config;
+ if (cfg == NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "catz: allow-new-zones statement missing from "
+ "config; cannot add zone from the catalog");
+ goto cleanup;
+ }
+
+ isc_buffer_init(&namebuf, nameb, DNS_NAME_FORMATSIZE);
+ dns_name_totext(dns_catz_entry_getname(ev->entry), true, &namebuf);
+ isc_buffer_putuint8(&namebuf, 0);
+
+ /* Zone shouldn't already exist */
+ result = dns_zt_find(ev->view->zonetable,
+ dns_catz_entry_getname(ev->entry), 0, NULL, &zone);
+
+ if (ev->mod == true) {
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: error \"%s\" while trying to "
+ "modify zone \"%s\"",
+ isc_result_totext(result),
+ nameb);
+ goto cleanup;
+ } else {
+ if (!dns_zone_getadded(zone)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING,
+ "catz: catz_addmodzone_taskaction: "
+ "zone '%s' is not a dynamically "
+ "added zone",
+ nameb);
+ goto cleanup;
+ }
+ if (dns_zone_get_parentcatz(zone) != ev->origin) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_delzone_taskaction: "
+ "zone '%s' exists in multiple "
+ "catalog zones",
+ nameb);
+ goto cleanup;
+ }
+ dns_zone_detach(&zone);
+ }
+
+ } else {
+ if (result != ISC_R_NOTFOUND && result != DNS_R_PARTIALMATCH) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: error \"%s\" while trying to "
+ "add zone \"%s\"",
+ isc_result_totext(result),
+ nameb);
+ goto cleanup;
+ } else { /* this can happen in case of DNS_R_PARTIALMATCH */
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ }
+ }
+ RUNTIME_CHECK(zone == NULL);
+ /* Create a config for new zone */
+ confbuf = NULL;
+ result = dns_catz_generate_zonecfg(ev->origin, ev->entry, &confbuf);
+ if (result == ISC_R_SUCCESS) {
+ cfg_parser_reset(cfg->add_parser);
+ result = cfg_parse_buffer3(cfg->add_parser, confbuf, "catz", 0,
+ &cfg_type_addzoneconf, &zoneconf);
+ isc_buffer_free(&confbuf);
+ }
+ /*
+ * Fail if either dns_catz_generate_zonecfg() or cfg_parse_buffer3()
+ * failed.
+ */
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "catz: error \"%s\" while trying to generate "
+ "config for zone \"%s\"",
+ isc_result_totext(result), nameb);
+ goto cleanup;
+ }
+ CHECK(cfg_map_get(zoneconf, "zone", &zlist));
+ if (!cfg_obj_islist(zlist))
+ CHECK(ISC_R_FAILURE);
+
+ /* For now we only support adding one zone at a time */
+ zoneobj = cfg_listelt_value(cfg_list_first(zlist));
+
+ /* Mark view unfrozen so that zone can be added */
+
+ result = isc_task_beginexclusive(task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ dns_view_thaw(ev->view);
+ result = configure_zone(cfg->config, zoneobj, cfg->vconfig,
+ ev->cbd->server->mctx, ev->view,
+ &ev->cbd->server->viewlist, cfg->actx,
+ true, false, ev->mod);
+ dns_view_freeze(ev->view);
+ isc_task_endexclusive(task);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: failed to configure zone \"%s\" - %d",
+ nameb, result);
+ goto cleanup;
+ }
+
+ /* Is it there yet? */
+ CHECK(dns_zt_find(ev->view->zonetable,
+ dns_catz_entry_getname(ev->entry), 0, NULL, &zone));
+
+ /*
+ * Load the zone from the master file. If this fails, we'll
+ * need to undo the configuration we've done already.
+ */
+ result = dns_zone_loadnew(zone);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_t *dbp = NULL;
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "catz: dns_zone_loadnew() failed "
+ "with %s; reverting.",
+ isc_result_totext(result));
+
+ /* If the zone loaded partially, unload it */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ /* Remove the zone from the zone table */
+ dns_zt_unmount(ev->view->zonetable, zone);
+ goto cleanup;
+ }
+
+ /* Flag the zone as having been added at runtime */
+ dns_zone_setadded(zone, true);
+ dns_zone_set_parentcatz(zone, ev->origin);
+
+ cleanup:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (zoneconf != NULL)
+ cfg_obj_destroy(cfg->add_parser, &zoneconf);
+ dns_catz_entry_detach(ev->origin, &ev->entry);
+ dns_catz_zone_detach(&ev->origin);
+ dns_view_detach(&ev->view);
+ isc_event_free(ISC_EVENT_PTR(&ev));
+}
+
+static void
+catz_delzone_taskaction(isc_task_t *task, isc_event_t *event0) {
+ catz_chgzone_event_t *ev = (catz_chgzone_event_t *) event0;
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_db_t *dbp = NULL;
+ char cname[DNS_NAME_FORMATSIZE];
+ const char * file;
+
+ result = isc_task_beginexclusive(task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ dns_name_format(dns_catz_entry_getname(ev->entry), cname,
+ DNS_NAME_FORMATSIZE);
+ result = dns_zt_find(ev->view->zonetable,
+ dns_catz_entry_getname(ev->entry), 0, NULL, &zone);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_delzone_taskaction: "
+ "zone '%s' not found", cname);
+ goto cleanup;
+ }
+
+ if (!dns_zone_getadded(zone)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_delzone_taskaction: "
+ "zone '%s' is not a dynamically added zone",
+ cname);
+ goto cleanup;
+ }
+
+ if (dns_zone_get_parentcatz(zone) != ev->origin) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_delzone_taskaction: zone "
+ "'%s' exists in multiple catalog zones",
+ cname);
+ goto cleanup;
+ }
+
+ /* Stop answering for this zone */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ CHECK(dns_zt_unmount(ev->view->zonetable, zone));
+ file = dns_zone_getfile(zone);
+ if (file != NULL)
+ isc_file_remove(file);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "catz: catz_delzone_taskaction: "
+ "zone '%s' deleted", cname);
+ cleanup:
+ isc_task_endexclusive(task);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ dns_catz_entry_detach(ev->origin, &ev->entry);
+ dns_catz_zone_detach(&ev->origin);
+ dns_view_detach(&ev->view);
+ isc_event_free(ISC_EVENT_PTR(&ev));
+}
+
+static isc_result_t
+catz_create_chg_task(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+ dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata,
+ isc_eventtype_t type)
+{
+ catz_chgzone_event_t *event;
+ isc_task_t *task;
+ isc_result_t result;
+ isc_taskaction_t action;
+
+ switch (type) {
+ case DNS_EVENT_CATZADDZONE:
+ case DNS_EVENT_CATZMODZONE:
+ action = catz_addmodzone_taskaction;
+ break;
+ case DNS_EVENT_CATZDELZONE:
+ action = catz_delzone_taskaction;
+ break;
+ default:
+ REQUIRE(0);
+ }
+
+ event = (catz_chgzone_event_t *) isc_event_allocate(view->mctx, origin,
+ type, action, NULL,
+ sizeof(*event));
+ if (event == NULL)
+ return (ISC_R_NOMEMORY);
+
+ event->cbd = (catz_cb_data_t *) udata;
+ event->entry = NULL;
+ event->origin = NULL;
+ event->view = NULL;
+ event->mod = (type == DNS_EVENT_CATZMODZONE);
+ dns_catz_entry_attach(entry, &event->entry);
+ dns_catz_zone_attach(origin, &event->origin);
+ dns_view_attach(view, &event->view);
+
+ task = NULL;
+ result = isc_taskmgr_excltask(taskmgr, &task);
+ REQUIRE(result == ISC_R_SUCCESS);
+ isc_task_send(task, ISC_EVENT_PTR(&event));
+ isc_task_detach(&task);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+catz_addzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+ dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata)
+{
+ return (catz_create_chg_task(entry, origin, view, taskmgr, udata,
+ DNS_EVENT_CATZADDZONE));
+}
+
+static isc_result_t
+catz_delzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+ dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata)
+{
+ return (catz_create_chg_task(entry, origin, view, taskmgr, udata,
+ DNS_EVENT_CATZDELZONE));
+}
+
+static isc_result_t
+catz_modzone(dns_catz_entry_t *entry, dns_catz_zone_t *origin,
+ dns_view_t *view, isc_taskmgr_t *taskmgr, void *udata)
+{
+ return (catz_create_chg_task(entry, origin, view, taskmgr, udata,
+ DNS_EVENT_CATZMODZONE));
+}
+
+static isc_result_t
+configure_catz_zone(dns_view_t *view, const cfg_obj_t *config,
+ const cfg_listelt_t *element)
+{
+ const cfg_obj_t *catz_obj, *obj;
+ dns_catz_zone_t *zone = NULL;
+ const char *str;
+ isc_result_t result;
+ dns_name_t origin;
+ dns_catz_options_t *opts;
+ dns_view_t *pview = NULL;
+
+ dns_name_init(&origin, NULL);
+ catz_obj = cfg_listelt_value(element);
+
+ str = cfg_obj_asstring(cfg_tuple_get(catz_obj, "zone name"));
+
+ result = dns_name_fromstring(&origin, str, DNS_NAME_DOWNCASE,
+ view->mctx);
+ if (result == ISC_R_SUCCESS && dns_name_equal(&origin, dns_rootname))
+ result = DNS_R_EMPTYLABEL;
+
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(catz_obj, ns_g_lctx, DNS_CATZ_ERROR_LEVEL,
+ "catz: invalid zone name '%s'", str);
+ goto cleanup;
+ }
+
+ result = dns_catz_add_zone(view->catzs, &origin, &zone);
+ if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS) {
+ cfg_obj_log(catz_obj, ns_g_lctx, DNS_CATZ_ERROR_LEVEL,
+ "catz: unable to create catalog zone '%s', "
+ "error %s",
+ str, isc_result_totext(result));
+ goto cleanup;
+ }
+
+ if (result == ISC_R_EXISTS) {
+ isc_ht_iter_t *it = NULL;
+
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name,
+ view->rdclass, &pview);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /*
+ * xxxwpk todo: reconfigure the zone!!!!
+ */
+ cfg_obj_log(catz_obj, ns_g_lctx, DNS_CATZ_ERROR_LEVEL,
+ "catz: catalog zone '%s' will not be reconfigured",
+ str);
+ /*
+ * We have to walk through all the member zones and attach
+ * them to current view
+ */
+ result = dns_catz_get_iterator(zone, &it);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(catz_obj, ns_g_lctx, DNS_CATZ_ERROR_LEVEL,
+ "catz: unable to create iterator");
+ goto cleanup;
+ }
+
+ for (result = isc_ht_iter_first(it);
+ result == ISC_R_SUCCESS;
+ result = isc_ht_iter_next(it))
+ {
+ dns_name_t *name = NULL;
+ dns_zone_t *dnszone = NULL;
+ dns_catz_entry_t *entry = NULL;
+ isc_result_t tresult;
+
+ isc_ht_iter_current(it, (void **) &entry);
+ name = dns_catz_entry_getname(entry);
+
+ tresult = dns_view_findzone(pview, name, &dnszone);
+ RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
+
+ dns_zone_setview(dnszone, view);
+ if (view->acache != NULL)
+ dns_zone_setacache(dnszone, view->acache);
+ dns_view_addzone(view, dnszone);
+
+ /*
+ * The dns_view_findzone() call above increments the
+ * zone's reference count, which we need to decrement
+ * back. However, as dns_zone_detach() sets the
+ * supplied pointer to NULL, calling it is deferred
+ * until the dnszone variable is no longer used.
+ */
+ dns_zone_detach(&dnszone);
+ }
+
+ isc_ht_iter_destroy(&it);
+
+ result = ISC_R_SUCCESS;
+ }
+
+ dns_catz_zone_resetdefoptions(zone);
+ opts = dns_catz_zone_getdefoptions(zone);
+
+ obj = cfg_tuple_get(catz_obj, "default-masters");
+ if (obj != NULL && cfg_obj_istuple(obj))
+ result = ns_config_getipandkeylist(config, obj,
+ view->mctx, &opts->masters);
+
+ obj = cfg_tuple_get(catz_obj, "in-memory");
+ if (obj != NULL && cfg_obj_isboolean(obj))
+ opts->in_memory = cfg_obj_asboolean(obj);
+
+ obj = cfg_tuple_get(catz_obj, "zone-directory");
+ if (!opts->in_memory && obj != NULL && cfg_obj_isstring(obj)) {
+ opts->zonedir = isc_mem_strdup(view->mctx,
+ cfg_obj_asstring(obj));
+ if (isc_file_isdirectory(opts->zonedir) != ISC_R_SUCCESS) {
+ cfg_obj_log(obj, ns_g_lctx, DNS_CATZ_ERROR_LEVEL,
+ "catz: zone-directory '%s' "
+ "not found; zone files will not be "
+ "saved", opts->zonedir);
+ opts->in_memory = true;
+ }
+ }
+
+ obj = cfg_tuple_get(catz_obj, "min-update-interval");
+ if (obj != NULL && cfg_obj_isuint32(obj))
+ opts->min_update_interval = cfg_obj_asuint32(obj);
+
+ cleanup:
+ if (pview != NULL)
+ dns_view_detach(&pview);
+ dns_name_free(&origin, view->mctx);
+
+ return (result);
+}
+
+static catz_cb_data_t ns_catz_cbdata;
+static dns_catz_zonemodmethods_t ns_catz_zonemodmethods = {
+ catz_addzone,
+ catz_modzone,
+ catz_delzone,
+ &ns_catz_cbdata
+};
+
+static isc_result_t
+configure_catz(dns_view_t *view, const cfg_obj_t *config,
+ const cfg_obj_t *catz_obj)
+{
+ const cfg_listelt_t *zone_element;
+ const dns_catz_zones_t *old = NULL;
+ dns_view_t *pview = NULL;
+ isc_result_t result;
+
+ /* xxxwpk TODO do it cleaner, once, somewhere */
+ ns_catz_cbdata.server = ns_g_server;
+
+ zone_element = cfg_list_first(cfg_tuple_get(catz_obj, "zone list"));
+ if (zone_element == NULL)
+ return (ISC_R_SUCCESS);
+
+ CHECK(dns_catz_new_zones(&view->catzs, &ns_catz_zonemodmethods,
+ view->mctx, ns_g_taskmgr, ns_g_timermgr));
+
+ result = dns_viewlist_find(&ns_g_server->viewlist, view->name,
+ view->rdclass, &pview);
+ if (result == ISC_R_SUCCESS)
+ old = pview->catzs;
+
+ if (old != NULL) {
+ dns_catz_catzs_detach(&view->catzs);
+ dns_catz_catzs_attach(pview->catzs, &view->catzs);
+ dns_catz_prereconfig(view->catzs);
+ }
+
+ while (zone_element != NULL) {
+ CHECK(configure_catz_zone(view, config, zone_element));
+ zone_element = cfg_list_next(zone_element);
+ }
+
+ if (old != NULL)
+ dns_catz_postreconfig(view->catzs);
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (pview != NULL)
+ dns_view_detach(&pview);
+
+ return (result);
+}
+
+#define CHECK_RRL(cond, pat, val1, val2) \
+ do { \
+ if (!(cond)) { \
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, \
+ pat, val1, val2); \
+ result = ISC_R_RANGE; \
+ goto cleanup; \
+ } \
+ } while (0)
+
+#define CHECK_RRL_RATE(rate, def, max_rate, name) \
+ do { \
+ obj = NULL; \
+ rrl->rate.str = name; \
+ result = cfg_map_get(map, name, &obj); \
+ if (result == ISC_R_SUCCESS) { \
+ rrl->rate.r = cfg_obj_asuint32(obj); \
+ CHECK_RRL(rrl->rate.r <= max_rate, \
+ name" %d > %d", \
+ rrl->rate.r, max_rate); \
+ } else { \
+ rrl->rate.r = def; \
+ } \
+ rrl->rate.scaled = rrl->rate.r; \
+ } while (0)
+
+static isc_result_t
+configure_rrl(dns_view_t *view, const cfg_obj_t *config, const cfg_obj_t *map) {
+ const cfg_obj_t *obj;
+ dns_rrl_t *rrl;
+ isc_result_t result;
+ int min_entries, i, j;
+
+ /*
+ * Most DNS servers have few clients, but intentinally open
+ * recursive and authoritative servers often have many.
+ * So start with a small number of entries unless told otherwise
+ * to reduce cold-start costs.
+ */
+ min_entries = 500;
+ obj = NULL;
+ result = cfg_map_get(map, "min-table-size", &obj);
+ if (result == ISC_R_SUCCESS) {
+ min_entries = cfg_obj_asuint32(obj);
+ if (min_entries < 1)
+ min_entries = 1;
+ }
+ result = dns_rrl_init(&rrl, view, min_entries);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ i = ISC_MAX(20000, min_entries);
+ obj = NULL;
+ result = cfg_map_get(map, "max-table-size", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= min_entries,
+ "max-table-size %d < min-table-size %d",
+ i, min_entries);
+ }
+ rrl->max_entries = i;
+
+ CHECK_RRL_RATE(responses_per_second, 0, DNS_RRL_MAX_RATE,
+ "responses-per-second");
+ CHECK_RRL_RATE(referrals_per_second,
+ rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
+ "referrals-per-second");
+ CHECK_RRL_RATE(nodata_per_second,
+ rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
+ "nodata-per-second");
+ CHECK_RRL_RATE(nxdomains_per_second,
+ rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
+ "nxdomains-per-second");
+ CHECK_RRL_RATE(errors_per_second,
+ rrl->responses_per_second.r, DNS_RRL_MAX_RATE,
+ "errors-per-second");
+
+ CHECK_RRL_RATE(all_per_second, 0, DNS_RRL_MAX_RATE,
+ "all-per-second");
+
+ CHECK_RRL_RATE(slip, 2, DNS_RRL_MAX_SLIP,
+ "slip");
+
+ i = 15;
+ obj = NULL;
+ result = cfg_map_get(map, "window", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= 1 && i <= DNS_RRL_MAX_WINDOW,
+ "window %d < 1 or > %d", i, DNS_RRL_MAX_WINDOW);
+ }
+ rrl->window = i;
+
+ i = 0;
+ obj = NULL;
+ result = cfg_map_get(map, "qps-scale", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= 1, "invalid 'qps-scale %d'%s", i, "");
+ }
+ rrl->qps_scale = i;
+ rrl->qps = 1.0;
+
+ i = 24;
+ obj = NULL;
+ result = cfg_map_get(map, "ipv4-prefix-length", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= 8 && i <= 32,
+ "invalid 'ipv4-prefix-length %d'%s", i, "");
+ }
+ rrl->ipv4_prefixlen = i;
+ if (i == 32)
+ rrl->ipv4_mask = 0xffffffff;
+ else
+ rrl->ipv4_mask = htonl(0xffffffff << (32-i));
+
+ i = 56;
+ obj = NULL;
+ result = cfg_map_get(map, "ipv6-prefix-length", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ CHECK_RRL(i >= 16 && i <= DNS_RRL_MAX_PREFIX,
+ "ipv6-prefix-length %d < 16 or > %d",
+ i, DNS_RRL_MAX_PREFIX);
+ }
+ rrl->ipv6_prefixlen = i;
+ for (j = 0; j < 4; ++j) {
+ if (i <= 0) {
+ rrl->ipv6_mask[j] = 0;
+ } else if (i < 32) {
+ rrl->ipv6_mask[j] = htonl(0xffffffff << (32-i));
+ } else {
+ rrl->ipv6_mask[j] = 0xffffffff;
+ }
+ i -= 32;
+ }
+
+ obj = NULL;
+ result = cfg_map_get(map, "exempt-clients", &obj);
+ if (result == ISC_R_SUCCESS) {
+ result = cfg_acl_fromconfig(obj, config, ns_g_lctx,
+ ns_g_aclconfctx, ns_g_mctx,
+ 0, &rrl->exempt);
+ CHECK_RRL(result == ISC_R_SUCCESS,
+ "invalid %s%s", "address match list", "");
+ }
+
+ obj = NULL;
+ result = cfg_map_get(map, "log-only", &obj);
+ if (result == ISC_R_SUCCESS && cfg_obj_asboolean(obj))
+ rrl->log_only = true;
+ else
+ rrl->log_only = false;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ dns_rrl_view_destroy(view);
+ return (result);
+}
+
+static isc_result_t
+add_soa(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_name_t *origin, dns_name_t *contact)
+{
+ dns_dbnode_t *node = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ unsigned char buf[DNS_SOA_BUFFERSIZE];
+
+ CHECK(dns_soa_buildrdata(origin, contact, dns_db_class(db),
+ 0, 28800, 7200, 604800, 86400, buf, &rdata));
+
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.type = rdata.type;
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.ttl = 86400;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ CHECK(dns_db_findnode(db, name, true, &node));
+ CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
+
+ cleanup:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_result_t
+add_ns(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
+ dns_name_t *nsname)
+{
+ dns_dbnode_t *node = NULL;
+ dns_rdata_ns_t ns;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdatalist_t rdatalist;
+ dns_rdataset_t rdataset;
+ isc_result_t result;
+ isc_buffer_t b;
+ unsigned char buf[DNS_NAME_MAXWIRE];
+
+ isc_buffer_init(&b, buf, sizeof(buf));
+
+ ns.common.rdtype = dns_rdatatype_ns;
+ ns.common.rdclass = dns_db_class(db);
+ ns.mctx = NULL;
+ dns_name_init(&ns.name, NULL);
+ dns_name_clone(nsname, &ns.name);
+ CHECK(dns_rdata_fromstruct(&rdata, dns_db_class(db), dns_rdatatype_ns,
+ &ns, &b));
+
+ dns_rdatalist_init(&rdatalist);
+ rdatalist.type = rdata.type;
+ rdatalist.rdclass = rdata.rdclass;
+ rdatalist.ttl = 86400;
+ ISC_LIST_APPEND(rdatalist.rdata, &rdata, link);
+
+ dns_rdataset_init(&rdataset);
+ CHECK(dns_rdatalist_tordataset(&rdatalist, &rdataset));
+ CHECK(dns_db_findnode(db, name, true, &node));
+ CHECK(dns_db_addrdataset(db, node, version, 0, &rdataset, 0, NULL));
+
+ cleanup:
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ return (result);
+}
+
+static isc_result_t
+create_empty_zone(dns_zone_t *zone, dns_name_t *name, dns_view_t *view,
+ const cfg_obj_t *zonelist, const char **empty_dbtype,
+ int empty_dbtypec, dns_zonestat_level_t statlevel)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ const cfg_listelt_t *element;
+ const cfg_obj_t *obj;
+ const cfg_obj_t *zconfig;
+ const cfg_obj_t *zoptions;
+ const char *rbt_dbtype[4] = { "rbt" };
+ const char *sep = ": view ";
+ const char *str;
+ const char *viewname = view->name;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_fixedname_t cfixed;
+ dns_fixedname_t fixed;
+ dns_fixedname_t nsfixed;
+ dns_name_t *contact;
+ dns_name_t *ns;
+ dns_name_t *zname;
+ dns_zone_t *myzone = NULL;
+ int rbt_dbtypec = 1;
+ isc_result_t result;
+ dns_namereln_t namereln;
+ int order;
+ unsigned int nlabels;
+
+ zname = dns_fixedname_initname(&fixed);
+ ns = dns_fixedname_initname(&nsfixed);
+ contact = dns_fixedname_initname(&cfixed);
+
+ /*
+ * Look for forward "zones" beneath this empty zone and if so
+ * create a custom db for the empty zone.
+ */
+ for (element = cfg_list_first(zonelist);
+ element != NULL;
+ element = cfg_list_next(element)) {
+
+ zconfig = cfg_listelt_value(element);
+ str = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+ CHECK(dns_name_fromstring(zname, str, 0, NULL));
+ namereln = dns_name_fullcompare(zname, name, &order, &nlabels);
+ if (namereln != dns_namereln_subdomain)
+ continue;
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "type", &obj);
+ if (obj != NULL &&
+ strcasecmp(cfg_obj_asstring(obj), "forward") == 0) {
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "forward", &obj);
+ if (obj == NULL)
+ continue;
+ if (strcasecmp(cfg_obj_asstring(obj), "only") != 0)
+ continue;
+ }
+ if (db == NULL) {
+ CHECK(dns_db_create(view->mctx, "rbt", name,
+ dns_dbtype_zone, view->rdclass,
+ 0, NULL, &db));
+ CHECK(dns_db_newversion(db, &version));
+ if (strcmp(empty_dbtype[2], "@") == 0)
+ dns_name_clone(name, ns);
+ else
+ CHECK(dns_name_fromstring(ns, empty_dbtype[2],
+ 0, NULL));
+ CHECK(dns_name_fromstring(contact, empty_dbtype[3],
+ 0, NULL));
+ CHECK(add_soa(db, version, name, ns, contact));
+ CHECK(add_ns(db, version, name, ns));
+ }
+ CHECK(add_ns(db, version, zname, dns_rootname));
+ }
+
+ /*
+ * Is the existing zone the ok to use?
+ */
+ if (zone != NULL) {
+ unsigned int typec;
+ const char **dbargv;
+
+ if (db != NULL) {
+ typec = rbt_dbtypec;
+ dbargv = rbt_dbtype;
+ } else {
+ typec = empty_dbtypec;
+ dbargv = empty_dbtype;
+ }
+
+ result = check_dbtype(zone, typec, dbargv, view->mctx);
+ if (result != ISC_R_SUCCESS)
+ zone = NULL;
+
+ if (zone != NULL && dns_zone_gettype(zone) != dns_zone_master)
+ zone = NULL;
+ if (zone != NULL && dns_zone_getfile(zone) != NULL)
+ zone = NULL;
+ if (zone != NULL) {
+ dns_zone_getraw(zone, &myzone);
+ if (myzone != NULL) {
+ dns_zone_detach(&myzone);
+ zone = NULL;
+ }
+ }
+ }
+
+ if (zone == NULL) {
+ CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &myzone));
+ zone = myzone;
+ CHECK(dns_zone_setorigin(zone, name));
+ CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
+ if (db == NULL)
+ CHECK(dns_zone_setdbtype(zone, empty_dbtypec,
+ empty_dbtype));
+ dns_zone_setclass(zone, view->rdclass);
+ dns_zone_settype(zone, dns_zone_master);
+ dns_zone_setstats(zone, ns_g_server->zonestats);
+ }
+
+ dns_zone_setoption(zone, ~DNS_ZONEOPT_NOCHECKNS, false);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true);
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+ dns_zone_setdialup(zone, dns_dialuptype_no);
+ dns_zone_setautomatic(zone, true);
+ if (view->queryacl != NULL)
+ dns_zone_setqueryacl(zone, view->queryacl);
+ else
+ dns_zone_clearqueryacl(zone);
+ if (view->queryonacl != NULL)
+ dns_zone_setqueryonacl(zone, view->queryonacl);
+ else
+ dns_zone_clearqueryonacl(zone);
+ dns_zone_clearupdateacl(zone);
+ if (view->transferacl != NULL)
+ dns_zone_setxfracl(zone, view->transferacl);
+ else
+ dns_zone_clearxfracl(zone);
+
+ CHECK(setquerystats(zone, view->mctx, statlevel));
+ if (db != NULL) {
+ dns_db_closeversion(db, &version, true);
+ CHECK(dns_zone_replacedb(zone, db, false));
+ }
+ dns_zone_setoption2(zone, DNS_ZONEOPT2_AUTOEMPTY, true);
+ dns_zone_setview(zone, view);
+ CHECK(dns_view_addzone(view, zone));
+
+ if (!strcmp(viewname, "_default")) {
+ sep = "";
+ viewname = "";
+ }
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "automatic empty zone%s%s: %s",
+ sep, viewname, namebuf);
+
+ cleanup:
+ if (myzone != NULL)
+ dns_zone_detach(&myzone);
+ if (version != NULL)
+ dns_db_closeversion(db, &version, false);
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ INSIST(version == NULL);
+
+ return (result);
+}
+
+#ifdef HAVE_DNSTAP
+static isc_result_t
+configure_dnstap(const cfg_obj_t **maps, dns_view_t *view) {
+ isc_result_t result;
+ const cfg_obj_t *obj, *obj2;
+ const cfg_listelt_t *element;
+ const char *dpath = ns_g_defaultdnstap;
+ const cfg_obj_t *dlist = NULL;
+ dns_dtmsgtype_t dttypes = 0;
+ dns_dtmode_t dmode;
+ unsigned int i;
+ struct fstrm_iothr_options *fopt = NULL;
+
+ result = ns_config_get(maps, "dnstap", &dlist);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_SUCCESS);
+
+ for (element = cfg_list_first(dlist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const char *str;
+ dns_dtmsgtype_t dt = 0;
+
+ obj = cfg_listelt_value(element);
+ obj2 = cfg_tuple_get(obj, "type");
+ str = cfg_obj_asstring(obj2);
+ if (strcasecmp(str, "client") == 0) {
+ dt |= DNS_DTTYPE_CQ|DNS_DTTYPE_CR;
+ } else if (strcasecmp(str, "auth") == 0) {
+ dt |= DNS_DTTYPE_AQ|DNS_DTTYPE_AR;
+ } else if (strcasecmp(str, "resolver") == 0) {
+ dt |= DNS_DTTYPE_RQ|DNS_DTTYPE_RR;
+ } else if (strcasecmp(str, "forwarder") == 0) {
+ dt |= DNS_DTTYPE_FQ|DNS_DTTYPE_FR;
+ } else if (strcasecmp(str, "all") == 0) {
+ dt |= DNS_DTTYPE_CQ|DNS_DTTYPE_CR|
+ DNS_DTTYPE_AQ|DNS_DTTYPE_AR|
+ DNS_DTTYPE_RQ|DNS_DTTYPE_RR|
+ DNS_DTTYPE_FQ|DNS_DTTYPE_FR;
+ }
+
+ obj2 = cfg_tuple_get(obj, "mode");
+ if (obj2 == NULL || cfg_obj_isvoid(obj2)) {
+ dttypes |= dt;
+ continue;
+ }
+
+ str = cfg_obj_asstring(obj2);
+ if (strcasecmp(str, "query") == 0) {
+ dt &= ~DNS_DTTYPE_RESPONSE;
+ } else if (strcasecmp(str, "response") == 0) {
+ dt &= ~DNS_DTTYPE_QUERY;
+ }
+
+ dttypes |= dt;
+ }
+
+ if (ns_g_server->dtenv == NULL && dttypes != 0) {
+ obj = NULL;
+ CHECKM(ns_config_get(maps, "dnstap-output", &obj),
+ "'dnstap-output' must be set if 'dnstap' is set");
+
+ obj2 = cfg_tuple_get(obj, "mode");
+ if (obj2 == NULL)
+ CHECKM(ISC_R_FAILURE, "dnstap-output mode not found");
+ if (strcasecmp(cfg_obj_asstring(obj2), "file") == 0)
+ dmode = dns_dtmode_file;
+ else
+ dmode = dns_dtmode_unix;
+
+ obj2 = cfg_tuple_get(obj, "path");
+ if (obj2 == NULL)
+ CHECKM(ISC_R_FAILURE, "dnstap-output path not found");
+
+ dpath = cfg_obj_asstring(obj2);
+
+ fopt = fstrm_iothr_options_init();
+ fstrm_iothr_options_set_num_input_queues(fopt, ns_g_cpus);
+ fstrm_iothr_options_set_queue_model(fopt,
+ FSTRM_IOTHR_QUEUE_MODEL_MPSC);
+
+ obj = NULL;
+ result = ns_config_get(maps, "fstrm-set-buffer-hint", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_buffer_hint(fopt, i);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "fstrm-set-flush-timeout", &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_flush_timeout(fopt, i);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "fstrm-set-input-queue-size",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_input_queue_size(fopt, i);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps,
+ "fstrm-set-output-notify-threshold",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_queue_notify_threshold(fopt,
+ i);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "fstrm-set-output-queue-model",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ if (strcasecmp(cfg_obj_asstring(obj), "spsc") == 0)
+ i = FSTRM_IOTHR_QUEUE_MODEL_SPSC;
+ else
+ i = FSTRM_IOTHR_QUEUE_MODEL_MPSC;
+ fstrm_iothr_options_set_queue_model(fopt, i);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "fstrm-set-output-queue-size",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_output_queue_size(fopt, i);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "fstrm-set-reopen-interval",
+ &obj);
+ if (result == ISC_R_SUCCESS) {
+ i = cfg_obj_asuint32(obj);
+ fstrm_iothr_options_set_reopen_interval(fopt, i);
+ }
+
+ CHECKM(dns_dt_create(ns_g_mctx, dmode, dpath, &fopt,
+ &ns_g_server->dtenv),
+ "unable to create dnstap environment");
+ }
+
+ if (ns_g_server->dtenv == NULL)
+ return (ISC_R_SUCCESS);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnstap-version", &obj);
+ if (result != ISC_R_SUCCESS) {
+ /* not specified; use the product and version */
+ dns_dt_setversion(ns_g_server->dtenv, PRODUCT " " VERSION);
+ } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) {
+ /* Quoted string */
+ dns_dt_setversion(ns_g_server->dtenv, cfg_obj_asstring(obj));
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnstap-identity", &obj);
+ if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
+ /* "hostname" is interpreted as boolean true */
+ char buf[256];
+ result = ns_os_gethostname(buf, sizeof(buf));
+ if (result == ISC_R_SUCCESS)
+ dns_dt_setidentity(ns_g_server->dtenv, buf);
+ } else if (result == ISC_R_SUCCESS && !cfg_obj_isvoid(obj)) {
+ /* Quoted string */
+ dns_dt_setidentity(ns_g_server->dtenv, cfg_obj_asstring(obj));
+ }
+
+ dns_dt_attach(ns_g_server->dtenv, &view->dtenv);
+ view->dttypes = dttypes;
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (fopt != NULL)
+ fstrm_iothr_options_destroy(&fopt);
+
+ return (result);
+}
+#endif /* HAVE_DNSTAP */
+
+static isc_result_t
+create_mapped_acl(void) {
+ isc_result_t result;
+ dns_acl_t *acl = NULL;
+ struct in6_addr in6 = IN6ADDR_V4MAPPED_INIT;
+ isc_netaddr_t addr;
+
+ isc_netaddr_fromin6(&addr, &in6);
+
+ result = dns_acl_create(ns_g_mctx, 1, &acl);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_iptable_addprefix2(acl->iptable, &addr, 96,
+ true, false);
+ if (result == ISC_R_SUCCESS)
+ dns_acl_attach(acl, &ns_g_mapped);
+ dns_acl_detach(&acl);
+ return (result);
+}
+
+/*
+ * 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, dns_viewlist_t *viewlist,
+ cfg_obj_t *config, cfg_obj_t *vconfig,
+ ns_cachelist_t *cachelist, const cfg_obj_t *bindkeys,
+ isc_mem_t *mctx, cfg_aclconfctx_t *actx,
+ bool need_hints)
+{
+ const cfg_obj_t *maps[4];
+ const cfg_obj_t *cfgmaps[3];
+ const cfg_obj_t *optionmaps[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;
+ const cfg_obj_t *dlzlist;
+ const cfg_obj_t *dlz;
+ const cfg_obj_t *dlvobj = NULL;
+ unsigned int dlzargc;
+ char **dlzargv;
+ const cfg_obj_t *dyndb_list;
+ const cfg_obj_t *disabled;
+ const cfg_obj_t *obj, *obj2;
+ const cfg_listelt_t *element;
+ in_port_t port;
+ dns_cache_t *cache = NULL;
+ isc_result_t result;
+ unsigned int cleaning_interval;
+ size_t max_cache_size;
+ uint32_t max_cache_size_percent = 0;
+ size_t max_acache_size;
+ size_t max_adb_size;
+ uint32_t lame_ttl, fail_ttl;
+ dns_tsig_keyring_t *ring = NULL;
+ dns_view_t *pview = NULL; /* Production view */
+ isc_mem_t *cmctx = NULL, *hmctx = NULL;
+ dns_dispatch_t *dispatch4 = NULL;
+ dns_dispatch_t *dispatch6 = NULL;
+ bool reused_cache = false;
+ bool shared_cache = false;
+ int i = 0, j = 0, k = 0;
+ const char *str;
+ const char *cachename = NULL;
+ dns_order_t *order = NULL;
+ uint32_t udpsize;
+ uint32_t maxbits;
+ unsigned int resopts = 0;
+ dns_zone_t *zone = NULL;
+ uint32_t max_clients_per_query;
+ bool empty_zones_enable;
+ const cfg_obj_t *disablelist = NULL;
+ isc_stats_t *resstats = NULL;
+ dns_stats_t *resquerystats = NULL;
+ bool auto_root = false;
+ ns_cache_t *nsc;
+ bool zero_no_soattl;
+ dns_acl_t *clients = NULL, *mapped = NULL, *excluded = NULL;
+ unsigned int query_timeout, ndisp;
+ bool old_rpz_ok = false;
+ isc_dscp_t dscp4 = -1, dscp6 = -1;
+ dns_dyndbctx_t *dctx = NULL;
+
+ REQUIRE(DNS_VIEW_VALID(view));
+
+ if (config != NULL)
+ (void)cfg_map_get(config, "options", &options);
+
+ /*
+ * maps: view options, options, defaults
+ * cfgmaps: view options, config
+ * optionmaps: view options, options
+ */
+ if (vconfig != NULL) {
+ voptions = cfg_tuple_get(vconfig, "options");
+ maps[i++] = voptions;
+ optionmaps[j++] = voptions;
+ cfgmaps[k++] = voptions;
+ }
+ if (options != NULL) {
+ maps[i++] = options;
+ optionmaps[j++] = options;
+ }
+
+ maps[i++] = ns_g_defaults;
+ maps[i] = NULL;
+ optionmaps[j] = NULL;
+ if (config != NULL)
+ cfgmaps[k++] = config;
+ cfgmaps[k] = NULL;
+
+ /*
+ * 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 = 0;
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > SIZE_MAX) {
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_WARNING,
+ "'max-acache-size "
+ "%" PRIu64 "' "
+ "is too large for this "
+ "system; reducing to %lu",
+ value, (unsigned long)SIZE_MAX);
+ value = SIZE_MAX;
+ }
+ max_acache_size = (size_t) value;
+ }
+ dns_acache_setcachesize(view->acache, max_acache_size);
+ }
+
+ /*
+ * Make the list of response policy zone names for a view that
+ * is used for real lookups and so cares about hints.
+ */
+ obj = NULL;
+ if (view->rdclass == dns_rdataclass_in && need_hints &&
+ ns_config_get(maps, "response-policy", &obj) == ISC_R_SUCCESS) {
+ CHECK(configure_rpz(view, obj, &old_rpz_ok));
+ }
+
+ obj = NULL;
+ if (view->rdclass == dns_rdataclass_in && need_hints &&
+ ns_config_get(maps, "catalog-zones", &obj) == ISC_R_SUCCESS) {
+ CHECK(configure_catz(view, config, obj));
+ }
+
+ /*
+ * Configure the zones.
+ */
+ zonelist = NULL;
+ if (voptions != NULL)
+ (void)cfg_map_get(voptions, "zone", &zonelist);
+ else
+ (void)cfg_map_get(config, "zone", &zonelist);
+
+ /*
+ * Load zone configuration
+ */
+ 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,
+ viewlist, actx, false, old_rpz_ok,
+ false));
+ }
+
+ /*
+ * If we're allowing added zones, then load zone configuration
+ * from the newzone file for zones that were added during previous
+ * runs.
+ */
+ CHECK(configure_newzones(view, config, vconfig, mctx, actx));
+
+ /*
+ * Create Dynamically Loadable Zone driver.
+ */
+ dlzlist = NULL;
+ if (voptions != NULL)
+ (void)cfg_map_get(voptions, "dlz", &dlzlist);
+ else
+ (void)cfg_map_get(config, "dlz", &dlzlist);
+
+ for (element = cfg_list_first(dlzlist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ dlz = cfg_listelt_value(element);
+
+ obj = NULL;
+ (void)cfg_map_get(dlz, "database", &obj);
+ if (obj != NULL) {
+ dns_dlzdb_t *dlzdb = NULL;
+ const cfg_obj_t *name, *search = NULL;
+ char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
+
+ if (s == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+
+ result = isc_commandline_strtoargv(mctx, s, &dlzargc,
+ &dlzargv, 0);
+ if (result != ISC_R_SUCCESS) {
+ isc_mem_free(mctx, s);
+ goto cleanup;
+ }
+
+ name = cfg_map_getname(dlz);
+ result = dns_dlzcreate(mctx, cfg_obj_asstring(name),
+ dlzargv[0], dlzargc, dlzargv,
+ &dlzdb);
+ isc_mem_free(mctx, s);
+ isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /*
+ * If the DLZ backend supports configuration,
+ * and is searchable, then call its configure
+ * method now. If not searchable, we'll take
+ * care of it when we process the zone statement.
+ */
+ (void)cfg_map_get(dlz, "search", &search);
+ if (search == NULL || cfg_obj_asboolean(search)) {
+ dlzdb->search = true;
+ result = dns_dlzconfigure(view, dlzdb,
+ dlzconfigure_callback);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ ISC_LIST_APPEND(view->dlz_searched,
+ dlzdb, link);
+ } else {
+ dlzdb->search = false;
+ ISC_LIST_APPEND(view->dlz_unsearched,
+ dlzdb, link);
+ }
+
+ }
+ }
+
+ /*
+ * Obtain configuration parameters that affect the decision of whether
+ * we can reuse/share an existing cache.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "cleaning-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ cleaning_interval = 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 = 0;
+ } else if (cfg_obj_ispercentage(obj)) {
+ max_cache_size = SIZE_AS_PERCENT;
+ max_cache_size_percent = cfg_obj_aspercentage(obj);
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > SIZE_MAX) {
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_WARNING,
+ "'max-cache-size "
+ "%" PRIu64 "' "
+ "is too large for this "
+ "system; reducing to %lu",
+ value, (unsigned long)SIZE_MAX);
+ value = SIZE_MAX;
+ }
+ max_cache_size = (size_t) value;
+ }
+
+ if (max_cache_size == SIZE_AS_PERCENT) {
+ uint64_t totalphys = isc_meminfo_totalphys();
+
+ max_cache_size =
+ (size_t) (totalphys * max_cache_size_percent/100);
+ if (totalphys == 0) {
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_WARNING,
+ "Unable to determine amount of physical "
+ "memory, setting 'max-cache-size' to "
+ "unlimited");
+ } else {
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_INFO,
+ "'max-cache-size %d%%' "
+ "- setting to %" PRIu64 "MB "
+ "(out of %" PRIu64 "MB)",
+ max_cache_size_percent,
+ (uint64_t)(max_cache_size / (1024*1024)),
+ totalphys / (1024*1024));
+ }
+ }
+
+ /* 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 = true;
+ } else if (strcasecmp(str, "warn") == 0) {
+ resopts |= DNS_RESOLVER_CHECKNAMES;
+ view->checknames = false;
+ } else if (strcasecmp(str, "ignore") == 0) {
+ view->checknames = false;
+ } else
+ INSIST(0);
+
+ obj = NULL;
+ result = ns_config_get(maps, "zero-no-soa-ttl-cache", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ zero_no_soattl = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dns64", &obj);
+ if (result == ISC_R_SUCCESS && strcmp(view->name, "_bind") &&
+ strcmp(view->name, "_meta")) {
+ isc_netaddr_t na, suffix, *sp;
+ unsigned int prefixlen;
+ const char *server, *contact;
+ const cfg_obj_t *myobj;
+
+ myobj = NULL;
+ result = ns_config_get(maps, "dns64-server", &myobj);
+ if (result == ISC_R_SUCCESS)
+ server = cfg_obj_asstring(myobj);
+ else
+ server = NULL;
+
+ myobj = NULL;
+ result = ns_config_get(maps, "dns64-contact", &myobj);
+ if (result == ISC_R_SUCCESS)
+ contact = cfg_obj_asstring(myobj);
+ else
+ contact = NULL;
+
+ for (element = cfg_list_first(obj);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *map = cfg_listelt_value(element);
+ dns_dns64_t *dns64 = NULL;
+ unsigned int dns64options = 0;
+
+ cfg_obj_asnetprefix(cfg_map_getname(map), &na,
+ &prefixlen);
+
+ obj = NULL;
+ (void)cfg_map_get(map, "suffix", &obj);
+ if (obj != NULL) {
+ sp = &suffix;
+ isc_netaddr_fromsockaddr(sp,
+ cfg_obj_assockaddr(obj));
+ } else
+ sp = NULL;
+
+ clients = mapped = excluded = NULL;
+ obj = NULL;
+ (void)cfg_map_get(map, "clients", &obj);
+ if (obj != NULL) {
+ result = cfg_acl_fromconfig(obj, config,
+ ns_g_lctx, actx,
+ mctx, 0, &clients);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ obj = NULL;
+ (void)cfg_map_get(map, "mapped", &obj);
+ if (obj != NULL) {
+ result = cfg_acl_fromconfig(obj, config,
+ ns_g_lctx, actx,
+ mctx, 0, &mapped);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ obj = NULL;
+ (void)cfg_map_get(map, "exclude", &obj);
+ if (obj != NULL) {
+ result = cfg_acl_fromconfig(obj, config,
+ ns_g_lctx, actx,
+ mctx, 0, &excluded);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ } else {
+ if (ns_g_mapped == NULL) {
+ result = create_mapped_acl();
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+ dns_acl_attach(ns_g_mapped, &excluded);
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(map, "recursive-only", &obj);
+ if (obj != NULL && cfg_obj_asboolean(obj))
+ dns64options |= DNS_DNS64_RECURSIVE_ONLY;
+
+ obj = NULL;
+ (void)cfg_map_get(map, "break-dnssec", &obj);
+ if (obj != NULL && cfg_obj_asboolean(obj))
+ dns64options |= DNS_DNS64_BREAK_DNSSEC;
+
+ result = dns_dns64_create(mctx, &na, prefixlen, sp,
+ clients, mapped, excluded,
+ dns64options, &dns64);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_dns64_append(&view->dns64, dns64);
+ view->dns64cnt++;
+ result = dns64_reverse(view, mctx, &na, prefixlen,
+ server, contact);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (clients != NULL)
+ dns_acl_detach(&clients);
+ if (mapped != NULL)
+ dns_acl_detach(&mapped);
+ if (excluded != NULL)
+ dns_acl_detach(&excluded);
+ }
+ }
+
+ 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);
+ if (cfg_obj_isboolean(obj)) {
+ view->enablevalidation = cfg_obj_asboolean(obj);
+ } else {
+ /* If dnssec-validation is not boolean, it must be "auto" */
+ view->enablevalidation = true;
+ auto_root = true;
+ }
+
+ 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;
+
+ /*
+ * Configure the view's cache.
+ *
+ * First, check to see if there are any attach-cache options. If yes,
+ * attempt to lookup an existing cache at attach it to the view. If
+ * there is not one, then try to reuse an existing cache if possible;
+ * otherwise create a new cache.
+ *
+ * Note that the ADB is not preserved or shared 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 or share 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. We check
+ * some of the configuration parameters that could invalidate the cache
+ * or otherwise make it unsharable, but there are other configuration
+ * options that should be checked. For example, if a view uses a
+ * forwarder, changes in the forwarder configuration may invalidate
+ * the cache. At the moment, it's the administrator's responsibility to
+ * ensure these configuration options don't invalidate reusing/sharing.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "attach-cache", &obj);
+ if (result == ISC_R_SUCCESS)
+ cachename = cfg_obj_asstring(obj);
+ else
+ cachename = view->name;
+ cache = NULL;
+ nsc = cachelist_find(cachelist, cachename, view->rdclass);
+ if (nsc != NULL) {
+ if (!cache_sharable(nsc->primaryview, view, zero_no_soattl,
+ cleaning_interval, max_cache_size)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "views %s and %s can't share the cache "
+ "due to configuration parameter mismatch",
+ nsc->primaryview->name, view->name);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ dns_cache_attach(nsc->cache, &cache);
+ shared_cache = true;
+ } else {
+ if (strcmp(cachename, view->name) == 0) {
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ cachename, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (pview != NULL) {
+ if (!cache_reusable(pview, view,
+ zero_no_soattl)) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_DEBUG(1),
+ "cache cannot be reused "
+ "for view %s due to "
+ "configuration parameter "
+ "mismatch", view->name);
+ } else {
+ 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 = true;
+ dns_cache_attach(pview->cache, &cache);
+ }
+ dns_view_getresstats(pview, &resstats);
+ dns_view_getresquerystats(pview,
+ &resquerystats);
+ dns_view_detach(&pview);
+ }
+ }
+ if (cache == NULL) {
+ /*
+ * Create a cache with the desired name. This normally
+ * equals the view name, but may also be a forward
+ * reference to a view that share the cache with this
+ * view but is not yet configured. If it is not the
+ * view name but not a forward reference either, then it
+ * is simply a named cache that is not shared.
+ *
+ * We use two separate memory contexts for the
+ * cache, for the main cache memory and the heap
+ * memory.
+ */
+ CHECK(isc_mem_create(0, 0, &cmctx));
+ isc_mem_setname(cmctx, "cache", NULL);
+ CHECK(isc_mem_create(0, 0, &hmctx));
+ isc_mem_setname(hmctx, "cache_heap", NULL);
+ CHECK(dns_cache_create3(cmctx, hmctx, ns_g_taskmgr,
+ ns_g_timermgr, view->rdclass,
+ cachename, "rbt", 0, NULL,
+ &cache));
+ isc_mem_detach(&cmctx);
+ isc_mem_detach(&hmctx);
+ }
+ nsc = isc_mem_get(mctx, sizeof(*nsc));
+ if (nsc == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ nsc->cache = NULL;
+ dns_cache_attach(cache, &nsc->cache);
+ nsc->primaryview = view;
+ nsc->needflush = false;
+ nsc->adbsizeadjusted = false;
+ nsc->rdclass = view->rdclass;
+ ISC_LINK_INIT(nsc, link);
+ ISC_LIST_APPEND(*cachelist, nsc, link);
+ }
+ dns_view_setcache2(view, cache, shared_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 && !shared_cache)
+ CHECK(dns_cache_load(cache));
+ }
+
+ dns_cache_setcleaninginterval(cache, cleaning_interval);
+ dns_cache_setcachesize(cache, max_cache_size);
+
+ dns_cache_detach(&cache);
+
+ /*
+ * Resolver.
+ *
+ * XXXRTH Hardwired number of tasks.
+ */
+ CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4, &dscp4,
+ (ISC_LIST_PREV(view, link)
+ == NULL)));
+ CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6, &dscp6,
+ (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;
+ }
+
+ if (resstats == NULL) {
+ CHECK(isc_stats_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);
+
+ ndisp = 4 * ISC_MIN(ns_g_udpdisp, MAX_UDP_DISPATCH);
+ CHECK(dns_view_createresolver(view, ns_g_taskmgr, RESOLVER_NTASKS,
+ ndisp, ns_g_socketmgr, ns_g_timermgr,
+ resopts, ns_g_dispatchmgr,
+ dispatch4, dispatch6));
+
+ if (dscp4 == -1)
+ dscp4 = ns_g_dscp;
+ if (dscp6 == -1)
+ dscp6 = ns_g_dscp;
+ if (dscp4 != -1)
+ dns_resolver_setquerydscp4(view->resolver, dscp4);
+ if (dscp6 != -1)
+ dns_resolver_setquerydscp6(view->resolver, dscp6);
+
+ /*
+ * Set the ADB cache size to 1/8th of the max-cache-size or
+ * MAX_ADB_SIZE_FOR_CACHESHARE when the cache is shared.
+ */
+ max_adb_size = 0;
+ if (max_cache_size != 0U) {
+ max_adb_size = max_cache_size / 8;
+ if (max_adb_size == 0U)
+ max_adb_size = 1; /* Force minimum. */
+ if (view != nsc->primaryview &&
+ max_adb_size > MAX_ADB_SIZE_FOR_CACHESHARE) {
+ max_adb_size = MAX_ADB_SIZE_FOR_CACHESHARE;
+ if (!nsc->adbsizeadjusted) {
+ dns_adb_setadbsize(nsc->primaryview->adb,
+ MAX_ADB_SIZE_FOR_CACHESHARE);
+ nsc->adbsizeadjusted = true;
+ }
+ }
+ }
+ dns_adb_setadbsize(view->adb, max_adb_size);
+
+ /*
+ * Set up ADB quotas
+ */
+ {
+ uint32_t fps, freq;
+ double low, high, discount;
+
+ obj = NULL;
+ result = ns_config_get(maps, "fetches-per-server", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ obj2 = cfg_tuple_get(obj, "fetches");
+ fps = cfg_obj_asuint32(obj2);
+ obj2 = cfg_tuple_get(obj, "response");
+ if (!cfg_obj_isvoid(obj2)) {
+ const char *resp = cfg_obj_asstring(obj2);
+ isc_result_t r;
+
+ if (strcasecmp(resp, "drop") == 0)
+ r = DNS_R_DROP;
+ else if (strcasecmp(resp, "fail") == 0)
+ r = DNS_R_SERVFAIL;
+ else
+ INSIST(0);
+
+ dns_resolver_setquotaresponse(view->resolver,
+ dns_quotatype_server, r);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "fetch-quota-params", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+
+ obj2 = cfg_tuple_get(obj, "frequency");
+ freq = cfg_obj_asuint32(obj2);
+
+ obj2 = cfg_tuple_get(obj, "low");
+ low = (double) cfg_obj_asfixedpoint(obj2) / 100.0;
+
+ obj2 = cfg_tuple_get(obj, "high");
+ high = (double) cfg_obj_asfixedpoint(obj2) / 100.0;
+
+ obj2 = cfg_tuple_get(obj, "discount");
+ discount = (double) cfg_obj_asfixedpoint(obj2) / 100.0;
+
+ dns_adb_setquota(view->adb, fps, freq, low, high, discount);
+ }
+
+ /*
+ * 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);
+
+ /*
+ * Set the resolver's query timeout.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "resolver-query-timeout", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ query_timeout = cfg_obj_asuint32(obj);
+ dns_resolver_settimeout(view->resolver, query_timeout);
+
+ /* Specify whether to use 0-TTL for negative response for SOA query */
+ dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl);
+
+ /*
+ * 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, (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 the maximum UDP when a COOKIE is not provided.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "nocookie-udp-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ udpsize = cfg_obj_asuint32(obj);
+ if (udpsize < 128)
+ udpsize = 128;
+ if (udpsize > view->maxudp)
+ udpsize = view->maxudp;
+ view->nocookieudp = udpsize;
+
+ /*
+ * Set the maximum rsa exponent bits.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "max-rsa-exponent-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ maxbits = cfg_obj_asuint32(obj);
+ if (maxbits != 0 && maxbits < 35)
+ maxbits = 35;
+ if (maxbits > 4096)
+ maxbits = 4096;
+ view->maxbits = maxbits;
+
+ /*
+ * 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));
+ }
+
+ /*
+ * Set supported DS/DLV digest types.
+ */
+ dns_resolver_reset_ds_digests(view->resolver);
+ disabled = NULL;
+ (void)ns_config_get(maps, "disable-ds-digests", &disabled);
+ if (disabled != NULL) {
+ for (element = cfg_list_first(disabled);
+ element != NULL;
+ element = cfg_list_next(element))
+ CHECK(disable_ds_digests(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 = 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.
+ */
+ CHECK(ns_tsigkeyring_fromconfig(config, vconfig, view->mctx, &ring));
+ if (ns_g_server->sessionkey != NULL) {
+ CHECK(dns_tsigkeyring_add(ring, ns_g_server->session_keyname,
+ ns_g_server->sessionkey));
+ }
+ dns_view_setkeyring(view, ring);
+ dns_tsigkeyring_detach(&ring);
+
+ /*
+ * See if we can re-use a dynamic key ring.
+ */
+ 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) {
+ dns_view_getdynamickeyring(pview, &ring);
+ if (ring != NULL)
+ dns_view_setdynamickeyring(view, ring);
+ dns_tsigkeyring_detach(&ring);
+ dns_view_detach(&pview);
+ } else
+ dns_view_restorekeyring(view);
+
+ /*
+ * Configure the view's peer list.
+ */
+ {
+ const cfg_obj_t *peers = NULL;
+ 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;
+
+ (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.
+ * (These are only meaningful at the view level, but 'config'
+ * must be passed so that named ACLs defined at the global level
+ * can be retrieved.)
+ */
+ CHECK(configure_view_acl(vconfig, config, NULL, "match-clients",
+ NULL, actx, ns_g_mctx,
+ &view->matchclients));
+ CHECK(configure_view_acl(vconfig, config, NULL, "match-destinations",
+ NULL, 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 = true;
+ else
+ view->matchrecursiveonly = 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-any", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->minimal_any = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "minimal-responses", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj))
+ view->minimalresponses = dns_minimal_yes;
+ else
+ view->minimalresponses = dns_minimal_no;
+ } else {
+ str = cfg_obj_asstring(obj);
+ if (strcasecmp(str, "no-auth") == 0) {
+ view->minimalresponses = dns_minimal_noauth;
+ } else if (strcasecmp(str, "no-auth-recursive") == 0) {
+ view->minimalresponses = dns_minimal_noauthrec;
+ } else
+ INSIST(0);
+ }
+
+ 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);
+
+ obj = NULL;
+ result = ns_config_get(maps, "trust-anchor-telemetry", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->trust_anchor_telemetry = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "root-key-sentinel", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->root_key_sentinel = cfg_obj_asboolean(obj);
+
+ /*
+ * 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 = 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 = true;
+ }
+
+ CHECK(configure_view_acl(vconfig, config, ns_g_config,
+ "allow-query-cache-on", NULL, actx,
+ ns_g_mctx, &view->cacheonacl));
+
+ /*
+ * Set the "allow-query", "allow-query-cache", "allow-recursion",
+ * and "allow-recursion-on" ACLs if configured in named.conf, but
+ * NOT from the global defaults. This is done by leaving the third
+ * argument to configure_view_acl() NULL.
+ *
+ * We ignore the global defaults here because these ACLs
+ * can inherit from each other. If any are still unset after
+ * applying the inheritance rules, we'll look up the defaults at
+ * that time.
+ */
+
+ /* named.conf only */
+ CHECK(configure_view_acl(vconfig, config, NULL,
+ "allow-query", NULL, actx,
+ ns_g_mctx, &view->queryacl));
+
+ /* named.conf only */
+ CHECK(configure_view_acl(vconfig, config, NULL,
+ "allow-query-cache", NULL, actx,
+ ns_g_mctx, &view->cacheacl));
+
+ if (strcmp(view->name, "_bind") != 0 &&
+ view->rdclass != dns_rdataclass_chaos)
+ {
+ /* named.conf only */
+ CHECK(configure_view_acl(vconfig, config, NULL,
+ "allow-recursion", NULL, actx,
+ ns_g_mctx, &view->recursionacl));
+ /* named.conf only */
+ CHECK(configure_view_acl(vconfig, config, NULL,
+ "allow-recursion-on", NULL, actx,
+ ns_g_mctx, &view->recursiononacl));
+ }
+
+ if (view->recursion) {
+ /*
+ * "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->cacheacl == NULL) {
+ if (view->recursionacl != NULL) {
+ dns_acl_attach(view->recursionacl,
+ &view->cacheacl);
+ } else if (view->queryacl != NULL) {
+ dns_acl_attach(view->queryacl,
+ &view->cacheacl);
+ }
+ }
+ if (view->recursionacl == NULL) {
+ if (view->cacheacl != NULL) {
+ dns_acl_attach(view->cacheacl,
+ &view->recursionacl);
+ } else if (view->queryacl != NULL) {
+ dns_acl_attach(view->queryacl,
+ &view->recursionacl);
+ }
+ }
+
+ /*
+ * If any are still unset, we now get default "allow-recursion",
+ * "allow-recursion-on" and "allow-query-cache" ACLs from
+ * the global config.
+ */
+ if (view->recursionacl == NULL) {
+ /* global default only */
+ CHECK(configure_view_acl(NULL, NULL, ns_g_config,
+ "allow-recursion", NULL,
+ actx, ns_g_mctx,
+ &view->recursionacl));
+ }
+ if (view->recursiononacl == NULL) {
+ /* global default only */
+ CHECK(configure_view_acl(NULL, NULL, ns_g_config,
+ "allow-recursion-on", NULL,
+ actx, ns_g_mctx,
+ &view->recursiononacl));
+ }
+ if (view->cacheacl == NULL) {
+ /* global default only */
+ CHECK(configure_view_acl(NULL, NULL, ns_g_config,
+ "allow-query-cache", NULL,
+ actx, ns_g_mctx,
+ &view->cacheacl));
+ }
+ } else if (view->cacheacl == NULL) {
+ /*
+ * We're not recursive; if "allow-query-cache" hasn't been
+ * set at the options/view level, set it to none.
+ */
+ CHECK(dns_acl_none(mctx, &view->cacheacl));
+ }
+
+ if (view->queryacl == NULL) {
+ /* global default only */
+ CHECK(configure_view_acl(NULL, NULL, ns_g_config,
+ "allow-query", NULL,
+ actx, ns_g_mctx,
+ &view->queryacl));
+ }
+
+ /*
+ * Ignore case when compressing responses to the specified
+ * clients. This causes case not always to be preserved,
+ * and is needed by some broken clients.
+ */
+ CHECK(configure_view_acl(vconfig, config, ns_g_config,
+ "no-case-compress", NULL, actx,
+ ns_g_mctx, &view->nocasecompress));
+
+ /*
+ * Disable name compression completely, this is a tradeoff
+ * between CPU and network usage.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "message-compression", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->msgcompression = cfg_obj_asboolean(obj);
+
+ /*
+ * Filter setting on addresses in the answer section.
+ */
+ CHECK(configure_view_acl(vconfig, config, ns_g_config,
+ "deny-answer-addresses", "acl",
+ actx, ns_g_mctx,
+ &view->denyansweracl));
+ CHECK(configure_view_nametable(vconfig, config, "deny-answer-addresses",
+ "except-from", ns_g_mctx,
+ &view->answeracl_exclude));
+
+ /*
+ * Filter setting on names (CNAME/DNAME targets) in the answer section.
+ */
+ CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
+ "name", ns_g_mctx,
+ &view->denyanswernames));
+ CHECK(configure_view_nametable(vconfig, config, "deny-answer-aliases",
+ "except-from", ns_g_mctx,
+ &view->answernames_exclude));
+
+ /*
+ * Configure sortlist, if set
+ */
+ CHECK(configure_view_sortlist(vconfig, config, actx, ns_g_mctx,
+ &view->sortlist));
+
+ /*
+ * Configure default allow-notify, allow-update
+ * and allow-update-forwarding ACLs, so they can be
+ * inherited by zones. (Note these cannot be set at
+ * options/view level.)
+ */
+ if (view->notifyacl == NULL) {
+ CHECK(configure_view_acl(vconfig, config, ns_g_config,
+ "allow-notify", NULL, actx,
+ ns_g_mctx, &view->notifyacl));
+ }
+ if (view->updateacl == NULL) {
+ CHECK(configure_view_acl(NULL, NULL, ns_g_config,
+ "allow-update", NULL, actx,
+ ns_g_mctx, &view->updateacl));
+ }
+ if (view->upfwdacl == NULL) {
+ CHECK(configure_view_acl(NULL, NULL, ns_g_config,
+ "allow-update-forwarding", NULL, actx,
+ ns_g_mctx, &view->upfwdacl));
+ }
+
+ /*
+ * Configure default allow-transer ACL so it can be inherited
+ * by zones. (Note this *can* be set at options or view level.)
+ */
+ if (view->transferacl == NULL) {
+ CHECK(configure_view_acl(vconfig, config, ns_g_config,
+ "allow-transfer", NULL, actx,
+ ns_g_mctx, &view->transferacl));
+ }
+
+ 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, "send-cookie", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->sendcookie = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "require-server-cookie", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->requireservercookie = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "v6-bias", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->v6bias = cfg_obj_asuint32(obj) * 1000;
+
+ 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, "max-recursion-depth", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setmaxdepth(view->resolver, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-recursion-queries", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_resolver_setmaxqueries(view->resolver, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "fetches-per-zone", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ obj2 = cfg_tuple_get(obj, "fetches");
+ dns_resolver_setfetchesperzone(view->resolver, cfg_obj_asuint32(obj2));
+ obj2 = cfg_tuple_get(obj, "response");
+ if (!cfg_obj_isvoid(obj2)) {
+ const char *resp = cfg_obj_asstring(obj2);
+ isc_result_t r;
+
+ if (strcasecmp(resp, "drop") == 0)
+ r = DNS_R_DROP;
+ else if (strcasecmp(resp, "fail") == 0)
+ r = DNS_R_SERVFAIL;
+ else
+ INSIST(0);
+
+ dns_resolver_setquotaresponse(view->resolver,
+ dns_quotatype_zone, r);
+ }
+
+#ifdef ALLOW_FILTER_AAAA
+ obj = NULL;
+ result = ns_config_get(maps, "filter-aaaa-on-v4", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj))
+ view->v4_aaaa = dns_aaaa_filter;
+ else
+ view->v4_aaaa = dns_aaaa_ok;
+ } else {
+ const char *v4_aaaastr = cfg_obj_asstring(obj);
+ if (strcasecmp(v4_aaaastr, "break-dnssec") == 0)
+ view->v4_aaaa = dns_aaaa_break_dnssec;
+ else
+ INSIST(0);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "filter-aaaa-on-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj))
+ view->v6_aaaa = dns_aaaa_filter;
+ else
+ view->v6_aaaa = dns_aaaa_ok;
+ } else {
+ const char *v6_aaaastr = cfg_obj_asstring(obj);
+ if (strcasecmp(v6_aaaastr, "break-dnssec") == 0)
+ view->v6_aaaa = dns_aaaa_break_dnssec;
+ else
+ INSIST(0);
+ }
+
+ CHECK(configure_view_acl(vconfig, config, ns_g_config,
+ "filter-aaaa", NULL, actx,
+ ns_g_mctx, &view->aaaa_acl));
+#endif
+ obj = NULL;
+ result = ns_config_get(maps, "prefetch", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const cfg_obj_t *trigger, *eligible;
+
+ trigger = cfg_tuple_get(obj, "trigger");
+ view->prefetch_trigger = cfg_obj_asuint32(trigger);
+ if (view->prefetch_trigger > 10)
+ view->prefetch_trigger = 10;
+ eligible = cfg_tuple_get(obj, "eligible");
+ if (cfg_obj_isvoid(eligible)) {
+ int m;
+ for (m = 1; maps[m] != NULL; m++) {
+ obj = NULL;
+ result = ns_config_get(&maps[m],
+ "prefetch", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ eligible = cfg_tuple_get(obj, "eligible");
+ if (cfg_obj_isuint32(eligible))
+ break;
+ }
+ INSIST(cfg_obj_isuint32(eligible));
+ }
+ view->prefetch_eligible = cfg_obj_asuint32(eligible);
+ if (view->prefetch_eligible < view->prefetch_trigger + 6)
+ view->prefetch_eligible = view->prefetch_trigger + 6;
+ }
+
+ 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(optionmaps, "dnssec-lookaside", &obj);
+ if (result == ISC_R_SUCCESS) {
+ /* "auto" is deprecated, log a warning if seen */
+ const char *dom;
+ dlvobj = cfg_listelt_value(cfg_list_first(obj));
+ dom = cfg_obj_asstring(cfg_tuple_get(dlvobj, "domain"));
+ if (cfg_obj_isvoid(cfg_tuple_get(dlvobj, "trust-anchor"))) {
+ /* If "no", skip; if "auto", log warning */
+ if (!strcasecmp(dom, "no")) {
+ result = ISC_R_NOTFOUND;
+ } else if (!strcasecmp(dom, "auto")) {
+ /*
+ * Warning logged by libbind9.
+ */
+ result = ISC_R_NOTFOUND;
+ }
+ }
+ }
+
+ if (result == ISC_R_SUCCESS) {
+ dns_name_t *dlv, *iscdlv;
+ dns_fixedname_t f;
+
+ /* Also log a warning if manually configured to dlv.isc.org */
+ iscdlv = dns_fixedname_initname(&f);
+ CHECK(dns_name_fromstring(iscdlv, "dlv.isc.org", 0, NULL));
+
+ for (element = cfg_list_first(obj);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ obj = cfg_tuple_get(obj, "trust-anchor");
+
+ dlv = dns_fixedname_name(&view->dlv_fixed);
+ CHECK(dns_name_fromstring(dlv, cfg_obj_asstring(obj),
+ DNS_NAME_DOWNCASE, NULL));
+ if (dns_name_equal(dlv, iscdlv)) {
+ /*
+ * Warning logged by libbind9.
+ */
+ view->dlv = NULL;
+ } else {
+ view->dlv = dlv;
+ }
+ }
+ } else {
+ view->dlv = NULL;
+ }
+
+ /*
+ * For now, there is only one kind of trusted keys, the
+ * "security roots".
+ */
+ CHECK(configure_view_dnsseckeys(view, vconfig, config, bindkeys,
+ auto_root, mctx));
+ 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, "nta-recheck", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->nta_recheck = cfg_obj_asuint32(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "nta-lifetime", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ view->nta_lifetime = cfg_obj_asuint32(obj);
+
+ 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, true);
+ if (result == ISC_R_SUCCESS && ! cfg_obj_isvoid(obj)) {
+ const cfg_obj_t *exclude;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ name = dns_fixedname_initname(&fixed);
+ for (element = cfg_list_first(obj);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ exclude = cfg_listelt_value(element);
+ CHECK(dns_name_fromstring(name,
+ cfg_obj_asstring(exclude),
+ 0, NULL));
+ CHECK(dns_view_excludedelegationonly(view, name));
+ }
+ } else
+ dns_view_setrootdelonly(view, false);
+
+ /*
+ * Load DynDB modules.
+ */
+ dyndb_list = NULL;
+ if (voptions != NULL)
+ (void)cfg_map_get(voptions, "dyndb", &dyndb_list);
+ else
+ (void)cfg_map_get(config, "dyndb", &dyndb_list);
+
+#ifdef HAVE_DLOPEN
+ for (element = cfg_list_first(dyndb_list);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *dyndb = cfg_listelt_value(element);
+
+ if (dctx == NULL) {
+ const void *hashinit = isc_hash_get_initializer();
+ CHECK(dns_dyndb_createctx(mctx, hashinit,
+ ns_g_lctx, view,
+ ns_g_server->zonemgr,
+ ns_g_server->task,
+ ns_g_timermgr, &dctx));
+ }
+
+ CHECK(configure_dyndb(dyndb, mctx, dctx));
+ }
+#endif
+
+ /*
+ * 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) {
+ empty_zones_enable = view->recursion;
+ } else if (view->rdclass == dns_rdataclass_in) {
+ if (obj != NULL)
+ empty_zones_enable = cfg_obj_asboolean(obj);
+ else
+ empty_zones_enable = view->recursion;
+ } else {
+ empty_zones_enable = false;
+ }
+
+ if (empty_zones_enable && !lwresd_g_useresolvconf) {
+ const char *empty;
+ int empty_zone = 0;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+ isc_buffer_t buffer;
+ char server[DNS_NAME_FORMATSIZE + 1];
+ char contact[DNS_NAME_FORMATSIZE + 1];
+ const char *empty_dbtype[4] =
+ { "_builtin", "empty", NULL, NULL };
+ int empty_dbtypec = 4;
+ dns_zonestat_level_t statlevel;
+
+ name = dns_fixedname_initname(&fixed);
+
+ obj = NULL;
+ result = ns_config_get(maps, "empty-server", &obj);
+ if (result == ISC_R_SUCCESS) {
+ CHECK(dns_name_fromstring(name, cfg_obj_asstring(obj),
+ 0, NULL));
+ isc_buffer_init(&buffer, server, sizeof(server) - 1);
+ CHECK(dns_name_totext(name, 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) {
+ CHECK(dns_name_fromstring(name, cfg_obj_asstring(obj),
+ 0, NULL));
+ isc_buffer_init(&buffer, contact, sizeof(contact) - 1);
+ CHECK(dns_name_totext(name, 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);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj))
+ statlevel = dns_zonestat_full;
+ else
+ statlevel = dns_zonestat_none;
+ } else {
+ const char *levelstr = cfg_obj_asstring(obj);
+ if (strcasecmp(levelstr, "full") == 0)
+ statlevel = dns_zonestat_full;
+ else if (strcasecmp(levelstr, "terse") == 0)
+ statlevel = dns_zonestat_terse;
+ else if (strcasecmp(levelstr, "none") == 0)
+ statlevel = dns_zonestat_none;
+ else
+ INSIST(0);
+ }
+
+ for (empty = empty_zones[empty_zone];
+ empty != NULL;
+ empty = empty_zones[++empty_zone])
+ {
+ dns_forwarders_t *dnsforwarders = NULL;
+
+ /*
+ * Look for zone on drop list.
+ */
+ CHECK(dns_name_fromstring(name, empty, 0, NULL));
+ if (disablelist != NULL &&
+ on_disable_list(disablelist, name))
+ continue;
+
+ /*
+ * This zone already exists.
+ */
+ (void)dns_view_findzone(view, name, &zone);
+ if (zone != NULL) {
+ 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,
+ &dnsforwarders);
+ if (result == ISC_R_SUCCESS &&
+ dnsforwarders->fwdpolicy == dns_fwdpolicy_only)
+ 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);
+ }
+
+ CHECK(create_empty_zone(zone, name, view, zonelist,
+ empty_dbtype, empty_dbtypec,
+ statlevel));
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ }
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "rate-limit", &obj);
+ if (result == ISC_R_SUCCESS) {
+ result = configure_rrl(view, config, obj);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ /*
+ * Set the servfail-ttl.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "servfail-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ fail_ttl = cfg_obj_asuint32(obj);
+ if (fail_ttl > 30)
+ fail_ttl = 30;
+ dns_view_setfailttl(view, fail_ttl);
+
+ /*
+ * Name space to look up redirect information in.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "nxdomain-redirect", &obj);
+ if (result == ISC_R_SUCCESS) {
+ dns_name_t *name = dns_fixedname_name(&view->redirectfixed);
+ CHECK(dns_name_fromstring(name, cfg_obj_asstring(obj), 0,
+ NULL));
+ view->redirectzone = name;
+ } else
+ view->redirectzone = NULL;
+
+#ifdef HAVE_DNSTAP
+ /*
+ * Set up the dnstap environment and configure message
+ * types to log.
+ */
+ CHECK(configure_dnstap(maps, view));
+#endif /* HAVE_DNSTAP */
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (clients != NULL)
+ dns_acl_detach(&clients);
+ if (mapped != NULL)
+ dns_acl_detach(&mapped);
+ if (excluded != NULL)
+ dns_acl_detach(&excluded);
+ if (ring != NULL)
+ dns_tsigkeyring_detach(&ring);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (dispatch4 != NULL)
+ dns_dispatch_detach(&dispatch4);
+ if (dispatch6 != NULL)
+ dns_dispatch_detach(&dispatch6);
+ if (resstats != NULL)
+ isc_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 (hmctx != NULL)
+ isc_mem_detach(&hmctx);
+
+ if (cache != NULL)
+ dns_cache_detach(&cache);
+ if (dctx != NULL)
+ dns_dyndb_destroyctx(&dctx);
+
+ 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)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > 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_constinit(&buffer, str, strlen(str));
+ isc_buffer_add(&buffer, strlen(str));
+ name = dns_fixedname_initname(&fixed);
+ CHECK(dns_name_fromtext(name, &buffer, dns_rootname, 0,
+ NULL));
+
+ portobj = cfg_tuple_get(alternate, "port");
+ if (cfg_obj_isuint32(portobj)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > 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, *dscpobj;
+ const cfg_obj_t *faddresses;
+ const cfg_listelt_t *element;
+ dns_fwdpolicy_t fwdpolicy = dns_fwdpolicy_none;
+ dns_forwarderlist_t fwdlist;
+ dns_forwarder_t *fwd;
+ isc_result_t result;
+ in_port_t port;
+ isc_dscp_t dscp = -1;
+
+ ISC_LIST_INIT(fwdlist);
+
+ /*
+ * 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)) {
+ uint32_t val = cfg_obj_asuint32(portobj);
+ if (val > 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;
+ }
+ }
+
+ /*
+ * DSCP value for forwarded requests.
+ */
+ dscp = ns_g_dscp;
+ if (forwarders != NULL) {
+ dscpobj = cfg_tuple_get(forwarders, "dscp");
+ if (cfg_obj_isuint32(dscpobj)) {
+ if (cfg_obj_asuint32(dscpobj) > 63) {
+ cfg_obj_log(dscpobj, ns_g_lctx, ISC_LOG_ERROR,
+ "dscp value '%u' is out of range",
+ cfg_obj_asuint32(dscpobj));
+ return (ISC_R_RANGE);
+ }
+ dscp = (isc_dscp_t)cfg_obj_asuint32(dscpobj);
+ }
+ }
+
+ 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);
+ fwd = isc_mem_get(view->mctx, sizeof(dns_forwarder_t));
+ if (fwd == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ fwd->addr = *cfg_obj_assockaddr(forwarder);
+ if (isc_sockaddr_getport(&fwd->addr) == 0)
+ isc_sockaddr_setport(&fwd->addr, port);
+ fwd->dscp = cfg_obj_getdscp(forwarder);
+ if (fwd->dscp == -1)
+ fwd->dscp = dscp;
+ ISC_LINK_INIT(fwd, link);
+ ISC_LIST_APPEND(fwdlist, fwd, link);
+ }
+
+ if (ISC_LIST_EMPTY(fwdlist)) {
+ if (forwardtype != NULL)
+ cfg_obj_log(forwardtype, 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_addfwd(view->fwdtable, origin, &fwdlist,
+ 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(fwdlist)) {
+ fwd = ISC_LIST_HEAD(fwdlist);
+ ISC_LIST_UNLINK(fwdlist, fwd, link);
+ isc_mem_put(view->mctx, fwd, sizeof(dns_forwarder_t));
+ }
+
+ return (result);
+}
+
+static isc_result_t
+get_viewinfo(const cfg_obj_t *vconfig, const char **namep,
+ dns_rdataclass_t *classp)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ const char *viewname;
+ dns_rdataclass_t viewclass;
+
+ REQUIRE(namep != NULL && *namep == NULL);
+ REQUIRE(classp != 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");
+ CHECK(ns_config_getclass(classobj, dns_rdataclass_in,
+ &viewclass));
+ if (dns_rdataclass_ismeta(viewclass)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "view '%s': class must not be meta",
+ viewname);
+ CHECK(ISC_R_FAILURE);
+ }
+ } else {
+ viewname = "_default";
+ viewclass = dns_rdataclass_in;
+ }
+
+ *namep = viewname;
+ *classp = viewclass;
+
+cleanup:
+ return (result);
+}
+
+/*
+ * Find a view based on its configuration info and attach to it.
+ *
+ * If 'vconfig' is NULL, attach to the default view.
+ */
+static isc_result_t
+find_view(const cfg_obj_t *vconfig, dns_viewlist_t *viewlist,
+ dns_view_t **viewp)
+{
+ isc_result_t result;
+ const char *viewname = NULL;
+ dns_rdataclass_t viewclass;
+ dns_view_t *view = NULL;
+
+ result = get_viewinfo(vconfig, &viewname, &viewclass);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = dns_viewlist_find(viewlist, viewname, viewclass, &view);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ *viewp = view;
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * 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 = NULL;
+ dns_rdataclass_t viewclass;
+ dns_view_t *view = NULL;
+
+ result = get_viewinfo(vconfig, &viewname, &viewclass);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ 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);
+
+ result = isc_entropy_getdata(ns_g_entropy, view->secret,
+ sizeof(view->secret), NULL, 0);
+ if (result != ISC_R_SUCCESS) {
+ dns_view_detach(&view);
+ return (result);
+ }
+
+#ifdef HAVE_GEOIP
+ view->aclenv.geoip = ns_g_geoip;
+#endif
+
+ 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,
+ dns_viewlist_t *viewlist, cfg_aclconfctx_t *aclconf,
+ bool added, bool old_rpz_ok,
+ bool modify)
+{
+ dns_view_t *pview = NULL; /* Production view */
+ dns_zone_t *zone = NULL; /* New or reused zone */
+ dns_zone_t *raw = NULL; /* New or reused raw 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 *ixfrfromdiffs = NULL;
+ const cfg_obj_t *only = NULL;
+ const cfg_obj_t *signing = NULL;
+ const cfg_obj_t *viewobj = 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;
+ dns_rpz_num_t rpz_num;
+ bool zone_is_catz = false;
+
+ 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_constinit(&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, 0, 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, "in-view", &viewobj);
+ if (viewobj != NULL) {
+ const char *inview = cfg_obj_asstring(viewobj);
+ dns_view_t *otherview = NULL;
+
+ if (viewlist == NULL) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "'in-view' option is not permitted in "
+ "dynamically added zones");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ result = dns_viewlist_find(viewlist, inview, view->rdclass,
+ &otherview);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "view '%s' is not yet defined.", inview);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ result = dns_view_findzone(otherview, origin, &zone);
+ dns_view_detach(&otherview);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "zone '%s' not defined in view '%s'",
+ zname, inview);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ CHECK(dns_view_addzone(view, zone));
+ dns_zone_detach(&zone);
+
+ /*
+ * If the zone contains a 'forwarders' statement, configure
+ * selective forwarding. Note: this is not inherited from the
+ * other view.
+ */
+ forwarders = NULL;
+ result = cfg_map_get(zoptions, "forwarders", &forwarders);
+ if (result == ISC_R_SUCCESS) {
+ forwardtype = NULL;
+ (void)cfg_map_get(zoptions, "forward", &forwardtype);
+ CHECK(configure_forward(config, view, origin,
+ forwarders, forwardtype));
+ }
+ result = ISC_R_SUCCESS;
+ 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);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+ 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);
+
+ CHECK(configure_hints(view, hintsfile));
+
+ /*
+ * 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);
+ CHECK(configure_forward(config, view, origin, forwarders,
+ forwardtype));
+
+ /*
+ * Forward zones may also set delegation only.
+ */
+ 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));
+ goto cleanup;
+ }
+
+ /*
+ * "delegation-only zones" aren't zones either.
+ */
+ if (strcasecmp(ztypestr, "delegation-only") == 0) {
+ result = dns_view_adddelegationonly(view, origin);
+ goto cleanup;
+ }
+
+ /*
+ * Redirect zones only require minimal configuration.
+ */
+ if (strcasecmp(ztypestr, "redirect") == 0) {
+ if (view->redirect != NULL) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "redirect zone already exists");
+ result = ISC_R_EXISTS;
+ goto cleanup;
+ }
+ result = dns_viewlist_find(viewlist, view->name,
+ view->rdclass, &pview);
+ if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS)
+ goto cleanup;
+ if (pview != NULL && pview->redirect != NULL) {
+ dns_zone_attach(pview->redirect, &zone);
+ dns_zone_setview(zone, view);
+ } else {
+ CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr,
+ &zone));
+ CHECK(dns_zone_setorigin(zone, origin));
+ dns_zone_setview(zone, view);
+ CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr,
+ zone));
+ dns_zone_setstats(zone, ns_g_server->zonestats);
+ }
+ CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf,
+ zone, NULL));
+ dns_zone_attach(zone, &view->redirect);
+ goto cleanup;
+ }
+
+ if (!modify) {
+ /*
+ * 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);
+ }
+
+ /*
+ * Note whether this is a response policy zone and which one if so.
+ */
+ for (rpz_num = 0; ; ++rpz_num) {
+ if (view->rpzs == NULL || rpz_num >= view->rpzs->p.num_zones) {
+ rpz_num = DNS_RPZ_INVALID_NUM;
+ break;
+ }
+ if (dns_name_equal(&view->rpzs->zones[rpz_num]->origin, origin))
+ break;
+ }
+
+ if (view->catzs != NULL &&
+ dns_catz_get_zone(view->catzs, origin) != NULL)
+ zone_is_catz = true;
+
+ /*
+ * 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)
+ * - The zone was not and is still not a response policy zone
+ * or the zone is a policy zone with an unchanged number
+ * and we are using the old policy zone summary data.
+ */
+ 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 && (rpz_num != dns_zone_get_rpz_num(zone) ||
+ (rpz_num != DNS_RPZ_INVALID_NUM && !old_rpz_ok)))
+ 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_zonemgr_createzone(ns_g_server->zonemgr, &zone));
+ 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 (rpz_num != DNS_RPZ_INVALID_NUM) {
+ result = dns_zone_rpz_enable(zone, view->rpzs, rpz_num);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': incompatible"
+ " masterfile-format or database"
+ " for a response policy zone",
+ zname);
+ goto cleanup;
+ }
+ }
+
+ if (zone_is_catz)
+ dns_zone_catz_enable(zone, view->catzs);
+
+ /*
+ * 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));
+ }
+
+ /*
+ * Mark whether the zone was originally added at runtime or not
+ */
+ dns_zone_setadded(zone, added);
+
+ signing = NULL;
+ if ((strcasecmp(ztypestr, "master") == 0 ||
+ strcasecmp(ztypestr, "slave") == 0) &&
+ cfg_map_get(zoptions, "inline-signing", &signing) == ISC_R_SUCCESS &&
+ cfg_obj_asboolean(signing))
+ {
+ dns_zone_getraw(zone, &raw);
+ if (raw == NULL) {
+ CHECK(dns_zone_create(&raw, mctx));
+ CHECK(dns_zone_setorigin(raw, origin));
+ dns_zone_setview(raw, view);
+ if (view->acache != NULL)
+ dns_zone_setacache(raw, view->acache);
+ dns_zone_setstats(raw, ns_g_server->zonestats);
+ CHECK(dns_zone_link(zone, raw));
+ }
+ if (cfg_map_get(zoptions, "ixfr-from-differences",
+ &ixfrfromdiffs) == ISC_R_SUCCESS)
+ {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "zone '%s': 'ixfr-from-differences' is "
+ "ignored for inline-signed zones",
+ zname);
+ }
+ }
+
+ /*
+ * Configure the zone.
+ */
+ CHECK(ns_zone_configure(config, vconfig, zconfig, aclconf, zone, raw));
+
+ /*
+ * Add the zone to its view in the new view list.
+ */
+ if (!modify)
+ CHECK(dns_view_addzone(view, zone));
+
+ if (zone_is_catz) {
+ /*
+ * force catz reload if the zone is loaded;
+ * if it's not it'll get reloaded on zone load
+ */
+ dns_db_t *db = NULL;
+
+ tresult = dns_zone_getdb(zone, &db);
+ if (tresult == ISC_R_SUCCESS) {
+ dns_catz_dbupdate_callback(db, view->catzs);
+ dns_db_detach(&db);
+ }
+
+ }
+
+ /*
+ * Ensure that zone keys are reloaded on reconfig
+ */
+ if ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN) != 0)
+ dns_zone_rekey(zone, false);
+
+ cleanup:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ if (pview != NULL)
+ dns_view_detach(&pview);
+
+ return (result);
+}
+
+/*
+ * Configure built-in zone for storing managed-key data.
+ */
+
+static isc_result_t
+add_keydata_zone(dns_view_t *view, const char *directory, isc_mem_t *mctx) {
+ isc_result_t result;
+ dns_view_t *pview = NULL;
+ dns_zone_t *zone = NULL;
+ dns_acl_t *none = NULL;
+ char filename[PATH_MAX];
+ bool defaultview;
+
+ REQUIRE(view != NULL);
+
+ /* See if we can re-use an existing keydata zone. */
+ result = dns_viewlist_find(&ns_g_server->viewlist,
+ view->name, view->rdclass,
+ &pview);
+ if (result != ISC_R_NOTFOUND &&
+ result != ISC_R_SUCCESS)
+ return (result);
+
+ if (pview != NULL && pview->managed_keys != NULL) {
+ dns_zone_attach(pview->managed_keys, &view->managed_keys);
+ dns_zone_setview(pview->managed_keys, view);
+ dns_view_detach(&pview);
+ dns_zone_synckeyzone(view->managed_keys);
+ return (ISC_R_SUCCESS);
+ }
+
+ /* No existing keydata zone was found; create one */
+ CHECK(dns_zonemgr_createzone(ns_g_server->zonemgr, &zone));
+ CHECK(dns_zone_setorigin(zone, dns_rootname));
+
+ defaultview = (strcmp(view->name, "_default") == 0);
+ CHECK(isc_file_sanitize(directory,
+ defaultview ? "managed-keys" : view->name,
+ defaultview ? "bind" : "mkeys",
+ filename, sizeof(filename)));
+ CHECK(dns_zone_setfile(zone, filename));
+
+ dns_zone_setview(zone, view);
+ dns_zone_settype(zone, dns_zone_key);
+ dns_zone_setclass(zone, view->rdclass);
+
+ CHECK(dns_zonemgr_managezone(ns_g_server->zonemgr, zone));
+
+ if (view->acache != NULL)
+ dns_zone_setacache(zone, view->acache);
+
+ CHECK(dns_acl_none(mctx, &none));
+ dns_zone_setqueryacl(zone, none);
+ dns_zone_setqueryonacl(zone, none);
+ dns_acl_detach(&none);
+
+ dns_zone_setdialup(zone, dns_dialuptype_no);
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NOCHECKNS, true);
+ dns_zone_setjournalsize(zone, 0);
+
+ dns_zone_setstats(zone, ns_g_server->zonestats);
+ CHECK(setquerystats(zone, mctx, dns_zonestat_none));
+
+ if (view->managed_keys != NULL)
+ dns_zone_detach(&view->managed_keys);
+ dns_zone_attach(zone, &view->managed_keys);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "set up managed keys zone for view %s, file '%s'",
+ view->name, filename);
+
+cleanup:
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (none != NULL)
+ dns_acl_detach(&none);
+
+ 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 isc_result_t
+scan_interfaces(ns_server_t *server, bool verbose) {
+ isc_result_t result;
+ bool match_mapped = server->aclenv.match_mapped;
+#ifdef HAVE_GEOIP
+ bool use_ecs = server->aclenv.geoip_use_ecs;
+#endif
+
+ result = 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;
+#ifdef HAVE_GEOIP
+ server->aclenv.geoip_use_ecs = use_ecs;
+#endif
+
+ return (result);
+}
+
+static isc_result_t
+add_listenelt(isc_mem_t *mctx, ns_listenlist_t *list, isc_sockaddr_t *addr,
+ isc_dscp_t dscp, bool 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, true);
+ if (result != ISC_R_SUCCESS)
+ goto clean;
+
+ result = ns_listenelt_create(mctx, isc_sockaddr_getport(addr),
+ dscp, 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;
+ isc_dscp_t dscp = -1;
+
+ 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.
+ */
+ /* XXXMPA fix dscp */
+ result = add_listenelt(mctx, list, &addr, dscp, 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);
+ dscp = dns_zone_getnotifysrc6dscp(zone);
+ result = add_listenelt(mctx, list, addrp, dscp, false);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+
+ addrp = dns_zone_getxfrsource6(zone);
+ dscp = dns_zone_getxfrsource6dscp(zone);
+ result = add_listenelt(mctx, list, addrp, dscp, false);
+ if (result != ISC_R_SUCCESS)
+ goto fail;
+ }
+
+ ns_interfacemgr_adjust(server->interfacemgr, list, 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. It is also called by ns_server_scan_interfaces(),
+ * invoked by "rndc scan"
+ */
+
+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, 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);
+ }
+}
+
+typedef struct {
+ isc_mem_t *mctx;
+ isc_task_t *task;
+ dns_rdataset_t rdataset;
+ dns_rdataset_t sigrdataset;
+ dns_fetch_t *fetch;
+} ns_tat_t;
+
+static int
+cid(const void *a, const void *b) {
+ const uint16_t ida = *(const uint16_t *)a;
+ const uint16_t idb = *(const uint16_t *)b;
+ if (ida < idb)
+ return (-1);
+ else if (ida > idb)
+ return (1);
+ else
+ return (0);
+}
+
+static void
+tat_done(isc_task_t *task, isc_event_t *event) {
+ dns_fetchevent_t *devent;
+ ns_tat_t *tat;
+
+ UNUSED(task);
+ INSIST(event != NULL && event->ev_type == DNS_EVENT_FETCHDONE);
+ INSIST(event->ev_arg != NULL);
+
+ tat = event->ev_arg;
+ devent = (dns_fetchevent_t *) event;
+
+ /* Free resources which are not of interest */
+ if (devent->node != NULL)
+ dns_db_detachnode(devent->db, &devent->node);
+ if (devent->db != NULL)
+ dns_db_detach(&devent->db);
+ isc_event_free(&event);
+ dns_resolver_destroyfetch(&tat->fetch);
+ if (dns_rdataset_isassociated(&tat->rdataset))
+ dns_rdataset_disassociate(&tat->rdataset);
+ if (dns_rdataset_isassociated(&tat->sigrdataset))
+ dns_rdataset_disassociate(&tat->sigrdataset);
+ isc_task_detach(&tat->task);
+ isc_mem_putanddetach(&tat->mctx, tat, sizeof(*tat));
+}
+
+struct dotat_arg {
+ dns_view_t *view;
+ isc_task_t *task;
+};
+
+/*%
+ * Prepare the QNAME for the TAT query to be sent by processing the trust
+ * anchors present at 'keynode' of 'keytable'. Store the result in 'dst' and
+ * the domain name which 'keynode' is associated with in 'origin'.
+ *
+ * A maximum of 12 key IDs can be reported in a single TAT query due to the
+ * 63-octet length limit for any single label in a domain name. If there are
+ * more than 12 keys configured at 'keynode', only the first 12 will be
+ * reported in the TAT query.
+ */
+static isc_result_t
+get_tat_qname(dns_name_t *dst, dns_name_t **origin, dns_keytable_t *keytable,
+ dns_keynode_t *keynode)
+{
+ dns_keynode_t *firstnode = keynode;
+ dns_keynode_t *nextnode;
+ unsigned int i, n = 0;
+ uint16_t ids[12];
+ isc_textregion_t r;
+ char label[64];
+ int m;
+
+ REQUIRE(origin != NULL && *origin == NULL);
+
+ do {
+ dst_key_t *key = dns_keynode_key(keynode);
+ if (key != NULL) {
+ *origin = dst_key_name(key);
+ if (n < (sizeof(ids)/sizeof(ids[0]))) {
+ ids[n] = dst_key_id(key);
+ n++;
+ }
+ }
+ nextnode = NULL;
+ (void)dns_keytable_nextkeynode(keytable, keynode, &nextnode);
+ if (keynode != firstnode)
+ dns_keytable_detachkeynode(keytable, &keynode);
+ keynode = nextnode;
+ } while (keynode != NULL);
+
+ if (n == 0) {
+ return (DNS_R_EMPTYNAME);
+ }
+
+ if (n > 1)
+ qsort(ids, n, sizeof(ids[0]), cid);
+
+ /*
+ * Encoded as "_ta-xxxx\(-xxxx\)*" where xxxx is the hex version of
+ * of the keyid.
+ */
+ label[0] = 0;
+ r.base = label;
+ r.length = sizeof(label);;
+ m = snprintf(r.base, r.length, "_ta");
+ if (m < 0 || (unsigned)m > r.length) {
+ return (ISC_R_FAILURE);
+ }
+ isc_textregion_consume(&r, m);
+ for (i = 0; i < n; i++) {
+ m = snprintf(r.base, r.length, "-%04x", ids[i]);
+ if (m < 0 || (unsigned)m > r.length) {
+ return (ISC_R_FAILURE);
+ }
+ isc_textregion_consume(&r, m);
+ }
+
+ return (dns_name_fromstring2(dst, label, *origin, 0, NULL));
+}
+
+static void
+dotat(dns_keytable_t *keytable, dns_keynode_t *keynode, void *arg) {
+ struct dotat_arg *dotat_arg = arg;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fixed, fdomain;
+ dns_name_t *tatname, *domain;
+ dns_rdataset_t nameservers;
+ dns_name_t *origin = NULL;
+ isc_result_t result;
+ dns_view_t *view;
+ isc_task_t *task;
+ ns_tat_t *tat;
+
+ REQUIRE(keytable != NULL);
+ REQUIRE(keynode != NULL);
+ REQUIRE(arg != NULL);
+
+ view = dotat_arg->view;
+ task = dotat_arg->task;
+
+ tatname = dns_fixedname_initname(&fixed);
+ result = get_tat_qname(tatname, &origin, keytable, keynode);
+ if (result != ISC_R_SUCCESS) {
+ return;
+ }
+
+ dns_name_format(tatname, namebuf, sizeof(namebuf));
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO,
+ "%s: sending trust-anchor-telemetry query '%s/NULL'",
+ view->name, namebuf);
+
+ tat = isc_mem_get(dotat_arg->view->mctx, sizeof(*tat));
+ if (tat == NULL)
+ return;
+
+ tat->mctx = NULL;
+ tat->task = NULL;
+ tat->fetch = NULL;
+ dns_rdataset_init(&tat->rdataset);
+ dns_rdataset_init(&tat->sigrdataset);
+ isc_mem_attach(dotat_arg->view->mctx, &tat->mctx);
+ isc_task_attach(task, &tat->task);
+
+ /*
+ * TAT queries should be sent to the authoritative servers for a given
+ * zone. If this function is called for a keytable node corresponding
+ * to a locally served zone, calling dns_resolver_createfetch() with
+ * NULL 'domain' and 'nameservers' arguments will cause 'tatname' to be
+ * resolved locally, without sending any TAT queries upstream.
+ *
+ * Work around this issue by calling dns_view_findzonecut() first. If
+ * the zone is served locally, the NS RRset for the given domain name
+ * will be retrieved from local data; if it is not, the deepest zone
+ * cut we have for it will be retrieved from cache. In either case,
+ * passing the results to dns_resolver_createfetch() will prevent it
+ * from returning NXDOMAIN for 'tatname' while still allowing it to
+ * chase down any potential delegations returned by upstream servers in
+ * order to eventually find the destination host to send the TAT query
+ * to.
+ *
+ * 'origin' holds the domain name at 'keynode', i.e. the domain name
+ * for which the trust anchors to be reported by this TAT query are
+ * defined.
+ *
+ * After the dns_view_findzonecut() call, 'domain' will hold the
+ * deepest zone cut we can find for 'origin' while 'nameservers' will
+ * hold the NS RRset at that zone cut.
+ */
+ domain = dns_fixedname_initname(&fdomain);
+ dns_rdataset_init(&nameservers);
+ result = dns_view_findzonecut(view, origin, domain, 0, 0, true,
+ &nameservers, NULL);
+ if (result == ISC_R_SUCCESS) {
+ result = dns_resolver_createfetch(view->resolver, tatname,
+ dns_rdatatype_null, domain,
+ &nameservers, NULL, 0,
+ tat->task, tat_done, tat,
+ &tat->rdataset,
+ &tat->sigrdataset,
+ &tat->fetch);
+ }
+
+ /*
+ * 'domain' holds the dns_name_t pointer inside a dst_key_t structure.
+ * dns_resolver_createfetch() creates its own copy of 'domain' if it
+ * succeeds. Thus, 'domain' is not freed here.
+ *
+ * Even if dns_view_findzonecut() returned something else than
+ * ISC_R_SUCCESS, it still could have associated 'nameservers'.
+ * dns_resolver_createfetch() creates its own copy of 'nameservers' if
+ * it succeeds. Thus, we need to check whether 'nameservers' is
+ * associated and release it if it is.
+ */
+ if (dns_rdataset_isassociated(&nameservers)) {
+ dns_rdataset_disassociate(&nameservers);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ isc_task_detach(&tat->task);
+ isc_mem_putanddetach(&tat->mctx, tat, sizeof(*tat));
+ }
+}
+
+static void
+tat_timer_tick(isc_task_t *task, isc_event_t *event) {
+ isc_result_t result;
+ ns_server_t *server = (ns_server_t *) event->ev_arg;
+ struct dotat_arg arg;
+ dns_view_t *view;
+ dns_keytable_t *secroots = NULL;
+
+ isc_event_free(&event);
+
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (!view->trust_anchor_telemetry ||
+ !view->enablevalidation)
+ {
+ continue;
+ }
+
+ result = dns_view_getsecroots(view, &secroots);
+ if (result != ISC_R_SUCCESS) {
+ continue;
+ }
+
+ arg.view = view;
+ arg.task = task;
+ (void)dns_keytable_forall(secroots, dotat, &arg);
+ dns_keytable_detach(&secroots);
+ }
+}
+
+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 %" PRIu64 ": %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,
+ bool 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;
+ case dns_zone_staticstub:
+ type = "static-stub";
+ break;
+ case dns_zone_redirect:
+ type = "redirect";
+ break;
+ default:
+ type = "other";
+ break;
+ }
+ dns_zone_log(zone, ISC_LOG_INFO, "(%s) removed", type);
+ return (ISC_R_SUCCESS);
+}
+
+static void
+cleanup_session_key(ns_server_t *server, isc_mem_t *mctx) {
+ if (server->session_keyfile != NULL) {
+ isc_file_remove(server->session_keyfile);
+ isc_mem_free(mctx, server->session_keyfile);
+ server->session_keyfile = NULL;
+ }
+
+ if (server->session_keyname != NULL) {
+ if (dns_name_dynamic(server->session_keyname))
+ dns_name_free(server->session_keyname, mctx);
+ isc_mem_put(mctx, server->session_keyname, sizeof(dns_name_t));
+ server->session_keyname = NULL;
+ }
+
+ if (server->sessionkey != NULL)
+ dns_tsigkey_detach(&server->sessionkey);
+
+ server->session_keyalg = DST_ALG_UNKNOWN;
+ server->session_keybits = 0;
+}
+
+static isc_result_t
+generate_session_key(const char *filename, const char *keynamestr,
+ dns_name_t *keyname, const char *algstr,
+ dns_name_t *algname, unsigned int algtype,
+ uint16_t bits, isc_mem_t *mctx,
+ dns_tsigkey_t **tsigkeyp)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dst_key_t *key = NULL;
+ isc_buffer_t key_txtbuffer;
+ isc_buffer_t key_rawbuffer;
+ char key_txtsecret[256];
+ char key_rawsecret[64];
+ isc_region_t key_rawregion;
+ isc_stdtime_t now;
+ dns_tsigkey_t *tsigkey = NULL;
+ FILE *fp = NULL;
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "generating session key for dynamic DNS");
+
+ /* generate key */
+ result = dst_key_generate(keyname, algtype, bits, 1, 0,
+ DNS_KEYPROTO_ANY, dns_rdataclass_in,
+ mctx, &key);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /*
+ * Dump the key to the buffer for later use. Should be done before
+ * we transfer the ownership of key to tsigkey.
+ */
+ isc_buffer_init(&key_rawbuffer, &key_rawsecret, sizeof(key_rawsecret));
+ CHECK(dst_key_tobuffer(key, &key_rawbuffer));
+
+ isc_buffer_usedregion(&key_rawbuffer, &key_rawregion);
+ isc_buffer_init(&key_txtbuffer, &key_txtsecret, sizeof(key_txtsecret));
+ CHECK(isc_base64_totext(&key_rawregion, -1, "", &key_txtbuffer));
+
+ /* Store the key in tsigkey. */
+ isc_stdtime_get(&now);
+ CHECK(dns_tsigkey_createfromkey(dst_key_name(key), algname, key,
+ false, NULL, now, now, mctx, NULL,
+ &tsigkey));
+
+ /* Dump the key to the key file. */
+ fp = ns_os_openfile(filename, S_IRUSR|S_IWUSR, true);
+ if (fp == NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "could not create %s", filename);
+ result = ISC_R_NOPERM;
+ goto cleanup;
+ }
+
+ fprintf(fp, "key \"%s\" {\n"
+ "\talgorithm %s;\n"
+ "\tsecret \"%.*s\";\n};\n", keynamestr, algstr,
+ (int) isc_buffer_usedlength(&key_txtbuffer),
+ (char*) isc_buffer_base(&key_txtbuffer));
+
+ CHECK(isc_stdio_flush(fp));
+ result = isc_stdio_close(fp);
+ fp = NULL;
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ dst_key_free(&key);
+
+ *tsigkeyp = tsigkey;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed to generate session key "
+ "for dynamic DNS: %s", isc_result_totext(result));
+ if (fp != NULL) {
+ (void)isc_stdio_close(fp);
+ (void)isc_file_remove(filename);
+ }
+ if (tsigkey != NULL)
+ dns_tsigkey_detach(&tsigkey);
+ if (key != NULL)
+ dst_key_free(&key);
+
+ return (result);
+}
+
+static isc_result_t
+configure_session_key(const cfg_obj_t **maps, ns_server_t *server,
+ isc_mem_t *mctx)
+{
+ const char *keyfile, *keynamestr, *algstr;
+ unsigned int algtype;
+ dns_fixedname_t fname;
+ dns_name_t *keyname, *algname;
+ isc_buffer_t buffer;
+ uint16_t bits;
+ const cfg_obj_t *obj;
+ bool need_deleteold = false;
+ bool need_createnew = false;
+ isc_result_t result;
+
+ obj = NULL;
+ result = ns_config_get(maps, "session-keyfile", &obj);
+ if (result == ISC_R_SUCCESS) {
+ if (cfg_obj_isvoid(obj))
+ keyfile = NULL; /* disable it */
+ else
+ keyfile = cfg_obj_asstring(obj);
+ } else
+ keyfile = ns_g_defaultsessionkeyfile;
+
+ obj = NULL;
+ result = ns_config_get(maps, "session-keyname", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ keynamestr = cfg_obj_asstring(obj);
+ isc_buffer_constinit(&buffer, keynamestr, strlen(keynamestr));
+ isc_buffer_add(&buffer, strlen(keynamestr));
+ keyname = dns_fixedname_initname(&fname);
+ result = dns_name_fromtext(keyname, &buffer, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ obj = NULL;
+ result = ns_config_get(maps, "session-keyalg", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ algstr = cfg_obj_asstring(obj);
+ algname = NULL;
+ result = ns_config_getkeyalgorithm2(algstr, &algname, &algtype, &bits);
+ if (result != ISC_R_SUCCESS) {
+ const char *s = " (keeping current key)";
+
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, "session-keyalg: "
+ "unsupported or unknown algorithm '%s'%s",
+ algstr,
+ server->session_keyfile != NULL ? s : "");
+ return (result);
+ }
+
+ /* See if we need to (re)generate a new key. */
+ if (keyfile == NULL) {
+ if (server->session_keyfile != NULL)
+ need_deleteold = true;
+ } else if (server->session_keyfile == NULL)
+ need_createnew = true;
+ else if (strcmp(keyfile, server->session_keyfile) != 0 ||
+ !dns_name_equal(server->session_keyname, keyname) ||
+ server->session_keyalg != algtype ||
+ server->session_keybits != bits) {
+ need_deleteold = true;
+ need_createnew = true;
+ }
+
+ if (need_deleteold) {
+ INSIST(server->session_keyfile != NULL);
+ INSIST(server->session_keyname != NULL);
+ INSIST(server->sessionkey != NULL);
+
+ cleanup_session_key(server, mctx);
+ }
+
+ if (need_createnew) {
+ INSIST(server->sessionkey == NULL);
+ INSIST(server->session_keyfile == NULL);
+ INSIST(server->session_keyname == NULL);
+ INSIST(server->session_keyalg == DST_ALG_UNKNOWN);
+ INSIST(server->session_keybits == 0);
+
+ server->session_keyname = isc_mem_get(mctx, sizeof(dns_name_t));
+ if (server->session_keyname == NULL)
+ goto cleanup;
+ dns_name_init(server->session_keyname, NULL);
+ CHECK(dns_name_dup(keyname, mctx, server->session_keyname));
+
+ server->session_keyfile = isc_mem_strdup(mctx, keyfile);
+ if (server->session_keyfile == NULL)
+ goto cleanup;
+
+ server->session_keyalg = algtype;
+ server->session_keybits = bits;
+
+ CHECK(generate_session_key(keyfile, keynamestr, keyname, algstr,
+ algname, algtype, bits, mctx,
+ &server->sessionkey));
+ }
+
+ return (result);
+
+ cleanup:
+ cleanup_session_key(server, mctx);
+ return (result);
+}
+
+#ifndef HAVE_LMDB
+static isc_result_t
+count_newzones(dns_view_t *view, ns_cfgctx_t *nzcfg, int *num_zonesp) {
+ isc_result_t result;
+
+ /* The new zone file may not exist. That is OK. */
+ if (!isc_file_exists(view->new_zone_file)) {
+ *num_zonesp = 0;
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * In the case of NZF files, we also parse the configuration in
+ * the file at this stage.
+ *
+ * This may be called in multiple views, so we reset
+ * the parser each time.
+ */
+ cfg_parser_reset(ns_g_addparser);
+ result = cfg_parse_file(ns_g_addparser, view->new_zone_file,
+ &cfg_type_addzoneconf, &nzcfg->nzf_config);
+ if (result == ISC_R_SUCCESS) {
+ int num_zones;
+
+ num_zones = count_zones(nzcfg->nzf_config);
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO,
+ "NZF file '%s' contains %d zones",
+ view->new_zone_file, num_zones);
+ if (num_zonesp != NULL)
+ *num_zonesp = num_zones;
+ } else {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "Error parsing NZF file '%s': %s",
+ view->new_zone_file,
+ isc_result_totext(result));
+ }
+
+ return (result);
+}
+
+#else /* HAVE_LMDB */
+
+static isc_result_t
+count_newzones(dns_view_t *view, ns_cfgctx_t *nzcfg, int *num_zonesp) {
+ isc_result_t result;
+ int n;
+
+ UNUSED(nzcfg);
+
+ REQUIRE(num_zonesp != NULL);
+
+ CHECK(migrate_nzf(view));
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "loading NZD zone count from '%s' "
+ "for view '%s'",
+ view->new_zone_db, view->name);
+
+ CHECK(nzd_count(view, &n));
+
+ *num_zonesp = n;
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO,
+ "NZD database '%s' contains %d zones",
+ view->new_zone_db, n);
+
+ cleanup:
+ if (result != ISC_R_SUCCESS)
+ *num_zonesp = 0;
+
+ return (ISC_R_SUCCESS);
+}
+
+#endif /* HAVE_LMDB */
+
+static isc_result_t
+setup_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
+ cfg_parser_t *conf_parser, cfg_aclconfctx_t *actx,
+ int *num_zones)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ bool allow = false;
+ ns_cfgctx_t *nzcfg = NULL;
+ const cfg_obj_t *maps[4];
+ const cfg_obj_t *options = NULL, *voptions = NULL;
+ const cfg_obj_t *nz = NULL;
+ const cfg_obj_t *obj = NULL;
+ int i = 0;
+ uint64_t mapsize = 0ULL;
+
+ REQUIRE(config != NULL);
+
+ if (vconfig != NULL)
+ voptions = cfg_tuple_get(vconfig, "options");
+ if (voptions != NULL)
+ maps[i++] = voptions;
+ result = cfg_map_get(config, "options", &options);
+ if (result == ISC_R_SUCCESS)
+ maps[i++] = options;
+ maps[i++] = ns_g_defaults;
+ maps[i] = NULL;
+
+ result = ns_config_get(maps, "allow-new-zones", &nz);
+ if (result == ISC_R_SUCCESS)
+ allow = cfg_obj_asboolean(nz);
+
+#ifdef HAVE_LMDB
+ result = ns_config_get(maps, "lmdb-mapsize", &obj);
+ if (result == ISC_R_SUCCESS && obj != NULL) {
+ mapsize = cfg_obj_asuint64(obj);
+ if (mapsize < (1ULL << 20)) { /* 1 megabyte */
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_ERROR,
+ "'lmdb-mapsize "
+ "%" PRId64 "' "
+ "is too small",
+ mapsize);
+ return (ISC_R_FAILURE);
+ } else if (mapsize > (1ULL << 40)) { /* 1 terabyte */
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_ERROR,
+ "'lmdb-mapsize "
+ "%" PRId64 "' "
+ "is too large",
+ mapsize);
+ return (ISC_R_FAILURE);
+ }
+ }
+#else
+ UNUSED(obj);
+#endif /* HAVE_LMDB */
+
+ /*
+ * A non-empty catalog-zones statement implies allow-new-zones
+ */
+ if (!allow) {
+ const cfg_obj_t *cz = NULL;
+ result = ns_config_get(maps, "catalog-zones", &cz);
+ if (result == ISC_R_SUCCESS) {
+ const cfg_listelt_t *e =
+ cfg_list_first(cfg_tuple_get(cz, "zone list"));
+ if (e != NULL)
+ allow = true;
+ }
+ }
+
+ if (!allow) {
+ dns_view_setnewzones(view, false, NULL, NULL, 0ULL);
+ if (num_zones != NULL)
+ *num_zones = 0;
+ return (ISC_R_SUCCESS);
+ }
+
+ nzcfg = isc_mem_get(view->mctx, sizeof(*nzcfg));
+ if (nzcfg == NULL) {
+ dns_view_setnewzones(view, false, NULL, NULL, 0ULL);
+ return (ISC_R_NOMEMORY);
+ }
+
+ /*
+ * We attach the parser that was used for config as well
+ * as the one that will be used for added zones, to avoid
+ * a shutdown race later.
+ */
+ memset(nzcfg, 0, sizeof(*nzcfg));
+ cfg_parser_attach(conf_parser, &nzcfg->conf_parser);
+ cfg_parser_attach(ns_g_addparser, &nzcfg->add_parser);
+ isc_mem_attach(view->mctx, &nzcfg->mctx);
+ cfg_aclconfctx_attach(actx, &nzcfg->actx);
+
+ result = dns_view_setnewzones(view, allow, nzcfg,
+ newzone_cfgctx_destroy, mapsize);
+ if (result != ISC_R_SUCCESS) {
+ dns_view_setnewzones(view, false, NULL, NULL, 0ULL);
+ return (result);
+ }
+
+ cfg_obj_attach(config, &nzcfg->config);
+ if (vconfig != NULL)
+ cfg_obj_attach(vconfig, &nzcfg->vconfig);
+
+ result = count_newzones(view, nzcfg, num_zones);
+ return (result);
+}
+
+static void
+configure_zone_setviewcommit(isc_result_t result, const cfg_obj_t *zconfig,
+ dns_view_t *view)
+{
+ const char *zname;
+ dns_fixedname_t fixorigin;
+ dns_name_t *origin;
+ isc_result_t result2;
+ dns_view_t *pview = NULL;
+ dns_zone_t *zone = NULL;
+ dns_zone_t *raw = NULL;
+
+ zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+ origin = dns_fixedname_initname(&fixorigin);
+
+ result2 = dns_name_fromstring(origin, zname, 0, NULL);
+ if (result2 != ISC_R_SUCCESS) {
+ return;
+ }
+
+ result2 = dns_viewlist_find(&ns_g_server->viewlist, view->name,
+ view->rdclass, &pview);
+ if (result2 != ISC_R_SUCCESS) {
+ return;
+ }
+
+ result2 = dns_view_findzone(pview, origin, &zone);
+ if (result2 != ISC_R_SUCCESS) {
+ dns_view_detach(&pview);
+ return;
+ }
+
+ dns_zone_getraw(zone, &raw);
+
+ if (result == ISC_R_SUCCESS) {
+ dns_zone_setviewcommit(zone);
+ if (raw != NULL)
+ dns_zone_setviewcommit(raw);
+ } else {
+ dns_zone_setviewrevert(zone);
+ if (raw != NULL)
+ dns_zone_setviewrevert(raw);
+ }
+
+ if (raw != NULL) {
+ dns_zone_detach(&raw);
+ }
+
+ dns_zone_detach(&zone);
+ dns_view_detach(&pview);
+}
+
+#ifndef HAVE_LMDB
+
+static isc_result_t
+configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
+ isc_mem_t *mctx, cfg_aclconfctx_t *actx)
+{
+ isc_result_t result;
+ ns_cfgctx_t *nzctx;
+ const cfg_obj_t *zonelist;
+ const cfg_listelt_t *element;
+
+ nzctx = view->new_zone_config;
+ if (nzctx == NULL || nzctx->nzf_config == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "loading additional zones for view '%s'",
+ view->name);
+
+ zonelist = NULL;
+ cfg_map_get(nzctx->nzf_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, &ns_g_server->viewlist, actx,
+ true, false, false));
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ for (element = cfg_list_first(zonelist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *zconfig = cfg_listelt_value(element);
+ configure_zone_setviewcommit(result, zconfig, view);
+ }
+
+ return (result);
+}
+
+#else /* HAVE_LMDB */
+
+static isc_result_t
+data_to_cfg(dns_view_t *view, MDB_val *key, MDB_val *data,
+ isc_buffer_t **text, cfg_obj_t **zoneconfig)
+{
+ isc_result_t result;
+ const char *zone_name;
+ size_t zone_name_len;
+ const char *zone_config;
+ size_t zone_config_len;
+ cfg_obj_t *zoneconf = NULL;
+
+ REQUIRE(view != NULL);
+ REQUIRE(key != NULL);
+ REQUIRE(data != NULL);
+ REQUIRE(text != NULL);
+ REQUIRE(zoneconfig != NULL && *zoneconfig == NULL);
+
+ if (*text == NULL) {
+ result = isc_buffer_allocate(view->mctx, text, 256);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ } else {
+ isc_buffer_clear(*text);
+ }
+
+ zone_name = (const char *) key->mv_data;
+ zone_name_len = key->mv_size;
+ INSIST(zone_name != NULL && zone_name_len > 0);
+
+ zone_config = (const char *) data->mv_data;
+ zone_config_len = data->mv_size;
+ INSIST(zone_config != NULL && zone_config_len > 0);
+
+ /* zone zonename { config; }; */
+ result = isc_buffer_reserve(text, 5 + zone_name_len + 1 +
+ zone_config_len + 2);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ putstr(text, "zone ");
+ putmem(text, (const void *) zone_name, zone_name_len);
+ putstr(text, " ");
+ putmem(text, (const void *) zone_config, zone_config_len);
+ putstr(text, ";\n");
+
+ cfg_parser_reset(ns_g_addparser);
+ result = cfg_parse_buffer3(ns_g_addparser, *text, zone_name, 0,
+ &cfg_type_addzoneconf, &zoneconf);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "parsing config for zone '%.*s' in "
+ "NZD database '%s' failed",
+ (int) zone_name_len, zone_name,
+ view->new_zone_db);
+ goto cleanup;
+ }
+
+ *zoneconfig = zoneconf;
+ zoneconf = NULL;
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (zoneconf != NULL) {
+ cfg_obj_destroy(ns_g_addparser, &zoneconf);
+ }
+
+ return (result);
+}
+
+/*%
+ * Prototype for a callback which can be used with for_all_newzone_cfgs().
+ */
+typedef isc_result_t (*newzone_cfg_cb_t)(const cfg_obj_t *zconfig,
+ cfg_obj_t *config, cfg_obj_t *vconfig,
+ isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *actx);
+
+/*%
+ * For each zone found in a NZD opened by the caller, create an object
+ * representing its configuration and invoke "callback" with the created
+ * object, "config", "vconfig", "mctx", "view" and "actx" as arguments (all
+ * these are non-global variables required to invoke configure_zone()).
+ * Immediately interrupt processing if an error is encountered while
+ * transforming NZD data into a zone configuration object or if "callback"
+ * returns an error.
+ */
+static isc_result_t
+for_all_newzone_cfgs(newzone_cfg_cb_t callback, cfg_obj_t *config,
+ cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *actx, MDB_txn *txn, MDB_dbi dbi)
+{
+ const cfg_obj_t *zconfig, *zlist;
+ isc_result_t result = ISC_R_SUCCESS;
+ cfg_obj_t *zconfigobj = NULL;
+ isc_buffer_t *text = NULL;
+ MDB_cursor *cursor = NULL;
+ MDB_val data, key;
+ int status;
+
+ status = mdb_cursor_open(txn, dbi, &cursor);
+ if (status != MDB_SUCCESS) {
+ return (ISC_R_FAILURE);
+ }
+
+ for (status = mdb_cursor_get(cursor, &key, &data, MDB_FIRST);
+ status == MDB_SUCCESS;
+ status = mdb_cursor_get(cursor, &key, &data, MDB_NEXT))
+ {
+ /*
+ * Create a configuration object from data fetched from NZD.
+ */
+ result = data_to_cfg(view, &key, &data, &text, &zconfigobj);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Extract zone configuration from configuration object.
+ */
+ zlist = NULL;
+ result = cfg_map_get(zconfigobj, "zone", &zlist);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ } else if (!cfg_obj_islist(zlist)) {
+ result = ISC_R_FAILURE;
+ break;
+ }
+ zconfig = cfg_listelt_value(cfg_list_first(zlist));
+
+ /*
+ * Invoke callback.
+ */
+ result = callback(zconfig, config, vconfig, mctx, view, actx);
+ if (result != ISC_R_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Destroy the configuration object created in this iteration.
+ */
+ cfg_obj_destroy(ns_g_addparser, &zconfigobj);
+ }
+
+ if (text != NULL) {
+ isc_buffer_free(&text);
+ }
+ if (zconfigobj != NULL) {
+ cfg_obj_destroy(ns_g_addparser, &zconfigobj);
+ }
+ mdb_cursor_close(cursor);
+
+ return (result);
+}
+
+/*%
+ * Attempt to configure a zone found in NZD and return the result.
+ */
+static isc_result_t
+configure_newzone(const cfg_obj_t *zconfig, cfg_obj_t *config,
+ cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *actx)
+{
+ return (configure_zone(config, zconfig, vconfig, mctx, view,
+ &ns_g_server->viewlist, actx, true,
+ false, false));
+}
+
+/*%
+ * Revert new view assignment for a zone found in NZD.
+ */
+static isc_result_t
+configure_newzone_revert(const cfg_obj_t *zconfig, cfg_obj_t *config,
+ cfg_obj_t *vconfig, isc_mem_t *mctx, dns_view_t *view,
+ cfg_aclconfctx_t *actx)
+{
+ UNUSED(config);
+ UNUSED(vconfig);
+ UNUSED(mctx);
+ UNUSED(actx);
+
+ configure_zone_setviewcommit(ISC_R_FAILURE, zconfig, view);
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+configure_newzones(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
+ isc_mem_t *mctx, cfg_aclconfctx_t *actx)
+{
+ isc_result_t result;
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+
+ if (view->new_zone_config == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ result = nzd_open(view, MDB_RDONLY, &txn, &dbi);
+ if (result != ISC_R_SUCCESS) {
+ return (ISC_R_SUCCESS);
+ }
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "loading NZD configs from '%s' "
+ "for view '%s'",
+ view->new_zone_db, view->name);
+
+ result = for_all_newzone_cfgs(configure_newzone, config, vconfig, mctx,
+ view, actx, txn, dbi);
+ if (result != ISC_R_SUCCESS) {
+ /*
+ * An error was encountered while attempting to configure zones
+ * found in NZD. As this error may have been caused by a
+ * configure_zone() failure, try restoring a sane configuration
+ * by reattaching all zones found in NZD to the old view. If
+ * this also fails, too bad, there is nothing more we can do in
+ * terms of trying to make things right.
+ */
+ (void) for_all_newzone_cfgs(configure_newzone_revert, config,
+ vconfig, mctx, view, actx, txn,
+ dbi);
+ }
+
+ (void) nzd_close(&txn, false);
+ return (result);
+}
+
+static isc_result_t
+get_newzone_config(dns_view_t *view, const char *zonename,
+ cfg_obj_t **zoneconfig)
+{
+ isc_result_t result;
+ int status;
+ cfg_obj_t *zoneconf = NULL;
+ isc_buffer_t *text = NULL;
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+ MDB_val key, data;
+ char zname[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_buffer_t b;
+
+ INSIST(zoneconfig != NULL && *zoneconfig == NULL);
+
+ CHECK(nzd_open(view, MDB_RDONLY, &txn, &dbi));
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO, "loading NZD config from '%s' "
+ "for zone '%s'",
+ view->new_zone_db, zonename);
+
+ /* Normalize zone name */
+ isc_buffer_constinit(&b, zonename, strlen(zonename));
+ isc_buffer_add(&b, strlen(zonename));
+ name = dns_fixedname_initname(&fname);
+ CHECK(dns_name_fromtext(name, &b, dns_rootname,
+ DNS_NAME_DOWNCASE, NULL));
+ dns_name_format(name, zname, sizeof(zname));
+
+ key.mv_data = zname;
+ key.mv_size = strlen(zname);
+
+ status = mdb_get(txn, dbi, &key, &data);
+ if (status != MDB_SUCCESS) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ CHECK(data_to_cfg(view, &key, &data, &text, &zoneconf));
+
+ *zoneconfig = zoneconf;
+ zoneconf = NULL;
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ (void) nzd_close(&txn, false);
+
+ if (zoneconf != NULL) {
+ cfg_obj_destroy(ns_g_addparser, &zoneconf);
+ }
+ if (text != NULL) {
+ isc_buffer_free(&text);
+ }
+
+ return (result);
+}
+
+#endif /* HAVE_LMDB */
+
+static int
+count_zones(const cfg_obj_t *conf) {
+ const cfg_obj_t *zonelist = NULL;
+ const cfg_listelt_t *element;
+ int n = 0;
+
+ REQUIRE(conf != NULL);
+
+ cfg_map_get(conf, "zone", &zonelist);
+ for (element = cfg_list_first(zonelist);
+ element != NULL;
+ element = cfg_list_next(element))
+ n++;
+
+ return (n);
+}
+
+static isc_result_t
+check_lockfile(ns_server_t *server, const cfg_obj_t *config,
+ bool first_time)
+{
+ isc_result_t result;
+ const char *filename = NULL;
+ const cfg_obj_t *maps[3];
+ const cfg_obj_t *options;
+ const cfg_obj_t *obj;
+ int i;
+
+ 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;
+
+ obj = NULL;
+ (void) ns_config_get(maps, "lock-file", &obj);
+
+ if (!first_time) {
+ if (obj != NULL && !cfg_obj_isstring(obj) &&
+ server->lockfile != NULL &&
+ strcmp(cfg_obj_asstring(obj), server->lockfile) != 0)
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING,
+ "changing 'lock-file' "
+ "has no effect until the "
+ "server is restarted");
+
+ return (ISC_R_SUCCESS);
+ }
+
+ if (obj != NULL) {
+ if (cfg_obj_isvoid(obj)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "skipping lock-file check ");
+ return (ISC_R_SUCCESS);
+ } else if (ns_g_forcelock) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "'lock-file' has no effect "
+ "because the server was run with -X");
+ server->lockfile = isc_mem_strdup(server->mctx,
+ ns_g_defaultlockfile);
+ } else {
+ filename = cfg_obj_asstring(obj);
+ server->lockfile = isc_mem_strdup(server->mctx,
+ filename);
+ }
+
+ if (server->lockfile == NULL)
+ return (ISC_R_NOMEMORY);
+ }
+
+ if (ns_g_forcelock && ns_g_defaultlockfile != NULL) {
+ INSIST(server->lockfile == NULL);
+ server->lockfile = isc_mem_strdup(server->mctx,
+ ns_g_defaultlockfile);
+ }
+
+ if (server->lockfile == NULL)
+ return (ISC_R_SUCCESS);
+
+ if (ns_os_issingleton(server->lockfile))
+ return (ISC_R_SUCCESS);
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "could not lock %s; another named "
+ "process may be running", server->lockfile);
+ return (ISC_R_FAILURE);
+}
+
+static isc_result_t
+load_configuration(const char *filename, ns_server_t *server,
+ bool first_time)
+{
+ cfg_obj_t *config = NULL, *bindkeys = NULL;
+ cfg_parser_t *conf_parser = NULL, *bindkeys_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, builtin_viewlist;
+ in_port_t listen_port, udpport_low, udpport_high;
+ int i;
+ int num_zones = 0;
+ bool exclusive = false;
+ isc_interval_t interval;
+ isc_logconfig_t *logc = NULL;
+ isc_portset_t *v4portset = NULL;
+ isc_portset_t *v6portset = NULL;
+ isc_resourcevalue_t nfiles;
+ isc_result_t result, tresult;
+ uint32_t heartbeat_interval;
+ uint32_t interface_interval;
+ uint32_t reserved;
+ uint32_t udpsize;
+ uint32_t transfer_message_size;
+ ns_cache_t *nsc;
+ ns_cachelist_t cachelist, tmpcachelist;
+ ns_altsecret_t *altsecret;
+ ns_altsecretlist_t altsecrets, tmpaltsecrets;
+ unsigned int maxsocks;
+ uint32_t softquota = 0;
+
+ ISC_LIST_INIT(viewlist);
+ ISC_LIST_INIT(builtin_viewlist);
+ ISC_LIST_INIT(cachelist);
+ ISC_LIST_INIT(altsecrets);
+
+ /* Create the ACL configuration context */
+ if (ns_g_aclconfctx != NULL) {
+ cfg_aclconfctx_detach(&ns_g_aclconfctx);
+ }
+ CHECK(cfg_aclconfctx_create(ns_g_mctx, &ns_g_aclconfctx));
+
+ /*
+ * Shut down all dyndb instances.
+ */
+ dns_dyndb_cleanup(false);
+
+ /*
+ * Parse the global default pseudo-config file.
+ */
+ if (first_time) {
+ result = ns_config_parsedefaults(ns_g_parser, &ns_g_config);
+ if (result != ISC_R_SUCCESS) {
+ ns_main_earlyfatal("unable to load "
+ "internal defaults: %s",
+ isc_result_totext(result));
+ }
+ RUNTIME_CHECK(cfg_map_get(ns_g_config, "options",
+ &ns_g_defaults) == ISC_R_SUCCESS);
+ }
+
+ /*
+ * Parse the configuration file using the new config code.
+ */
+ 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, &conf_parser));
+ cfg_parser_setcallback(conf_parser, directory_callback, NULL);
+ result = cfg_parse_file(conf_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 (conf_parser != NULL) {
+ cfg_parser_destroy(&conf_parser);
+ }
+ CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx, &conf_parser));
+ result = ns_lwresd_parseeresolvconf(ns_g_mctx, conf_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;
+
+ /*
+ * If bind.keys exists, load it. If "dnssec-validation auto"
+ * is turned on, the root key found there will be used as a
+ * default trust anchor.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "bindkeys-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->bindkeysfile,
+ cfg_obj_asstring(obj)), "strdup");
+
+ if (access(server->bindkeysfile, R_OK) == 0) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "reading built-in trust anchors "
+ "from file '%s'", server->bindkeysfile);
+
+ CHECK(cfg_parser_create(ns_g_mctx, ns_g_lctx,
+ &bindkeys_parser));
+
+ result = cfg_parse_file(bindkeys_parser, server->bindkeysfile,
+ &cfg_type_bindkeys, &bindkeys);
+ CHECK(result);
+ } else {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "unable to open '%s'; using built-in keys "
+ "instead", server->bindkeysfile);
+ }
+
+ /* Ensure exclusive access to configuration data. */
+ if (!exclusive) {
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ exclusive = true;
+ }
+
+ /*
+ * Set process limits, which (usually) needs to be done as root.
+ */
+ set_limits(maps);
+
+ /*
+ * Check the process lockfile.
+ */
+ CHECK(check_lockfile(server, config, first_time));
+
+ /*
+ * 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 (%" PRIu64 ")"
+ " 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);
+
+#ifdef HAVE_GEOIP
+ /*
+ * Initialize GeoIP databases from the configured location.
+ * This should happen before configuring any ACLs, so that we
+ * know what databases are available and can reject any GeoIP
+ * ACLs that can't work.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "geoip-directory", &obj);
+ if (result == ISC_R_SUCCESS && cfg_obj_isstring(obj)) {
+ char *dir;
+ DE_CONST(cfg_obj_asstring(obj), dir);
+ ns_geoip_load(dir);
+ } else {
+ ns_geoip_load(NULL);
+ }
+ ns_g_aclconfctx->geoip = ns_g_geoip;
+
+ obj = NULL;
+ result = ns_config_get(maps, "geoip-use-ecs", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ ns_g_server->aclenv.geoip_use_ecs = cfg_obj_asboolean(obj);
+#endif /* HAVE_GEOIP */
+
+ /*
+ * 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) {
+ int margin = ISC_MAX(100, ns_g_cpus + 1);
+ if (margin > server->recursionquota.max - 100) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "'recursive-clients %d' too low when "
+ "running with %d worker threads",
+ server->recursionquota.max, ns_g_cpus);
+ CHECK(ISC_R_RANGE);
+ }
+ softquota = server->recursionquota.max - margin;
+ } else {
+ softquota = (server->recursionquota.max * 90) / 100;
+ }
+
+ isc_quota_soft(&server->recursionquota, softquota);
+
+ /*
+ * Set "blackhole". Only legal at options level; there is
+ * no default.
+ */
+ CHECK(configure_view_acl(NULL, config, NULL, "blackhole", NULL,
+ ns_g_aclconfctx, ns_g_mctx,
+ &server->blackholeacl));
+ if (server->blackholeacl != NULL) {
+ dns_dispatchmgr_setblackhole(ns_g_dispatchmgr,
+ server->blackholeacl);
+ }
+
+ /*
+ * Set "keep-response-order". Only legal at options or
+ * global defaults level.
+ */
+ CHECK(configure_view_acl(NULL, config, ns_g_config,
+ "keep-response-order", NULL,
+ ns_g_aclconfctx, ns_g_mctx,
+ &server->keepresporder));
+
+ 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, ns_g_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, 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);
+ }
+ if (!ns_g_disable4) {
+ 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, false);
+ }
+
+ (void)ns_config_get(maps, "use-v6-udp-ports", &usev6ports);
+ if (usev6ports != NULL) {
+ portset_fromconf(v6portset, usev6ports, 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);
+ }
+ if (!ns_g_disable6) {
+ 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, 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 = (uint16_t)udpsize;
+
+ /* Set the transfer message size for TCP */
+ obj = NULL;
+ result = ns_config_get(maps, "transfer-message-size", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ transfer_message_size = cfg_obj_asuint32(obj);
+ if (transfer_message_size < 512) {
+ transfer_message_size = 512;
+ } else if (transfer_message_size > 65535) {
+ transfer_message_size = 65535;
+ }
+ server->transfer_tcp_message_size = (uint16_t) transfer_message_size;
+
+ /*
+ * 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, "notify-rate", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_setnotifyrate(server->zonemgr, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "startup-notify-rate", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zonemgr_setstartupnotifyrate(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");
+ }
+
+ /*
+ * Determing the default DSCP code point.
+ */
+ CHECKM(ns_config_getdscp(config, &ns_g_dscp), "dscp");
+
+ /*
+ * 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 > 0) && (ns_g_listen < 10)) {
+ ns_g_listen = 10;
+ }
+
+ /*
+ * 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) {
+ /* check return code? */
+ (void)ns_listenlist_fromconfig(clistenon, config,
+ ns_g_aclconfctx,
+ ns_g_mctx, AF_INET,
+ &listenon);
+ } else if (!ns_g_lwresdonly) {
+ /*
+ * Not specified, use default.
+ */
+ CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
+ -1, 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) {
+ /* check return code? */
+ (void)ns_listenlist_fromconfig(clistenon, config,
+ ns_g_aclconfctx,
+ ns_g_mctx, AF_INET6,
+ &listenon);
+ } else if (!ns_g_lwresdonly) {
+ /*
+ * Not specified, use default.
+ */
+ CHECK(ns_listenlist_default(ns_g_mctx, listen_port,
+ -1, true, &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.
+ */
+ result = scan_interfaces(server, true);
+
+ /*
+ * Check that named is able to TCP listen on at least one
+ * interface. Otherwise, another named process could be running
+ * and we should fail.
+ */
+ if (first_time && (result == ISC_R_ADDRINUSE)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "unable to listen on any configured interfaces");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /*
+ * 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, 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, false));
+ }
+ server->interface_interval = interface_interval;
+
+ /*
+ * Enable automatic interface scans.
+ */
+ obj = NULL;
+ result = ns_config_get(maps, "automatic-interface-scan", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ server->interface_auto = cfg_obj_asboolean(obj);
+
+ /*
+ * 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, 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, false));
+ }
+ server->heartbeat_interval = heartbeat_interval;
+
+ isc_interval_set(&interval, 1200, 0);
+ CHECK(isc_timer_reset(server->pps_timer, isc_timertype_ticker, NULL,
+ &interval, false));
+
+ isc_interval_set(&interval, ns_g_tat_interval, 0);
+ CHECK(isc_timer_reset(server->tat_timer, isc_timertype_ticker, NULL,
+ &interval, false));
+
+ /*
+ * Write the PID file.
+ */
+ 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);
+ }
+
+ /*
+ * Configure the server-wide session key. This must be done before
+ * configure views because zone configuration may need to know
+ * session-keyname.
+ *
+ * Failure of session key generation isn't fatal at this time; if it
+ * turns out that a session key is really needed but doesn't exist,
+ * we'll treat it as a fatal error then.
+ */
+ (void)configure_session_key(maps, server, ns_g_mctx);
+
+ views = NULL;
+ (void)cfg_map_get(config, "view", &views);
+
+ /*
+ * Create the views and count all the configured zones in
+ * order to correctly size the zone manager's task table.
+ * (We only count zones for configured views; the built-in
+ * "bind" view can be ignored as it only adds a negligible
+ * number of zones.)
+ *
+ * If we're allowing new zones, we need to be able to find the
+ * new zone file and count those as well. So we setup the new
+ * zone configuration context, but otherwise view configuration
+ * waits until after the zone manager's task list has been sized.
+ */
+ for (element = cfg_list_first(views);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ cfg_obj_t *vconfig = cfg_listelt_value(element);
+ const cfg_obj_t *voptions = cfg_tuple_get(vconfig, "options");
+ int nzf_num_zones;
+
+ view = NULL;
+
+ CHECK(create_view(vconfig, &viewlist, &view));
+ INSIST(view != NULL);
+
+ num_zones += count_zones(voptions);
+
+ CHECK(setup_newzones(view, config, vconfig, conf_parser,
+ ns_g_aclconfctx, &nzf_num_zones));
+ num_zones += nzf_num_zones;
+
+ dns_view_detach(&view);
+ }
+
+ /*
+ * If there were no explicit views then we do the default
+ * view here.
+ */
+ if (views == NULL) {
+ int nzf_num_zones;
+
+ CHECK(create_view(NULL, &viewlist, &view));
+ INSIST(view != NULL);
+
+ num_zones = count_zones(config);
+
+ CHECK(setup_newzones(view, config, NULL, conf_parser,
+ ns_g_aclconfctx, &nzf_num_zones));
+ num_zones += nzf_num_zones;
+
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Zones have been counted; set the zone manager task pool size.
+ */
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "sizing zone task pool based on %d zones", num_zones);
+ CHECK(dns_zonemgr_setsize(ns_g_server->zonemgr, num_zones));
+
+ /*
+ * 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.
+ */
+ for (element = cfg_list_first(views);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ cfg_obj_t *vconfig = cfg_listelt_value(element);
+
+ view = NULL;
+ CHECK(find_view(vconfig, &viewlist, &view));
+ CHECK(configure_view(view, &viewlist, config, vconfig,
+ &cachelist, bindkeys, ns_g_mctx,
+ ns_g_aclconfctx, 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) {
+ view = NULL;
+ CHECK(find_view(NULL, &viewlist, &view));
+ CHECK(configure_view(view, &viewlist, config, NULL,
+ &cachelist, bindkeys,
+ ns_g_mctx, ns_g_aclconfctx, true));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ }
+
+ /*
+ * Create (or recreate) the built-in views.
+ */
+ 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))
+ {
+ cfg_obj_t *vconfig = cfg_listelt_value(element);
+
+ CHECK(create_view(vconfig, &builtin_viewlist, &view));
+ CHECK(configure_view(view, &viewlist, config, vconfig,
+ &cachelist, bindkeys,
+ ns_g_mctx, ns_g_aclconfctx, false));
+ dns_view_freeze(view);
+ dns_view_detach(&view);
+ view = NULL;
+ }
+
+ /* Now combine the two viewlists into one */
+ ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link);
+
+ /*
+ * Commit any dns_zone_setview() calls on all zones in the new
+ * view.
+ */
+ for (view = ISC_LIST_HEAD(viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ dns_view_setviewcommit(view);
+ }
+
+ /* Swap our new view list with the production one. */
+ tmpviewlist = server->viewlist;
+ server->viewlist = viewlist;
+ viewlist = tmpviewlist;
+
+ /* Make the view list available to each of the views */
+ view = ISC_LIST_HEAD(server->viewlist);
+ while (view != NULL) {
+ view->viewlist = &server->viewlist;
+ view = ISC_LIST_NEXT(view, link);
+ }
+
+ /* Swap our new cache list with the production one. */
+ tmpcachelist = server->cachelist;
+ server->cachelist = cachelist;
+ cachelist = tmpcachelist;
+
+ /* 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,
+ ns_g_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);
+ int level = ISC_LOG_ERROR;
+ result = isc_entropy_createfilesource(ns_g_entropy,
+ randomdev);
+#ifdef PATH_RANDOMDEV
+ if (ns_g_fallbackentropy != NULL) {
+ level = ISC_LOG_INFO;
+ }
+#endif
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ level,
+ "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
+ }
+ }
+
+#ifdef HAVE_LMDB
+ /*
+ * If we're using LMDB, we may have created newzones databases
+ * as root, making it impossible to reopen them later after
+ * switching to a new userid. We close them now, and reopen
+ * after relinquishing privileges them.
+ */
+ if (first_time) {
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ nzd_env_close(view);
+ }
+ }
+#endif /* HAVE_LMDB */
+
+ /*
+ * Relinquish root privileges.
+ */
+ if (first_time) {
+ ns_os_changeuser();
+ }
+
+ /*
+ * Check that the working directory is writable.
+ */
+ if (!isc_file_isdirwritable(".")) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "the working directory is not writable");
+ }
+
+#ifdef HAVE_LMDB
+ /*
+ * Reopen NZD databases.
+ */
+ if (first_time) {
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ nzd_env_reopen(view);
+ }
+ }
+#endif /* HAVE_LMDB */
+
+ /*
+ * 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) {
+ const cfg_obj_t *logobj = NULL;
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "not using config file logging "
+ "statement for logging due to "
+ "-g option");
+
+ (void)cfg_map_get(config, "logging", &logobj);
+ if (logobj != NULL) {
+ result = ns_log_configure(NULL, logobj);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "checking logging configuration "
+ "failed: %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ }
+ } else {
+ const cfg_obj_t *logobj = 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'");
+ }
+
+ CHECKM(isc_logconfig_use(ns_g_lctx, logc),
+ "installing logging configuration");
+ logc = NULL;
+
+ 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) {
+ 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 = true;
+ }
+ }
+ }
+ }
+
+ 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_mem_debugging & ISC_MEM_DEBUGRECORD);
+ }
+
+ 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, "secroots-file", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ CHECKM(setstring(server, &server->secrootsfile, 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 = true;
+ } else {
+ server->version_set = 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 = true;
+ } else {
+ server->hostname_set = false;
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "server-id", &obj);
+ server->server_usehostname = false;
+ if (result == ISC_R_SUCCESS && cfg_obj_isboolean(obj)) {
+ /* The parser translates "hostname" to 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 = false;
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "answer-cookie", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ server->answercookie = cfg_obj_asboolean(obj);
+
+ obj = NULL;
+ result = ns_config_get(maps, "cookie-algorithm", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ if (strcasecmp(cfg_obj_asstring(obj), "aes") == 0) {
+#if defined(HAVE_OPENSSL_AES) || defined(HAVE_OPENSSL_EVP_AES)
+ server->cookiealg = ns_cookiealg_aes;
+#else
+ INSIST(0);
+#endif
+ } else if (strcasecmp(cfg_obj_asstring(obj), "sha1") == 0) {
+ server->cookiealg = ns_cookiealg_sha1;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "sha256") == 0) {
+ server->cookiealg = ns_cookiealg_sha256;
+ } else {
+ INSIST(0);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "cookie-secret", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *str;
+ bool first = true;
+ isc_buffer_t b;
+ unsigned int usedlength;
+
+ for (element = cfg_list_first(obj);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(obj);
+
+ if (first) {
+ memset(server->secret, 0,
+ sizeof(server->secret));
+ isc_buffer_init(&b, server->secret,
+ sizeof(server->secret));
+ result = isc_hex_decodestring(str, &b);
+ if (result != ISC_R_SUCCESS &&
+ result != ISC_R_NOSPACE)
+ goto cleanup;
+ first = false;
+ } else {
+ altsecret = isc_mem_get(server->mctx,
+ sizeof(*altsecret));
+ if (altsecret == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto cleanup;
+ }
+ isc_buffer_init(&b, altsecret->secret,
+ sizeof(altsecret->secret));
+ result = isc_hex_decodestring(str, &b);
+ if (result != ISC_R_SUCCESS &&
+ result != ISC_R_NOSPACE) {
+ isc_mem_put(server->mctx, altsecret,
+ sizeof(*altsecret));
+ goto cleanup;
+ }
+ ISC_LIST_INITANDAPPEND(altsecrets,
+ altsecret, link);
+ }
+
+ usedlength = isc_buffer_usedlength(&b);
+ switch (server->cookiealg) {
+ case ns_cookiealg_aes:
+ if (usedlength != ISC_AES128_KEYLENGTH) {
+ CHECKM(ISC_R_RANGE,
+ "AES cookie-secret must be "
+ "128 bits");
+ }
+ break;
+ case ns_cookiealg_sha1:
+ if (usedlength != ISC_SHA1_DIGESTLENGTH) {
+ CHECKM(ISC_R_RANGE,
+ "SHA1 cookie-secret must be "
+ "160 bits");
+ }
+ break;
+ case ns_cookiealg_sha256:
+ if (usedlength != ISC_SHA256_DIGESTLENGTH) {
+ CHECKM(ISC_R_RANGE,
+ "SHA256 cookie-secret must be "
+ "256 bits");
+ }
+ break;
+ }
+ }
+ } else {
+ result = isc_entropy_getdata(ns_g_entropy,
+ server->secret,
+ sizeof(server->secret),
+ NULL,
+ 0);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+ }
+
+ /*
+ * Swap altsecrets lists.
+ */
+ tmpaltsecrets = server->altsecrets;
+ server->altsecrets = altsecrets;
+ altsecrets = tmpaltsecrets;
+
+ (void) ns_server_loadnta(server);
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (logc != NULL) {
+ isc_logconfig_destroy(&logc);
+ }
+
+ if (v4portset != NULL) {
+ isc_portset_destroy(ns_g_mctx, &v4portset);
+ }
+
+ if (v6portset != NULL) {
+ isc_portset_destroy(ns_g_mctx, &v6portset);
+ }
+
+ if (conf_parser != NULL) {
+ if (config != NULL) {
+ cfg_obj_destroy(conf_parser, &config);
+ }
+ cfg_parser_destroy(&conf_parser);
+ }
+
+ if (bindkeys_parser != NULL) {
+ if (bindkeys != NULL) {
+ cfg_obj_destroy(bindkeys_parser, &bindkeys);
+ }
+ cfg_parser_destroy(&bindkeys_parser);
+ }
+
+ if (view != NULL) {
+ dns_view_detach(&view);
+ }
+
+ ISC_LIST_APPENDLIST(viewlist, builtin_viewlist, link);
+
+ /*
+ * 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)
+ {
+ dns_view_setviewrevert(view);
+ (void)dns_zt_apply(view->zonetable, false,
+ removed, view);
+ }
+ dns_view_detach(&view);
+ }
+
+ /* Same cleanup for cache list. */
+ while ((nsc = ISC_LIST_HEAD(cachelist)) != NULL) {
+ ISC_LIST_UNLINK(cachelist, nsc, link);
+ dns_cache_detach(&nsc->cache);
+ isc_mem_put(server->mctx, nsc, sizeof(*nsc));
+ }
+
+ /* Same cleanup for altsecrets list. */
+ while ((altsecret = ISC_LIST_HEAD(altsecrets)) != NULL) {
+ ISC_LIST_UNLINK(altsecrets, altsecret, link);
+ isc_mem_put(server->mctx, altsecret, sizeof(*altsecret));
+ }
+
+ /*
+ * 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);
+ }
+
+ /*
+ * Record the time of most recent configuration
+ */
+ tresult = isc_time_now(&ns_g_configtime);
+ if (tresult != ISC_R_SUCCESS) {
+ ns_main_earlyfatal("isc_time_now() failed: %s",
+ isc_result_totext(result));
+ }
+
+ /* Relinquish exclusive access to configuration data. */
+ if (exclusive) {
+ 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
+view_loaded(void *arg) {
+ isc_result_t result;
+ ns_zoneload_t *zl = (ns_zoneload_t *) arg;
+ ns_server_t *server = zl->server;
+ bool reconfig = zl->reconfig;
+ unsigned int refs;
+
+
+ /*
+ * 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.
+ *
+ * We use the zoneload reference counter to let us
+ * know when all views are finished.
+ */
+ isc_refcount_decrement(&zl->refs, &refs);
+ if (refs != 0)
+ return (ISC_R_SUCCESS);
+
+ isc_refcount_destroy(&zl->refs);
+ isc_mem_put(server->mctx, zl, sizeof (*zl));
+
+ /*
+ * To maintain compatibility with log parsing tools that might
+ * be looking for this string after "rndc reconfig", we keep it
+ * as it is
+ */
+ if (reconfig) {
+ 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_NOTICE,
+ "all zones loaded");
+ }
+
+ CHECKFATAL(dns_zonemgr_forcemaint(server->zonemgr),
+ "forcing zone maintenance");
+
+ ns_os_started();
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_NOTICE, "running");
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_zones(ns_server_t *server, bool init, bool reconfig) {
+ isc_result_t result;
+ dns_view_t *view;
+ ns_zoneload_t *zl;
+ unsigned int refs = 0;
+
+ zl = isc_mem_get(server->mctx, sizeof (*zl));
+ if (zl == NULL)
+ return (ISC_R_NOMEMORY);
+ zl->server = server;
+ zl->reconfig = reconfig;
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ isc_refcount_init(&zl->refs, 1);
+
+ /*
+ * Schedule zones to be loaded from disk.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (view->managed_keys != NULL) {
+ result = dns_zone_load(view->managed_keys);
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_UPTODATE &&
+ result != DNS_R_CONTINUE)
+ goto cleanup;
+ }
+ if (view->redirect != NULL) {
+ result = dns_zone_load(view->redirect);
+ if (result != ISC_R_SUCCESS &&
+ result != DNS_R_UPTODATE &&
+ result != DNS_R_CONTINUE)
+ goto cleanup;
+ }
+
+ /*
+ * 'dns_view_asyncload' calls view_loaded if there are no
+ * zones.
+ */
+ isc_refcount_increment(&zl->refs, NULL);
+ CHECK(dns_view_asyncload2(view, view_loaded, zl, reconfig));
+ }
+
+ cleanup:
+ isc_refcount_decrement(&zl->refs, &refs);
+ if (refs == 0) {
+ isc_refcount_destroy(&zl->refs);
+ isc_mem_put(server->mctx, zl, sizeof (*zl));
+ } else if (init) {
+ /*
+ * Place the task manager into privileged mode. This
+ * ensures that after we leave task-exclusive mode, no
+ * other tasks will be able to run except for the ones
+ * that are loading zones. (This should only be done during
+ * the initial server setup; it isn't necessary during
+ * a reload.)
+ */
+ isc_taskmgr_setmode(ns_g_taskmgr, isc_taskmgrmode_privileged);
+ }
+
+ 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->task, &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, tat_timer_tick,
+ server, &server->tat_timer),
+ "creating trust anchor telemetry 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, ns_g_lctx, &ns_g_parser),
+ "creating default configuration parser");
+
+ CHECKFATAL(cfg_parser_create(ns_g_mctx, ns_g_lctx, &ns_g_addparser),
+ "creating additional configuration parser");
+
+ if (ns_g_lwresdonly)
+ CHECKFATAL(load_configuration(lwresd_g_conffile, server,
+ true),
+ "loading configuration");
+ else
+ CHECKFATAL(load_configuration(ns_g_conffile, server, true),
+ "loading configuration");
+
+ isc_hash_init();
+
+ CHECKFATAL(load_zones(server, true, false), "loading zones");
+#ifdef ENABLE_AFL
+ ns_g_run_done = true;
+#endif
+}
+
+void
+ns_server_flushonshutdown(ns_server_t *server, bool 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;
+ bool flush = server->flushonshutdown;
+ ns_cache_t *nsc;
+ ns_altsecret_t *altsecret;
+
+ 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, true);
+ cleanup_session_key(server, server->mctx);
+
+ if (ns_g_aclconfctx != NULL)
+ cfg_aclconfctx_detach(&ns_g_aclconfctx);
+
+ cfg_obj_destroy(ns_g_parser, &ns_g_config);
+ cfg_parser_destroy(&ns_g_parser);
+ cfg_parser_destroy(&ns_g_addparser);
+
+ (void) ns_server_saventa(server);
+
+ 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);
+ }
+
+ dns_dyndb_cleanup(true);
+
+ while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) {
+ ISC_LIST_UNLINK(server->cachelist, nsc, link);
+ dns_cache_detach(&nsc->cache);
+ isc_mem_put(server->mctx, nsc, sizeof(*nsc));
+ }
+
+ while ((altsecret = ISC_LIST_HEAD(server->altsecrets)) != NULL) {
+ ISC_LIST_UNLINK(server->altsecrets, altsecret, link);
+ isc_mem_put(server->mctx, altsecret, sizeof(*altsecret));
+ }
+
+ isc_timer_detach(&server->interface_timer);
+ isc_timer_detach(&server->heartbeat_timer);
+ isc_timer_detach(&server->pps_timer);
+ isc_timer_detach(&server->tat_timer);
+
+ ns_interfacemgr_shutdown(server->interfacemgr);
+ ns_interfacemgr_detach(&server->interfacemgr);
+
+ dns_dispatchmgr_destroy(&ns_g_dispatchmgr);
+
+ dns_zonemgr_shutdown(server->zonemgr);
+
+ if (ns_g_sessionkey != NULL) {
+ dns_tsigkey_detach(&ns_g_sessionkey);
+ dns_name_free(&ns_g_sessionkeyname, server->mctx);
+ }
+
+ if (server->keepresporder != NULL)
+ dns_acl_detach(&server->keepresporder);
+
+ if (server->blackholeacl != NULL)
+ dns_acl_detach(&server->blackholeacl);
+
+#ifdef HAVE_DNSTAP
+ dns_dt_shutdown();
+#endif
+#ifdef HAVE_GEOIP
+ dns_geoip_shutdown();
+#endif
+
+ 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);
+
+#ifdef HAVE_GEOIP
+ /* Initialize GeoIP before using ACL environment */
+ ns_geoip_init();
+ server->aclenv.geoip = ns_g_geoip;
+#endif
+
+ /* Initialize server data structures. */
+ server->zonemgr = NULL;
+ server->interfacemgr = NULL;
+ ISC_LIST_INIT(server->viewlist);
+ server->in_roothints = NULL;
+ server->blackholeacl = NULL;
+ server->keepresporder = NULL;
+
+ /* Must be first. */
+ CHECKFATAL(dst_lib_init2(ns_g_mctx, ns_g_entropy,
+ ns_g_engine, ISC_ENTROPY_GOODONLY),
+ "initializing DST");
+
+ 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");
+
+ 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, as well as all exclusive
+ * tasks.
+ */
+ CHECKFATAL(isc_task_create(ns_g_taskmgr, 0, &server->task),
+ "creating server task");
+ isc_task_setname(server->task, "server", server);
+ isc_taskmgr_setexcltask(ns_g_taskmgr, server->task);
+ 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->tat_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");
+ CHECKFATAL(dns_zonemgr_setsize(server->zonemgr, 1000),
+ "dns_zonemgr_setsize");
+
+ 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->rcodestats = NULL;
+ server->zonestats = NULL;
+ server->resolverstats = NULL;
+ server->sockstats = NULL;
+ server->udpinstats4 = NULL;
+ server->udpoutstats4 = NULL;
+ server->udpinstats6 = NULL;
+ server->udpoutstats6 = NULL;
+ server->tcpinstats4 = NULL;
+ server->tcpoutstats4 = NULL;
+ server->tcpinstats6 = NULL;
+ server->tcpoutstats6 = NULL;
+ CHECKFATAL(isc_stats_create(server->mctx, &server->sockstats,
+ isc_sockstatscounter_max),
+ "isc_stats_create");
+ isc_socketmgr_setstats(ns_g_socketmgr, server->sockstats);
+
+ server->bindkeysfile = isc_mem_strdup(server->mctx, "bind.keys");
+ CHECKFATAL(server->bindkeysfile == NULL ? ISC_R_NOMEMORY :
+ ISC_R_SUCCESS,
+ "isc_mem_strdup");
+
+ server->dumpfile = isc_mem_strdup(server->mctx, "named_dump.db");
+ CHECKFATAL(server->dumpfile == NULL ? ISC_R_NOMEMORY : ISC_R_SUCCESS,
+ "isc_mem_strdup");
+
+ server->secrootsfile = isc_mem_strdup(server->mctx, "named.secroots");
+ CHECKFATAL(server->secrootsfile == 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 = false;
+ server->hostname = NULL;
+ server->version_set = false;
+ server->version = NULL;
+ server->server_usehostname = false;
+ server->server_id = NULL;
+
+ CHECKFATAL(isc_stats_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_rcodestats_create(ns_g_mctx, &server->rcodestats),
+ "dns_stats_create (rcode)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->zonestats,
+ dns_zonestatscounter_max),
+ "dns_stats_create (zone)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->resolverstats,
+ dns_resstatscounter_max),
+ "dns_stats_create (resolver)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->udpinstats4,
+ dns_sizecounter_in_max),
+ "dns_stats_create (inbound UDP IPv4 traffic size)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->udpoutstats4,
+ dns_sizecounter_out_max),
+ "dns_stats_create (outbound UDP IPv4 traffic size)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->udpinstats6,
+ dns_sizecounter_in_max),
+ "dns_stats_create (inbound UDP IPv6 traffic size)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->udpoutstats6,
+ dns_sizecounter_out_max),
+ "dns_stats_create (outbound UDP IPv6 traffic size)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->tcpinstats4,
+ dns_sizecounter_in_max),
+ "dns_stats_create (inbound TCP IPv4 traffic size)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->tcpoutstats4,
+ dns_sizecounter_out_max),
+ "dns_stats_create (outbound TCP IPv4 traffic size)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->tcpinstats6,
+ dns_sizecounter_in_max),
+ "dns_stats_create (inbound TCP IPv6 traffic size)");
+
+ CHECKFATAL(isc_stats_create(ns_g_mctx, &server->tcpoutstats6,
+ dns_sizecounter_out_max),
+ "dns_stats_create (outbound TCP IPv6 traffic size)");
+
+ server->flushonshutdown = false;
+ server->log_queries = 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);
+
+ ISC_LIST_INIT(server->cachelist);
+
+ ISC_LIST_INIT(server->altsecrets);
+
+ server->sessionkey = NULL;
+ server->session_keyfile = NULL;
+ server->session_keyname = NULL;
+ server->session_keyalg = DST_ALG_UNKNOWN;
+ server->session_keybits = 0;
+
+ server->lockfile = NULL;
+
+ server->dtenv = NULL;
+ server->answercookie = true;
+
+ 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));
+
+#ifdef HAVE_DNSTAP
+ if (server->dtenv != NULL)
+ dns_dt_detach(&server->dtenv);
+#endif /* HAVE_DNSTAP */
+
+ ns_controls_destroy(&server->controls);
+
+ isc_stats_detach(&server->nsstats);
+ dns_stats_detach(&server->rcvquerystats);
+ dns_stats_detach(&server->opcodestats);
+ dns_stats_detach(&server->rcodestats);
+ isc_stats_detach(&server->zonestats);
+ isc_stats_detach(&server->resolverstats);
+ isc_stats_detach(&server->sockstats);
+ isc_stats_detach(&server->udpinstats4);
+ isc_stats_detach(&server->udpoutstats4);
+ isc_stats_detach(&server->udpinstats6);
+ isc_stats_detach(&server->udpoutstats6);
+ isc_stats_detach(&server->tcpinstats4);
+ isc_stats_detach(&server->tcpoutstats4);
+ isc_stats_detach(&server->tcpinstats6);
+ isc_stats_detach(&server->tcpoutstats6);
+
+ isc_mem_free(server->mctx, server->statsfile);
+ isc_mem_free(server->mctx, server->bindkeysfile);
+ isc_mem_free(server->mctx, server->dumpfile);
+ isc_mem_free(server->mctx, server->secrootsfile);
+ 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);
+ if (server->lockfile != NULL)
+ isc_mem_free(server->mctx, server->lockfile);
+
+ if (server->zonemgr != NULL)
+ 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));
+ INSIST(ISC_LIST_EMPTY(server->cachelist));
+
+ 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)");
+ ns_os_shutdown();
+ 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, bool 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,
+ UDPBUFFERS, 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, false);
+ if (result == ISC_R_SUCCESS) {
+ end_reserved_dispatches(server, 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, false, 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);
+}
+
+/*
+ * 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);
+}
+
+void
+ns_server_scan_interfaces(ns_server_t *server) {
+ isc_result_t result;
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_DEBUG(1),
+ "automatic interface rescan");
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ scan_interfaces(server, true);
+ isc_task_endexclusive(server->task);
+}
+
+/*
+ * Get the next token from lexer 'lex'.
+ *
+ * NOTE: the token value for string tokens always uses the same pointer
+ * value. Multiple calls to this function on the same lexer will always
+ * return either that value (lex->data) or NULL. It is necessary to copy
+ * the token into local storage if it needs to be referenced after the next
+ * call to next_token().
+ */
+static char *
+next_token(isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result;
+ isc_token_t token;
+
+ token.type = isc_tokentype_unknown;
+ result = isc_lex_gettoken(lex, ISC_LEXOPT_EOF|ISC_LEXOPT_QSTRING,
+ &token);
+
+ switch (result) {
+ case ISC_R_NOMORE:
+ (void) isc_lex_close(lex);
+ break;
+ case ISC_R_SUCCESS:
+ if (token.type == isc_tokentype_eof)
+ (void) isc_lex_close(lex);
+ break;
+ case ISC_R_NOSPACE:
+ if (text != NULL) {
+ (void) putstr(text, "token too large");
+ (void) putnull(text);
+ }
+ return (NULL);
+ default:
+ if (text != NULL) {
+ (void) putstr(text, isc_result_totext(result));
+ (void) putnull(text);
+ }
+ return (NULL);
+ }
+
+ if (token.type == isc_tokentype_string ||
+ token.type == isc_tokentype_qstring)
+ return (token.value.as_textregion.base);
+
+ return (NULL);
+}
+
+/*
+ * Find the zone specified in the control channel command, if any.
+ * If a zone is specified, point '*zonep' at it, otherwise
+ * set '*zonep' to NULL, and f 'zonename' is not NULL, copy
+ * the zone name into it (N.B. 'zonename' must have space to hold
+ * a full DNS name).
+ *
+ * If 'zonetxt' is set, the caller has already pulled a token
+ * off the command line that is to be used as the zone name. (This
+ * is sometimes done when it's necessary to check for an optional
+ * argument before the zone name, as in "rndc sync [-clean] zone".)
+ */
+static isc_result_t
+zone_from_args(ns_server_t *server, isc_lex_t *lex, const char *zonetxt,
+ dns_zone_t **zonep, char *zonename,
+ isc_buffer_t **text, bool skip)
+{
+ char *ptr;
+ char *classtxt;
+ const char *viewtxt = NULL;
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ isc_result_t result;
+ dns_view_t *view = NULL;
+ dns_rdataclass_t rdclass;
+ char problem[DNS_NAME_FORMATSIZE + 500] = "";
+ char zonebuf[DNS_NAME_FORMATSIZE];
+
+ REQUIRE(zonep != NULL && *zonep == NULL);
+
+ if (skip) {
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Look for the zone name. */
+ if (zonetxt == NULL)
+ zonetxt = next_token(lex, text);
+ if (zonetxt == NULL)
+ return (ISC_R_SUCCESS);
+
+ /* Copy zonetxt because it'll be overwritten by next_token() */
+ strlcpy(zonebuf, zonetxt, DNS_NAME_FORMATSIZE);
+ if (zonename != NULL)
+ strlcpy(zonename, zonetxt, DNS_NAME_FORMATSIZE);
+
+ name = dns_fixedname_initname(&fname);
+ CHECK(dns_name_fromstring(name, zonebuf, 0, NULL));
+
+ /* Look for the optional class name. */
+ classtxt = next_token(lex, text);
+ if (classtxt != NULL) {
+ isc_textregion_t r;
+ r.base = classtxt;
+ r.length = strlen(classtxt);
+ CHECK(dns_rdataclass_fromtext(&rdclass, &r));
+
+ /* Look for the optional view name. */
+ viewtxt = next_token(lex, text);
+ } else
+ rdclass = dns_rdataclass_in;
+
+ if (viewtxt == NULL) {
+ result = dns_viewlist_findzone(&server->viewlist, name,
+ (classtxt == NULL),
+ rdclass, zonep);
+ if (result == ISC_R_NOTFOUND)
+ snprintf(problem, sizeof(problem),
+ "no matching zone '%s' in any view",
+ zonebuf);
+ else if (result == ISC_R_MULTIPLE)
+ snprintf(problem, sizeof(problem),
+ "zone '%s' was found in multiple views",
+ zonebuf);
+ } else {
+ result = dns_viewlist_find(&server->viewlist, viewtxt,
+ rdclass, &view);
+ if (result != ISC_R_SUCCESS) {
+ snprintf(problem, sizeof(problem),
+ "no matching view '%s'", viewtxt);
+ goto report;
+ }
+
+ result = dns_zt_find(view->zonetable, name, 0, NULL, zonep);
+ if (result != ISC_R_SUCCESS)
+ snprintf(problem, sizeof(problem),
+ "no matching zone '%s' in view '%s'",
+ zonebuf, viewtxt);
+ }
+
+ /* Partial match? */
+ if (result != ISC_R_SUCCESS && *zonep != NULL)
+ dns_zone_detach(zonep);
+ if (result == DNS_R_PARTIALMATCH)
+ result = ISC_R_NOTFOUND;
+ report:
+ if (result != ISC_R_SUCCESS) {
+ isc_result_t tresult;
+
+ tresult = putstr(text, problem);
+ if (tresult == ISC_R_SUCCESS)
+ (void) putnull(text);
+ }
+
+ cleanup:
+ if (view != NULL)
+ dns_view_detach(&view);
+
+ return (result);
+}
+
+/*
+ * Act on a "retransfer" command from the command channel.
+ */
+isc_result_t
+ns_server_retransfercommand(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text)
+{
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_zone_t *raw = NULL;
+ dns_zonetype_t type;
+
+ result = zone_from_args(server, lex, NULL, &zone, NULL,
+ text, true);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL) {
+ dns_zone_detach(&zone);
+ dns_zone_attach(raw, &zone);
+ dns_zone_detach(&raw);
+ }
+ 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, isc_lex_t *lex,
+ 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, lex, NULL, &zone, NULL,
+ text, true);
+ 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) {
+ (void) putstr(text, msg);
+ (void) putnull(text);
+ }
+ return (result);
+}
+
+/*
+ * Act on a "reconfig" command from the command channel.
+ */
+isc_result_t
+ns_server_reconfigcommand(ns_server_t *server) {
+ isc_result_t result;
+
+ CHECK(loadconfig(server));
+
+ result = load_zones(server, false, true);
+ if (result == ISC_R_SUCCESS)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "scheduled loading new zones");
+ 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:
+ return (result);
+}
+
+/*
+ * Act on a "notify" command from the command channel.
+ */
+isc_result_t
+ns_server_notifycommand(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text)
+{
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ const char msg[] = "zone notify queued";
+
+ result = zone_from_args(server, lex, NULL, &zone, NULL,
+ text, true);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ dns_zone_notify(zone);
+ dns_zone_detach(&zone);
+ (void) putstr(text, msg);
+ (void) putnull(text);
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * Act on a "refresh" command from the command channel.
+ */
+isc_result_t
+ns_server_refreshcommand(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text)
+{
+ isc_result_t result;
+ dns_zone_t *zone = NULL, *raw = NULL;
+ const char msg1[] = "zone refresh queued";
+ const char msg2[] = "not a slave or stub zone";
+ dns_zonetype_t type;
+
+ result = zone_from_args(server, lex, NULL, &zone, NULL,
+ text, true);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL) {
+ dns_zone_detach(&zone);
+ dns_zone_attach(raw, &zone);
+ dns_zone_detach(&raw);
+ }
+
+ type = dns_zone_gettype(zone);
+ if (type == dns_zone_slave || type == dns_zone_stub) {
+ dns_zone_refresh(zone);
+ dns_zone_detach(&zone);
+ (void) putstr(text, msg1);
+ (void) putnull(text);
+ return (ISC_R_SUCCESS);
+ }
+
+ dns_zone_detach(&zone);
+ (void) putstr(text, msg2);
+ (void) putnull(text);
+ return (ISC_R_FAILURE);
+}
+
+isc_result_t
+ns_server_togglequerylog(ns_server_t *server, isc_lex_t *lex) {
+ bool value;
+ char *ptr;
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ value = server->log_queries ? false : true;
+ } else if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
+ !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true")) {
+ value = true;
+ } else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
+ !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false")) {
+ value = false;
+ } else {
+ return (DNS_R_SYNTAX);
+ }
+
+ if (server->log_queries == value)
+ return (ISC_R_SUCCESS);
+
+ server->log_queries = value;
+
+ 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,
+ uint16_t family, 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, family, &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,
+ uint16_t family, ns_listenelt_t **target)
+{
+ isc_result_t result;
+ const cfg_obj_t *portobj, *dscpobj;
+ in_port_t port;
+ isc_dscp_t dscp = -1;
+ 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) >= 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);
+ }
+
+ dscpobj = cfg_tuple_get(listener, "dscp");
+ if (!cfg_obj_isuint32(dscpobj))
+ dscp = ns_g_dscp;
+ else {
+ if (cfg_obj_asuint32(dscpobj) > 63) {
+ cfg_obj_log(dscpobj, ns_g_lctx, ISC_LOG_ERROR,
+ "dscp value '%u' is out of range",
+ cfg_obj_asuint32(dscpobj));
+ return (ISC_R_RANGE);
+ }
+ dscp = (isc_dscp_t)cfg_obj_asuint32(dscpobj);
+ }
+
+ result = ns_listenelt_create(mctx, port, dscp, NULL, &delt);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"),
+ config, ns_g_lctx, actx, mctx, 0,
+ family, &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);
+
+ 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, 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, 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->dumpcache && dns_view_iscacheshared(dctx->view->view)) {
+ fprintf(dctx->fp,
+ ";\n; Cache of view '%s' is shared as '%s'\n",
+ dctx->view->view->name,
+ dns_cache_getname(dctx->view->view->cache));
+ } else 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' (cache %s)\n;\n",
+ dctx->view->view->name,
+ dns_cache_getname(dctx->view->view->cache));
+ 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->dumpadb || dctx->dumpbad || dctx->dumpfail) &&
+ dctx->cache == NULL && dctx->view->view->cachedb != NULL)
+ dns_db_attach(dctx->view->view->cachedb, &dctx->cache);
+
+ if (dctx->cache != NULL) {
+ if (dctx->dumpadb)
+ dns_adb_dump(dctx->view->view->adb, dctx->fp);
+ if (dctx->dumpbad)
+ dns_resolver_printbadcache(dctx->view->view->resolver,
+ dctx->fp);
+ if (dctx->dumpfail)
+ dns_badcache_print(dctx->view->view->failcache,
+ "SERVFAIL cache", 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,
+ 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;
+ POST(result);
+ 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, isc_lex_t *lex, isc_buffer_t **text) {
+ struct dumpcontext *dctx = NULL;
+ dns_view_t *view;
+ isc_result_t result;
+ char *ptr;
+ const char *sep;
+ bool found;
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ 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 = true;
+ dctx->dumpadb = true;
+ dctx->dumpbad = true;
+ dctx->dumpfail = true;
+ dctx->dumpzones = 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);
+
+ ptr = next_token(lex, NULL);
+ sep = (ptr == NULL) ? "" : ": ";
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "dumpdb started%s%s", sep, (ptr != NULL) ? ptr : "");
+
+ if (ptr != NULL && strcmp(ptr, "-all") == 0) {
+ /* also dump zones */
+ dctx->dumpzones = true;
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-cache") == 0) {
+ /* this is the default */
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-zones") == 0) {
+ /* only dump zones, suppress caches */
+ dctx->dumpadb = false;
+ dctx->dumpbad = false;
+ dctx->dumpcache = false;
+ dctx->dumpfail = false;
+ dctx->dumpzones = true;
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-adb") == 0) {
+ /* only dump adb, suppress other caches */
+ dctx->dumpbad = false;
+ dctx->dumpcache = false;
+ dctx->dumpfail = false;
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-bad") == 0) {
+ /* only dump badcache, suppress other caches */
+ dctx->dumpadb = false;
+ dctx->dumpcache = false;
+ dctx->dumpfail = false;
+ ptr = next_token(lex, NULL);
+ } else if (ptr != NULL && strcmp(ptr, "-fail") == 0) {
+ /* only dump servfail cache, suppress other caches */
+ dctx->dumpadb = false;
+ dctx->dumpbad = false;
+ dctx->dumpcache = false;
+ ptr = next_token(lex, NULL);
+ }
+
+ nextview:
+ found = false;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (ptr != NULL && strcmp(view->name, ptr) != 0)
+ continue;
+ found = true;
+ CHECK(add_view_tolist(dctx, view));
+ }
+ if (ptr != NULL) {
+ if (!found) {
+ putstr(text, "view '");
+ putstr(text, ptr);
+ putstr(text, "' not found");
+ putnull(text);
+ result = ISC_R_NOTFOUND;
+ dumpdone(dctx, result);
+ return (result);
+ }
+ ptr = next_token(lex, NULL);
+ 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_dumpsecroots(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text)
+{
+ dns_view_t *view;
+ dns_keytable_t *secroots = NULL;
+ dns_ntatable_t *ntatable = NULL;
+ isc_result_t result;
+ char *ptr;
+ FILE *fp = NULL;
+ isc_time_t now;
+ char tbuf[64];
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* "-" here means print the output instead of dumping to file */
+ ptr = next_token(lex, text);
+ if (ptr != NULL && strcmp(ptr, "-") == 0)
+ ptr = next_token(lex, text);
+ else {
+ result = isc_stdio_open(server->secrootsfile, "w", &fp);
+ if (result != ISC_R_SUCCESS) {
+ (void) putstr(text, "could not open ");
+ (void) putstr(text, server->secrootsfile);
+ CHECKMF(result, "could not open secroots dump file",
+ server->secrootsfile);
+ }
+ }
+
+ TIME_NOW(&now);
+ isc_time_formattimestamp(&now, tbuf, sizeof(tbuf));
+ CHECK(putstr(text, "secure roots as of "));
+ CHECK(putstr(text, tbuf));
+ CHECK(putstr(text, ":\n"));
+
+ do {
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (ptr != NULL && strcmp(view->name, ptr) != 0)
+ continue;
+ if (secroots != NULL)
+ dns_keytable_detach(&secroots);
+ result = dns_view_getsecroots(view, &secroots);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ continue;
+ }
+ CHECK(putstr(text, "\n Start view "));
+ CHECK(putstr(text, view->name));
+ CHECK(putstr(text, "\n Secure roots:\n\n"));
+ CHECK(dns_keytable_totext(secroots, text));
+
+ if (ntatable != NULL)
+ dns_ntatable_detach(&ntatable);
+ result = dns_view_getntatable(view, &ntatable);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ continue;
+ }
+ CHECK(putstr(text, "\n Negative trust anchors:\n\n"));
+ CHECK(dns_ntatable_totext(ntatable, text));
+ }
+ if (ptr != NULL)
+ ptr = next_token(lex, text);
+ } while (ptr != NULL);
+
+ cleanup:
+ if (isc_buffer_usedlength(*text) > 0) {
+ if (fp != NULL)
+ (void)putstr(text, "\n");
+ else
+ (void)putnull(text);
+ }
+ if (secroots != NULL)
+ dns_keytable_detach(&secroots);
+ if (ntatable != NULL)
+ dns_ntatable_detach(&ntatable);
+ if (fp != NULL) {
+ fprintf(fp, "%.*s", (int) isc_buffer_usedlength(*text),
+ (char *) isc_buffer_base(*text));
+ isc_buffer_clear(*text);
+ (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,
+ "dumpsecroots complete");
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "dumpsecroots failed: %s",
+ dns_result_totext(result));
+ return (result);
+}
+
+isc_result_t
+ns_server_dumprecursing(ns_server_t *server) {
+ FILE *fp = NULL;
+ dns_view_t *view;
+ 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);
+
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ fprintf(fp, ";\n; Active fetch domains [view: %s]\n;\n",
+ view->name);
+ dns_resolver_dumpfetches(view->resolver,
+ isc_statsformat_file, fp);
+ }
+
+ 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, isc_lex_t *lex) {
+ char *ptr;
+ char *endp;
+ long newlevel;
+
+ UNUSED(server);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Look for the new level name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL) {
+ if (ns_g_debuglevel < 99)
+ ns_g_debuglevel++;
+ } else {
+ newlevel = strtol(ptr, &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 %u", ns_g_debuglevel);
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_server_validation(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text)
+{
+ char *ptr;
+ dns_view_t *view;
+ bool changed = false;
+ isc_result_t result;
+ bool enable = true, set = true, first = true;
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Find out what we are to do. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ if (!strcasecmp(ptr, "on") || !strcasecmp(ptr, "yes") ||
+ !strcasecmp(ptr, "enable") || !strcasecmp(ptr, "true")) {
+ enable = true;
+ } else if (!strcasecmp(ptr, "off") || !strcasecmp(ptr, "no") ||
+ !strcasecmp(ptr, "disable") || !strcasecmp(ptr, "false")) {
+ enable = false;
+ } else if (!strcasecmp(ptr, "check") || !strcasecmp(ptr, "status")) {
+ set = false;
+ } else {
+ return (DNS_R_SYNTAX);
+ }
+
+ /* Look for the view name. */
+ ptr = next_token(lex, text);
+
+ 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 (ptr != NULL && strcasecmp(ptr, view->name) != 0)
+ continue;
+ CHECK(dns_view_flushcache(view));
+
+ if (set) {
+ view->enablevalidation = enable;
+ changed = true;
+ } else {
+ if (!first)
+ CHECK(putstr(text, "\n"));
+ CHECK(putstr(text, "DNSSEC validation is "));
+ CHECK(putstr(text, view->enablevalidation
+ ? "enabled" : "disabled"));
+ CHECK(putstr(text, " (view "));
+ CHECK(putstr(text, view->name));
+ CHECK(putstr(text, ")"));
+ CHECK(putnull(text));
+ first = false;
+ }
+ }
+
+ if (!set)
+ result = ISC_R_SUCCESS;
+ else if (changed)
+ result = ISC_R_SUCCESS;
+ else
+ result = ISC_R_FAILURE;
+ cleanup:
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+ns_server_flushcache(ns_server_t *server, isc_lex_t *lex) {
+ char *ptr;
+ dns_view_t *view;
+ bool flushed;
+ bool found;
+ isc_result_t result;
+ ns_cache_t *nsc;
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Look for the view name. */
+ ptr = next_token(lex, NULL);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ flushed = true;
+ found = false;
+
+ /*
+ * Flushing a cache is tricky when caches are shared by multiple views.
+ * We first identify which caches should be flushed in the local cache
+ * list, flush these caches, and then update other views that refer to
+ * the flushed cache DB.
+ */
+ if (ptr != NULL) {
+ /*
+ * Mark caches that need to be flushed. This is an O(#view^2)
+ * operation in the very worst case, but should be normally
+ * much more lightweight because only a few (most typically just
+ * one) views will match.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (strcasecmp(ptr, view->name) != 0)
+ continue;
+ found = true;
+ for (nsc = ISC_LIST_HEAD(server->cachelist);
+ nsc != NULL;
+ nsc = ISC_LIST_NEXT(nsc, link)) {
+ if (nsc->cache == view->cache)
+ break;
+ }
+ INSIST(nsc != NULL);
+ nsc->needflush = true;
+ }
+ } else
+ found = true;
+
+ /* Perform flush */
+ for (nsc = ISC_LIST_HEAD(server->cachelist);
+ nsc != NULL;
+ nsc = ISC_LIST_NEXT(nsc, link)) {
+ if (ptr != NULL && !nsc->needflush)
+ continue;
+ nsc->needflush = true;
+ result = dns_view_flushcache2(nsc->primaryview, false);
+ if (result != ISC_R_SUCCESS) {
+ flushed = false;
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing cache in view '%s' failed: %s",
+ nsc->primaryview->name,
+ isc_result_totext(result));
+ }
+ }
+
+ /*
+ * Fix up views that share a flushed cache: let the views update the
+ * cache DB they're referring to. This could also be an expensive
+ * operation, but should typically be marginal: the inner loop is only
+ * necessary for views that share a cache, and if there are many such
+ * views the number of shared cache should normally be small.
+ * A worst case is that we have n views and n/2 caches, each shared by
+ * two views. Then this will be a O(n^2/4) operation.
+ */
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (!dns_view_iscacheshared(view))
+ continue;
+ for (nsc = ISC_LIST_HEAD(server->cachelist);
+ nsc != NULL;
+ nsc = ISC_LIST_NEXT(nsc, link)) {
+ if (!nsc->needflush || nsc->cache != view->cache)
+ continue;
+ result = dns_view_flushcache2(view, true);
+ if (result != ISC_R_SUCCESS) {
+ flushed = false;
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "fixing cache in view '%s' "
+ "failed: %s", view->name,
+ isc_result_totext(result));
+ }
+ }
+ }
+
+ /* Cleanup the cache list. */
+ for (nsc = ISC_LIST_HEAD(server->cachelist);
+ nsc != NULL;
+ nsc = ISC_LIST_NEXT(nsc, link)) {
+ nsc->needflush = false;
+ }
+
+ if (flushed && found) {
+ if (ptr != NULL)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing cache in view '%s' succeeded",
+ ptr);
+ 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", ptr);
+ result = ISC_R_NOTFOUND;
+ } else
+ result = ISC_R_FAILURE;
+ }
+ isc_task_endexclusive(server->task);
+ return (result);
+}
+
+isc_result_t
+ns_server_flushnode(ns_server_t *server, isc_lex_t *lex, bool tree) {
+ char *ptr, *viewname;
+ char target[DNS_NAME_FORMATSIZE];
+ dns_view_t *view;
+ bool flushed;
+ bool found;
+ isc_result_t result;
+ isc_buffer_t b;
+ dns_fixedname_t fixed;
+ dns_name_t *name;
+
+ /* Skip the command name. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Find the domain name to flush. */
+ ptr = next_token(lex, NULL);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ strlcpy(target, ptr, DNS_NAME_FORMATSIZE);
+ isc_buffer_constinit(&b, target, strlen(target));
+ isc_buffer_add(&b, strlen(target));
+ name = dns_fixedname_initname(&fixed);
+ result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ /* Look for the view name. */
+ viewname = next_token(lex, NULL);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ flushed = true;
+ found = 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 = true;
+ /*
+ * It's a little inefficient to try flushing name for all views
+ * if some of the views share a single cache. But since the
+ * operation is lightweight we prefer simplicity here.
+ */
+ result = dns_view_flushnode(view, name, tree);
+ if (result != ISC_R_SUCCESS) {
+ flushed = false;
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing %s '%s' in cache view '%s' "
+ "failed: %s",
+ tree ? "tree" : "name",
+ 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 %s '%s' in cache view '%s' "
+ "succeeded",
+ tree ? "tree" : "name",
+ target, viewname);
+ else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flushing %s '%s' in all cache views "
+ "succeeded",
+ tree ? "tree" : "name",
+ target);
+ result = ISC_R_SUCCESS;
+ } else {
+ if (!found)
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "flushing %s '%s' in cache view '%s' "
+ "failed: view not found",
+ tree ? "tree" : "name",
+ 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) {
+ isc_result_t result;
+ unsigned int zonecount, xferrunning, xferdeferred, soaqueries;
+ unsigned int automatic;
+ const char *ob = "", *cb = "", *alt = "";
+ char boottime[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char configtime[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char line[1024], hostname[256];
+
+ 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);
+ automatic = dns_zonemgr_getcount(server->zonemgr,
+ DNS_ZONESTATE_AUTOMATIC);
+
+ isc_time_formathttptimestamp(&ns_g_boottime, boottime,
+ sizeof(boottime));
+ isc_time_formathttptimestamp(&ns_g_configtime, configtime,
+ sizeof(configtime));
+
+ snprintf(line, sizeof(line), "version: %s %s%s%s <id:%s>%s%s%s\n",
+ ns_g_product, ns_g_version,
+ (*ns_g_description != '\0') ? " " : "",
+ ns_g_description, ns_g_srcid, ob, alt, cb);
+ CHECK(putstr(text, line));
+
+ result = ns_os_gethostname(hostname, sizeof(hostname));
+ if (result != ISC_R_SUCCESS)
+ strlcpy(hostname, "localhost", sizeof(hostname));
+ snprintf(line, sizeof(line), "running on %s: %s\n",
+ hostname, ns_os_uname());
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "boot time: %s\n", boottime);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "last configured: %s\n", configtime);
+ CHECK(putstr(text, line));
+
+ if (ns_g_chrootdir != NULL) {
+ snprintf(line, sizeof(line), "configuration file: %s (%s%s)\n",
+ ns_g_conffile, ns_g_chrootdir, ns_g_conffile);
+ } else {
+ snprintf(line, sizeof(line), "configuration file: %s\n",
+ ns_g_conffile);
+ }
+ CHECK(putstr(text, line));
+
+#ifdef ISC_PLATFORM_USETHREADS
+ snprintf(line, sizeof(line), "CPUs found: %u\n", ns_g_cpus_detected);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "worker threads: %u\n", ns_g_cpus);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "UDP listeners per interface: %u\n",
+ ns_g_udpdisp);
+ CHECK(putstr(text, line));
+#else
+ snprintf(line, sizeof(line), "CPUs found: N/A (threads disabled)\n");
+ CHECK(putstr(text, line));
+#endif
+
+ snprintf(line, sizeof(line), "number of zones: %u (%u automatic)\n",
+ zonecount, automatic);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "debug level: %u\n", ns_g_debuglevel);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "xfers running: %u\n", xferrunning);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "xfers deferred: %u\n", xferdeferred);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "soa queries in progress: %u\n",
+ soaqueries);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "query logging is %s\n",
+ server->log_queries ? "ON" : "OFF");
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "recursive clients: %d/%d/%d\n",
+ server->recursionquota.used, server->recursionquota.soft,
+ server->recursionquota.max);
+ CHECK(putstr(text, line));
+
+ snprintf(line, sizeof(line), "tcp clients: %d/%d\n",
+ server->tcpquota.used, server->tcpquota.max);
+ CHECK(putstr(text, line));
+
+ CHECK(putstr(text, "server is up and running"));
+ CHECK(putnull(text));
+
+ return (ISC_R_SUCCESS);
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+ns_server_testgen(isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result;
+ char *ptr;
+ unsigned long count;
+ unsigned long i;
+ const unsigned char chars[] = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ count = 26;
+ else
+ count = strtoul(ptr, NULL, 10);
+
+ CHECK(isc_buffer_reserve(text, count));
+ for (i = 0; i < count; i++)
+ CHECK(putuint8(text, chars[i % (sizeof(chars) - 1)]));
+
+ CHECK(putnull(text));
+
+ cleanup:
+ return (result);
+}
+
+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);
+ origin = dns_fixedname_initname(&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,
+ 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, isc_lex_t *lex,
+ isc_buffer_t **text)
+{
+ isc_result_t result;
+ dns_view_t *view;
+ unsigned int foundkeys = 0;
+ char *ptr, *viewname;
+ char target[DNS_NAME_FORMATSIZE];
+ char fbuf[16];
+
+ (void)next_token(lex, text); /* skip command name */
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ strlcpy(target, ptr, DNS_NAME_FORMATSIZE);
+
+ viewname = next_token(lex, text);
+
+ 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);
+
+ snprintf(fbuf, sizeof(fbuf), "%u", foundkeys);
+
+ CHECK(putstr(text, fbuf));
+ CHECK(putstr(text, " tsig keys deleted."));
+ CHECK(putnull(text));
+
+ cleanup:
+ return (result);
+}
+
+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;
+ const char *viewname;
+
+ if (view != NULL)
+ viewname = view->name;
+ else
+ viewname = "(global)";
+
+ dns_name_init(&foundname, NULL);
+ origin = dns_fixedname_initname(&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) {
+ dns_name_format(&tkey->name, namestr, sizeof(namestr));
+ if (tkey->generated) {
+ dns_name_format(tkey->creator, creatorstr,
+ sizeof(creatorstr));
+ if (*foundkeys != 0)
+ CHECK(putstr(text, "\n"));
+ CHECK(putstr(text, "view \""));
+ CHECK(putstr(text, viewname));
+ CHECK(putstr(text,
+ "\"; type \"dynamic\"; key \""));
+ CHECK(putstr(text, namestr));
+ CHECK(putstr(text, "\"; creator \""));
+ CHECK(putstr(text, creatorstr));
+ CHECK(putstr(text, "\";"));
+ } else {
+ if (*foundkeys != 0)
+ CHECK(putstr(text, "\n"));
+ CHECK(putstr(text, "view \""));
+ CHECK(putstr(text, viewname));
+ CHECK(putstr(text,
+ "\"; type \"static\"; key \""));
+ CHECK(putstr(text, namestr));
+ CHECK(putstr(text, "\";"));
+ }
+ (*foundkeys)++;
+ }
+ result = dns_rbtnodechain_next(&chain, &foundname, origin);
+ if (result == ISC_R_NOMORE || result == DNS_R_NEWORIGIN)
+ break;
+ }
+
+ return (ISC_R_SUCCESS);
+ cleanup:
+ dns_rbtnodechain_invalidate(&chain);
+ return (result);
+}
+
+isc_result_t
+ns_server_tsiglist(ns_server_t *server, isc_buffer_t **text) {
+ isc_result_t result;
+ 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)
+ CHECK(putstr(text, "no tsig keys found."));
+
+ if (isc_buffer_usedlength(*text) > 0)
+ CHECK(putnull(text));
+
+ cleanup:
+ return (result);
+}
+
+/*
+ * Act on a "sign" or "loadkeys" command from the command channel.
+ */
+isc_result_t
+ns_server_rekey(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result;
+ dns_zone_t *zone = NULL;
+ dns_zonetype_t type;
+ uint16_t keyopts;
+ bool fullsign = false;
+ char *ptr;
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ if (strcasecmp(ptr, NS_COMMAND_SIGN) == 0)
+ fullsign = true;
+
+ result = zone_from_args(server, lex, NULL, &zone, NULL,
+ text, false);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (zone == NULL)
+ return (ISC_R_UNEXPECTEDEND); /* XXX: or do all zones? */
+
+ type = dns_zone_gettype(zone);
+ if (type != dns_zone_master) {
+ dns_zone_detach(&zone);
+ return (DNS_R_NOTMASTER);
+ }
+
+ keyopts = dns_zone_getkeyopts(zone);
+
+ /* "rndc loadkeys" requires "auto-dnssec maintain". */
+ if ((keyopts & DNS_ZONEKEY_ALLOW) == 0)
+ result = ISC_R_NOPERM;
+ else if ((keyopts & DNS_ZONEKEY_MAINTAIN) == 0 && !fullsign)
+ result = ISC_R_NOPERM;
+ else
+ dns_zone_rekey(zone, fullsign);
+
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+/*
+ * Act on a "sync" command from the command channel.
+*/
+static isc_result_t
+synczone(dns_zone_t *zone, void *uap) {
+ bool cleanup = *(bool *)uap;
+ isc_result_t result;
+ dns_zone_t *raw = NULL;
+ char *journal;
+
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL) {
+ synczone(raw, uap);
+ dns_zone_detach(&raw);
+ }
+
+ result = dns_zone_flush(zone);
+ if (result != ISC_R_SUCCESS)
+ cleanup = false;
+ if (cleanup) {
+ journal = dns_zone_getjournal(zone);
+ if (journal != NULL)
+ (void)isc_file_remove(journal);
+ }
+
+ return (result);
+}
+
+isc_result_t
+ns_server_sync(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result, tresult;
+ dns_view_t *view;
+ dns_zone_t *zone = NULL;
+ char classstr[DNS_RDATACLASS_FORMATSIZE];
+ char zonename[DNS_NAME_FORMATSIZE];
+ const char *vname, *sep, *arg;
+ bool cleanup = false;
+
+ (void) next_token(lex, text);
+
+ arg = next_token(lex, text);
+ if (arg != NULL &&
+ (strcmp(arg, "-clean") == 0 || strcmp(arg, "-clear") == 0)) {
+ cleanup = true;
+ arg = next_token(lex, text);
+ }
+
+ result = zone_from_args(server, lex, arg, &zone, NULL,
+ text, false);
+ 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_zt_apply(view->zonetable, false,
+ synczone, &cleanup);
+ 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,
+ "dumping all zones%s: %s",
+ cleanup ? ", removing journal files" : "",
+ isc_result_totext(result));
+ return (tresult);
+ }
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = synczone(zone, &cleanup);
+ isc_task_endexclusive(server->task);
+
+ view = dns_zone_getview(zone);
+ if (strcmp(view->name, "_default") == 0 ||
+ strcmp(view->name, "_bind") == 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,
+ "sync: dumping zone '%s/%s'%s%s%s: %s",
+ zonename, classstr, sep, vname,
+ cleanup ? ", removing journal file" : "",
+ isc_result_totext(result));
+ dns_zone_detach(&zone);
+ return (result);
+}
+
+/*
+ * Act on a "freeze" or "thaw" command from the command channel.
+ */
+isc_result_t
+ns_server_freeze(ns_server_t *server, bool freeze,
+ isc_lex_t *lex, isc_buffer_t **text)
+{
+ isc_result_t result, tresult;
+ dns_zone_t *mayberaw = NULL, *raw = NULL;
+ dns_zonetype_t type;
+ char classstr[DNS_RDATACLASS_FORMATSIZE];
+ char zonename[DNS_NAME_FORMATSIZE];
+ dns_view_t *view;
+ const char *vname, *sep;
+ bool frozen;
+ const char *msg = NULL;
+
+ result = zone_from_args(server, lex, NULL, &mayberaw, NULL,
+ text, true);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ if (mayberaw == 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);
+ }
+ dns_zone_getraw(mayberaw, &raw);
+ if (raw != NULL) {
+ dns_zone_detach(&mayberaw);
+ dns_zone_attach(raw, &mayberaw);
+ dns_zone_detach(&raw);
+ }
+ type = dns_zone_gettype(mayberaw);
+ if (type != dns_zone_master) {
+ dns_zone_detach(&mayberaw);
+ return (DNS_R_NOTMASTER);
+ }
+
+ if (freeze && !dns_zone_isdynamic(mayberaw, true)) {
+ dns_zone_detach(&mayberaw);
+ return (DNS_R_NOTDYNAMIC);
+ }
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ frozen = dns_zone_getupdatedisabled(mayberaw);
+ if (freeze) {
+ if (frozen) {
+ msg = "WARNING: The zone was already frozen.\n"
+ "Someone else may be editing it or "
+ "it may still be re-loading.";
+ result = DNS_R_FROZEN;
+ }
+ if (result == ISC_R_SUCCESS) {
+ result = dns_zone_flush(mayberaw);
+ if (result != ISC_R_SUCCESS)
+ msg = "Flushing the zone updates to "
+ "disk failed.";
+ }
+ if (result == ISC_R_SUCCESS)
+ dns_zone_setupdatedisabled(mayberaw, freeze);
+ } else {
+ if (frozen) {
+ result = dns_zone_loadandthaw(mayberaw);
+ switch (result) {
+ case ISC_R_SUCCESS:
+ case DNS_R_UPTODATE:
+ msg = "The zone reload and thaw was "
+ "successful.";
+ result = ISC_R_SUCCESS;
+ break;
+ case DNS_R_CONTINUE:
+ msg = "A zone reload and thaw was started.\n"
+ "Check the logs to see the result.";
+ result = ISC_R_SUCCESS;
+ break;
+ }
+ }
+ }
+ isc_task_endexclusive(server->task);
+
+ if (msg != NULL) {
+ (void) putstr(text, msg);
+ (void) putnull(text);
+ }
+
+ view = dns_zone_getview(mayberaw);
+ if (strcmp(view->name, "_default") == 0 ||
+ strcmp(view->name, "_bind") == 0)
+ {
+ vname = "";
+ sep = "";
+ } else {
+ vname = view->name;
+ sep = " ";
+ }
+ dns_rdataclass_format(dns_zone_getclass(mayberaw), classstr,
+ sizeof(classstr));
+ dns_name_format(dns_zone_getorigin(mayberaw),
+ 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(&mayberaw);
+ 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) {
+ return (putstr(text, "use svcadm(1M) to manage named"));
+}
+#endif /* HAVE_LIBSCF */
+
+#ifndef HAVE_LMDB
+
+/*
+ * Emit a comment at the top of the nzf file containing the viewname
+ * Expects the fp to already be open for writing
+ */
+#define HEADER1 "# New zone file for view: "
+#define HEADER2 "\n# This file contains configuration for zones added by\n" \
+ "# the 'rndc addzone' command. DO NOT EDIT BY HAND.\n"
+static isc_result_t
+add_comment(FILE *fp, const char *viewname) {
+ isc_result_t result;
+ CHECK(isc_stdio_write(HEADER1, sizeof(HEADER1) - 1, 1, fp, NULL));
+ CHECK(isc_stdio_write(viewname, strlen(viewname), 1, fp, NULL));
+ CHECK(isc_stdio_write(HEADER2, sizeof(HEADER2) - 1, 1, fp, NULL));
+ cleanup:
+ return (result);
+}
+
+static void
+dumpzone(void *arg, const char *buf, int len) {
+ FILE *fp = arg;
+
+ (void) isc_stdio_write(buf, len, 1, fp, NULL);
+}
+
+static isc_result_t
+nzf_append(dns_view_t *view, const cfg_obj_t *zconfig) {
+ isc_result_t result;
+ off_t offset;
+ FILE *fp = NULL;
+ bool offsetok = false;
+
+ LOCK(&view->new_zone_lock);
+
+ CHECK(isc_stdio_open(view->new_zone_file, "a", &fp));
+ CHECK(isc_stdio_seek(fp, 0, SEEK_END));
+
+ CHECK(isc_stdio_tell(fp, &offset));
+ offsetok = true;
+ if (offset == 0)
+ CHECK(add_comment(fp, view->name));
+
+ CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
+ cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp);
+ CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL));
+ CHECK(isc_stdio_flush(fp));
+ result = isc_stdio_close(fp);
+ fp = NULL;
+
+ cleanup:
+ if (fp != NULL) {
+ (void)isc_stdio_close(fp);
+ if (offsetok) {
+ isc_result_t result2;
+
+ result2 = isc_file_truncate(view->new_zone_file,
+ offset);
+ if (result2 != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "Error truncating NZF file '%s' "
+ "during rollback from append: "
+ "%s",
+ view->new_zone_file,
+ isc_result_totext(result2));
+ }
+ }
+ }
+ UNLOCK(&view->new_zone_lock);
+ return (result);
+}
+
+static isc_result_t
+nzf_writeconf(const cfg_obj_t *config, dns_view_t *view) {
+ const cfg_obj_t *zl = NULL;
+ cfg_list_t *list;
+ const cfg_listelt_t *elt;
+
+ FILE *fp = NULL;
+ char tmp[1024];
+ isc_result_t result;
+
+ result = isc_file_template("", "nzf-XXXXXXXX", tmp, sizeof(tmp));
+ if (result == ISC_R_SUCCESS)
+ result = isc_file_openunique(tmp, &fp);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ cfg_map_get(config, "zone", &zl);
+ if (!cfg_obj_islist(zl))
+ CHECK(ISC_R_FAILURE);
+
+ DE_CONST(&zl->value.list, list);
+
+ CHECK(add_comment(fp, view->name)); /* force a comment */
+
+ for (elt = ISC_LIST_HEAD(*list);
+ elt != NULL;
+ elt = ISC_LIST_NEXT(elt, link))
+ {
+ const cfg_obj_t *zconfig = cfg_listelt_value(elt);
+
+ CHECK(isc_stdio_write("zone ", 5, 1, fp, NULL));
+ cfg_printx(zconfig, CFG_PRINTER_ONELINE, dumpzone, fp);
+ CHECK(isc_stdio_write(";\n", 2, 1, fp, NULL));
+ }
+
+ CHECK(isc_stdio_flush(fp));
+ result = isc_stdio_close(fp);
+ fp = NULL;
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ CHECK(isc_file_rename(tmp, view->new_zone_file));
+ return (result);
+
+ cleanup:
+ if (fp != NULL)
+ (void)isc_stdio_close(fp);
+ (void)isc_file_remove(tmp);
+ return (result);
+}
+
+#else /* HAVE_LMDB */
+
+static void
+nzd_setkey(MDB_val *key, dns_name_t *name, char *namebuf, size_t buflen) {
+ dns_fixedname_t fixed;
+
+ dns_fixedname_init(&fixed);
+ dns_name_downcase(name, dns_fixedname_name(&fixed), NULL);
+ dns_name_format(dns_fixedname_name(&fixed), namebuf, buflen);
+
+ key->mv_data = namebuf;
+ key->mv_size = strlen(namebuf);
+}
+
+static void
+dumpzone(void *arg, const char *buf, int len) {
+ isc_buffer_t **text = arg;
+
+ putmem(text, buf, len);
+}
+
+static isc_result_t
+nzd_save(MDB_txn **txnp, MDB_dbi dbi, dns_zone_t *zone,
+ const cfg_obj_t *zconfig)
+{
+ isc_result_t result;
+ int status;
+ dns_view_t *view;
+ bool commit = false;
+ isc_buffer_t *text = NULL;
+ char namebuf[1024];
+ MDB_val key, data;
+
+ view = dns_zone_getview(zone);
+
+ nzd_setkey(&key, dns_zone_getorigin(zone), namebuf, sizeof(namebuf));
+
+ LOCK(&view->new_zone_lock);
+
+ if (zconfig == NULL) {
+ /* We're deleting the zone from the database */
+ status = mdb_del(*txnp, dbi, &key, NULL);
+ if (status != MDB_SUCCESS && status != MDB_NOTFOUND) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "Error deleting zone %s "
+ "from NZD database: %s",
+ namebuf, mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ } else if (status != MDB_NOTFOUND) {
+ commit = true;
+ }
+ } else {
+ /* We're creating or overwriting the zone */
+ const cfg_obj_t *zoptions;
+
+ result = isc_buffer_allocate(view->mctx, &text, 256);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "Unable to allocate buffer in "
+ "nzd_save(): %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+ if (zoptions == NULL) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "Unable to get options from config in "
+ "nzd_save()");
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &text);
+
+ data.mv_data = isc_buffer_base(text);
+ data.mv_size = isc_buffer_usedlength(text);
+
+ status = mdb_put(*txnp, dbi, &key, &data, 0);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "Error inserting zone in "
+ "NZD database: %s",
+ mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ commit = true;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (!commit || result != ISC_R_SUCCESS) {
+ (void) mdb_txn_abort(*txnp);
+ } else {
+ status = mdb_txn_commit(*txnp);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "Error committing "
+ "NZD database: %s",
+ mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ }
+ }
+ *txnp = NULL;
+
+ UNLOCK(&view->new_zone_lock);
+
+ if (text != NULL) {
+ isc_buffer_free(&text);
+ }
+
+ return (result);
+}
+
+static isc_result_t
+nzd_writable(dns_view_t *view) {
+ isc_result_t result = ISC_R_SUCCESS;
+ int status;
+ MDB_dbi dbi;
+ MDB_txn *txn = NULL;
+
+ REQUIRE(view != NULL);
+
+ status = mdb_txn_begin((MDB_env *) view->new_zone_dbenv, 0, 0, &txn);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING, "mdb_txn_begin: %s",
+ mdb_strerror(status));
+ return (ISC_R_FAILURE);
+ }
+
+ status = mdb_dbi_open(txn, NULL, 0, &dbi);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING, "mdb_dbi_open: %s",
+ mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ }
+
+ mdb_txn_abort(txn);
+ return (result);
+}
+
+static isc_result_t
+nzd_open(dns_view_t *view, unsigned int flags, MDB_txn **txnp, MDB_dbi *dbi) {
+ int status;
+ MDB_txn *txn = NULL;
+
+ REQUIRE(view != NULL);
+ REQUIRE(txnp != NULL && *txnp == NULL);
+ REQUIRE(dbi != NULL);
+
+ status = mdb_txn_begin((MDB_env *) view->new_zone_dbenv, 0,
+ flags, &txn);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING, "mdb_txn_begin: %s",
+ mdb_strerror(status));
+ goto cleanup;
+ }
+
+ status = mdb_dbi_open(txn, NULL, 0, dbi);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING, "mdb_dbi_open: %s",
+ mdb_strerror(status));
+ goto cleanup;
+ }
+
+ *txnp = txn;
+
+ cleanup:
+ if (status != MDB_SUCCESS) {
+ if (txn != NULL) {
+ mdb_txn_abort(txn);
+ }
+ return (ISC_R_FAILURE);
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+/*
+ * nzd_env_close() and nzd_env_reopen are a kluge to address the
+ * problem of an NZD file possibly being created before we drop
+ * root privileges.
+ */
+static void
+nzd_env_close(dns_view_t *view) {
+ const char *dbpath = NULL;
+ char dbpath_copy[PATH_MAX];
+ char lockpath[PATH_MAX];
+ int status, ret;
+
+ if (view->new_zone_dbenv == NULL) {
+ return;
+ }
+
+ status = mdb_env_get_path(view->new_zone_dbenv, &dbpath);
+ INSIST(status == MDB_SUCCESS);
+ snprintf(lockpath, sizeof(lockpath), "%s-lock", dbpath);
+ strlcpy(dbpath_copy, dbpath, sizeof(dbpath_copy));
+ mdb_env_close((MDB_env *) view->new_zone_dbenv);
+
+ /*
+ * Database files must be owned by the eventual user, not by root.
+ */
+ ret = chown(dbpath_copy, ns_os_uid(), -1);
+ UNUSED(ret);
+
+ /*
+ * Some platforms need the lockfile not to exist when we reopen the
+ * environment.
+ */
+ (void) isc_file_remove(lockpath);
+
+ view->new_zone_dbenv = NULL;
+}
+
+static isc_result_t
+nzd_env_reopen(dns_view_t *view) {
+ isc_result_t result;
+ MDB_env *env = NULL;
+ int status;
+
+ if (view->new_zone_db == NULL) {
+ return (ISC_R_SUCCESS);
+ }
+
+ nzd_env_close(view);
+
+ status = mdb_env_create(&env);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+ "mdb_env_create failed: %s",
+ mdb_strerror(status));
+ CHECK(ISC_R_FAILURE);
+ }
+
+ if (view->new_zone_mapsize != 0ULL) {
+ status = mdb_env_set_mapsize(env, view->new_zone_mapsize);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+ "mdb_env_set_mapsize failed: %s",
+ mdb_strerror(status));
+ CHECK(ISC_R_FAILURE);
+ }
+ }
+
+ status = mdb_env_open(env, view->new_zone_db, DNS_LMDB_FLAGS, 0600);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
+ ISC_LOGMODULE_OTHER, ISC_LOG_ERROR,
+ "mdb_env_open of '%s' failed: %s",
+ view->new_zone_db, mdb_strerror(status));
+ CHECK(ISC_R_FAILURE);
+ }
+
+ view->new_zone_dbenv = env;
+ env = NULL;
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (env != NULL) {
+ mdb_env_close(env);
+ }
+ return (result);
+}
+
+static isc_result_t
+nzd_close(MDB_txn **txnp, bool commit) {
+ isc_result_t result = ISC_R_SUCCESS;
+ int status;
+
+ REQUIRE(txnp != NULL);
+
+ if (*txnp != NULL) {
+ if (commit) {
+ status = mdb_txn_commit(*txnp);
+ if (status != MDB_SUCCESS) {
+ result = ISC_R_FAILURE;
+ }
+ } else {
+ mdb_txn_abort(*txnp);
+ }
+ *txnp = NULL;
+ }
+
+ return (result);
+}
+
+static isc_result_t
+nzd_count(dns_view_t *view, int *countp) {
+ isc_result_t result;
+ int status;
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+ MDB_stat statbuf;
+
+ REQUIRE(countp != NULL);
+
+ result = nzd_open(view, MDB_RDONLY, &txn, &dbi);
+ if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ status = mdb_stat(txn, dbi, &statbuf);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING, "mdb_stat: %s",
+ mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ *countp = statbuf.ms_entries;
+
+ cleanup:
+ (void) nzd_close(&txn, false);
+
+ return (result);
+}
+
+static isc_result_t
+migrate_nzf(dns_view_t *view) {
+ isc_result_t result;
+ cfg_obj_t *nzf_config = NULL;
+ int status, n;
+ isc_buffer_t *text = NULL;
+ bool commit = false;
+ const cfg_obj_t *zonelist;
+ const cfg_listelt_t *element;
+ char tempname[PATH_MAX];
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+ MDB_val key, data;
+
+ /*
+ * If NZF file doesn't exist, or NZD DB exists and already
+ * has data, return without attempting migration.
+ */
+ if (!isc_file_exists(view->new_zone_file)) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ result = nzd_count(view, &n);
+ if (result == ISC_R_SUCCESS && n > 0) {
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_INFO,
+ "Migrating zones from NZF file '%s' to "
+ "NZD database '%s'",
+ view->new_zone_file, view->new_zone_db);
+ /*
+ * Instead of blindly copying lines, we parse the NZF file using
+ * the configuration parser, because it validates it against the
+ * config type, giving us a guarantee that valid configuration
+ * will be written to DB.
+ */
+ cfg_parser_reset(ns_g_addparser);
+ result = cfg_parse_file(ns_g_addparser, view->new_zone_file,
+ &cfg_type_addzoneconf, &nzf_config);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "Error parsing NZF file '%s': %s",
+ view->new_zone_file,
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+ zonelist = NULL;
+ CHECK(cfg_map_get(nzf_config, "zone", &zonelist));
+ if (!cfg_obj_islist(zonelist)) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ CHECK(nzd_open(view, 0, &txn, &dbi));
+
+ CHECK(isc_buffer_allocate(view->mctx, &text, 256));
+
+ for (element = cfg_list_first(zonelist);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *zconfig;
+ const cfg_obj_t *zoptions;
+ char zname[DNS_NAME_FORMATSIZE];
+ dns_fixedname_t fname;
+ dns_name_t *name;
+ const char *origin;
+ isc_buffer_t b;
+
+ zconfig = cfg_listelt_value(element);
+
+ origin = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
+ if (origin == NULL) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /* Normalize zone name */
+ isc_buffer_constinit(&b, origin, strlen(origin));
+ isc_buffer_add(&b, strlen(origin));
+ name = dns_fixedname_initname(&fname);
+ CHECK(dns_name_fromtext(name, &b, dns_rootname,
+ DNS_NAME_DOWNCASE, NULL));
+ dns_name_format(name, zname, sizeof(zname));
+
+ key.mv_data = zname;
+ key.mv_size = strlen(zname);
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+ if (zoptions == NULL) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ isc_buffer_clear(text);
+ cfg_printx(zoptions, CFG_PRINTER_ONELINE, dumpzone, &text);
+
+ data.mv_data = isc_buffer_base(text);
+ data.mv_size = isc_buffer_usedlength(text);
+
+ status = mdb_put(txn, dbi, &key, &data, MDB_NOOVERWRITE);
+ if (status != MDB_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "Error inserting zone in "
+ "NZD database: %s",
+ mdb_strerror(status));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ commit = true;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ /*
+ * Leaving the NZF file in place is harmless as we won't use it
+ * if an NZD database is found for the view. But we rename NZF file
+ * to a backup name here.
+ */
+ strlcpy(tempname, view->new_zone_file, sizeof(tempname));
+ if (strlen(tempname) < sizeof(tempname) - 1) {
+ strlcat(tempname, "~", sizeof(tempname));
+ isc_file_rename(view->new_zone_file, tempname);
+ }
+
+ cleanup:
+ if (result != ISC_R_SUCCESS) {
+ (void) nzd_close(&txn, false);
+ } else {
+ result = nzd_close(&txn, commit);
+ }
+
+ if (text != NULL) {
+ isc_buffer_free(&text);
+ }
+
+ if (nzf_config != NULL) {
+ cfg_obj_destroy(ns_g_addparser, &nzf_config);
+ }
+
+ return (result);
+}
+
+#endif /* HAVE_LMDB */
+
+static isc_result_t
+newzone_parse(ns_server_t *server, char *command, dns_view_t **viewp,
+ cfg_obj_t **zoneconfp, const cfg_obj_t **zoneobjp,
+ isc_buffer_t **text)
+{
+ isc_result_t result;
+ isc_buffer_t argbuf;
+ cfg_obj_t *zoneconf = NULL;
+ const cfg_obj_t *zlist = NULL;
+ const cfg_obj_t *zoneobj = NULL;
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *obj = NULL;
+ const char *viewname = NULL;
+ dns_rdataclass_t rdclass;
+ dns_view_t *view = NULL;
+ const char *bn;
+
+ REQUIRE(viewp != NULL && *viewp == NULL);
+ REQUIRE(zoneobjp != NULL && *zoneobjp == NULL);
+ REQUIRE(zoneconfp != NULL && *zoneconfp == NULL);
+
+ /* Try to parse the argument string */
+ isc_buffer_init(&argbuf, command, (unsigned int) strlen(command));
+ isc_buffer_add(&argbuf, strlen(command));
+
+ if (strncasecmp(command, "add", 3) == 0)
+ bn = "addzone";
+ else if (strncasecmp(command, "mod", 3) == 0)
+ bn = "modzone";
+ else
+ INSIST(0);
+
+ /*
+ * Convert the "addzone" or "modzone" to just "zone", for
+ * the benefit of the parser
+ */
+ isc_buffer_forward(&argbuf, 3);
+
+ cfg_parser_reset(ns_g_addparser);
+ CHECK(cfg_parse_buffer3(ns_g_addparser, &argbuf, bn, 0,
+ &cfg_type_addzoneconf, &zoneconf));
+ CHECK(cfg_map_get(zoneconf, "zone", &zlist));
+ if (!cfg_obj_islist(zlist))
+ CHECK(ISC_R_FAILURE);
+
+ /* For now we only support adding one zone at a time */
+ zoneobj = cfg_listelt_value(cfg_list_first(zlist));
+
+ /* Check the zone type for ones that are not supported by addzone. */
+ zoptions = cfg_tuple_get(zoneobj, "options");
+
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "type", &obj);
+ if (obj == NULL) {
+ (void) cfg_map_get(zoptions, "in-view", &obj);
+ if (obj != NULL) {
+ (void) putstr(text,
+ "'in-view' zones not supported by ");
+ (void) putstr(text, bn);
+ } else
+ (void) putstr(text, "zone type not specified");
+ CHECK(ISC_R_FAILURE);
+ }
+
+ if (strcasecmp(cfg_obj_asstring(obj), "hint") == 0 ||
+ strcasecmp(cfg_obj_asstring(obj), "forward") == 0 ||
+ strcasecmp(cfg_obj_asstring(obj), "redirect") == 0 ||
+ strcasecmp(cfg_obj_asstring(obj), "delegation-only") == 0)
+ {
+ (void) putstr(text, "'");
+ (void) putstr(text, cfg_obj_asstring(obj));
+ (void) putstr(text, "' zones not supported by ");
+ (void) putstr(text, bn);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ /* Make sense of optional class argument */
+ obj = cfg_tuple_get(zoneobj, "class");
+ CHECK(ns_config_getclass(obj, dns_rdataclass_in, &rdclass));
+
+ /* Make sense of optional view argument */
+ obj = cfg_tuple_get(zoneobj, "view");
+ if (obj && cfg_obj_isstring(obj))
+ viewname = cfg_obj_asstring(obj);
+ if (viewname == NULL || *viewname == '\0')
+ viewname = "_default";
+ result = dns_viewlist_find(&server->viewlist, viewname, rdclass,
+ &view);
+ if (result == ISC_R_NOTFOUND) {
+ (void) putstr(text, "no matching view found for '");
+ (void) putstr(text, viewname);
+ (void) putstr(text, "'");
+ goto cleanup;
+ } else if (result != ISC_R_SUCCESS) {
+ goto cleanup;
+ }
+
+ *viewp = view;
+ *zoneobjp = zoneobj;
+ *zoneconfp = zoneconf;
+
+ return (ISC_R_SUCCESS);
+
+ cleanup:
+ if (zoneconf != NULL)
+ cfg_obj_destroy(ns_g_addparser, &zoneconf);
+ if (view != NULL)
+ dns_view_detach(&view);
+
+ return (result);
+}
+
+static isc_result_t
+delete_zoneconf(dns_view_t *view, cfg_parser_t *pctx,
+ const cfg_obj_t *config, const dns_name_t *zname,
+ nzfwriter_t nzfwriter)
+{
+ isc_result_t result = ISC_R_NOTFOUND;
+ const cfg_listelt_t *elt = NULL;
+ const cfg_obj_t *zl = NULL;
+ cfg_list_t *list;
+ dns_fixedname_t myfixed;
+ dns_name_t *myname;
+
+ REQUIRE(view != NULL);
+ REQUIRE(pctx != NULL);
+ REQUIRE(config != NULL);
+ REQUIRE(zname != NULL);
+
+ LOCK(&view->new_zone_lock);
+
+ cfg_map_get(config, "zone", &zl);
+
+ if (!cfg_obj_islist(zl))
+ CHECK(ISC_R_FAILURE);
+
+ DE_CONST(&zl->value.list, list);
+
+ myname = dns_fixedname_initname(&myfixed);
+
+ for (elt = ISC_LIST_HEAD(*list);
+ elt != NULL;
+ elt = ISC_LIST_NEXT(elt, link))
+ {
+ const cfg_obj_t *zconf = cfg_listelt_value(elt);
+ const char *zn;
+ cfg_listelt_t *e;
+
+ zn = cfg_obj_asstring(cfg_tuple_get(zconf, "name"));
+ result = dns_name_fromstring(myname, zn, 0, NULL);
+ if (result != ISC_R_SUCCESS ||
+ !dns_name_equal(zname, myname))
+ continue;
+
+ DE_CONST(elt, e);
+ ISC_LIST_UNLINK(*list, e, link);
+ cfg_obj_destroy(pctx, &e->obj);
+ isc_mem_put(pctx->mctx, e, sizeof(*e));
+ result = ISC_R_SUCCESS;
+ break;
+ }
+
+ /*
+ * Write config to NZF file if appropriate
+ */
+ if (nzfwriter != NULL && view->new_zone_file != NULL)
+ result = nzfwriter(config, view);
+
+ cleanup:
+ UNLOCK(&view->new_zone_lock);
+ return (result);
+}
+
+static isc_result_t
+do_addzone(ns_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
+ dns_name_t *name, cfg_obj_t *zoneconf, const cfg_obj_t *zoneobj,
+ isc_buffer_t **text)
+{
+ isc_result_t result, tresult;
+ dns_zone_t *zone = NULL;
+#ifndef HAVE_LMDB
+ FILE *fp = NULL;
+ bool cleanup_config = false;
+#else /* HAVE_LMDB */
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+
+ UNUSED(zoneconf);
+#endif /* HAVE_LMDB */
+
+ /* Zone shouldn't already exist */
+ result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ if (result == ISC_R_SUCCESS) {
+ result = ISC_R_EXISTS;
+ goto cleanup;
+ } else if (result == DNS_R_PARTIALMATCH) {
+ /* Create our sub-zone anyway */
+ dns_zone_detach(&zone);
+ zone = NULL;
+ } else if (result != ISC_R_NOTFOUND)
+ goto cleanup;
+
+#ifndef HAVE_LMDB
+ /*
+ * Make sure we can open the configuration save file
+ */
+ result = isc_stdio_open(view->new_zone_file, "a", &fp);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "unable to create '"));
+ TCHECK(putstr(text, view->new_zone_file));
+ TCHECK(putstr(text, "': "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+
+ (void)isc_stdio_close(fp);
+ fp = NULL;
+#else /* HAVE_LMDB */
+ /* Make sure we can open the NZD database */
+ result = nzd_writable(view);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "unable to open NZD database for '"));
+ TCHECK(putstr(text, view->new_zone_db));
+ TCHECK(putstr(text, "'"));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+#endif /* HAVE_LMDB */
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ /* Mark view unfrozen and configure zone */
+ dns_view_thaw(view);
+ result = configure_zone(cfg->config, zoneobj, cfg->vconfig,
+ server->mctx, view, &server->viewlist,
+ cfg->actx, true, false, false);
+ dns_view_freeze(view);
+
+ isc_task_endexclusive(server->task);
+
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "configure_zone failed: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+
+ /* Is it there yet? */
+ result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx,
+ NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "added new zone was not found: %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+
+#ifndef HAVE_LMDB
+ /*
+ * If there wasn't a previous newzone config, just save the one
+ * we've created. If there was a previous one, merge the new
+ * zone into it.
+ */
+ if (cfg->nzf_config == NULL) {
+ cfg_obj_attach(zoneconf, &cfg->nzf_config);
+ } else {
+ cfg_obj_t *z;
+ DE_CONST(zoneobj, z);
+ CHECK(cfg_parser_mapadd(cfg->add_parser,
+ cfg->nzf_config, z, "zone"));
+ }
+ cleanup_config = true;
+#endif /* HAVE_LMDB */
+
+ /*
+ * Load the zone from the master file. If this fails, we'll
+ * need to undo the configuration we've done already.
+ */
+ result = dns_zone_loadnew(zone);
+ if (result != ISC_R_SUCCESS) {
+ dns_db_t *dbp = NULL;
+
+ TCHECK(putstr(text, "dns_zone_loadnew failed: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "addzone failed; reverting.");
+
+ /* If the zone loaded partially, unload it */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ /* Remove the zone from the zone table */
+ dns_zt_unmount(view->zonetable, zone);
+ goto cleanup;
+ }
+
+ /* Flag the zone as having been added at runtime */
+ dns_zone_setadded(zone, true);
+
+#ifdef HAVE_LMDB
+ /* Save the new zone configuration into the NZD */
+ CHECK(nzd_open(view, 0, &txn, &dbi));
+ CHECK(nzd_save(&txn, dbi, zone, zoneobj));
+#else
+ /* Append the zone configuration to the NZF */
+ result = nzf_append(view, zoneobj);
+#endif /* HAVE_LMDB */
+
+ cleanup:
+
+#ifndef HAVE_LMDB
+ if (fp != NULL)
+ (void)isc_stdio_close(fp);
+ if (result != ISC_R_SUCCESS && cleanup_config) {
+ tresult = delete_zoneconf(view, cfg->add_parser,
+ cfg->nzf_config, name,
+ NULL);
+ RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
+ }
+#else /* HAVE_LMDB */
+ if (txn != NULL)
+ (void) nzd_close(&txn, false);
+#endif /* HAVE_LMDB */
+
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ return (result);
+}
+
+static isc_result_t
+do_modzone(ns_server_t *server, ns_cfgctx_t *cfg, dns_view_t *view,
+ dns_name_t *name, const char *zname, const cfg_obj_t *zoneobj,
+ isc_buffer_t **text)
+{
+ isc_result_t result, tresult;
+ dns_zone_t *zone = NULL;
+ bool added;
+ bool exclusive = false;
+#ifndef HAVE_LMDB
+ FILE *fp = NULL;
+ cfg_obj_t *z;
+#else /* HAVE_LMDB */
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+#endif /* HAVE_LMDB */
+
+ /* Zone must already exist */
+ result = dns_zt_find(view->zonetable, name, 0, NULL, &zone);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ added = dns_zone_getadded(zone);
+ dns_zone_detach(&zone);
+
+#ifndef HAVE_LMDB
+ cfg = (ns_cfgctx_t *) view->new_zone_config;
+ if (cfg == NULL) {
+ TCHECK(putstr(text, "new zone config is not set"));
+ CHECK(ISC_R_FAILURE);
+ }
+#endif
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ exclusive = true;
+
+#ifndef HAVE_LMDB
+ /* Make sure we can open the configuration save file */
+ result = isc_stdio_open(view->new_zone_file, "a", &fp);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "unable to open '"));
+ TCHECK(putstr(text, view->new_zone_file));
+ TCHECK(putstr(text, "': "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+ (void)isc_stdio_close(fp);
+ fp = NULL;
+#else /* HAVE_LMDB */
+ /* Make sure we can open the NZD database */
+ result = nzd_writable(view);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "unable to open NZD database for '"));
+ TCHECK(putstr(text, view->new_zone_db));
+ TCHECK(putstr(text, "'"));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+#endif /* HAVE_LMDB */
+
+ /* Reconfigure the zone */
+ dns_view_thaw(view);
+ result = configure_zone(cfg->config, zoneobj, cfg->vconfig,
+ server->mctx, view, &server->viewlist,
+ cfg->actx, true, false, true);
+ dns_view_freeze(view);
+
+ exclusive = false;
+ isc_task_endexclusive(server->task);
+
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "configure_zone failed: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+
+ /* Is it there yet? */
+ CHECK(dns_zt_find(view->zonetable, name, 0, NULL, &zone));
+
+#ifndef HAVE_LMDB
+ /* Remove old zone from configuration (and NZF file if applicable) */
+ if (added) {
+ result = delete_zoneconf(view, cfg->add_parser,
+ cfg->nzf_config,
+ dns_zone_getorigin(zone),
+ nzf_writeconf);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "former zone configuration "
+ "not deleted: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+ }
+#endif /* HAVE_LMDB */
+
+ if (!added) {
+ if (cfg->vconfig == NULL) {
+ result = delete_zoneconf(view, cfg->conf_parser,
+ cfg->config,
+ dns_zone_getorigin(zone),
+ NULL);
+ } else {
+ const cfg_obj_t *voptions =
+ cfg_tuple_get(cfg->vconfig, "options");
+ result = delete_zoneconf(view, cfg->conf_parser,
+ voptions,
+ dns_zone_getorigin(zone),
+ NULL);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "former zone configuration "
+ "not deleted: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+ }
+
+ /* Load the zone from the master file if it needs reloading. */
+ result = dns_zone_loadnew(zone);
+
+ /*
+ * Dynamic zones need no reloading, so we can pass this result.
+ */
+ if (result == DNS_R_DYNAMIC)
+ result = ISC_R_SUCCESS;
+
+ if (result != ISC_R_SUCCESS) {
+ dns_db_t *dbp = NULL;
+
+ TCHECK(putstr(text, "failed to load zone '"));
+ TCHECK(putstr(text, zname));
+ TCHECK(putstr(text, "': "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ TCHECK(putstr(text, "\nThe zone is no longer being served. "));
+ TCHECK(putstr(text, "Use 'rndc addzone' to correct\n"));
+ TCHECK(putstr(text, "the problem and restore service."));
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "modzone failed; removing zone.");
+
+ /* If the zone loaded partially, unload it */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ /* Remove the zone from the zone table */
+ dns_zt_unmount(view->zonetable, zone);
+ goto cleanup;
+ }
+
+#ifndef HAVE_LMDB
+ /* Store the new zone configuration; also in NZF if applicable */
+ DE_CONST(zoneobj, z);
+ CHECK(cfg_parser_mapadd(cfg->add_parser, cfg->nzf_config, z, "zone"));
+#endif /* HAVE_LMDB */
+
+ if (added) {
+#ifdef HAVE_LMDB
+ CHECK(nzd_open(view, 0, &txn, &dbi));
+ CHECK(nzd_save(&txn, dbi, zone, zoneobj));
+#else
+ result = nzf_append(view, zoneobj);
+ if (result != ISC_R_SUCCESS) {
+ TCHECK(putstr(text, "\nNew zone config not saved: "));
+ TCHECK(putstr(text, isc_result_totext(result)));
+ goto cleanup;
+ }
+#endif /* HAVE_LMDB */
+
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zname));
+ TCHECK(putstr(text, "' reconfigured."));
+
+ } else {
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zname));
+ TCHECK(putstr(text, "' must also be reconfigured in\n"));
+ TCHECK(putstr(text, "named.conf to make changes permanent."));
+ }
+
+ cleanup:
+ if (exclusive)
+ isc_task_endexclusive(server->task);
+
+#ifndef HAVE_LMDB
+ if (fp != NULL)
+ (void)isc_stdio_close(fp);
+#else /* HAVE_LMDB */
+ if (txn != NULL)
+ (void) nzd_close(&txn, false);
+#endif /* HAVE_LMDB */
+
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ return (result);
+}
+
+/*
+ * Act on an "addzone" or "modzone" command from the command channel.
+ */
+isc_result_t
+ns_server_changezone(ns_server_t *server, char *command, isc_buffer_t **text) {
+ isc_result_t result;
+ bool addzone;
+ ns_cfgctx_t *cfg = NULL;
+ cfg_obj_t *zoneconf = NULL;
+ const cfg_obj_t *zoneobj = NULL;
+ const char *zonename;
+ dns_view_t *view = NULL;
+ isc_buffer_t buf;
+ dns_fixedname_t fname;
+ dns_name_t *dnsname;
+
+ if (strncasecmp(command, "add", 3) == 0)
+ addzone = true;
+ else {
+ INSIST(strncasecmp(command, "mod", 3) == 0);
+ addzone = false;
+ }
+
+ CHECK(newzone_parse(server, command, &view, &zoneconf,
+ &zoneobj, text));
+
+ /* Are we accepting new zones in this view? */
+#ifdef HAVE_LMDB
+ if (view->new_zone_db == NULL)
+#else
+ if (view->new_zone_file == NULL)
+#endif /* HAVE_LMDB */
+ {
+ (void) putstr(text, "Not allowing new zones in view '");
+ (void) putstr(text, view->name);
+ (void) putstr(text, "'");
+ result = ISC_R_NOPERM;
+ goto cleanup;
+ }
+
+ cfg = (ns_cfgctx_t *) view->new_zone_config;
+ if (cfg == NULL) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ zonename = cfg_obj_asstring(cfg_tuple_get(zoneobj, "name"));
+ isc_buffer_constinit(&buf, zonename, strlen(zonename));
+ isc_buffer_add(&buf, strlen(zonename));
+
+ dnsname = dns_fixedname_initname(&fname);
+ CHECK(dns_name_fromtext(dnsname, &buf, dns_rootname, 0, NULL));
+
+ if (addzone)
+ CHECK(do_addzone(server, cfg, view, dnsname, zoneconf,
+ zoneobj, text));
+ else
+ CHECK(do_modzone(server, cfg, view, dnsname, zonename,
+ zoneobj, text));
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "%s zone %s in view %s via %s",
+ addzone ? "added" : "updated",
+ zonename, view->name,
+ addzone ? NS_COMMAND_ADDZONE : NS_COMMAND_MODZONE);
+
+ /* Changing a zone counts as reconfiguration */
+ CHECK(isc_time_now(&ns_g_configtime));
+
+ cleanup:
+ if (isc_buffer_usedlength(*text) > 0)
+ (void) putnull(text);
+ if (zoneconf != NULL)
+ cfg_obj_destroy(ns_g_addparser, &zoneconf);
+ if (view != NULL)
+ dns_view_detach(&view);
+
+ return (result);
+}
+
+static bool
+inuse(const char* file, bool first, isc_buffer_t **text) {
+ if (file != NULL && isc_file_exists(file)) {
+ if (first)
+ (void) putstr(text,
+ "The following files were in use "
+ "and may now be removed:\n");
+ else
+ (void) putstr(text, "\n");
+ (void) putstr(text, file);
+ (void) putnull(text);
+ return (false);
+ }
+ return (first);
+}
+
+typedef struct {
+ dns_zone_t *zone;
+ bool cleanup;
+} ns_dzctx_t;
+
+/*
+ * Carry out a zone deletion scheduled by ns_server_delzone().
+ */
+static void
+rmzone(isc_task_t *task, isc_event_t *event) {
+ ns_dzctx_t *dz = (ns_dzctx_t *)event->ev_arg;
+ dns_zone_t *zone, *raw = NULL, *mayberaw;
+ char zonename[DNS_NAME_FORMATSIZE];
+ dns_view_t *view;
+ ns_cfgctx_t *cfg;
+ dns_db_t *dbp = NULL;
+ bool added;
+ isc_result_t result;
+#ifdef HAVE_LMDB
+ MDB_txn *txn = NULL;
+ MDB_dbi dbi;
+#endif
+
+ REQUIRE(dz != NULL);
+
+ isc_event_free(&event);
+
+ /* Dig out configuration for this zone */
+ zone = dz->zone;
+ view = dns_zone_getview(zone);
+ cfg = (ns_cfgctx_t *) view->new_zone_config;
+ 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,
+ "deleting zone %s in view %s via delzone",
+ zonename, view->name);
+
+ /* Remove the zone from configuration (and NZF file if applicable) */
+ added = dns_zone_getadded(zone);
+
+ if (added && cfg != NULL) {
+#ifdef HAVE_LMDB
+ /* Make sure we can open the NZD database */
+ result = nzd_open(view, 0, &txn, &dbi);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR,
+ "unable to open NZD database for '%s'",
+ view->new_zone_db);
+ } else {
+ result = nzd_save(&txn, dbi, zone, NULL);
+ }
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "unable to "
+ "delete zone configuration: %s",
+ isc_result_totext(result));
+ }
+#else
+ result = delete_zoneconf(view, cfg->add_parser,
+ cfg->nzf_config,
+ dns_zone_getorigin(zone),
+ nzf_writeconf);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "unable to "
+ "delete zone configuration: %s",
+ isc_result_totext(result));
+ }
+#endif /* HAVE_LMDB */
+ }
+
+ if (!added && cfg != NULL) {
+ if (cfg->vconfig != NULL) {
+ const cfg_obj_t *voptions =
+ cfg_tuple_get(cfg->vconfig, "options");
+ result = delete_zoneconf(view, cfg->conf_parser,
+ voptions,
+ dns_zone_getorigin(zone),
+ NULL);
+ } else {
+ result = delete_zoneconf(view, cfg->conf_parser,
+ cfg->config,
+ dns_zone_getorigin(zone),
+ NULL);
+ }
+ if (result != ISC_R_SUCCESS){
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "unable to "
+ "delete zone configuration: %s",
+ isc_result_totext(result));
+ }
+ }
+
+ /* Unload zone database */
+ if (dns_zone_getdb(zone, &dbp) == ISC_R_SUCCESS) {
+ dns_db_detach(&dbp);
+ dns_zone_unload(zone);
+ }
+
+ /* Clean up stub/slave zone files if requested to do so */
+ dns_zone_getraw(zone, &raw);
+ mayberaw = (raw != NULL) ? raw : zone;
+
+ if (added && dz->cleanup) {
+ const char *file;
+
+ file = dns_zone_getfile(mayberaw);
+ result = isc_file_remove(file);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "file %s not removed: %s",
+ file, isc_result_totext(result));
+ }
+
+ file = dns_zone_getjournal(mayberaw);
+ result = isc_file_remove(file);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "file %s not removed: %s",
+ file, isc_result_totext(result));
+ }
+
+ if (zone != mayberaw) {
+ file = dns_zone_getfile(zone);
+ result = isc_file_remove(file);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING,
+ "file %s not removed: %s",
+ file, isc_result_totext(result));
+ }
+
+ file = dns_zone_getjournal(zone);
+ result = isc_file_remove(file);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER,
+ ISC_LOG_WARNING,
+ "file %s not removed: %s",
+ file, isc_result_totext(result));
+ }
+ }
+ }
+
+#ifdef HAVE_LMDB
+ if (txn != NULL)
+ (void) nzd_close(&txn, false);
+#endif
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ dns_zone_detach(&zone);
+ isc_mem_put(ns_g_mctx, dz, sizeof(*dz));
+ isc_task_detach(&task);
+}
+
+/*
+ * Act on a "delzone" command from the command channel.
+ */
+isc_result_t
+ns_server_delzone(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result, tresult;
+ dns_zone_t *zone = NULL;
+ dns_zone_t *raw = NULL;
+ dns_zone_t *mayberaw;
+ dns_view_t *view = NULL;
+ char zonename[DNS_NAME_FORMATSIZE];
+ bool cleanup = false;
+ const char *ptr;
+ bool added;
+ ns_dzctx_t *dz = NULL;
+ isc_event_t *dzevent = NULL;
+ isc_task_t *task = NULL;
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Find out what we are to do. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ if (strcmp(ptr, "-clean") == 0 || strcmp(ptr, "-clear") == 0) {
+ cleanup = true;
+ ptr = next_token(lex, text);
+ }
+
+ CHECK(zone_from_args(server, lex, ptr, &zone, zonename,
+ text, false));
+ if (zone == NULL) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+
+ INSIST(zonename != NULL);
+
+ /* Is this a policy zone? */
+ if (dns_zone_get_rpz_num(zone) != DNS_RPZ_INVALID_NUM) {
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zonename));
+ TCHECK(putstr(text,
+ "' cannot be deleted: response-policy zone."));
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ view = dns_zone_getview(zone);
+ CHECK(dns_zt_unmount(view->zonetable, zone));
+
+ /* Send cleanup event */
+ dz = isc_mem_get(ns_g_mctx, sizeof(*dz));
+ if (dz == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ dz->cleanup = cleanup;
+ dz->zone = NULL;
+ dns_zone_attach(zone, &dz->zone);
+ dzevent = isc_event_allocate(ns_g_mctx, server, NS_EVENT_DELZONE,
+ rmzone, dz, sizeof(isc_event_t));
+ if (dzevent == NULL)
+ CHECK(ISC_R_NOMEMORY);
+
+ dns_zone_gettask(zone, &task);
+ isc_task_send(task, &dzevent);
+ dz = NULL;
+
+ /* Inform user about cleaning up stub/slave zone files */
+ dns_zone_getraw(zone, &raw);
+ mayberaw = (raw != NULL) ? raw : zone;
+
+ added = dns_zone_getadded(zone);
+ if (!added) {
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zonename));
+ TCHECK(putstr(text,
+ "' is no longer active and will be deleted.\n"));
+ TCHECK(putstr(text, "To keep it from returning "));
+ TCHECK(putstr(text, "when the server is restarted, it\n"));
+ TCHECK(putstr(text, "must also be removed from named.conf."));
+ } else if (cleanup) {
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zonename));
+ TCHECK(putstr(text, "' and associated files will be deleted."));
+ } else if (dns_zone_gettype(mayberaw) == dns_zone_slave ||
+ dns_zone_gettype(mayberaw) == dns_zone_stub)
+ {
+ bool first;
+ const char *file;
+
+ TCHECK(putstr(text, "zone '"));
+ TCHECK(putstr(text, zonename));
+ TCHECK(putstr(text, "' will be deleted."));
+
+ file = dns_zone_getfile(mayberaw);
+ first = inuse(file, true, text);
+
+ file = dns_zone_getjournal(mayberaw);
+ first = inuse(file, first, text);
+
+ if (zone != mayberaw) {
+ file = dns_zone_getfile(zone);
+ first = inuse(file, first, text);
+
+ file = dns_zone_getjournal(zone);
+ (void) inuse(file, first, text);
+ }
+ }
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "zone %s scheduled for removal via delzone", zonename);
+
+ /* Removing a zone counts as reconfiguration */
+ CHECK(isc_time_now(&ns_g_configtime));
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (isc_buffer_usedlength(*text) > 0)
+ (void) putnull(text);
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+ if (dz != NULL) {
+ dns_zone_detach(&dz->zone);
+ isc_mem_put(ns_g_mctx, dz, sizeof(*dz));
+ }
+
+ return (result);
+}
+
+static const cfg_obj_t *
+find_name_in_list_from_map(const cfg_obj_t *config,
+ const char *map_key_for_list,
+ const char *name)
+{
+ const cfg_obj_t *list = NULL;
+ const cfg_listelt_t *element;
+ const cfg_obj_t *obj = NULL;
+ dns_fixedname_t fixed1, fixed2;
+ dns_name_t *name1 = NULL, *name2 = NULL;
+ isc_result_t result;
+
+ if (strcmp(map_key_for_list, "zone") == 0) {
+ name1 = dns_fixedname_initname(&fixed1);
+ name2 = dns_fixedname_initname(&fixed2);
+ result = dns_name_fromstring(name1, name, 0, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ }
+
+ cfg_map_get(config, map_key_for_list, &list);
+ for (element = cfg_list_first(list);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const char *vname;
+
+ obj = cfg_listelt_value(element);
+ INSIST(obj != NULL);
+ vname = cfg_obj_asstring(cfg_tuple_get(obj, "name"));
+ if (vname == NULL) {
+ obj = NULL;
+ continue;
+ }
+
+ if (name1 != NULL) {
+ result = dns_name_fromstring(name2, vname, 0, NULL);
+ if (result == ISC_R_SUCCESS &&
+ dns_name_equal(name1, name2))
+ break;
+ } else if (strcasecmp(vname, name) == 0)
+ break;
+
+ obj = NULL;
+ }
+
+ return (obj);
+}
+
+static void
+emitzone(void *arg, const char *buf, int len) {
+ isc_buffer_t **tpp = arg;
+ putmem(tpp, buf, len);
+}
+
+/*
+ * Act on a "showzone" command from the command channel.
+ */
+isc_result_t
+ns_server_showzone(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result;
+ const cfg_obj_t *vconfig = NULL, *zconfig = NULL;
+ char zonename[DNS_NAME_FORMATSIZE];
+ const cfg_obj_t *map;
+ dns_view_t *view = NULL;
+ dns_zone_t *zone = NULL;
+ ns_cfgctx_t *cfg = NULL;
+ bool exclusive = false;
+#ifdef HAVE_LMDB
+ cfg_obj_t *nzconfig = NULL;
+#endif /* HAVE_LMDB */
+
+ /* Parse parameters */
+ CHECK(zone_from_args(server, lex, NULL, &zone, zonename,
+ text, true));
+ if (zone == NULL) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+
+ view = dns_zone_getview(zone);
+ dns_zone_detach(&zone);
+
+ cfg = (ns_cfgctx_t *) view->new_zone_config;
+ if (cfg == NULL) {
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ exclusive = true;
+
+ /* Find the view statement */
+ vconfig = find_name_in_list_from_map(cfg->config, "view", view->name);
+
+ /* Find the zone statement */
+ if (vconfig != NULL)
+ map = cfg_tuple_get(vconfig, "options");
+ else
+ map = cfg->config;
+
+ zconfig = find_name_in_list_from_map(map, "zone", zonename);
+
+#ifndef HAVE_LMDB
+ if (zconfig == NULL && cfg->nzf_config != NULL)
+ zconfig = find_name_in_list_from_map(cfg->nzf_config,
+ "zone", zonename);
+#else /* HAVE_LMDB */
+ if (zconfig == NULL) {
+ const cfg_obj_t *zlist = NULL;
+ CHECK(get_newzone_config(view, zonename, &nzconfig));
+ CHECK(cfg_map_get(nzconfig, "zone", &zlist));
+ if (!cfg_obj_islist(zlist))
+ CHECK(ISC_R_FAILURE);
+
+ zconfig = cfg_listelt_value(cfg_list_first(zlist));
+ }
+#endif /* HAVE_LMDB */
+
+ if (zconfig == NULL)
+ CHECK(ISC_R_NOTFOUND);
+
+ putstr(text, "zone ");
+ cfg_printx(zconfig, CFG_PRINTER_ONELINE, emitzone, text);
+ putstr(text, ";");
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+#ifdef HAVE_LMDB
+ if (nzconfig != NULL)
+ cfg_obj_destroy(ns_g_addparser, &nzconfig);
+#endif /* HAVE_LMDB */
+ if (isc_buffer_usedlength(*text) > 0)
+ (void) putnull(text);
+ if (exclusive)
+ isc_task_endexclusive(server->task);
+
+ return (result);
+}
+
+static void
+newzone_cfgctx_destroy(void **cfgp) {
+ ns_cfgctx_t *cfg;
+
+ REQUIRE(cfgp != NULL && *cfgp != NULL);
+
+ cfg = *cfgp;
+
+ if (cfg->conf_parser != NULL) {
+ if (cfg->config != NULL)
+ cfg_obj_destroy(cfg->conf_parser, &cfg->config);
+ if (cfg->vconfig != NULL)
+ cfg_obj_destroy(cfg->conf_parser, &cfg->vconfig);
+ cfg_parser_destroy(&cfg->conf_parser);
+ }
+ if (cfg->add_parser != NULL) {
+ if (cfg->nzf_config != NULL)
+ cfg_obj_destroy(cfg->add_parser, &cfg->nzf_config);
+ cfg_parser_destroy(&cfg->add_parser);
+ }
+
+ if (cfg->actx != NULL)
+ cfg_aclconfctx_detach(&cfg->actx);
+
+ isc_mem_putanddetach(&cfg->mctx, cfg, sizeof(*cfg));
+ *cfgp = NULL;
+}
+
+static isc_result_t
+generate_salt(unsigned char *salt, size_t saltlen) {
+ int i, n;
+ union {
+ unsigned char rnd[256];
+ uint32_t rnd32[64];
+ } rnd;
+ unsigned char text[512 + 1];
+ isc_region_t r;
+ isc_buffer_t buf;
+ isc_result_t result;
+
+ if (saltlen > 256U)
+ return (ISC_R_RANGE);
+
+ n = (int) (saltlen + sizeof(uint32_t) - 1) / sizeof(uint32_t);
+ for (i = 0; i < n; i++)
+ isc_random_get(&rnd.rnd32[i]);
+
+ memmove(salt, rnd.rnd, saltlen);
+
+ r.base = rnd.rnd;
+ r.length = (unsigned int) saltlen;
+
+ isc_buffer_init(&buf, text, sizeof(text));
+ result = isc_hex_totext(&r, 2, "", &buf);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ text[saltlen * 2] = 0;
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "generated salt: %s", text);
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_server_signing(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_zone_t *zone = NULL;
+ dns_name_t *origin;
+ dns_db_t *db = NULL;
+ dns_dbnode_t *node = NULL;
+ dns_dbversion_t *version = NULL;
+ dns_rdatatype_t privatetype;
+ dns_rdataset_t privset;
+ bool first = true;
+ bool list = false, clear = false;
+ bool chain = false;
+ bool setserial = false;
+ uint32_t serial = 0;
+ char keystr[DNS_SECALG_FORMATSIZE + 7]; /* <5-digit keyid>/<alg> */
+ unsigned short hash = 0, flags = 0, iter = 0, saltlen = 0;
+ unsigned char salt[255];
+ const char *ptr;
+ size_t n;
+
+ dns_rdataset_init(&privset);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Find out what we are to do. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ if (strcasecmp(ptr, "-list") == 0)
+ list = true;
+ else if ((strcasecmp(ptr, "-clear") == 0) ||
+ (strcasecmp(ptr, "-clean") == 0))
+ {
+ clear = true;
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ strlcpy(keystr, ptr, sizeof(keystr));
+ } else if (strcasecmp(ptr, "-nsec3param") == 0) {
+ char hashbuf[64], flagbuf[64], iterbuf[64];
+ char nbuf[256];
+
+ chain = true;
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ if (strcasecmp(ptr, "none") == 0)
+ hash = 0;
+ else {
+ strlcpy(hashbuf, ptr, sizeof(hashbuf));
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ strlcpy(flagbuf, ptr, sizeof(flagbuf));
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ strlcpy(iterbuf, ptr, sizeof(iterbuf));
+
+ n = snprintf(nbuf, sizeof(nbuf), "%s %s %s",
+ hashbuf, flagbuf, iterbuf);
+ if (n == sizeof(nbuf))
+ return (ISC_R_NOSPACE);
+ n = sscanf(nbuf, "%hu %hu %hu", &hash, &flags, &iter);
+ if (n != 3U)
+ return (ISC_R_BADNUMBER);
+
+ if (hash > 0xffU || flags > 0xffU)
+ return (ISC_R_RANGE);
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ } else if (strcasecmp(ptr, "auto") == 0) {
+ /* Auto-generate a random salt.
+ * XXXMUKS: This currently uses the
+ * minimum recommended length by RFC
+ * 5155 (64 bits). It should be made
+ * configurable.
+ */
+ saltlen = 8;
+ CHECK(generate_salt(salt, saltlen));
+ } else if (strcmp(ptr, "-") != 0) {
+ isc_buffer_t buf;
+
+ isc_buffer_init(&buf, salt, sizeof(salt));
+ CHECK(isc_hex_decodestring(ptr, &buf));
+ saltlen = isc_buffer_usedlength(&buf);
+ }
+ }
+ } else if (strcasecmp(ptr, "-serial") == 0) {
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ CHECK(isc_parse_uint32(&serial, ptr, 10));
+ setserial = true;
+ } else
+ CHECK(DNS_R_SYNTAX);
+
+ CHECK(zone_from_args(server, lex, NULL, &zone, NULL,
+ text, false));
+ if (zone == NULL)
+ CHECK(ISC_R_UNEXPECTEDEND);
+
+ if (clear) {
+ CHECK(dns_zone_keydone(zone, keystr));
+ (void) putstr(text, "request queued");
+ (void) putnull(text);
+ } else if (chain) {
+ CHECK(dns_zone_setnsec3param(zone, (uint8_t)hash,
+ (uint8_t)flags, iter,
+ (uint8_t)saltlen, salt,
+ true));
+ (void) putstr(text, "nsec3param request queued");
+ (void) putnull(text);
+ } else if (setserial) {
+ CHECK(dns_zone_setserial(zone, serial));
+ (void) putstr(text, "serial request queued");
+ (void) putnull(text);
+ } else if (list) {
+ privatetype = dns_zone_getprivatetype(zone);
+ origin = dns_zone_getorigin(zone);
+ CHECK(dns_zone_getdb(zone, &db));
+ CHECK(dns_db_findnode(db, origin, false, &node));
+ dns_db_currentversion(db, &version);
+
+ result = dns_db_findrdataset(db, node, version, privatetype,
+ dns_rdatatype_none, 0,
+ &privset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ (void) putstr(text, "No signing records found");
+ (void) putnull(text);
+ result = ISC_R_SUCCESS;
+ goto cleanup;
+ }
+
+ for (result = dns_rdataset_first(&privset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(&privset))
+ {
+ dns_rdata_t priv = DNS_RDATA_INIT;
+ char output[BUFSIZ];
+ isc_buffer_t buf;
+
+ dns_rdataset_current(&privset, &priv);
+
+ isc_buffer_init(&buf, output, sizeof(output));
+ CHECK(dns_private_totext(&priv, &buf));
+ if (!first)
+ CHECK(putstr(text, "\n"));
+ CHECK(putstr(text, output));
+ first = false;
+ }
+ if (!first)
+ CHECK(putnull(text));
+
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+ }
+
+ cleanup:
+ if (dns_rdataset_isassociated(&privset))
+ dns_rdataset_disassociate(&privset);
+ if (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (version != NULL)
+ dns_db_closeversion(db, &version, false);
+ if (db != NULL)
+ dns_db_detach(&db);
+ if (zone != NULL)
+ dns_zone_detach(&zone);
+
+ return (result);
+}
+
+static isc_result_t
+putmem(isc_buffer_t **b, const char *str, size_t len) {
+ isc_result_t result;
+
+ result = isc_buffer_reserve(b, (unsigned int)len);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_NOSPACE);
+
+ isc_buffer_putmem(*b, (const unsigned char *)str, (unsigned int)len);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+putstr(isc_buffer_t **b, const char *str) {
+ return (putmem(b, str, strlen(str)));
+}
+
+static isc_result_t
+putuint8(isc_buffer_t **b, uint8_t val) {
+ isc_result_t result;
+
+ result = isc_buffer_reserve(b, 1);
+ if (result != ISC_R_SUCCESS)
+ return (ISC_R_NOSPACE);
+
+ isc_buffer_putuint8(*b, val);
+ return (ISC_R_SUCCESS);
+}
+
+static inline isc_result_t
+putnull(isc_buffer_t **b) {
+ return (putuint8(b, 0));
+}
+
+isc_result_t
+ns_server_zonestatus(ns_server_t *server, isc_lex_t *lex,
+ isc_buffer_t **text)
+{
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_zone_t *zone = NULL, *raw = NULL, *mayberaw = NULL;
+ const char *type, *file;
+ char zonename[DNS_NAME_FORMATSIZE];
+ uint32_t serial, signed_serial, nodes;
+ char serbuf[16], sserbuf[16], nodebuf[16];
+ char resignbuf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE + 2];
+ char lbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char xbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char rbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char kbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ char rtbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ isc_time_t loadtime, expiretime, refreshtime;
+ isc_time_t refreshkeytime, resigntime;
+ dns_zonetype_t zonetype;
+ bool dynamic = false, frozen = false;
+ bool hasraw = false;
+ bool secure, maintain, allow;
+ dns_db_t *db = NULL, *rawdb = NULL;
+ char **incfiles = NULL;
+ int nfiles = 0;
+
+ isc_time_settoepoch(&loadtime);
+ isc_time_settoepoch(&refreshtime);
+ isc_time_settoepoch(&expiretime);
+ isc_time_settoepoch(&refreshkeytime);
+ isc_time_settoepoch(&resigntime);
+
+ CHECK(zone_from_args(server, lex, NULL, &zone, zonename,
+ text, true));
+ if (zone == NULL) {
+ result = ISC_R_UNEXPECTEDEND;
+ goto cleanup;
+ }
+
+ /* Inline signing? */
+ CHECK(dns_zone_getdb(zone, &db));
+ dns_zone_getraw(zone, &raw);
+ hasraw = (raw != NULL);
+ if (hasraw) {
+ mayberaw = raw;
+ zonetype = dns_zone_gettype(raw);
+ CHECK(dns_zone_getdb(raw, &rawdb));
+ } else {
+ mayberaw = zone;
+ zonetype = dns_zone_gettype(zone);
+ }
+
+ switch (zonetype) {
+ case dns_zone_master:
+ type = "master";
+ break;
+ case dns_zone_slave:
+ type = "slave";
+ break;
+ case dns_zone_stub:
+ type = "stub";
+ break;
+ case dns_zone_staticstub:
+ type = "staticstub";
+ break;
+ case dns_zone_redirect:
+ type = "redirect";
+ break;
+ case dns_zone_key:
+ type = "key";
+ break;
+ case dns_zone_dlz:
+ type = "dlz";
+ break;
+ default:
+ type = "unknown";
+ }
+
+ /* Serial number */
+ serial = dns_zone_getserial(mayberaw);
+ snprintf(serbuf, sizeof(serbuf), "%u", serial);
+ if (hasraw) {
+ signed_serial = dns_zone_getserial(zone);
+ snprintf(sserbuf, sizeof(sserbuf), "%u", signed_serial);
+ }
+
+ /* Database node count */
+ nodes = dns_db_nodecount(hasraw ? rawdb : db);
+ snprintf(nodebuf, sizeof(nodebuf), "%u", nodes);
+
+ /* Security */
+ secure = dns_db_issecure(db);
+ allow = (dns_zone_getkeyopts(zone) & DNS_ZONEKEY_ALLOW);
+ maintain = (dns_zone_getkeyopts(zone) & DNS_ZONEKEY_MAINTAIN);
+
+ /* Master files */
+ file = dns_zone_getfile(mayberaw);
+ nfiles = dns_zone_getincludes(mayberaw, &incfiles);
+
+ /* Load time */
+ dns_zone_getloadtime(zone, &loadtime);
+ isc_time_formathttptimestamp(&loadtime, lbuf, sizeof(lbuf));
+
+ /* Refresh/expire times */
+ if (zonetype == dns_zone_slave ||
+ zonetype == dns_zone_stub ||
+ zonetype == dns_zone_redirect)
+ {
+ dns_zone_getexpiretime(mayberaw, &expiretime);
+ isc_time_formathttptimestamp(&expiretime, xbuf, sizeof(xbuf));
+ dns_zone_getrefreshtime(mayberaw, &refreshtime);
+ isc_time_formathttptimestamp(&refreshtime, rbuf, sizeof(rbuf));
+ }
+
+ /* Key refresh time */
+ if (zonetype == dns_zone_master ||
+ (zonetype == dns_zone_slave && hasraw))
+ {
+ dns_zone_getrefreshkeytime(zone, &refreshkeytime);
+ isc_time_formathttptimestamp(&refreshkeytime, kbuf,
+ sizeof(kbuf));
+ }
+
+ /* Dynamic? */
+ if (zonetype == dns_zone_master) {
+ dynamic = dns_zone_isdynamic(mayberaw, true);
+ frozen = dynamic && !dns_zone_isdynamic(mayberaw, false);
+ }
+
+ /* Next resign event */
+ if (secure && (zonetype == dns_zone_master ||
+ (zonetype == dns_zone_slave && hasraw)) &&
+ ((dns_zone_getkeyopts(zone) & DNS_ZONEKEY_NORESIGN) == 0))
+ {
+ dns_name_t *name;
+ dns_fixedname_t fixed;
+ dns_rdataset_t next;
+
+ dns_rdataset_init(&next);
+ name = dns_fixedname_initname(&fixed);
+
+ result = dns_db_getsigningtime(db, &next, name);
+ if (result == ISC_R_SUCCESS) {
+ isc_stdtime_t timenow;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char typebuf[DNS_RDATATYPE_FORMATSIZE];
+
+ isc_stdtime_get(&timenow);
+ dns_name_format(name, namebuf, sizeof(namebuf));
+ dns_rdatatype_format(next.covers,
+ typebuf, sizeof(typebuf));
+ snprintf(resignbuf, sizeof(resignbuf),
+ "%s/%s", namebuf, typebuf);
+ isc_time_set(&resigntime, next.resign -
+ dns_zone_getsigresigninginterval(zone), 0);
+ isc_time_formathttptimestamp(&resigntime, rtbuf,
+ sizeof(rtbuf));
+ dns_rdataset_disassociate(&next);
+ }
+ }
+
+ /* Create text */
+ CHECK(putstr(text, "name: "));
+ CHECK(putstr(text, zonename));
+
+ CHECK(putstr(text, "\ntype: "));
+ CHECK(putstr(text, type));
+
+ if (file != NULL) {
+ int i;
+ CHECK(putstr(text, "\nfiles: "));
+ CHECK(putstr(text, file));
+ for (i = 0; i < nfiles; i++) {
+ CHECK(putstr(text, ", "));
+ if (incfiles[i] != NULL) {
+ CHECK(putstr(text, incfiles[i]));
+ }
+ }
+ }
+
+ CHECK(putstr(text, "\nserial: "));
+ CHECK(putstr(text, serbuf));
+ if (hasraw) {
+ CHECK(putstr(text, "\nsigned serial: "));
+ CHECK(putstr(text, sserbuf));
+ }
+
+ CHECK(putstr(text, "\nnodes: "));
+ CHECK(putstr(text, nodebuf));
+
+ if (! isc_time_isepoch(&loadtime)) {
+ CHECK(putstr(text, "\nlast loaded: "));
+ CHECK(putstr(text, lbuf));
+ }
+
+ if (! isc_time_isepoch(&refreshtime)) {
+ CHECK(putstr(text, "\nnext refresh: "));
+ CHECK(putstr(text, rbuf));
+ }
+
+ if (! isc_time_isepoch(&expiretime)) {
+ CHECK(putstr(text, "\nexpires: "));
+ CHECK(putstr(text, xbuf));
+ }
+
+ if (secure) {
+ CHECK(putstr(text, "\nsecure: yes"));
+ if (hasraw) {
+ CHECK(putstr(text, "\ninline signing: yes"));
+ } else {
+ CHECK(putstr(text, "\ninline signing: no"));
+ }
+ } else {
+ CHECK(putstr(text, "\nsecure: no"));
+ }
+
+ if (maintain) {
+ CHECK(putstr(text, "\nkey maintenance: automatic"));
+ if (! isc_time_isepoch(&refreshkeytime)) {
+ CHECK(putstr(text, "\nnext key event: "));
+ CHECK(putstr(text, kbuf));
+ }
+ } else if (allow) {
+ CHECK(putstr(text, "\nkey maintenance: on command"));
+ } else if (secure || hasraw) {
+ CHECK(putstr(text, "\nkey maintenance: none"));
+ }
+
+ if (!isc_time_isepoch(&resigntime)) {
+ CHECK(putstr(text, "\nnext resign node: "));
+ CHECK(putstr(text, resignbuf));
+ CHECK(putstr(text, "\nnext resign time: "));
+ CHECK(putstr(text, rtbuf));
+ }
+
+ if (dynamic) {
+ CHECK(putstr(text, "\ndynamic: yes"));
+ if (frozen) {
+ CHECK(putstr(text, "\nfrozen: yes"));
+ } else {
+ CHECK(putstr(text, "\nfrozen: no"));
+ }
+ } else {
+ CHECK(putstr(text, "\ndynamic: no"));
+ }
+
+ CHECK(putstr(text, "\nreconfigurable via modzone: "));
+ CHECK(putstr(text, dns_zone_getadded(zone) ? "yes" : "no"));
+
+ cleanup:
+ /* Indicate truncated output if possible. */
+ if (result == ISC_R_NOSPACE) {
+ (void) putstr(text, "\n...");
+ }
+ if ((result == ISC_R_SUCCESS || result == ISC_R_NOSPACE)) {
+ (void) putnull(text);
+ }
+
+ if (db != NULL) {
+ dns_db_detach(&db);
+ }
+ if (rawdb != NULL) {
+ dns_db_detach(&rawdb);
+ }
+ if (incfiles != NULL && mayberaw != NULL) {
+ int i;
+ isc_mem_t *mctx = dns_zone_getmctx(mayberaw);
+
+ for (i = 0; i < nfiles; i++) {
+ if (incfiles[i] != NULL) {
+ isc_mem_free(mctx, incfiles[i]);
+ }
+ }
+ isc_mem_free(mctx, incfiles);
+ }
+ if (raw != NULL) {
+ dns_zone_detach(&raw);
+ }
+ if (zone != NULL) {
+ dns_zone_detach(&zone);
+ }
+ return (result);
+}
+
+static inline bool
+argcheck(char *cmd, const char *full) {
+ size_t l;
+
+ if (cmd == NULL || cmd[0] != '-')
+ return (false);
+
+ cmd++;
+ l = strlen(cmd);
+ if (l > strlen(full) || strncasecmp(cmd, full, l) != 0)
+ return (false);
+
+ return (true);
+}
+
+isc_result_t
+ns_server_nta(ns_server_t *server, isc_lex_t *lex, bool readonly,
+ isc_buffer_t **text)
+{
+ dns_view_t *view;
+ dns_ntatable_t *ntatable = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ char *ptr, *nametext = NULL, *viewname;
+ char namebuf[DNS_NAME_FORMATSIZE];
+ isc_stdtime_t now, when;
+ isc_time_t t;
+ char tbuf[64];
+ const char *msg = NULL;
+ bool dump = false, force = false;
+ dns_fixedname_t fn;
+ dns_name_t *ntaname;
+ dns_ttl_t ntattl;
+ bool ttlset = false, excl = false;
+ dns_rdataclass_t rdclass = dns_rdataclass_in;
+
+ UNUSED(force);
+
+ ntaname = dns_fixedname_initname(&fn);
+
+ /* Skip the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ for (;;) {
+ /* Check for options */
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (argcheck(ptr, "dump")) {
+ dump = true;
+ } else if (argcheck(ptr, "remove")) {
+ ntattl = 0;
+ ttlset = true;
+ } else if (argcheck(ptr, "force")) {
+ force = true;
+ continue;
+ } else if (argcheck(ptr, "lifetime")) {
+ isc_textregion_t tr;
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ msg = "No lifetime specified";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+
+ tr.base = ptr;
+ tr.length = strlen(ptr);
+ result = dns_ttl_fromtext(&tr, &ntattl);
+ if (result != ISC_R_SUCCESS) {
+ msg = "could not parse NTA lifetime";
+ CHECK(result);
+ }
+
+ if (ntattl > 604800) {
+ msg = "NTA lifetime cannot exceed one week";
+ CHECK(ISC_R_RANGE);
+ }
+
+ ttlset = true;
+ continue;
+ } else if (argcheck(ptr, "class")) {
+ isc_textregion_t tr;
+
+ ptr = next_token(lex, text);
+ if (ptr == NULL) {
+ msg = "No class specified";
+ CHECK(ISC_R_UNEXPECTEDEND);
+ }
+
+ tr.base = ptr;
+ tr.length = strlen(ptr);
+ CHECK(dns_rdataclass_fromtext(&rdclass, &tr));
+ continue;
+ } else {
+ nametext = ptr;
+ }
+
+ break;
+ }
+
+ /*
+ * If -dump was specified, list NTA's and return
+ */
+ if (dump) {
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (ntatable != NULL) {
+ dns_ntatable_detach(&ntatable);
+ }
+ result = dns_view_getntatable(view, &ntatable);
+ if (result == ISC_R_NOTFOUND) {
+ continue;
+ }
+ CHECK(dns_ntatable_totext(ntatable, text));
+ }
+ CHECK(putnull(text));
+
+ goto cleanup;
+ }
+
+ if (readonly) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_CONTROL, ISC_LOG_INFO,
+ "rejecting restricted control channel "
+ "NTA command");
+ CHECK(ISC_R_FAILURE);
+ }
+
+ /* Get the NTA name. */
+ if (nametext == NULL) {
+ nametext = next_token(lex, text);
+ }
+ if (nametext == NULL) {
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ /* Copy nametext as it'll be overwritten by next_token() */
+ strlcpy(namebuf, nametext, DNS_NAME_FORMATSIZE);
+
+ if (strcmp(namebuf, ".") == 0) {
+ ntaname = dns_rootname;
+ } else {
+ isc_buffer_t b;
+ isc_buffer_init(&b, namebuf, strlen(namebuf));
+ isc_buffer_add(&b, strlen(namebuf));
+ CHECK(dns_name_fromtext(ntaname, &b, dns_rootname, 0, NULL));
+ }
+
+ /* Look for the view name. */
+ viewname = next_token(lex, text);
+
+ isc_stdtime_get(&now);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ excl = true;
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ static bool first = true;
+
+ if (viewname != NULL && strcmp(view->name, viewname) != 0) {
+ continue;
+ }
+
+ if (view->rdclass != rdclass && rdclass != dns_rdataclass_any) {
+ continue;
+ }
+
+ if (view->nta_lifetime == 0) {
+ continue;
+ }
+
+ if (!ttlset) {
+ ntattl = view->nta_lifetime;
+ }
+
+ if (ntatable != NULL) {
+ dns_ntatable_detach(&ntatable);
+ }
+
+ result = dns_view_getntatable(view, &ntatable);
+ if (result == ISC_R_NOTFOUND) {
+ result = ISC_R_SUCCESS;
+ continue;
+ }
+
+ result = dns_view_flushnode(view, ntaname, true);
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "flush tree '%s' in cache view '%s': %s",
+ namebuf, view->name,
+ isc_result_totext(result));
+
+ if (ntattl != 0) {
+ CHECK(dns_ntatable_add(ntatable, ntaname,
+ force, now, ntattl));
+
+ when = now + ntattl;
+ isc_time_set(&t, when, 0);
+ isc_time_formattimestamp(&t, tbuf, sizeof(tbuf));
+
+ if (!first) {
+ CHECK(putstr(text, "\n"));
+ }
+ first = false;
+
+ CHECK(putstr(text, "Negative trust anchor added: "));
+ CHECK(putstr(text, namebuf));
+ CHECK(putstr(text, "/"));
+ CHECK(putstr(text, view->name));
+ CHECK(putstr(text, ", expires "));
+ CHECK(putstr(text, tbuf));
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "added NTA '%s' (%d sec) in view '%s'",
+ namebuf, ntattl, view->name);
+ } else {
+ CHECK(dns_ntatable_delete(ntatable, ntaname));
+
+ if (!first) {
+ CHECK(putstr(text, "\n"));
+ }
+ first = false;
+
+ CHECK(putstr(text, "Negative trust anchor removed: "));
+ CHECK(putstr(text, namebuf));
+ CHECK(putstr(text, "/"));
+ CHECK(putstr(text, view->name));
+
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_INFO,
+ "removed NTA '%s' in view %s",
+ namebuf, view->name);
+ }
+
+ result = dns_view_saventa(view);
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "error writing NTA file "
+ "for view '%s': %s",
+ view->name, isc_result_totext(result));
+ }
+ }
+
+ CHECK(putnull(text));
+
+ cleanup:
+ if (msg != NULL) {
+ (void) putstr(text, msg);
+ (void) putnull(text);
+ }
+ if (excl) {
+ isc_task_endexclusive(server->task);
+ }
+ if (ntatable != NULL) {
+ dns_ntatable_detach(&ntatable);
+ }
+ return (result);
+}
+
+isc_result_t
+ns_server_saventa(ns_server_t *server) {
+ dns_view_t *view;
+
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ isc_result_t result = dns_view_saventa(view);
+
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "error writing NTA file "
+ "for view '%s': %s",
+ view->name, isc_result_totext(result));
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+ns_server_loadnta(ns_server_t *server) {
+ dns_view_t *view;
+
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ isc_result_t result = dns_view_loadnta(view);
+
+ if ((result != ISC_R_SUCCESS) &&
+ (result != ISC_R_FILENOTFOUND) &&
+ (result != ISC_R_NOTFOUND))
+ {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "error loading NTA file "
+ "for view '%s': %s",
+ view->name, isc_result_totext(result));
+ }
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+mkey_refresh(dns_view_t *view, isc_buffer_t **text) {
+ isc_result_t result;
+ char msg[DNS_NAME_FORMATSIZE + 500] = "";
+
+ snprintf(msg, sizeof(msg),
+ "refreshing managed keys for '%s'", view->name);
+ CHECK(putstr(text, msg));
+ CHECK(dns_zone_synckeyzone(view->managed_keys));
+
+ cleanup:
+ return (result);
+}
+
+static isc_result_t
+mkey_dumpzone(dns_view_t *view, isc_buffer_t **text) {
+ isc_result_t result;
+ dns_db_t *db = NULL;
+ dns_dbversion_t *ver = NULL;
+ dns_rriterator_t rrit;
+ isc_stdtime_t now;
+ dns_name_t *prevname = NULL;
+
+ isc_stdtime_get(&now);
+
+ CHECK(dns_zone_getdb(view->managed_keys, &db));
+ dns_db_currentversion(db, &ver);
+ dns_rriterator_init(&rrit, db, ver, 0);
+ for (result = dns_rriterator_first(&rrit);
+ result == ISC_R_SUCCESS;
+ result = dns_rriterator_nextrrset(&rrit))
+ {
+ char buf[DNS_NAME_FORMATSIZE + 500];
+ dns_name_t *name = NULL;
+ dns_rdataset_t *kdset = NULL;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_keydata_t kd;
+ uint32_t ttl;
+
+ dns_rriterator_current(&rrit, &name, &ttl, &kdset, NULL);
+ if (kdset == NULL || kdset->type != dns_rdatatype_keydata ||
+ !dns_rdataset_isassociated(kdset))
+ continue;
+
+ if (name != prevname) {
+ char nbuf[DNS_NAME_FORMATSIZE];
+ dns_name_format(name, nbuf, sizeof(nbuf));
+ snprintf(buf, sizeof(buf), "\n\n name: %s", nbuf);
+ CHECK(putstr(text, buf));
+ }
+
+
+ for (result = dns_rdataset_first(kdset);
+ result == ISC_R_SUCCESS;
+ result = dns_rdataset_next(kdset))
+ {
+ char alg[DNS_SECALG_FORMATSIZE];
+ char tbuf[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ dns_keytag_t keyid;
+ isc_region_t r;
+ isc_time_t t;
+ bool revoked;
+
+ dns_rdata_reset(&rdata);
+ dns_rdataset_current(kdset, &rdata);
+ result = dns_rdata_tostruct(&rdata, &kd, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+
+ dns_rdata_toregion(&rdata, &r);
+ isc_region_consume(&r, 12);
+ keyid = dst_region_computeid(&r, kd.algorithm);
+
+ snprintf(buf, sizeof(buf), "\n keyid: %u", keyid);
+ CHECK(putstr(text, buf));
+
+ dns_secalg_format(kd.algorithm, alg, sizeof(alg));
+ snprintf(buf, sizeof(buf), "\n\talgorithm: %s", alg);
+ CHECK(putstr(text, buf));
+
+ revoked = (kd.flags & DNS_KEYFLAG_REVOKE);
+ snprintf(buf, sizeof(buf), "\n\tflags:%s%s%s",
+ revoked ? " REVOKE" : "",
+ ((kd.flags & DNS_KEYFLAG_KSK) != 0)
+ ? " SEP" : "",
+ (kd.flags == 0) ? " (none)" : "");
+ CHECK(putstr(text, buf));
+
+ isc_time_set(&t, kd.refresh, 0);
+ isc_time_formathttptimestamp(&t, tbuf, sizeof(tbuf));
+ snprintf(buf, sizeof(buf),
+ "\n\tnext refresh: %s", tbuf);
+ CHECK(putstr(text, buf));
+
+ if (kd.removehd != 0) {
+ isc_time_set(&t, kd.removehd, 0);
+ isc_time_formathttptimestamp(&t, tbuf,
+ sizeof(tbuf));
+ snprintf(buf, sizeof(buf),
+ "\n\tremove at: %s", tbuf);
+ CHECK(putstr(text, buf));
+ }
+
+ isc_time_set(&t, kd.addhd, 0);
+ isc_time_formathttptimestamp(&t, tbuf, sizeof(tbuf));
+ if (kd.addhd == 0)
+ snprintf(buf, sizeof(buf), "\n\tno trust");
+ else if (revoked)
+ snprintf(buf, sizeof(buf),
+ "\n\ttrust revoked");
+ else if (kd.addhd <= now)
+ snprintf(buf, sizeof(buf),
+ "\n\ttrusted since: %s", tbuf);
+ else if (kd.addhd > now)
+ snprintf(buf, sizeof(buf),
+ "\n\ttrust pending: %s", tbuf);
+ CHECK(putstr(text, buf));
+ }
+ }
+
+ if (result == ISC_R_NOMORE)
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (ver != NULL) {
+ dns_rriterator_destroy(&rrit);
+ dns_db_closeversion(db, &ver, false);
+ }
+ if (db != NULL)
+ dns_db_detach(&db);
+
+ return (result);
+}
+
+static isc_result_t
+mkey_status(dns_view_t *view, isc_buffer_t **text) {
+ isc_result_t result;
+ char msg[ISC_FORMATHTTPTIMESTAMP_SIZE];
+ isc_time_t t;
+
+ CHECK(putstr(text, "view: "));
+ CHECK(putstr(text, view->name));
+
+ CHECK(putstr(text, "\nnext scheduled event: "));
+
+ dns_zone_getrefreshkeytime(view->managed_keys, &t);
+ if (isc_time_isepoch(&t)) {
+ CHECK(putstr(text, "never"));
+ } else {
+ isc_time_formathttptimestamp(&t, msg, sizeof(msg));
+ CHECK(putstr(text, msg));
+ }
+
+ CHECK(mkey_dumpzone(view, text));
+
+ cleanup:
+ return (result);
+}
+
+isc_result_t
+ns_server_mkeys(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
+ char *cmd, *classtxt, *viewtxt = NULL;
+ isc_result_t result = ISC_R_SUCCESS;
+ dns_view_t *view = NULL;
+ dns_rdataclass_t rdclass;
+ char msg[DNS_NAME_FORMATSIZE + 500] = "";
+ enum { NONE, STATUS, REFRESH, SYNC } opt = NONE;
+ bool found = false;
+ bool first = true;
+
+ /* Skip rndc command name */
+ cmd = next_token(lex, text);
+ if (cmd == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* Get managed-keys subcommand */
+ cmd = next_token(lex, text);
+ if (cmd == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ if (strcasecmp(cmd, "status") == 0)
+ opt = STATUS;
+ else if (strcasecmp(cmd, "refresh") == 0)
+ opt = REFRESH;
+ else if (strcasecmp(cmd, "sync") == 0)
+ opt = SYNC;
+ else {
+ snprintf(msg, sizeof(msg), "unknown command '%s'", cmd);
+ (void) putstr(text, msg);
+ result = ISC_R_UNEXPECTED;
+ goto cleanup;
+ }
+
+ /* Look for the optional class name. */
+ classtxt = next_token(lex, text);
+ if (classtxt != NULL) {
+ /* Look for the optional view name. */
+ viewtxt = next_token(lex, text);
+ }
+
+ if (classtxt == NULL) {
+ rdclass = dns_rdataclass_in;
+ } else {
+ isc_textregion_t r;
+ r.base = classtxt;
+ r.length = strlen(classtxt);
+ result = dns_rdataclass_fromtext(&rdclass, &r);
+ if (result != ISC_R_SUCCESS) {
+ if (viewtxt == NULL) {
+ rdclass = dns_rdataclass_in;
+ viewtxt = classtxt;
+ result = ISC_R_SUCCESS;
+ } else {
+ snprintf(msg, sizeof(msg),
+ "unknown class '%s'", classtxt);
+ (void) putstr(text, msg);
+ goto cleanup;
+ }
+ }
+ }
+
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link))
+ {
+ if (viewtxt != NULL &&
+ (rdclass != view->rdclass ||
+ strcmp(view->name, viewtxt) != 0))
+ continue;
+
+ if (view->managed_keys == NULL) {
+ if (viewtxt != NULL) {
+ snprintf(msg, sizeof(msg),
+ "view '%s': no managed keys", viewtxt);
+ CHECK(putstr(text, msg));
+ goto cleanup;
+ } else
+ continue;
+ }
+
+ found = true;
+
+ switch (opt) {
+ case REFRESH:
+ CHECK(mkey_refresh(view, text));
+ break;
+ case STATUS:
+ if (!first)
+ CHECK(putstr(text, "\n\n"));
+ CHECK(mkey_status(view, text));
+ first = false;
+ break;
+ case SYNC:
+ CHECK(dns_zone_flush(view->managed_keys));
+ break;
+ default:
+ INSIST(0);
+ }
+
+ if (viewtxt != NULL)
+ break;
+ }
+
+ if (!found)
+ CHECK(putstr(text, "no views with managed keys"));
+
+ cleanup:
+ if (isc_buffer_usedlength(*text) > 0)
+ (void) putnull(text);
+
+ return (result);
+}
+
+isc_result_t
+ns_server_dnstap(ns_server_t *server, isc_lex_t *lex, isc_buffer_t **text) {
+#ifdef HAVE_DNSTAP
+ char *ptr;
+ isc_result_t result;
+ bool reopen = false;
+ int backups = 0;
+
+ if (server->dtenv == NULL)
+ return (ISC_R_NOTFOUND);
+
+ /* Check the command name. */
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+
+ /* "dnstap-reopen" was used in 9.11.0b1 */
+ if (strcasecmp(ptr, "dnstap-reopen") == 0) {
+ reopen = true;
+ } else {
+ ptr = next_token(lex, text);
+ if (ptr == NULL)
+ return (ISC_R_UNEXPECTEDEND);
+ }
+
+ if (reopen || strcasecmp(ptr, "-reopen") == 0) {
+ backups = -1;
+ } else if ((strcasecmp(ptr, "-roll") == 0)) {
+ unsigned int n;
+ ptr = next_token(lex, text);
+ if (ptr != NULL) {
+ unsigned int u;
+ n = sscanf(ptr, "%u", &u);
+ if (n != 1U || u > INT_MAX)
+ return (ISC_R_BADNUMBER);
+ backups = u;
+ }
+ } else
+ return (DNS_R_SYNTAX);
+
+ result = isc_task_beginexclusive(server->task);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ result = dns_dt_reopen(server->dtenv, backups);
+ isc_task_endexclusive(server->task);
+ return (result);
+#else
+ UNUSED(server);
+ UNUSED(lex);
+ UNUSED(text);
+ return (ISC_R_NOTIMPLEMENTED);
+#endif
+}
diff --git a/bin/named/sortlist.c b/bin/named/sortlist.c
new file mode 100644
index 0000000..20a131c
--- /dev/null
+++ b/bin/named/sortlist.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id: 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..b1559f8
--- /dev/null
+++ b/bin/named/statschannel.c
@@ -0,0 +1,3583 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/httpd.h>
+#include <isc/json.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/socket.h>
+#include <isc/stats.h>
+#include <isc/string.h>
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/cache.h>
+#include <dns/db.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/rdataclass.h>
+#include <dns/rdatatype.h>
+#include <dns/resolver.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 referenced 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 struct
+stats_dumparg {
+ isc_statsformat_t type;
+ void *arg; /* type dependent argument */
+ int ncounters; /* for general statistics */
+ int *counterindices; /* for general statistics */
+ uint64_t *countervalues; /* for general statistics */
+ isc_result_t result;
+} stats_dumparg_t;
+
+static isc_once_t once = ISC_ONCE_INIT;
+
+#if defined(HAVE_LIBXML2) || defined(HAVE_JSON)
+#define EXTENDED_STATS
+#else
+#undef EXTENDED_STATS
+#endif
+
+#ifdef EXTENDED_STATS
+static const char *
+user_zonetype( dns_zone_t *zone ) {
+ dns_zonetype_t ztype;
+ dns_view_t *view;
+ static const struct zt {
+ const dns_zonetype_t type;
+ const char *const string;
+ } typemap[] = {
+ { dns_zone_none, "none" },
+ { dns_zone_master, "master" },
+ { dns_zone_slave, "slave" },
+ { dns_zone_stub, "stub" },
+ { dns_zone_staticstub, "static-stub" },
+ { dns_zone_key, "key" },
+ { dns_zone_dlz, "dlz" },
+ { dns_zone_redirect, "redirect" },
+ { 0, NULL }
+ };
+ const struct zt *tp;
+
+ if ((dns_zone_getoptions2(zone) & DNS_ZONEOPT2_AUTOEMPTY) != 0)
+ return ("builtin");
+
+ view = dns_zone_getview(zone);
+ if (view != NULL && strcmp(view->name, "_bind") == 0)
+ return ("builtin");
+
+ ztype = dns_zone_gettype(zone);
+ for (tp = typemap; tp->string != NULL && tp->type != ztype; tp++)
+ /* empty */;
+ return (tp->string);
+}
+#endif
+
+/*%
+ * 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 *adbstats_desc[dns_adbstats_max];
+static const char *zonestats_desc[dns_zonestatscounter_max];
+static const char *sockstats_desc[isc_sockstatscounter_max];
+static const char *dnssecstats_desc[dns_dnssecstats_max];
+static const char *udpinsizestats_desc[dns_sizecounter_in_max];
+static const char *udpoutsizestats_desc[dns_sizecounter_out_max];
+static const char *tcpinsizestats_desc[dns_sizecounter_in_max];
+static const char *tcpoutsizestats_desc[dns_sizecounter_out_max];
+static const char *dnstapstats_desc[dns_dnstapcounter_max];
+#if defined(EXTENDED_STATS)
+static const char *nsstats_xmldesc[dns_nsstatscounter_max];
+static const char *resstats_xmldesc[dns_resstatscounter_max];
+static const char *adbstats_xmldesc[dns_adbstats_max];
+static const char *zonestats_xmldesc[dns_zonestatscounter_max];
+static const char *sockstats_xmldesc[isc_sockstatscounter_max];
+static const char *dnssecstats_xmldesc[dns_dnssecstats_max];
+static const char *udpinsizestats_xmldesc[dns_sizecounter_in_max];
+static const char *udpoutsizestats_xmldesc[dns_sizecounter_out_max];
+static const char *tcpinsizestats_xmldesc[dns_sizecounter_in_max];
+static const char *tcpoutsizestats_xmldesc[dns_sizecounter_out_max];
+static const char *dnstapstats_xmldesc[dns_dnstapcounter_max];
+#else
+#define nsstats_xmldesc NULL
+#define resstats_xmldesc NULL
+#define adbstats_xmldesc NULL
+#define zonestats_xmldesc NULL
+#define sockstats_xmldesc NULL
+#define dnssecstats_xmldesc NULL
+#define udpinsizestats_xmldesc NULL
+#define udpoutsizestats_xmldesc NULL
+#define tcpinsizestats_xmldesc NULL
+#define tcpoutsizestats_xmldesc NULL
+#define dnstapstats_xmldesc NULL
+#endif /* EXTENDED_STATS */
+
+#define TRY0(a) do { xmlrc = (a); if (xmlrc < 0) goto error; } while(0)
+
+/*%
+ * Mapping arrays to represent statistics counters in the order of our
+ * preference, regardless of the order of counter indices. For example,
+ * nsstats_desc[nsstats_index[0]] will be the description that is shown first.
+ */
+static int nsstats_index[dns_nsstatscounter_max];
+static int resstats_index[dns_resstatscounter_max];
+static int adbstats_index[dns_adbstats_max];
+static int zonestats_index[dns_zonestatscounter_max];
+static int sockstats_index[isc_sockstatscounter_max];
+static int dnssecstats_index[dns_dnssecstats_max];
+static int udpinsizestats_index[dns_sizecounter_in_max];
+static int udpoutsizestats_index[dns_sizecounter_out_max];
+static int tcpinsizestats_index[dns_sizecounter_in_max];
+static int tcpoutsizestats_index[dns_sizecounter_out_max];
+static int dnstapstats_index[dns_dnstapcounter_max];
+
+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 != NULL && fdescs[counter] == NULL);
+#if defined(EXTENDED_STATS)
+ REQUIRE(xdescs != NULL && xdescs[counter] == NULL);
+#endif
+
+ fdescs[counter] = fdesc;
+#if defined(EXTENDED_STATS)
+ xdescs[counter] = xdesc;
+#else
+ UNUSED(xdesc);
+ UNUSED(xdescs);
+#endif
+}
+
+static void
+init_desc(void) {
+ int i;
+
+ /* Initialize name server statistics */
+ for (i = 0; i < dns_nsstatscounter_max; i++)
+ nsstats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_nsstatscounter_max; i++)
+ nsstats_xmldesc[i] = NULL;
+#endif
+
+#define SET_NSSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_nsstatscounter_ ## counterid, \
+ dns_nsstatscounter_max, \
+ desc, nsstats_desc, xmldesc, nsstats_xmldesc); \
+ nsstats_index[i++] = dns_nsstatscounter_ ## counterid; \
+ } while (0)
+
+ i = 0;
+ SET_NSSTATDESC(requestv4, "IPv4 requests received", "Requestv4");
+ SET_NSSTATDESC(requestv6, "IPv6 requests received", "Requestv6");
+ SET_NSSTATDESC(edns0in, "requests with EDNS(0) received", "ReqEdns0");
+ SET_NSSTATDESC(badednsver,
+ "requests with unsupported EDNS version received",
+ "ReqBadEDNSVer");
+ SET_NSSTATDESC(tsigin, "requests with TSIG received", "ReqTSIG");
+ SET_NSSTATDESC(sig0in, "requests with SIG(0) received", "ReqSIG0");
+ SET_NSSTATDESC(invalidsig, "requests with invalid signature",
+ "ReqBadSIG");
+ SET_NSSTATDESC(requesttcp, "TCP requests received", "ReqTCP");
+ SET_NSSTATDESC(authrej, "auth queries rejected", "AuthQryRej");
+ SET_NSSTATDESC(recurserej, "recursive queries rejected", "RecQryRej");
+ SET_NSSTATDESC(xfrrej, "transfer requests rejected", "XfrRej");
+ SET_NSSTATDESC(updaterej, "update requests rejected", "UpdateRej");
+ SET_NSSTATDESC(response, "responses sent", "Response");
+ SET_NSSTATDESC(truncatedresp, "truncated responses sent",
+ "TruncatedResp");
+ SET_NSSTATDESC(edns0out, "responses with EDNS(0) sent", "RespEDNS0");
+ SET_NSSTATDESC(tsigout, "responses with TSIG sent", "RespTSIG");
+ SET_NSSTATDESC(sig0out, "responses with SIG(0) sent", "RespSIG0");
+ SET_NSSTATDESC(success, "queries resulted in successful answer",
+ "QrySuccess");
+ SET_NSSTATDESC(authans, "queries resulted in authoritative answer",
+ "QryAuthAns");
+ SET_NSSTATDESC(nonauthans,
+ "queries resulted in non authoritative answer",
+ "QryNoauthAns");
+ SET_NSSTATDESC(referral, "queries resulted in referral answer",
+ "QryReferral");
+ SET_NSSTATDESC(nxrrset, "queries resulted in nxrrset", "QryNxrrset");
+ SET_NSSTATDESC(servfail, "queries resulted in SERVFAIL", "QrySERVFAIL");
+ SET_NSSTATDESC(formerr, "queries resulted in FORMERR", "QryFORMERR");
+ SET_NSSTATDESC(nxdomain, "queries resulted in NXDOMAIN", "QryNXDOMAIN");
+ SET_NSSTATDESC(recursion, "queries caused recursion", "QryRecursion");
+ SET_NSSTATDESC(duplicate, "duplicate queries received", "QryDuplicate");
+ SET_NSSTATDESC(dropped, "queries dropped", "QryDropped");
+ SET_NSSTATDESC(failure, "other query failures", "QryFailure");
+ SET_NSSTATDESC(xfrdone, "requested transfers completed", "XfrReqDone");
+ SET_NSSTATDESC(updatereqfwd, "update requests forwarded",
+ "UpdateReqFwd");
+ SET_NSSTATDESC(updaterespfwd, "update responses forwarded",
+ "UpdateRespFwd");
+ SET_NSSTATDESC(updatefwdfail, "update forward failed", "UpdateFwdFail");
+ SET_NSSTATDESC(updatedone, "updates completed", "UpdateDone");
+ SET_NSSTATDESC(updatefail, "updates failed", "UpdateFail");
+ SET_NSSTATDESC(updatebadprereq,
+ "updates rejected due to prerequisite failure",
+ "UpdateBadPrereq");
+ SET_NSSTATDESC(recursclients, "recursing clients",
+ "RecursClients");
+ SET_NSSTATDESC(dns64, "queries answered by DNS64", "DNS64");
+ SET_NSSTATDESC(ratedropped, "responses dropped for rate limits",
+ "RateDropped");
+ SET_NSSTATDESC(rateslipped, "responses truncated for rate limits",
+ "RateSlipped");
+ SET_NSSTATDESC(rpz_rewrites, "response policy zone rewrites",
+ "RPZRewrites");
+ SET_NSSTATDESC(udp, "UDP queries received", "QryUDP");
+ SET_NSSTATDESC(tcp, "TCP queries received", "QryTCP");
+ SET_NSSTATDESC(nsidopt, "NSID option received", "NSIDOpt");
+ SET_NSSTATDESC(expireopt, "Expire option received", "ExpireOpt");
+ SET_NSSTATDESC(otheropt, "Other EDNS option received", "OtherOpt");
+ SET_NSSTATDESC(cookiein, "COOKIE option received", "CookieIn");
+ SET_NSSTATDESC(cookienew, "COOKIE - client only", "CookieNew");
+ SET_NSSTATDESC(cookiebadsize, "COOKIE - bad size", "CookieBadSize");
+ SET_NSSTATDESC(cookiebadtime, "COOKIE - bad time", "CookieBadTime");
+ SET_NSSTATDESC(cookienomatch, "COOKIE - no match", "CookieNoMatch");
+ SET_NSSTATDESC(cookiematch, "COOKIE - match", "CookieMatch");
+ SET_NSSTATDESC(ecsopt, "EDNS client subnet option received", "ECSOpt");
+ SET_NSSTATDESC(nxdomainredirect,
+ "queries resulted in NXDOMAIN that were redirected",
+ "QryNXRedir");
+ SET_NSSTATDESC(nxdomainredirect_rlookup,
+ "queries resulted in NXDOMAIN that were redirected and "
+ "resulted in a successful remote lookup",
+ "QryNXRedirRLookup");
+ SET_NSSTATDESC(badcookie, "sent badcookie response", "QryBADCOOKIE");
+ SET_NSSTATDESC(keytagopt, "Keytag option received", "KeyTagOpt");
+ INSIST(i == dns_nsstatscounter_max);
+
+ /* Initialize resolver statistics */
+ for (i = 0; i < dns_resstatscounter_max; i++)
+ resstats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_resstatscounter_max; i++)
+ resstats_xmldesc[i] = NULL;
+#endif
+
+#define SET_RESSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_resstatscounter_ ## counterid, \
+ dns_resstatscounter_max, \
+ desc, resstats_desc, xmldesc, resstats_xmldesc); \
+ resstats_index[i++] = dns_resstatscounter_ ## counterid; \
+ } while (0)
+
+ i = 0;
+ SET_RESSTATDESC(queryv4, "IPv4 queries sent", "Queryv4");
+ SET_RESSTATDESC(queryv6, "IPv6 queries sent", "Queryv6");
+ SET_RESSTATDESC(responsev4, "IPv4 responses received", "Responsev4");
+ SET_RESSTATDESC(responsev6, "IPv6 responses received", "Responsev6");
+ SET_RESSTATDESC(nxdomain, "NXDOMAIN received", "NXDOMAIN");
+ SET_RESSTATDESC(servfail, "SERVFAIL received", "SERVFAIL");
+ SET_RESSTATDESC(formerr, "FORMERR received", "FORMERR");
+ SET_RESSTATDESC(othererror, "other errors received", "OtherError");
+ SET_RESSTATDESC(edns0fail, "EDNS(0) query failures", "EDNS0Fail");
+ SET_RESSTATDESC(mismatch, "mismatch responses received", "Mismatch");
+ SET_RESSTATDESC(truncated, "truncated responses received", "Truncated");
+ SET_RESSTATDESC(lame, "lame delegations received", "Lame");
+ SET_RESSTATDESC(retry, "query retries", "Retry");
+ SET_RESSTATDESC(dispabort, "queries aborted due to quota",
+ "QueryAbort");
+ SET_RESSTATDESC(dispsockfail, "failures in opening query sockets",
+ "QuerySockFail");
+ SET_RESSTATDESC(disprequdp, "UDP queries in progress", "QueryCurUDP");
+ SET_RESSTATDESC(dispreqtcp, "TCP queries in progress", "QueryCurTCP");
+ SET_RESSTATDESC(querytimeout, "query timeouts", "QueryTimeout");
+ SET_RESSTATDESC(gluefetchv4, "IPv4 NS address fetches", "GlueFetchv4");
+ SET_RESSTATDESC(gluefetchv6, "IPv6 NS address fetches", "GlueFetchv6");
+ SET_RESSTATDESC(gluefetchv4fail, "IPv4 NS address fetch failed",
+ "GlueFetchv4Fail");
+ SET_RESSTATDESC(gluefetchv6fail, "IPv6 NS address fetch failed",
+ "GlueFetchv6Fail");
+ SET_RESSTATDESC(val, "DNSSEC validation attempted", "ValAttempt");
+ SET_RESSTATDESC(valsuccess, "DNSSEC validation succeeded", "ValOk");
+ SET_RESSTATDESC(valnegsuccess, "DNSSEC NX validation succeeded",
+ "ValNegOk");
+ SET_RESSTATDESC(valfail, "DNSSEC validation failed", "ValFail");
+ SET_RESSTATDESC(queryrtt0, "queries with RTT < "
+ DNS_RESOLVER_QRYRTTCLASS0STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS0STR);
+ SET_RESSTATDESC(queryrtt1, "queries with RTT "
+ DNS_RESOLVER_QRYRTTCLASS0STR "-"
+ DNS_RESOLVER_QRYRTTCLASS1STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS1STR);
+ SET_RESSTATDESC(queryrtt2, "queries with RTT "
+ DNS_RESOLVER_QRYRTTCLASS1STR "-"
+ DNS_RESOLVER_QRYRTTCLASS2STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS2STR);
+ SET_RESSTATDESC(queryrtt3, "queries with RTT "
+ DNS_RESOLVER_QRYRTTCLASS2STR "-"
+ DNS_RESOLVER_QRYRTTCLASS3STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS3STR);
+ SET_RESSTATDESC(queryrtt4, "queries with RTT "
+ DNS_RESOLVER_QRYRTTCLASS3STR "-"
+ DNS_RESOLVER_QRYRTTCLASS4STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR);
+ SET_RESSTATDESC(queryrtt5, "queries with RTT > "
+ DNS_RESOLVER_QRYRTTCLASS4STR "ms",
+ "QryRTT" DNS_RESOLVER_QRYRTTCLASS4STR "+");
+ SET_RESSTATDESC(nfetch, "active fetches", "NumFetch");
+ SET_RESSTATDESC(buckets, "bucket size", "BucketSize");
+ SET_RESSTATDESC(refused, "REFUSED received", "REFUSED");
+ SET_RESSTATDESC(cookienew, "COOKIE send with client cookie only",
+ "ClientCookieOut");
+ SET_RESSTATDESC(cookieout, "COOKIE sent with client and server cookie",
+ "ServerCookieOut");
+ SET_RESSTATDESC(cookiein, "COOKIE replies received", "CookieIn");
+ SET_RESSTATDESC(cookieok, "COOKIE client ok", "CookieClientOk");
+ SET_RESSTATDESC(badvers, "bad EDNS version", "BadEDNSVersion");
+ SET_RESSTATDESC(badcookie, "bad cookie rcode", "BadCookieRcode");
+ SET_RESSTATDESC(zonequota, "spilled due to zone quota", "ZoneQuota");
+ SET_RESSTATDESC(serverquota, "spilled due to server quota",
+ "ServerQuota");
+ SET_RESSTATDESC(nextitem, "waited for next item", "NextItem");
+
+ INSIST(i == dns_resstatscounter_max);
+
+ /* Initialize adb statistics */
+ for (i = 0; i < dns_adbstats_max; i++)
+ adbstats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_adbstats_max; i++)
+ adbstats_xmldesc[i] = NULL;
+#endif
+
+#define SET_ADBSTATDESC(id, desc, xmldesc) \
+ do { \
+ set_desc(dns_adbstats_ ## id, dns_adbstats_max, \
+ desc, adbstats_desc, xmldesc, adbstats_xmldesc); \
+ adbstats_index[i++] = dns_adbstats_ ## id; \
+ } while (0)
+ i = 0;
+ SET_ADBSTATDESC(nentries, "Address hash table size", "nentries");
+ SET_ADBSTATDESC(entriescnt, "Addresses in hash table", "entriescnt");
+ SET_ADBSTATDESC(nnames, "Name hash table size", "nnames");
+ SET_ADBSTATDESC(namescnt, "Names in hash table", "namescnt");
+
+ INSIST(i == dns_adbstats_max);
+
+ /* Initialize zone statistics */
+ for (i = 0; i < dns_zonestatscounter_max; i++)
+ zonestats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_zonestatscounter_max; i++)
+ zonestats_xmldesc[i] = NULL;
+#endif
+
+#define SET_ZONESTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_zonestatscounter_ ## counterid, \
+ dns_zonestatscounter_max, \
+ desc, zonestats_desc, xmldesc, zonestats_xmldesc); \
+ zonestats_index[i++] = dns_zonestatscounter_ ## counterid; \
+ } while (0)
+
+ i = 0;
+ SET_ZONESTATDESC(notifyoutv4, "IPv4 notifies sent", "NotifyOutv4");
+ SET_ZONESTATDESC(notifyoutv6, "IPv6 notifies sent", "NotifyOutv6");
+ SET_ZONESTATDESC(notifyinv4, "IPv4 notifies received", "NotifyInv4");
+ SET_ZONESTATDESC(notifyinv6, "IPv6 notifies received", "NotifyInv6");
+ SET_ZONESTATDESC(notifyrej, "notifies rejected", "NotifyRej");
+ SET_ZONESTATDESC(soaoutv4, "IPv4 SOA queries sent", "SOAOutv4");
+ SET_ZONESTATDESC(soaoutv6, "IPv6 SOA queries sent", "SOAOutv6");
+ SET_ZONESTATDESC(axfrreqv4, "IPv4 AXFR requested", "AXFRReqv4");
+ SET_ZONESTATDESC(axfrreqv6, "IPv6 AXFR requested", "AXFRReqv6");
+ SET_ZONESTATDESC(ixfrreqv4, "IPv4 IXFR requested", "IXFRReqv4");
+ SET_ZONESTATDESC(ixfrreqv6, "IPv6 IXFR requested", "IXFRReqv6");
+ SET_ZONESTATDESC(xfrsuccess, "transfer requests succeeded",
+ "XfrSuccess");
+ SET_ZONESTATDESC(xfrfail, "transfer requests failed", "XfrFail");
+ INSIST(i == dns_zonestatscounter_max);
+
+ /* Initialize socket statistics */
+ for (i = 0; i < isc_sockstatscounter_max; i++)
+ sockstats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < isc_sockstatscounter_max; i++)
+ sockstats_xmldesc[i] = NULL;
+#endif
+
+#define SET_SOCKSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(isc_sockstatscounter_ ## counterid, \
+ isc_sockstatscounter_max, \
+ desc, sockstats_desc, xmldesc, sockstats_xmldesc); \
+ sockstats_index[i++] = isc_sockstatscounter_ ## counterid; \
+ } while (0)
+
+ i = 0;
+ SET_SOCKSTATDESC(udp4open, "UDP/IPv4 sockets opened", "UDP4Open");
+ SET_SOCKSTATDESC(udp6open, "UDP/IPv6 sockets opened", "UDP6Open");
+ SET_SOCKSTATDESC(tcp4open, "TCP/IPv4 sockets opened", "TCP4Open");
+ SET_SOCKSTATDESC(tcp6open, "TCP/IPv6 sockets opened", "TCP6Open");
+ SET_SOCKSTATDESC(unixopen, "Unix domain sockets opened", "UnixOpen");
+ SET_SOCKSTATDESC(rawopen, "Raw sockets opened", "RawOpen");
+ SET_SOCKSTATDESC(udp4openfail, "UDP/IPv4 socket open failures",
+ "UDP4OpenFail");
+ SET_SOCKSTATDESC(udp6openfail, "UDP/IPv6 socket open failures",
+ "UDP6OpenFail");
+ SET_SOCKSTATDESC(tcp4openfail, "TCP/IPv4 socket open failures",
+ "TCP4OpenFail");
+ SET_SOCKSTATDESC(tcp6openfail, "TCP/IPv6 socket open failures",
+ "TCP6OpenFail");
+ SET_SOCKSTATDESC(unixopenfail, "Unix domain socket open failures",
+ "UnixOpenFail");
+ SET_SOCKSTATDESC(rawopenfail, "Raw socket open failures",
+ "RawOpenFail");
+ SET_SOCKSTATDESC(udp4close, "UDP/IPv4 sockets closed", "UDP4Close");
+ SET_SOCKSTATDESC(udp6close, "UDP/IPv6 sockets closed", "UDP6Close");
+ SET_SOCKSTATDESC(tcp4close, "TCP/IPv4 sockets closed", "TCP4Close");
+ SET_SOCKSTATDESC(tcp6close, "TCP/IPv6 sockets closed", "TCP6Close");
+ SET_SOCKSTATDESC(unixclose, "Unix domain sockets closed", "UnixClose");
+ SET_SOCKSTATDESC(fdwatchclose, "FDwatch sockets closed",
+ "FDWatchClose");
+ SET_SOCKSTATDESC(rawclose, "Raw sockets closed", "RawClose");
+ SET_SOCKSTATDESC(udp4bindfail, "UDP/IPv4 socket bind failures",
+ "UDP4BindFail");
+ SET_SOCKSTATDESC(udp6bindfail, "UDP/IPv6 socket bind failures",
+ "UDP6BindFail");
+ SET_SOCKSTATDESC(tcp4bindfail, "TCP/IPv4 socket bind failures",
+ "TCP4BindFail");
+ SET_SOCKSTATDESC(tcp6bindfail, "TCP/IPv6 socket bind failures",
+ "TCP6BindFail");
+ SET_SOCKSTATDESC(unixbindfail, "Unix domain socket bind failures",
+ "UnixBindFail");
+ SET_SOCKSTATDESC(fdwatchbindfail, "FDwatch socket bind failures",
+ "FdwatchBindFail");
+ SET_SOCKSTATDESC(udp4connectfail, "UDP/IPv4 socket connect failures",
+ "UDP4ConnFail");
+ SET_SOCKSTATDESC(udp6connectfail, "UDP/IPv6 socket connect failures",
+ "UDP6ConnFail");
+ SET_SOCKSTATDESC(tcp4connectfail, "TCP/IPv4 socket connect failures",
+ "TCP4ConnFail");
+ SET_SOCKSTATDESC(tcp6connectfail, "TCP/IPv6 socket connect failures",
+ "TCP6ConnFail");
+ SET_SOCKSTATDESC(unixconnectfail, "Unix domain socket connect failures",
+ "UnixConnFail");
+ SET_SOCKSTATDESC(fdwatchconnectfail, "FDwatch socket connect failures",
+ "FDwatchConnFail");
+ SET_SOCKSTATDESC(udp4connect, "UDP/IPv4 connections established",
+ "UDP4Conn");
+ SET_SOCKSTATDESC(udp6connect, "UDP/IPv6 connections established",
+ "UDP6Conn");
+ SET_SOCKSTATDESC(tcp4connect, "TCP/IPv4 connections established",
+ "TCP4Conn");
+ SET_SOCKSTATDESC(tcp6connect, "TCP/IPv6 connections established",
+ "TCP6Conn");
+ SET_SOCKSTATDESC(unixconnect, "Unix domain connections established",
+ "UnixConn");
+ SET_SOCKSTATDESC(fdwatchconnect,
+ "FDwatch domain connections established",
+ "FDwatchConn");
+ SET_SOCKSTATDESC(tcp4acceptfail, "TCP/IPv4 connection accept failures",
+ "TCP4AcceptFail");
+ SET_SOCKSTATDESC(tcp6acceptfail, "TCP/IPv6 connection accept failures",
+ "TCP6AcceptFail");
+ SET_SOCKSTATDESC(unixacceptfail,
+ "Unix domain connection accept failures",
+ "UnixAcceptFail");
+ SET_SOCKSTATDESC(tcp4accept, "TCP/IPv4 connections accepted",
+ "TCP4Accept");
+ SET_SOCKSTATDESC(tcp6accept, "TCP/IPv6 connections accepted",
+ "TCP6Accept");
+ SET_SOCKSTATDESC(unixaccept, "Unix domain connections accepted",
+ "UnixAccept");
+ SET_SOCKSTATDESC(udp4sendfail, "UDP/IPv4 send errors", "UDP4SendErr");
+ SET_SOCKSTATDESC(udp6sendfail, "UDP/IPv6 send errors", "UDP6SendErr");
+ SET_SOCKSTATDESC(tcp4sendfail, "TCP/IPv4 send errors", "TCP4SendErr");
+ SET_SOCKSTATDESC(tcp6sendfail, "TCP/IPv6 send errors", "TCP6SendErr");
+ SET_SOCKSTATDESC(unixsendfail, "Unix domain send errors",
+ "UnixSendErr");
+ SET_SOCKSTATDESC(fdwatchsendfail, "FDwatch send errors",
+ "FDwatchSendErr");
+ SET_SOCKSTATDESC(udp4recvfail, "UDP/IPv4 recv errors", "UDP4RecvErr");
+ SET_SOCKSTATDESC(udp6recvfail, "UDP/IPv6 recv errors", "UDP6RecvErr");
+ SET_SOCKSTATDESC(tcp4recvfail, "TCP/IPv4 recv errors", "TCP4RecvErr");
+ SET_SOCKSTATDESC(tcp6recvfail, "TCP/IPv6 recv errors", "TCP6RecvErr");
+ SET_SOCKSTATDESC(unixrecvfail, "Unix domain recv errors",
+ "UnixRecvErr");
+ SET_SOCKSTATDESC(fdwatchrecvfail, "FDwatch recv errors",
+ "FDwatchRecvErr");
+ SET_SOCKSTATDESC(rawrecvfail, "Raw recv errors", "RawRecvErr");
+ SET_SOCKSTATDESC(udp4active, "UDP/IPv4 sockets active", "UDP4Active");
+ SET_SOCKSTATDESC(udp6active, "UDP/IPv6 sockets active", "UDP6Active");
+ SET_SOCKSTATDESC(tcp4active, "TCP/IPv4 sockets active", "TCP4Active");
+ SET_SOCKSTATDESC(tcp6active, "TCP/IPv6 sockets active", "TCP6Active");
+ SET_SOCKSTATDESC(unixactive, "Unix domain sockets active",
+ "UnixActive");
+ SET_SOCKSTATDESC(rawactive, "Raw sockets active", "RawActive");
+ INSIST(i == isc_sockstatscounter_max);
+
+ /* Initialize DNSSEC statistics */
+ for (i = 0; i < dns_dnssecstats_max; i++)
+ dnssecstats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_dnssecstats_max; i++)
+ dnssecstats_xmldesc[i] = NULL;
+#endif
+
+#define SET_DNSSECSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_dnssecstats_ ## counterid, \
+ dns_dnssecstats_max, \
+ desc, dnssecstats_desc, \
+ xmldesc, dnssecstats_xmldesc); \
+ dnssecstats_index[i++] = dns_dnssecstats_ ## counterid; \
+ } while (0)
+
+ i = 0;
+ SET_DNSSECSTATDESC(asis, "dnssec validation success with signer "
+ "\"as is\"", "DNSSECasis");
+ SET_DNSSECSTATDESC(downcase, "dnssec validation success with signer "
+ "lower cased", "DNSSECdowncase");
+ SET_DNSSECSTATDESC(wildcard, "dnssec validation of wildcard signature",
+ "DNSSECwild");
+ SET_DNSSECSTATDESC(fail, "dnssec validation failures", "DNSSECfail");
+ INSIST(i == dns_dnssecstats_max);
+
+ /* Initialize dnstap statistics */
+ for (i = 0; i < dns_dnstapcounter_max; i++)
+ dnstapstats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ for (i = 0; i < dns_dnstapcounter_max; i++)
+ dnstapstats_xmldesc[i] = NULL;
+#endif
+
+#define SET_DNSTAPSTATDESC(counterid, desc, xmldesc) \
+ do { \
+ set_desc(dns_dnstapcounter_ ## counterid, \
+ dns_dnstapcounter_max, \
+ desc, dnstapstats_desc, \
+ xmldesc, dnstapstats_xmldesc); \
+ dnstapstats_index[i++] = dns_dnstapcounter_ ## counterid; \
+ } while (0)
+ i = 0;
+ SET_DNSTAPSTATDESC(success, "dnstap messges written", "DNSTAPsuccess");
+ SET_DNSTAPSTATDESC(drop, "dnstap messages dropped", "DNSTAPdropped");
+ INSIST(i == dns_dnstapcounter_max);
+
+ /* 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_adbstats_max; i++)
+ INSIST(adbstats_desc[i] != NULL);
+ for (i = 0; i < dns_zonestatscounter_max; i++)
+ INSIST(zonestats_desc[i] != NULL);
+ for (i = 0; i < isc_sockstatscounter_max; i++)
+ INSIST(sockstats_desc[i] != NULL);
+ for (i = 0; i < dns_dnssecstats_max; i++)
+ INSIST(dnssecstats_desc[i] != NULL);
+ for (i = 0; i < dns_dnstapcounter_max; i++)
+ INSIST(dnstapstats_desc[i] != NULL);
+#if defined(EXTENDED_STATS)
+ 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_adbstats_max; i++)
+ INSIST(adbstats_xmldesc[i] != NULL);
+ for (i = 0; i < dns_zonestatscounter_max; i++)
+ INSIST(zonestats_xmldesc[i] != NULL);
+ for (i = 0; i < isc_sockstatscounter_max; i++)
+ INSIST(sockstats_xmldesc[i] != NULL);
+ for (i = 0; i < dns_dnssecstats_max; i++)
+ INSIST(dnssecstats_xmldesc[i] != NULL);
+ for (i = 0; i < dns_dnstapcounter_max; i++)
+ INSIST(dnstapstats_xmldesc[i] != NULL);
+#endif
+
+ /* Initialize traffic size statistics */
+ for (i = 0; i < dns_sizecounter_in_max; i++) {
+ udpinsizestats_desc[i] = NULL;
+ tcpinsizestats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ udpinsizestats_xmldesc[i] = NULL;
+ tcpinsizestats_xmldesc[i] = NULL;
+#endif
+ }
+ for (i = 0; i < dns_sizecounter_out_max; i++) {
+ udpoutsizestats_desc[i] = NULL;
+ tcpoutsizestats_desc[i] = NULL;
+#if defined(EXTENDED_STATS)
+ udpoutsizestats_xmldesc[i] = NULL;
+ tcpoutsizestats_xmldesc[i] = NULL;
+#endif
+ }
+
+#define SET_SIZESTATDESC(counterid, desc, xmldesc, inout) \
+ do { \
+ set_desc(dns_sizecounter_ ## inout ## _ ## counterid, \
+ dns_sizecounter_ ## inout ## _max, \
+ desc, udp ## inout ## sizestats_desc, \
+ xmldesc, udp ## inout ## sizestats_xmldesc); \
+ set_desc(dns_sizecounter_ ## inout ## _ ## counterid, \
+ dns_sizecounter_ ## inout ## _max, \
+ desc, tcp ## inout ## sizestats_desc, \
+ xmldesc, tcp ## inout ## sizestats_xmldesc); \
+ udp ## inout ## sizestats_index[i] = dns_sizecounter_ ## inout ## _ ## counterid; \
+ tcp ## inout ## sizestats_index[i] = dns_sizecounter_ ## inout ## _ ## counterid; \
+ i++; \
+ } while (0)
+
+ i = 0;
+ SET_SIZESTATDESC(0, "requests received 0-15 bytes", "0-15", in);
+ SET_SIZESTATDESC(16, "requests received 16-31 bytes", "16-31", in);
+ SET_SIZESTATDESC(32, "requests received 32-47 bytes", "32-47", in);
+ SET_SIZESTATDESC(48, "requests received 48-63 bytes", "48-63", in);
+ SET_SIZESTATDESC(64, "requests received 64-79 bytes", "64-79", in);
+ SET_SIZESTATDESC(80, "requests received 80-95 bytes", "80-95", in);
+ SET_SIZESTATDESC(96, "requests received 96-111 bytes", "96-111", in);
+ SET_SIZESTATDESC(112, "requests received 112-127 bytes", "112-127", in);
+ SET_SIZESTATDESC(128, "requests received 128-143 bytes", "128-143", in);
+ SET_SIZESTATDESC(144, "requests received 144-159 bytes", "144-159", in);
+ SET_SIZESTATDESC(160, "requests received 160-175 bytes", "160-175", in);
+ SET_SIZESTATDESC(176, "requests received 176-191 bytes", "176-191", in);
+ SET_SIZESTATDESC(192, "requests received 192-207 bytes", "192-207", in);
+ SET_SIZESTATDESC(208, "requests received 208-223 bytes", "208-223", in);
+ SET_SIZESTATDESC(224, "requests received 224-239 bytes", "224-239", in);
+ SET_SIZESTATDESC(240, "requests received 240-255 bytes", "240-255", in);
+ SET_SIZESTATDESC(256, "requests received 256-271 bytes", "256-271", in);
+ SET_SIZESTATDESC(272, "requests received 272-287 bytes", "272-287", in);
+ SET_SIZESTATDESC(288, "requests received 288+ bytes", "288+", in);
+ INSIST(i == dns_sizecounter_in_max);
+
+ i = 0;
+ SET_SIZESTATDESC(0, "responses sent 0-15 bytes", "0-15", out);
+ SET_SIZESTATDESC(16, "responses sent 16-31 bytes", "16-31", out);
+ SET_SIZESTATDESC(32, "responses sent 32-47 bytes", "32-47", out);
+ SET_SIZESTATDESC(48, "responses sent 48-63 bytes", "48-63", out);
+ SET_SIZESTATDESC(64, "responses sent 64-79 bytes", "64-79", out);
+ SET_SIZESTATDESC(80, "responses sent 80-95 bytes", "80-95", out);
+ SET_SIZESTATDESC(96, "responses sent 96-111 bytes", "96-111", out);
+ SET_SIZESTATDESC(112, "responses sent 112-127 bytes", "112-127", out);
+ SET_SIZESTATDESC(128, "responses sent 128-143 bytes", "128-143", out);
+ SET_SIZESTATDESC(144, "responses sent 144-159 bytes", "144-159", out);
+ SET_SIZESTATDESC(160, "responses sent 160-175 bytes", "160-175", out);
+ SET_SIZESTATDESC(176, "responses sent 176-191 bytes", "176-191", out);
+ SET_SIZESTATDESC(192, "responses sent 192-207 bytes", "192-207", out);
+ SET_SIZESTATDESC(208, "responses sent 208-223 bytes", "208-223", out);
+ SET_SIZESTATDESC(224, "responses sent 224-239 bytes", "224-239", out);
+ SET_SIZESTATDESC(240, "responses sent 240-255 bytes", "240-255", out);
+ SET_SIZESTATDESC(256, "responses sent 256-271 bytes", "256-271", out);
+ SET_SIZESTATDESC(272, "responses sent 272-287 bytes", "272-287", out);
+ SET_SIZESTATDESC(288, "responses sent 288-303 bytes", "288-303", out);
+ SET_SIZESTATDESC(304, "responses sent 304-319 bytes", "304-319", out);
+ SET_SIZESTATDESC(320, "responses sent 320-335 bytes", "320-335", out);
+ SET_SIZESTATDESC(336, "responses sent 336-351 bytes", "336-351", out);
+ SET_SIZESTATDESC(352, "responses sent 352-367 bytes", "352-367", out);
+ SET_SIZESTATDESC(368, "responses sent 368-383 bytes", "368-383", out);
+ SET_SIZESTATDESC(384, "responses sent 384-399 bytes", "384-399", out);
+ SET_SIZESTATDESC(400, "responses sent 400-415 bytes", "400-415", out);
+ SET_SIZESTATDESC(416, "responses sent 416-431 bytes", "416-431", out);
+ SET_SIZESTATDESC(432, "responses sent 432-447 bytes", "432-447", out);
+ SET_SIZESTATDESC(448, "responses sent 448-463 bytes", "448-463", out);
+ SET_SIZESTATDESC(464, "responses sent 464-479 bytes", "464-479", out);
+ SET_SIZESTATDESC(480, "responses sent 480-495 bytes", "480-495", out);
+ SET_SIZESTATDESC(496, "responses sent 496-511 bytes", "496-511", out);
+ SET_SIZESTATDESC(512, "responses sent 512-527 bytes", "512-527", out);
+ SET_SIZESTATDESC(528, "responses sent 528-543 bytes", "528-543", out);
+ SET_SIZESTATDESC(544, "responses sent 544-559 bytes", "544-559", out);
+ SET_SIZESTATDESC(560, "responses sent 560-575 bytes", "560-575", out);
+ SET_SIZESTATDESC(576, "responses sent 576-591 bytes", "576-591", out);
+ SET_SIZESTATDESC(592, "responses sent 592-607 bytes", "592-607", out);
+ SET_SIZESTATDESC(608, "responses sent 608-623 bytes", "608-623", out);
+ SET_SIZESTATDESC(624, "responses sent 624-639 bytes", "624-639", out);
+ SET_SIZESTATDESC(640, "responses sent 640-655 bytes", "640-655", out);
+ SET_SIZESTATDESC(656, "responses sent 656-671 bytes", "656-671", out);
+ SET_SIZESTATDESC(672, "responses sent 672-687 bytes", "672-687", out);
+ SET_SIZESTATDESC(688, "responses sent 688-703 bytes", "688-703", out);
+ SET_SIZESTATDESC(704, "responses sent 704-719 bytes", "704-719", out);
+ SET_SIZESTATDESC(720, "responses sent 720-735 bytes", "720-735", out);
+ SET_SIZESTATDESC(736, "responses sent 736-751 bytes", "736-751", out);
+ SET_SIZESTATDESC(752, "responses sent 752-767 bytes", "752-767", out);
+ SET_SIZESTATDESC(768, "responses sent 768-783 bytes", "768-783", out);
+ SET_SIZESTATDESC(784, "responses sent 784-799 bytes", "784-799", out);
+ SET_SIZESTATDESC(800, "responses sent 800-815 bytes", "800-815", out);
+ SET_SIZESTATDESC(816, "responses sent 816-831 bytes", "816-831", out);
+ SET_SIZESTATDESC(832, "responses sent 832-847 bytes", "832-847", out);
+ SET_SIZESTATDESC(848, "responses sent 848-863 bytes", "848-863", out);
+ SET_SIZESTATDESC(864, "responses sent 864-879 bytes", "864-879", out);
+ SET_SIZESTATDESC(880, "responses sent 880-895 bytes", "880-895", out);
+ SET_SIZESTATDESC(896, "responses sent 896-911 bytes", "896-911", out);
+ SET_SIZESTATDESC(912, "responses sent 912-927 bytes", "912-927", out);
+ SET_SIZESTATDESC(928, "responses sent 928-943 bytes", "928-943", out);
+ SET_SIZESTATDESC(944, "responses sent 944-959 bytes", "944-959", out);
+ SET_SIZESTATDESC(960, "responses sent 960-975 bytes", "960-975", out);
+ SET_SIZESTATDESC(976, "responses sent 976-991 bytes", "976-991", out);
+ SET_SIZESTATDESC(992, "responses sent 992-1007 bytes", "992-1007", out);
+ SET_SIZESTATDESC(1008, "responses sent 1008-1023 bytes", "1008-1023", out);
+ SET_SIZESTATDESC(1024, "responses sent 1024-1039 bytes", "1024-1039", out);
+ SET_SIZESTATDESC(1040, "responses sent 1040-1055 bytes", "1040-1055", out);
+ SET_SIZESTATDESC(1056, "responses sent 1056-1071 bytes", "1056-1071", out);
+ SET_SIZESTATDESC(1072, "responses sent 1072-1087 bytes", "1072-1087", out);
+ SET_SIZESTATDESC(1088, "responses sent 1088-1103 bytes", "1088-1103", out);
+ SET_SIZESTATDESC(1104, "responses sent 1104-1119 bytes", "1104-1119", out);
+ SET_SIZESTATDESC(1120, "responses sent 1120-1135 bytes", "1120-1135", out);
+ SET_SIZESTATDESC(1136, "responses sent 1136-1151 bytes", "1136-1151", out);
+ SET_SIZESTATDESC(1152, "responses sent 1152-1167 bytes", "1152-1167", out);
+ SET_SIZESTATDESC(1168, "responses sent 1168-1183 bytes", "1168-1183", out);
+ SET_SIZESTATDESC(1184, "responses sent 1184-1199 bytes", "1184-1199", out);
+ SET_SIZESTATDESC(1200, "responses sent 1200-1215 bytes", "1200-1215", out);
+ SET_SIZESTATDESC(1216, "responses sent 1216-1231 bytes", "1216-1231", out);
+ SET_SIZESTATDESC(1232, "responses sent 1232-1247 bytes", "1232-1247", out);
+ SET_SIZESTATDESC(1248, "responses sent 1248-1263 bytes", "1248-1263", out);
+ SET_SIZESTATDESC(1264, "responses sent 1264-1279 bytes", "1264-1279", out);
+ SET_SIZESTATDESC(1280, "responses sent 1280-1295 bytes", "1280-1295", out);
+ SET_SIZESTATDESC(1296, "responses sent 1296-1311 bytes", "1296-1311", out);
+ SET_SIZESTATDESC(1312, "responses sent 1312-1327 bytes", "1312-1327", out);
+ SET_SIZESTATDESC(1328, "responses sent 1328-1343 bytes", "1328-1343", out);
+ SET_SIZESTATDESC(1344, "responses sent 1344-1359 bytes", "1344-1359", out);
+ SET_SIZESTATDESC(1360, "responses sent 1360-1375 bytes", "1360-1375", out);
+ SET_SIZESTATDESC(1376, "responses sent 1376-1391 bytes", "1376-1391", out);
+ SET_SIZESTATDESC(1392, "responses sent 1392-1407 bytes", "1392-1407", out);
+ SET_SIZESTATDESC(1408, "responses sent 1408-1423 bytes", "1408-1423", out);
+ SET_SIZESTATDESC(1424, "responses sent 1424-1439 bytes", "1424-1439", out);
+ SET_SIZESTATDESC(1440, "responses sent 1440-1455 bytes", "1440-1455", out);
+ SET_SIZESTATDESC(1456, "responses sent 1456-1471 bytes", "1456-1471", out);
+ SET_SIZESTATDESC(1472, "responses sent 1472-1487 bytes", "1472-1487", out);
+ SET_SIZESTATDESC(1488, "responses sent 1488-1503 bytes", "1488-1503", out);
+ SET_SIZESTATDESC(1504, "responses sent 1504-1519 bytes", "1504-1519", out);
+ SET_SIZESTATDESC(1520, "responses sent 1520-1535 bytes", "1520-1535", out);
+ SET_SIZESTATDESC(1536, "responses sent 1536-1551 bytes", "1536-1551", out);
+ SET_SIZESTATDESC(1552, "responses sent 1552-1567 bytes", "1552-1567", out);
+ SET_SIZESTATDESC(1568, "responses sent 1568-1583 bytes", "1568-1583", out);
+ SET_SIZESTATDESC(1584, "responses sent 1584-1599 bytes", "1584-1599", out);
+ SET_SIZESTATDESC(1600, "responses sent 1600-1615 bytes", "1600-1615", out);
+ SET_SIZESTATDESC(1616, "responses sent 1616-1631 bytes", "1616-1631", out);
+ SET_SIZESTATDESC(1632, "responses sent 1632-1647 bytes", "1632-1647", out);
+ SET_SIZESTATDESC(1648, "responses sent 1648-1663 bytes", "1648-1663", out);
+ SET_SIZESTATDESC(1664, "responses sent 1664-1679 bytes", "1664-1679", out);
+ SET_SIZESTATDESC(1680, "responses sent 1680-1695 bytes", "1680-1695", out);
+ SET_SIZESTATDESC(1696, "responses sent 1696-1711 bytes", "1696-1711", out);
+ SET_SIZESTATDESC(1712, "responses sent 1712-1727 bytes", "1712-1727", out);
+ SET_SIZESTATDESC(1728, "responses sent 1728-1743 bytes", "1728-1743", out);
+ SET_SIZESTATDESC(1744, "responses sent 1744-1759 bytes", "1744-1759", out);
+ SET_SIZESTATDESC(1760, "responses sent 1760-1775 bytes", "1760-1775", out);
+ SET_SIZESTATDESC(1776, "responses sent 1776-1791 bytes", "1776-1791", out);
+ SET_SIZESTATDESC(1792, "responses sent 1792-1807 bytes", "1792-1807", out);
+ SET_SIZESTATDESC(1808, "responses sent 1808-1823 bytes", "1808-1823", out);
+ SET_SIZESTATDESC(1824, "responses sent 1824-1839 bytes", "1824-1839", out);
+ SET_SIZESTATDESC(1840, "responses sent 1840-1855 bytes", "1840-1855", out);
+ SET_SIZESTATDESC(1856, "responses sent 1856-1871 bytes", "1856-1871", out);
+ SET_SIZESTATDESC(1872, "responses sent 1872-1887 bytes", "1872-1887", out);
+ SET_SIZESTATDESC(1888, "responses sent 1888-1903 bytes", "1888-1903", out);
+ SET_SIZESTATDESC(1904, "responses sent 1904-1919 bytes", "1904-1919", out);
+ SET_SIZESTATDESC(1920, "responses sent 1920-1935 bytes", "1920-1935", out);
+ SET_SIZESTATDESC(1936, "responses sent 1936-1951 bytes", "1936-1951", out);
+ SET_SIZESTATDESC(1952, "responses sent 1952-1967 bytes", "1952-1967", out);
+ SET_SIZESTATDESC(1968, "responses sent 1968-1983 bytes", "1968-1983", out);
+ SET_SIZESTATDESC(1984, "responses sent 1984-1999 bytes", "1984-1999", out);
+ SET_SIZESTATDESC(2000, "responses sent 2000-2015 bytes", "2000-2015", out);
+ SET_SIZESTATDESC(2016, "responses sent 2016-2031 bytes", "2016-2031", out);
+ SET_SIZESTATDESC(2032, "responses sent 2032-2047 bytes", "2032-2047", out);
+ SET_SIZESTATDESC(2048, "responses sent 2048-2063 bytes", "2048-2063", out);
+ SET_SIZESTATDESC(2064, "responses sent 2064-2079 bytes", "2064-2079", out);
+ SET_SIZESTATDESC(2080, "responses sent 2080-2095 bytes", "2080-2095", out);
+ SET_SIZESTATDESC(2096, "responses sent 2096-2111 bytes", "2096-2111", out);
+ SET_SIZESTATDESC(2112, "responses sent 2112-2127 bytes", "2112-2127", out);
+ SET_SIZESTATDESC(2128, "responses sent 2128-2143 bytes", "2128-2143", out);
+ SET_SIZESTATDESC(2144, "responses sent 2144-2159 bytes", "2144-2159", out);
+ SET_SIZESTATDESC(2160, "responses sent 2160-2175 bytes", "2160-2175", out);
+ SET_SIZESTATDESC(2176, "responses sent 2176-2191 bytes", "2176-2191", out);
+ SET_SIZESTATDESC(2192, "responses sent 2192-2207 bytes", "2192-2207", out);
+ SET_SIZESTATDESC(2208, "responses sent 2208-2223 bytes", "2208-2223", out);
+ SET_SIZESTATDESC(2224, "responses sent 2224-2239 bytes", "2224-2239", out);
+ SET_SIZESTATDESC(2240, "responses sent 2240-2255 bytes", "2240-2255", out);
+ SET_SIZESTATDESC(2256, "responses sent 2256-2271 bytes", "2256-2271", out);
+ SET_SIZESTATDESC(2272, "responses sent 2272-2287 bytes", "2272-2287", out);
+ SET_SIZESTATDESC(2288, "responses sent 2288-2303 bytes", "2288-2303", out);
+ SET_SIZESTATDESC(2304, "responses sent 2304-2319 bytes", "2304-2319", out);
+ SET_SIZESTATDESC(2320, "responses sent 2320-2335 bytes", "2320-2335", out);
+ SET_SIZESTATDESC(2336, "responses sent 2336-2351 bytes", "2336-2351", out);
+ SET_SIZESTATDESC(2352, "responses sent 2352-2367 bytes", "2352-2367", out);
+ SET_SIZESTATDESC(2368, "responses sent 2368-2383 bytes", "2368-2383", out);
+ SET_SIZESTATDESC(2384, "responses sent 2384-2399 bytes", "2384-2399", out);
+ SET_SIZESTATDESC(2400, "responses sent 2400-2415 bytes", "2400-2415", out);
+ SET_SIZESTATDESC(2416, "responses sent 2416-2431 bytes", "2416-2431", out);
+ SET_SIZESTATDESC(2432, "responses sent 2432-2447 bytes", "2432-2447", out);
+ SET_SIZESTATDESC(2448, "responses sent 2448-2463 bytes", "2448-2463", out);
+ SET_SIZESTATDESC(2464, "responses sent 2464-2479 bytes", "2464-2479", out);
+ SET_SIZESTATDESC(2480, "responses sent 2480-2495 bytes", "2480-2495", out);
+ SET_SIZESTATDESC(2496, "responses sent 2496-2511 bytes", "2496-2511", out);
+ SET_SIZESTATDESC(2512, "responses sent 2512-2527 bytes", "2512-2527", out);
+ SET_SIZESTATDESC(2528, "responses sent 2528-2543 bytes", "2528-2543", out);
+ SET_SIZESTATDESC(2544, "responses sent 2544-2559 bytes", "2544-2559", out);
+ SET_SIZESTATDESC(2560, "responses sent 2560-2575 bytes", "2560-2575", out);
+ SET_SIZESTATDESC(2576, "responses sent 2576-2591 bytes", "2576-2591", out);
+ SET_SIZESTATDESC(2592, "responses sent 2592-2607 bytes", "2592-2607", out);
+ SET_SIZESTATDESC(2608, "responses sent 2608-2623 bytes", "2608-2623", out);
+ SET_SIZESTATDESC(2624, "responses sent 2624-2639 bytes", "2624-2639", out);
+ SET_SIZESTATDESC(2640, "responses sent 2640-2655 bytes", "2640-2655", out);
+ SET_SIZESTATDESC(2656, "responses sent 2656-2671 bytes", "2656-2671", out);
+ SET_SIZESTATDESC(2672, "responses sent 2672-2687 bytes", "2672-2687", out);
+ SET_SIZESTATDESC(2688, "responses sent 2688-2703 bytes", "2688-2703", out);
+ SET_SIZESTATDESC(2704, "responses sent 2704-2719 bytes", "2704-2719", out);
+ SET_SIZESTATDESC(2720, "responses sent 2720-2735 bytes", "2720-2735", out);
+ SET_SIZESTATDESC(2736, "responses sent 2736-2751 bytes", "2736-2751", out);
+ SET_SIZESTATDESC(2752, "responses sent 2752-2767 bytes", "2752-2767", out);
+ SET_SIZESTATDESC(2768, "responses sent 2768-2783 bytes", "2768-2783", out);
+ SET_SIZESTATDESC(2784, "responses sent 2784-2799 bytes", "2784-2799", out);
+ SET_SIZESTATDESC(2800, "responses sent 2800-2815 bytes", "2800-2815", out);
+ SET_SIZESTATDESC(2816, "responses sent 2816-2831 bytes", "2816-2831", out);
+ SET_SIZESTATDESC(2832, "responses sent 2832-2847 bytes", "2832-2847", out);
+ SET_SIZESTATDESC(2848, "responses sent 2848-2863 bytes", "2848-2863", out);
+ SET_SIZESTATDESC(2864, "responses sent 2864-2879 bytes", "2864-2879", out);
+ SET_SIZESTATDESC(2880, "responses sent 2880-2895 bytes", "2880-2895", out);
+ SET_SIZESTATDESC(2896, "responses sent 2896-2911 bytes", "2896-2911", out);
+ SET_SIZESTATDESC(2912, "responses sent 2912-2927 bytes", "2912-2927", out);
+ SET_SIZESTATDESC(2928, "responses sent 2928-2943 bytes", "2928-2943", out);
+ SET_SIZESTATDESC(2944, "responses sent 2944-2959 bytes", "2944-2959", out);
+ SET_SIZESTATDESC(2960, "responses sent 2960-2975 bytes", "2960-2975", out);
+ SET_SIZESTATDESC(2976, "responses sent 2976-2991 bytes", "2976-2991", out);
+ SET_SIZESTATDESC(2992, "responses sent 2992-3007 bytes", "2992-3007", out);
+ SET_SIZESTATDESC(3008, "responses sent 3008-3023 bytes", "3008-3023", out);
+ SET_SIZESTATDESC(3024, "responses sent 3024-3039 bytes", "3024-3039", out);
+ SET_SIZESTATDESC(3040, "responses sent 3040-3055 bytes", "3040-3055", out);
+ SET_SIZESTATDESC(3056, "responses sent 3056-3071 bytes", "3056-3071", out);
+ SET_SIZESTATDESC(3072, "responses sent 3072-3087 bytes", "3072-3087", out);
+ SET_SIZESTATDESC(3088, "responses sent 3088-3103 bytes", "3088-3103", out);
+ SET_SIZESTATDESC(3104, "responses sent 3104-3119 bytes", "3104-3119", out);
+ SET_SIZESTATDESC(3120, "responses sent 3120-3135 bytes", "3120-3135", out);
+ SET_SIZESTATDESC(3136, "responses sent 3136-3151 bytes", "3136-3151", out);
+ SET_SIZESTATDESC(3152, "responses sent 3152-3167 bytes", "3152-3167", out);
+ SET_SIZESTATDESC(3168, "responses sent 3168-3183 bytes", "3168-3183", out);
+ SET_SIZESTATDESC(3184, "responses sent 3184-3199 bytes", "3184-3199", out);
+ SET_SIZESTATDESC(3200, "responses sent 3200-3215 bytes", "3200-3215", out);
+ SET_SIZESTATDESC(3216, "responses sent 3216-3231 bytes", "3216-3231", out);
+ SET_SIZESTATDESC(3232, "responses sent 3232-3247 bytes", "3232-3247", out);
+ SET_SIZESTATDESC(3248, "responses sent 3248-3263 bytes", "3248-3263", out);
+ SET_SIZESTATDESC(3264, "responses sent 3264-3279 bytes", "3264-3279", out);
+ SET_SIZESTATDESC(3280, "responses sent 3280-3295 bytes", "3280-3295", out);
+ SET_SIZESTATDESC(3296, "responses sent 3296-3311 bytes", "3296-3311", out);
+ SET_SIZESTATDESC(3312, "responses sent 3312-3327 bytes", "3312-3327", out);
+ SET_SIZESTATDESC(3328, "responses sent 3328-3343 bytes", "3328-3343", out);
+ SET_SIZESTATDESC(3344, "responses sent 3344-3359 bytes", "3344-3359", out);
+ SET_SIZESTATDESC(3360, "responses sent 3360-3375 bytes", "3360-3375", out);
+ SET_SIZESTATDESC(3376, "responses sent 3376-3391 bytes", "3376-3391", out);
+ SET_SIZESTATDESC(3392, "responses sent 3392-3407 bytes", "3392-3407", out);
+ SET_SIZESTATDESC(3408, "responses sent 3408-3423 bytes", "3408-3423", out);
+ SET_SIZESTATDESC(3424, "responses sent 3424-3439 bytes", "3424-3439", out);
+ SET_SIZESTATDESC(3440, "responses sent 3440-3455 bytes", "3440-3455", out);
+ SET_SIZESTATDESC(3456, "responses sent 3456-3471 bytes", "3456-3471", out);
+ SET_SIZESTATDESC(3472, "responses sent 3472-3487 bytes", "3472-3487", out);
+ SET_SIZESTATDESC(3488, "responses sent 3488-3503 bytes", "3488-3503", out);
+ SET_SIZESTATDESC(3504, "responses sent 3504-3519 bytes", "3504-3519", out);
+ SET_SIZESTATDESC(3520, "responses sent 3520-3535 bytes", "3520-3535", out);
+ SET_SIZESTATDESC(3536, "responses sent 3536-3551 bytes", "3536-3551", out);
+ SET_SIZESTATDESC(3552, "responses sent 3552-3567 bytes", "3552-3567", out);
+ SET_SIZESTATDESC(3568, "responses sent 3568-3583 bytes", "3568-3583", out);
+ SET_SIZESTATDESC(3584, "responses sent 3584-3599 bytes", "3584-3599", out);
+ SET_SIZESTATDESC(3600, "responses sent 3600-3615 bytes", "3600-3615", out);
+ SET_SIZESTATDESC(3616, "responses sent 3616-3631 bytes", "3616-3631", out);
+ SET_SIZESTATDESC(3632, "responses sent 3632-3647 bytes", "3632-3647", out);
+ SET_SIZESTATDESC(3648, "responses sent 3648-3663 bytes", "3648-3663", out);
+ SET_SIZESTATDESC(3664, "responses sent 3664-3679 bytes", "3664-3679", out);
+ SET_SIZESTATDESC(3680, "responses sent 3680-3695 bytes", "3680-3695", out);
+ SET_SIZESTATDESC(3696, "responses sent 3696-3711 bytes", "3696-3711", out);
+ SET_SIZESTATDESC(3712, "responses sent 3712-3727 bytes", "3712-3727", out);
+ SET_SIZESTATDESC(3728, "responses sent 3728-3743 bytes", "3728-3743", out);
+ SET_SIZESTATDESC(3744, "responses sent 3744-3759 bytes", "3744-3759", out);
+ SET_SIZESTATDESC(3760, "responses sent 3760-3775 bytes", "3760-3775", out);
+ SET_SIZESTATDESC(3776, "responses sent 3776-3791 bytes", "3776-3791", out);
+ SET_SIZESTATDESC(3792, "responses sent 3792-3807 bytes", "3792-3807", out);
+ SET_SIZESTATDESC(3808, "responses sent 3808-3823 bytes", "3808-3823", out);
+ SET_SIZESTATDESC(3824, "responses sent 3824-3839 bytes", "3824-3839", out);
+ SET_SIZESTATDESC(3840, "responses sent 3840-3855 bytes", "3840-3855", out);
+ SET_SIZESTATDESC(3856, "responses sent 3856-3871 bytes", "3856-3871", out);
+ SET_SIZESTATDESC(3872, "responses sent 3872-3887 bytes", "3872-3887", out);
+ SET_SIZESTATDESC(3888, "responses sent 3888-3903 bytes", "3888-3903", out);
+ SET_SIZESTATDESC(3904, "responses sent 3904-3919 bytes", "3904-3919", out);
+ SET_SIZESTATDESC(3920, "responses sent 3920-3935 bytes", "3920-3935", out);
+ SET_SIZESTATDESC(3936, "responses sent 3936-3951 bytes", "3936-3951", out);
+ SET_SIZESTATDESC(3952, "responses sent 3952-3967 bytes", "3952-3967", out);
+ SET_SIZESTATDESC(3968, "responses sent 3968-3983 bytes", "3968-3983", out);
+ SET_SIZESTATDESC(3984, "responses sent 3984-3999 bytes", "3984-3999", out);
+ SET_SIZESTATDESC(4000, "responses sent 4000-4015 bytes", "4000-4015", out);
+ SET_SIZESTATDESC(4016, "responses sent 4016-4031 bytes", "4016-4031", out);
+ SET_SIZESTATDESC(4032, "responses sent 4032-4047 bytes", "4032-4047", out);
+ SET_SIZESTATDESC(4048, "responses sent 4048-4063 bytes", "4048-4063", out);
+ SET_SIZESTATDESC(4064, "responses sent 4064-4079 bytes", "4064-4079", out);
+ SET_SIZESTATDESC(4080, "responses sent 4080-4095 bytes", "4080-4095", out);
+ SET_SIZESTATDESC(4096, "responses sent 4096+ bytes", "4096+", out);
+ INSIST(i == dns_sizecounter_out_max);
+
+ /* 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_adbstats_max; i++)
+ INSIST(adbstats_desc[i] != NULL);
+ for (i = 0; i < dns_zonestatscounter_max; i++)
+ INSIST(zonestats_desc[i] != NULL);
+ for (i = 0; i < isc_sockstatscounter_max; i++)
+ INSIST(sockstats_desc[i] != NULL);
+ for (i = 0; i < dns_dnssecstats_max; i++)
+ INSIST(dnssecstats_desc[i] != NULL);
+ for (i = 0; i < dns_sizecounter_in_max; i++) {
+ INSIST(udpinsizestats_desc[i] != NULL);
+ INSIST(tcpinsizestats_desc[i] != NULL);
+ }
+ for (i = 0; i < dns_sizecounter_out_max; i++) {
+ INSIST(udpoutsizestats_desc[i] != NULL);
+ INSIST(tcpoutsizestats_desc[i] != NULL);
+ }
+#if defined(EXTENDED_STATS)
+ 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_adbstats_max; i++)
+ INSIST(adbstats_xmldesc[i] != NULL);
+ for (i = 0; i < dns_zonestatscounter_max; i++)
+ INSIST(zonestats_xmldesc[i] != NULL);
+ for (i = 0; i < isc_sockstatscounter_max; i++)
+ INSIST(sockstats_xmldesc[i] != NULL);
+ for (i = 0; i < dns_dnssecstats_max; i++)
+ INSIST(dnssecstats_xmldesc[i] != NULL);
+ for (i = 0; i < dns_sizecounter_in_max; i++) {
+ INSIST(udpinsizestats_xmldesc[i] != NULL);
+ INSIST(tcpinsizestats_xmldesc[i] != NULL);
+ }
+ for (i = 0; i < dns_sizecounter_out_max; i++) {
+ INSIST(udpoutsizestats_xmldesc[i] != NULL);
+ INSIST(tcpoutsizestats_xmldesc[i] != NULL);
+ }
+#endif
+}
+
+/*%
+ * Dump callback functions.
+ */
+static void
+generalstat_dump(isc_statscounter_t counter, uint64_t val, void *arg) {
+ stats_dumparg_t *dumparg = arg;
+
+ REQUIRE(counter < dumparg->ncounters);
+ dumparg->countervalues[counter] = val;
+}
+
+static isc_result_t
+dump_counters(isc_stats_t *stats, isc_statsformat_t type, void *arg,
+ const char *category, const char **desc, int ncounters,
+ int *indices, uint64_t *values, int options)
+{
+ int i, idx;
+ uint64_t value;
+ stats_dumparg_t dumparg;
+ FILE *fp;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+ int xmlrc;
+#endif
+#ifdef HAVE_JSON
+ json_object *job, *cat, *counter;
+#endif
+
+#if !defined(EXTENDED_STATS)
+ UNUSED(category);
+#endif
+
+ dumparg.type = type;
+ dumparg.ncounters = ncounters;
+ dumparg.counterindices = indices;
+ dumparg.countervalues = values;
+
+ memset(values, 0, sizeof(values[0]) * ncounters);
+ isc_stats_dump(stats, generalstat_dump, &dumparg, options);
+
+#ifdef HAVE_JSON
+ cat = job = (json_object *) arg;
+ if (ncounters > 0 && type == isc_statsformat_json) {
+ if (category != NULL) {
+ cat = json_object_new_object();
+ if (cat == NULL)
+ return (ISC_R_NOMEMORY);
+ json_object_object_add(job, category, cat);
+ }
+ }
+#endif
+
+ for (i = 0; i < ncounters; i++) {
+ idx = indices[i];
+ value = values[idx];
+
+ if (value == 0 && (options & ISC_STATSDUMP_VERBOSE) == 0)
+ continue;
+
+ switch (dumparg.type) {
+ case isc_statsformat_file:
+ fp = arg;
+ fprintf(fp, "%20" PRIu64 " %s\n",
+ value, desc[idx]);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = (xmlTextWriterPtr) arg;
+
+ if (category != NULL) {
+ /* <NameOfCategory> */
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR
+ category));
+
+ /* <name> inside category */
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR
+ "name"));
+ TRY0(xmlTextWriterWriteString(writer,
+ ISC_XMLCHAR
+ desc[idx]));
+ TRY0(xmlTextWriterEndElement(writer));
+ /* </name> */
+
+ /* <counter> */
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR
+ "counter"));
+ TRY0(xmlTextWriterWriteFormatString(writer,
+ "%" PRIu64, value));
+
+ TRY0(xmlTextWriterEndElement(writer));
+ /* </counter> */
+ TRY0(xmlTextWriterEndElement(writer));
+ /* </NameOfCategory> */
+
+ } else {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR
+ "counter"));
+ TRY0(xmlTextWriterWriteAttribute(writer,
+ ISC_XMLCHAR
+ "name",
+ ISC_XMLCHAR
+ desc[idx]));
+ TRY0(xmlTextWriterWriteFormatString(writer,
+ "%" PRIu64, value));
+ TRY0(xmlTextWriterEndElement(writer));
+ /* counter */
+ }
+
+#endif
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON
+ counter = json_object_new_int64(value);
+ if (counter == NULL)
+ return (ISC_R_NOMEMORY);
+ json_object_object_add(cat, desc[idx], counter);
+#endif
+ break;
+ }
+ }
+ return (ISC_R_SUCCESS);
+#ifdef HAVE_LIBXML2
+ error:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "failed at dump_counters()");
+ return (ISC_R_FAILURE);
+#endif
+}
+
+static void
+rdtypestat_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
+ char typebuf[64];
+ const char *typestr;
+ stats_dumparg_t *dumparg = arg;
+ FILE *fp;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+ int xmlrc;
+#endif
+#ifdef HAVE_JSON
+ json_object *zoneobj, *obj;
+#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 isc_statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" PRIu64 " %s\n", val, typestr);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR typestr));
+
+ TRY0(xmlTextWriterWriteFormatString(writer,
+ "%" PRIu64,
+ val));
+
+ TRY0(xmlTextWriterEndElement(writer)); /* type */
+#endif
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON
+ zoneobj = (json_object *) dumparg->arg;
+ obj = json_object_new_int64(val);
+ if (obj == NULL)
+ return;
+ json_object_object_add(zoneobj, typestr, obj);
+#endif
+ break;
+ }
+ return;
+#ifdef HAVE_LIBXML2
+ error:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "failed at rdtypestat_dump()");
+ dumparg->result = ISC_R_FAILURE;
+ return;
+#endif
+}
+
+static void
+rdatasetstats_dump(dns_rdatastatstype_t type, uint64_t val, void *arg) {
+ stats_dumparg_t *dumparg = arg;
+ FILE *fp;
+ char typebuf[64];
+ const char *typestr;
+ bool nxrrset = false;
+ bool stale = false;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+ int xmlrc;
+#endif
+#ifdef HAVE_JSON
+ json_object *zoneobj, *obj;
+ char buf[1024];
+#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 = true;
+
+ if ((DNS_RDATASTATSTYPE_ATTR(type) & DNS_RDATASTATSTYPE_ATTR_STALE)
+ != 0)
+ stale = true;
+
+ switch (dumparg->type) {
+ case isc_statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" PRIu64 " %s%s%s\n", val,
+ stale ? "#" : "", nxrrset ? "!" : "", typestr);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "rrset"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "name"));
+ TRY0(xmlTextWriterWriteFormatString(writer, "%s%s%s",
+ stale ? "#" : "",
+ nxrrset ? "!" : "", typestr));
+ TRY0(xmlTextWriterEndElement(writer)); /* name */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteFormatString(writer,
+ "%" PRIu64,
+ val));
+ TRY0(xmlTextWriterEndElement(writer)); /* counter */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* rrset */
+#endif
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON
+ zoneobj = (json_object *) dumparg->arg;
+ snprintf(buf, sizeof(buf), "%s%s%s",
+ stale ? "#" : "", nxrrset ? "!" : "", typestr);
+ obj = json_object_new_int64(val);
+ if (obj == NULL)
+ return;
+ json_object_object_add(zoneobj, buf, obj);
+#endif
+ break;
+ }
+ return;
+#ifdef HAVE_LIBXML2
+ error:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "failed at rdatasetstats_dump()");
+ dumparg->result = ISC_R_FAILURE;
+#endif
+
+}
+
+static void
+opcodestat_dump(dns_opcode_t code, uint64_t val, void *arg) {
+ FILE *fp;
+ isc_buffer_t b;
+ char codebuf[64];
+ stats_dumparg_t *dumparg = arg;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+ int xmlrc;
+#endif
+#ifdef HAVE_JSON
+ json_object *zoneobj, *obj;
+#endif
+
+ isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1);
+ dns_opcode_totext(code, &b);
+ codebuf[isc_buffer_usedlength(&b)] = '\0';
+
+ switch (dumparg->type) {
+ case isc_statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" PRIu64 " %s\n", val, codebuf);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR codebuf ));
+ TRY0(xmlTextWriterWriteFormatString(writer,
+ "%" PRIu64,
+ val));
+ TRY0(xmlTextWriterEndElement(writer)); /* counter */
+#endif
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON
+ zoneobj = (json_object *) dumparg->arg;
+ obj = json_object_new_int64(val);
+ if (obj == NULL)
+ return;
+ json_object_object_add(zoneobj, codebuf, obj);
+#endif
+ break;
+ }
+ return;
+
+#ifdef HAVE_LIBXML2
+ error:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "failed at opcodestat_dump()");
+ dumparg->result = ISC_R_FAILURE;
+ return;
+#endif
+}
+
+static void
+rcodestat_dump(dns_rcode_t code, uint64_t val, void *arg) {
+ FILE *fp;
+ isc_buffer_t b;
+ char codebuf[64];
+ stats_dumparg_t *dumparg = arg;
+#ifdef HAVE_LIBXML2
+ xmlTextWriterPtr writer;
+ int xmlrc;
+#endif
+#ifdef HAVE_JSON
+ json_object *zoneobj, *obj;
+#endif
+
+ isc_buffer_init(&b, codebuf, sizeof(codebuf) - 1);
+ dns_rcode_totext(code, &b);
+ codebuf[isc_buffer_usedlength(&b)] = '\0';
+
+ switch (dumparg->type) {
+ case isc_statsformat_file:
+ fp = dumparg->arg;
+ fprintf(fp, "%20" PRIu64 " %s\n", val, codebuf);
+ break;
+ case isc_statsformat_xml:
+#ifdef HAVE_LIBXML2
+ writer = dumparg->arg;
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counter"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR codebuf ));
+ TRY0(xmlTextWriterWriteFormatString(writer,
+ "%" PRIu64,
+ val));
+ TRY0(xmlTextWriterEndElement(writer)); /* counter */
+#endif
+ break;
+ case isc_statsformat_json:
+#ifdef HAVE_JSON
+ zoneobj = (json_object *) dumparg->arg;
+ obj = json_object_new_int64(val);
+ if (obj == NULL)
+ return;
+ json_object_object_add(zoneobj, codebuf, obj);
+#endif
+ break;
+ }
+ return;
+
+#ifdef HAVE_LIBXML2
+ error:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "failed at rcodestat_dump()");
+ dumparg->result = ISC_R_FAILURE;
+ return;
+#endif
+}
+
+#ifdef HAVE_LIBXML2
+/*
+ * Which statistics to include when rendering to XML
+ */
+#define STATS_XML_STATUS 0x00 /* display only common statistics */
+#define STATS_XML_SERVER 0x01
+#define STATS_XML_ZONES 0x02
+#define STATS_XML_TASKS 0x04
+#define STATS_XML_NET 0x08
+#define STATS_XML_MEM 0x10
+#define STATS_XML_TRAFFIC 0x20
+#define STATS_XML_ALL 0xff
+
+static isc_result_t
+zone_xmlrender(dns_zone_t *zone, void *arg) {
+ isc_result_t result;
+ char buf[1024 + 32]; /* sufficiently large for zone name and class */
+ dns_rdataclass_t rdclass;
+ uint32_t serial;
+ xmlTextWriterPtr writer = arg;
+ isc_stats_t *zonestats;
+ dns_stats_t *rcvquerystats;
+ dns_zonestat_level_t statlevel;
+ uint64_t nsstat_values[dns_nsstatscounter_max];
+ int xmlrc;
+ stats_dumparg_t dumparg;
+ const char *ztype;
+
+ statlevel = dns_zone_getstatlevel(zone);
+ if (statlevel == dns_zonestat_none)
+ return (ISC_R_SUCCESS);
+
+ dumparg.type = isc_statsformat_xml;
+ dumparg.arg = writer;
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "zone"));
+
+ dns_zone_nameonly(zone, buf, sizeof(buf));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR buf));
+
+ rdclass = dns_zone_getclass(zone);
+ dns_rdataclass_format(rdclass, buf, sizeof(buf));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "rdataclass",
+ ISC_XMLCHAR buf));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "type"));
+ ztype = user_zonetype(zone);
+ if (ztype != NULL)
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ztype));
+ else
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "unknown"));
+ TRY0(xmlTextWriterEndElement(writer)); /* type */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "serial"));
+ if (dns_zone_getserial2(zone, &serial) == ISC_R_SUCCESS)
+ TRY0(xmlTextWriterWriteFormatString(writer, "%u", serial));
+ else
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR "-"));
+ TRY0(xmlTextWriterEndElement(writer)); /* serial */
+
+ zonestats = dns_zone_getrequeststats(zone);
+ rcvquerystats = dns_zone_getrcvquerystats(zone);
+ if (statlevel == dns_zonestat_full && zonestats != NULL) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "rcode"));
+
+ result = dump_counters(zonestats, isc_statsformat_xml, writer,
+ NULL, nsstats_xmldesc,
+ dns_nsstatscounter_max, nsstats_index,
+ nsstat_values, ISC_STATSDUMP_VERBOSE);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+ /* counters type="rcode"*/
+ TRY0(xmlTextWriterEndElement(writer));
+ }
+
+ if (statlevel == dns_zonestat_full && rcvquerystats != NULL) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "qtype"));
+
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(rcvquerystats, rdtypestat_dump,
+ &dumparg, 0);
+ if(dumparg.result != ISC_R_SUCCESS)
+ goto error;
+
+ /* counters type="qtype"*/
+ TRY0(xmlTextWriterEndElement(writer));
+ }
+
+ TRY0(xmlTextWriterEndElement(writer)); /* zone */
+
+ return (ISC_R_SUCCESS);
+ error:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "Failed at zone_xmlrender()");
+ return (ISC_R_FAILURE);
+}
+
+static isc_result_t
+generatexml(ns_server_t *server, uint32_t flags,
+ int *buflen, xmlChar **buf)
+{
+ char boottime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ char configtime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ char nowstr[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ isc_time_t now;
+ xmlTextWriterPtr writer = NULL;
+ xmlDocPtr doc = NULL;
+ int xmlrc;
+ dns_view_t *view;
+ stats_dumparg_t dumparg;
+ dns_stats_t *cacherrstats;
+ uint64_t nsstat_values[dns_nsstatscounter_max];
+ uint64_t resstat_values[dns_resstatscounter_max];
+ uint64_t adbstat_values[dns_adbstats_max];
+ uint64_t zonestat_values[dns_zonestatscounter_max];
+ uint64_t sockstat_values[isc_sockstatscounter_max];
+ uint64_t udpinsizestat_values[dns_sizecounter_in_max];
+ uint64_t udpoutsizestat_values[dns_sizecounter_out_max];
+ uint64_t tcpinsizestat_values[dns_sizecounter_in_max];
+ uint64_t tcpoutsizestat_values[dns_sizecounter_out_max];
+#ifdef HAVE_DNSTAP
+ uint64_t dnstapstat_values[dns_dnstapcounter_max];
+#endif
+ isc_result_t result;
+
+ isc_time_now(&now);
+ isc_time_formatISO8601ms(&ns_g_boottime, boottime, sizeof boottime);
+ isc_time_formatISO8601ms(&ns_g_configtime, configtime, sizeof configtime);
+ isc_time_formatISO8601ms(&now, nowstr, sizeof nowstr);
+
+ writer = xmlNewTextWriterDoc(&doc, 0);
+ if (writer == NULL)
+ goto error;
+ 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 "statistics"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "version",
+ ISC_XMLCHAR "3.8"));
+
+ /* Set common fields for statistics dump */
+ dumparg.type = isc_statsformat_xml;
+ dumparg.arg = writer;
+
+ /* Render server information */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "server"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "boot-time"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR boottime));
+ TRY0(xmlTextWriterEndElement(writer)); /* boot-time */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "config-time"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR configtime));
+ TRY0(xmlTextWriterEndElement(writer)); /* config-time */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "current-time"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR nowstr));
+ TRY0(xmlTextWriterEndElement(writer)); /* current-time */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "version"));
+ TRY0(xmlTextWriterWriteString(writer, ISC_XMLCHAR ns_g_version));
+ TRY0(xmlTextWriterEndElement(writer)); /* version */
+
+ if ((flags & STATS_XML_SERVER) != 0) {
+ dumparg.result = ISC_R_SUCCESS;
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "opcode"));
+
+ dns_opcodestats_dump(server->opcodestats, opcodestat_dump,
+ &dumparg, ISC_STATSDUMP_VERBOSE);
+ if (dumparg.result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "rcode"));
+
+ dns_rcodestats_dump(server->rcodestats, rcodestat_dump,
+ &dumparg, ISC_STATSDUMP_VERBOSE);
+ if (dumparg.result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer));
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "qtype"));
+
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(server->rcvquerystats, rdtypestat_dump,
+ &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS)
+ goto error;
+ TRY0(xmlTextWriterEndElement(writer)); /* counters */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "nsstat"));
+
+ result = dump_counters(server->nsstats, isc_statsformat_xml,
+ writer, NULL, nsstats_xmldesc,
+ dns_nsstatscounter_max,
+ nsstats_index, nsstat_values,
+ ISC_STATSDUMP_VERBOSE);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* /nsstat */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "zonestat"));
+
+ result = dump_counters(server->zonestats, isc_statsformat_xml,
+ writer, NULL, zonestats_xmldesc,
+ dns_zonestatscounter_max,
+ zonestats_index, zonestat_values,
+ ISC_STATSDUMP_VERBOSE);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* /zonestat */
+
+ /*
+ * Most of the common resolver statistics entries are 0, so
+ * we don't use the verbose dump here.
+ */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "resstat"));
+ result = dump_counters(server->resolverstats,
+ isc_statsformat_xml, writer,
+ NULL, resstats_xmldesc,
+ dns_resstatscounter_max,
+ resstats_index, resstat_values, 0);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+ TRY0(xmlTextWriterEndElement(writer)); /* resstat */
+
+#ifdef HAVE_DNSTAP
+ if (server->dtenv != NULL) {
+ isc_stats_t *dnstapstats = NULL;
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer,
+ ISC_XMLCHAR "type",
+ ISC_XMLCHAR "dnstap"));
+ dns_dt_getstats(ns_g_server->dtenv, &dnstapstats);
+ result = dump_counters(dnstapstats,
+ isc_statsformat_xml, writer,
+ NULL, dnstapstats_xmldesc,
+ dns_dnstapcounter_max,
+ dnstapstats_index,
+ dnstapstat_values, 0);
+ isc_stats_detach(&dnstapstats);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+ TRY0(xmlTextWriterEndElement(writer)); /* dnstap */
+ }
+#endif
+ }
+
+ if ((flags & STATS_XML_NET) != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "sockstat"));
+
+ result = dump_counters(server->sockstats, isc_statsformat_xml,
+ writer, NULL, sockstats_xmldesc,
+ isc_sockstatscounter_max,
+ sockstats_index, sockstat_values,
+ ISC_STATSDUMP_VERBOSE);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* /sockstat */
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* /server */
+
+ if ((flags & STATS_XML_TRAFFIC) != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "traffic"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ipv4"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "udp"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "request-size"));
+
+ result = dump_counters(server->udpinstats4,
+ isc_statsformat_xml, writer,
+ NULL, udpinsizestats_xmldesc,
+ dns_sizecounter_in_max,
+ udpinsizestats_index,
+ udpinsizestat_values, 0);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "response-size"));
+
+ result = dump_counters(server->udpoutstats4,
+ isc_statsformat_xml, writer,
+ NULL, udpoutsizestats_xmldesc,
+ dns_sizecounter_out_max,
+ udpoutsizestats_index,
+ udpoutsizestat_values, 0);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </udp> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tcp"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "request-size"));
+
+ result = dump_counters(server->tcpinstats4,
+ isc_statsformat_xml, writer,
+ NULL, tcpinsizestats_xmldesc,
+ dns_sizecounter_in_max,
+ tcpinsizestats_index,
+ tcpinsizestat_values, 0);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "response-size"));
+
+ result = dump_counters(server->tcpoutstats4,
+ isc_statsformat_xml, writer,
+ NULL, tcpoutsizestats_xmldesc,
+ dns_sizecounter_out_max,
+ tcpoutsizestats_index,
+ tcpoutsizestat_values, 0);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </tcp> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </ipv4> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "ipv6"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "udp"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "request-size"));
+
+ result = dump_counters(server->udpinstats6,
+ isc_statsformat_xml, writer,
+ NULL, udpinsizestats_xmldesc,
+ dns_sizecounter_in_max,
+ udpinsizestats_index,
+ udpinsizestat_values, 0);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "response-size"));
+
+ result = dump_counters(server->udpoutstats6,
+ isc_statsformat_xml, writer,
+ NULL, udpoutsizestats_xmldesc,
+ dns_sizecounter_out_max,
+ udpoutsizestats_index,
+ udpoutsizestat_values, 0);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </udp> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "tcp"));
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "request-size"));
+
+ result = dump_counters(server->tcpinstats6,
+ isc_statsformat_xml, writer,
+ NULL, tcpinsizestats_xmldesc,
+ dns_sizecounter_in_max,
+ tcpinsizestats_index,
+ tcpinsizestat_values, 0);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "response-size"));
+
+ result = dump_counters(server->tcpoutstats6,
+ isc_statsformat_xml, writer,
+ NULL, tcpoutsizestats_xmldesc,
+ dns_sizecounter_out_max,
+ tcpoutsizestats_index,
+ tcpoutsizestat_values, 0);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+
+ TRY0(xmlTextWriterEndElement(writer)); /* </counters> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </tcp> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </ipv6> */
+ TRY0(xmlTextWriterEndElement(writer)); /* </traffic> */
+ }
+
+ /*
+ * Render views. 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 &&
+ ((flags & (STATS_XML_SERVER | STATS_XML_ZONES)) != 0))
+ {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "view"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "name",
+ ISC_XMLCHAR view->name));
+
+ if ((flags & STATS_XML_ZONES) != 0) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "zones"));
+ result = dns_zt_apply(view->zonetable, true,
+ zone_xmlrender, writer);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+ TRY0(xmlTextWriterEndElement(writer)); /* /zones */
+ }
+
+ if ((flags & STATS_XML_SERVER) == 0) {
+ TRY0(xmlTextWriterEndElement(writer)); /* /view */
+ view = ISC_LIST_NEXT(view, link);
+ continue;
+ }
+
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "resqtype"));
+
+ if (view->resquerystats != NULL) {
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(view->resquerystats,
+ rdtypestat_dump, &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS)
+ goto error;
+ }
+ TRY0(xmlTextWriterEndElement(writer));
+
+ /* <resstats> */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "resstats"));
+ if (view->resstats != NULL) {
+ result = dump_counters(view->resstats,
+ isc_statsformat_xml, writer,
+ NULL, resstats_xmldesc,
+ dns_resstatscounter_max,
+ resstats_index, resstat_values,
+ ISC_STATSDUMP_VERBOSE);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* </resstats> */
+
+ cacherrstats = dns_db_getrrsetstats(view->cachedb);
+ if (cacherrstats != NULL) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "cache"));
+ TRY0(xmlTextWriterWriteAttribute(writer,
+ ISC_XMLCHAR "name",
+ ISC_XMLCHAR
+ dns_cache_getname(view->cache)));
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatasetstats_dump(cacherrstats, rdatasetstats_dump,
+ &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS)
+ goto error;
+ TRY0(xmlTextWriterEndElement(writer)); /* cache */
+ }
+
+ /* <adbstats> */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "adbstat"));
+ if (view->adbstats != NULL) {
+ result = dump_counters(view->adbstats,
+ isc_statsformat_xml, writer,
+ NULL, adbstats_xmldesc,
+ dns_adbstats_max,
+ adbstats_index, adbstat_values,
+ ISC_STATSDUMP_VERBOSE);
+ if (result != ISC_R_SUCCESS)
+ goto error;
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* </adbstats> */
+
+ /* <cachestats> */
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "counters"));
+ TRY0(xmlTextWriterWriteAttribute(writer, ISC_XMLCHAR "type",
+ ISC_XMLCHAR "cachestats"));
+ TRY0(dns_cache_renderxml(view->cache, writer));
+ TRY0(xmlTextWriterEndElement(writer)); /* </cachestats> */
+
+ TRY0(xmlTextWriterEndElement(writer)); /* view */
+
+ view = ISC_LIST_NEXT(view, link);
+ }
+ TRY0(xmlTextWriterEndElement(writer)); /* /views */
+
+ if ((flags & STATS_XML_NET) != 0) {
+ TRY0(xmlTextWriterStartElement(writer,
+ ISC_XMLCHAR "socketmgr"));
+ TRY0(isc_socketmgr_renderxml(ns_g_socketmgr, writer));
+ TRY0(xmlTextWriterEndElement(writer)); /* /socketmgr */
+ }
+
+ if ((flags & STATS_XML_TASKS) != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "taskmgr"));
+ TRY0(isc_taskmgr_renderxml(ns_g_taskmgr, writer));
+ TRY0(xmlTextWriterEndElement(writer)); /* /taskmgr */
+ }
+
+ if ((flags & STATS_XML_MEM) != 0) {
+ TRY0(xmlTextWriterStartElement(writer, ISC_XMLCHAR "memory"));
+ TRY0(isc_mem_renderxml(writer));
+ TRY0(xmlTextWriterEndElement(writer)); /* /memory */
+ }
+
+
+ TRY0(xmlTextWriterEndElement(writer)); /* /statistics */
+ TRY0(xmlTextWriterEndDocument(writer));
+
+ xmlFreeTextWriter(writer);
+
+ xmlDocDumpFormatMemoryEnc(doc, buf, buflen, "UTF-8", 0);
+ if (*buf == NULL)
+ goto error;
+ xmlFreeDoc(doc);
+ return (ISC_R_SUCCESS);
+
+ error:
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_SERVER,
+ ISC_LOG_ERROR, "failed generating XML response");
+ if (writer != NULL)
+ xmlFreeTextWriter(writer);
+ if (doc != NULL)
+ xmlFreeDoc(doc);
+ return (ISC_R_FAILURE);
+}
+
+static void
+wrap_xmlfree(isc_buffer_t *buffer, void *arg) {
+ UNUSED(arg);
+
+ xmlFree(isc_buffer_base(buffer));
+}
+
+static isc_result_t
+render_xml(uint32_t flags, const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ unsigned char *msg = NULL;
+ int msglen;
+ ns_server_t *server = arg;
+ isc_result_t result;
+
+ UNUSED(url);
+ UNUSED(urlinfo);
+ UNUSED(headers);
+ UNUSED(querystring);
+
+ result = generatexml(server, flags, &msglen, &msg);
+
+ if (result == ISC_R_SUCCESS) {
+ *retcode = 200;
+ *retmsg = "OK";
+ *mimetype = "text/xml";
+ isc_buffer_reinit(b, msg, msglen);
+ isc_buffer_add(b, msglen);
+ *freecb = wrap_xmlfree;
+ *freecb_args = NULL;
+ } else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at rendering XML()");
+
+ return (result);
+}
+
+static isc_result_t
+render_xml_all(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_xml(STATS_XML_ALL, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_status(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_xml(STATS_XML_STATUS, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_server(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_xml(STATS_XML_SERVER, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_zones(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_xml(STATS_XML_ZONES, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_net(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_xml(STATS_XML_NET, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_tasks(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_xml(STATS_XML_TASKS, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_mem(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_xml(STATS_XML_MEM, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_xml_traffic(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_xml(STATS_XML_TRAFFIC, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+#endif /* HAVE_LIBXML2 */
+
+#ifdef HAVE_JSON
+/*
+ * Which statistics to include when rendering to JSON
+ */
+#define STATS_JSON_STATUS 0x00 /* display only common statistics */
+#define STATS_JSON_SERVER 0x01
+#define STATS_JSON_ZONES 0x02
+#define STATS_JSON_TASKS 0x04
+#define STATS_JSON_NET 0x08
+#define STATS_JSON_MEM 0x10
+#define STATS_JSON_TRAFFIC 0x20
+#define STATS_JSON_ALL 0xff
+
+#define CHECK(m) do { \
+ result = (m); \
+ if (result != ISC_R_SUCCESS) \
+ goto error; \
+} while (0)
+
+#define CHECKMEM(m) do { \
+ if (m == NULL) { \
+ result = ISC_R_NOMEMORY;\
+ goto error;\
+ } \
+} while(0)
+
+static void
+wrap_jsonfree(isc_buffer_t *buffer, void *arg) {
+ json_object_put(isc_buffer_base(buffer));
+ if (arg != NULL)
+ json_object_put((json_object *) arg);
+}
+
+static json_object *
+addzone(char *name, char *classname, const char *ztype,
+ uint32_t serial, bool add_serial)
+{
+ json_object *node = json_object_new_object();
+
+ if (node == NULL)
+ return (NULL);
+
+ json_object_object_add(node, "name", json_object_new_string(name));
+ json_object_object_add(node, "class",
+ json_object_new_string(classname));
+ if (add_serial)
+ json_object_object_add(node, "serial",
+ json_object_new_int64(serial));
+ if (ztype != NULL)
+ json_object_object_add(node, "type",
+ json_object_new_string(ztype));
+ return (node);
+}
+
+static isc_result_t
+zone_jsonrender(dns_zone_t *zone, void *arg) {
+ isc_result_t result = ISC_R_SUCCESS;
+ char buf[1024 + 32]; /* sufficiently large for zone name and class */
+ char classbuf[64]; /* sufficiently large for class */
+ char *zone_name_only = NULL;
+ char *class_only = NULL;
+ dns_rdataclass_t rdclass;
+ uint32_t serial;
+ uint64_t nsstat_values[dns_nsstatscounter_max];
+ isc_stats_t *zonestats;
+ dns_stats_t *rcvquerystats;
+ json_object *zonearray = (json_object *) arg;
+ json_object *zoneobj = NULL;
+ dns_zonestat_level_t statlevel;
+
+ statlevel = dns_zone_getstatlevel(zone);
+ if (statlevel == dns_zonestat_none)
+ return (ISC_R_SUCCESS);
+
+ dns_zone_nameonly(zone, buf, sizeof(buf));
+ zone_name_only = buf;
+
+ rdclass = dns_zone_getclass(zone);
+ dns_rdataclass_format(rdclass, classbuf, sizeof(classbuf));
+ class_only = classbuf;
+
+ if (dns_zone_getserial2(zone, &serial) != ISC_R_SUCCESS)
+ zoneobj = addzone(zone_name_only, class_only,
+ user_zonetype(zone), 0, false);
+ else
+ zoneobj = addzone(zone_name_only, class_only,
+ user_zonetype(zone), serial, true);
+
+ if (zoneobj == NULL)
+ return (ISC_R_NOMEMORY);
+
+ zonestats = dns_zone_getrequeststats(zone);
+ rcvquerystats = dns_zone_getrcvquerystats(zone);
+ if (statlevel == dns_zonestat_full && zonestats != NULL) {
+ json_object *counters = json_object_new_object();
+ if (counters == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto error;
+ }
+
+ result = dump_counters(zonestats, isc_statsformat_json,
+ counters, NULL, nsstats_xmldesc,
+ dns_nsstatscounter_max, nsstats_index,
+ nsstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(zoneobj, "rcodes", counters);
+ else
+ json_object_put(counters);
+ }
+
+ if (statlevel == dns_zonestat_full && rcvquerystats != NULL) {
+ stats_dumparg_t dumparg;
+ json_object *counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ dumparg.type = isc_statsformat_json;
+ dumparg.arg = counters;
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(rcvquerystats, rdtypestat_dump,
+ &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(zoneobj, "qtypes", counters);
+ else
+ json_object_put(counters);
+ }
+
+ json_object_array_add(zonearray, zoneobj);
+ zoneobj = NULL;
+ result = ISC_R_SUCCESS;
+
+ error:
+ if (zoneobj != NULL)
+ json_object_put(zoneobj);
+ return (result);
+}
+
+static isc_result_t
+generatejson(ns_server_t *server, size_t *msglen,
+ const char **msg, json_object **rootp, uint32_t flags)
+{
+ dns_view_t *view;
+ isc_result_t result = ISC_R_SUCCESS;
+ json_object *bindstats, *viewlist, *counters, *obj;
+ json_object *traffic = NULL;
+ json_object *udpreq4 = NULL, *udpresp4 = NULL;
+ json_object *tcpreq4 = NULL, *tcpresp4 = NULL;
+ json_object *udpreq6 = NULL, *udpresp6 = NULL;
+ json_object *tcpreq6 = NULL, *tcpresp6 = NULL;
+ uint64_t nsstat_values[dns_nsstatscounter_max];
+ uint64_t resstat_values[dns_resstatscounter_max];
+ uint64_t adbstat_values[dns_adbstats_max];
+ uint64_t zonestat_values[dns_zonestatscounter_max];
+ uint64_t sockstat_values[isc_sockstatscounter_max];
+ uint64_t udpinsizestat_values[dns_sizecounter_in_max];
+ uint64_t udpoutsizestat_values[dns_sizecounter_out_max];
+ uint64_t tcpinsizestat_values[dns_sizecounter_in_max];
+ uint64_t tcpoutsizestat_values[dns_sizecounter_out_max];
+#ifdef HAVE_DNSTAP
+ uint64_t dnstapstat_values[dns_dnstapcounter_max];
+#endif
+ stats_dumparg_t dumparg;
+ char boottime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ char configtime[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ char nowstr[sizeof "yyyy-mm-ddThh:mm:ss.sssZ"];
+ isc_time_t now;
+
+ REQUIRE(msglen != NULL);
+ REQUIRE(msg != NULL && *msg == NULL);
+ REQUIRE(rootp == NULL || *rootp == NULL);
+
+ bindstats = json_object_new_object();
+ if (bindstats == NULL)
+ return (ISC_R_NOMEMORY);
+
+ /*
+ * These statistics are included no matter which URL we use.
+ */
+ obj = json_object_new_string("1.2");
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "json-stats-version", obj);
+
+ isc_time_now(&now);
+ isc_time_formatISO8601ms(&ns_g_boottime,
+ boottime, sizeof(boottime));
+ isc_time_formatISO8601ms(&ns_g_configtime,
+ configtime, sizeof configtime);
+ isc_time_formatISO8601ms(&now, nowstr, sizeof(nowstr));
+
+ obj = json_object_new_string(boottime);
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "boot-time", obj);
+
+ obj = json_object_new_string(configtime);
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "config-time", obj);
+
+ obj = json_object_new_string(nowstr);
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "current-time", obj);
+ obj = json_object_new_string(ns_g_version);
+ CHECKMEM(obj);
+ json_object_object_add(bindstats, "version", obj);
+
+ if ((flags & STATS_JSON_SERVER) != 0) {
+ /* OPCODE counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.type = isc_statsformat_json;
+ dumparg.arg = counters;
+
+ dns_opcodestats_dump(server->opcodestats,
+ opcodestat_dump, &dumparg,
+ ISC_STATSDUMP_VERBOSE);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(bindstats, "opcodes", counters);
+ else
+ json_object_put(counters);
+
+ /* OPCODE counters */
+ counters = json_object_new_object();
+
+ dumparg.type = isc_statsformat_json;
+ dumparg.arg = counters;
+
+ dns_rcodestats_dump(server->rcodestats, rcodestat_dump,
+ &dumparg, ISC_STATSDUMP_VERBOSE);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(bindstats, "rcodes", counters);
+ else
+ json_object_put(counters);
+
+ /* QTYPE counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ dns_rdatatypestats_dump(server->rcvquerystats,
+ rdtypestat_dump, &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(bindstats, "qtypes", counters);
+ else
+ json_object_put(counters);
+
+ /* server stat counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ result = dump_counters(server->nsstats, isc_statsformat_json,
+ counters, NULL, nsstats_xmldesc,
+ dns_nsstatscounter_max,
+ nsstats_index, nsstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(bindstats, "nsstats", counters);
+ else
+ json_object_put(counters);
+
+ /* zone stat counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ result = dump_counters(server->zonestats, isc_statsformat_json,
+ counters, NULL, zonestats_xmldesc,
+ dns_zonestatscounter_max,
+ zonestats_index, zonestat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(bindstats, "zonestats",
+ counters);
+ else
+ json_object_put(counters);
+
+ /* resolver stat counters */
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ result = dump_counters(server->resolverstats,
+ isc_statsformat_json, counters, NULL,
+ resstats_xmldesc,
+ dns_resstatscounter_max,
+ resstats_index, resstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(bindstats, "resstats", counters);
+ else
+ json_object_put(counters);
+
+#ifdef HAVE_DNSTAP
+ /* dnstap stat counters */
+ if (ns_g_server->dtenv != NULL) {
+ isc_stats_t *dnstapstats = NULL;
+ dns_dt_getstats(ns_g_server->dtenv, &dnstapstats);
+ counters = json_object_new_object();
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+ result = dump_counters(dnstapstats,
+ isc_statsformat_json, counters,
+ NULL, dnstapstats_xmldesc,
+ dns_dnstapcounter_max,
+ dnstapstats_index,
+ dnstapstat_values, 0);
+ isc_stats_detach(&dnstapstats);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(bindstats,
+ "dnstapstats",
+ counters);
+ else
+ json_object_put(counters);
+ }
+#endif
+ }
+
+ if ((flags & (STATS_JSON_ZONES | STATS_JSON_SERVER)) != 0) {
+ viewlist = json_object_new_object();
+ CHECKMEM(viewlist);
+
+ json_object_object_add(bindstats, "views", viewlist);
+
+ view = ISC_LIST_HEAD(server->viewlist);
+ while (view != NULL) {
+ json_object *za, *v = json_object_new_object();
+
+ CHECKMEM(v);
+ json_object_object_add(viewlist, view->name, v);
+
+ za = json_object_new_array();
+ CHECKMEM(za);
+
+ if ((flags & STATS_JSON_ZONES) != 0) {
+ result = dns_zt_apply(view->zonetable, true,
+ zone_jsonrender, za);
+ if (result != ISC_R_SUCCESS) {
+ goto error;
+ }
+ }
+
+ if (json_object_array_length(za) != 0)
+ json_object_object_add(v, "zones", za);
+ else
+ json_object_put(za);
+
+ if ((flags & STATS_JSON_SERVER) != 0) {
+ json_object *res;
+ dns_stats_t *dstats;
+ isc_stats_t *istats;
+
+ res = json_object_new_object();
+ CHECKMEM(res);
+ json_object_object_add(v, "resolver", res);
+
+ istats = view->resstats;
+ if (istats != NULL) {
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ result = dump_counters(istats,
+ isc_statsformat_json,
+ counters, NULL,
+ resstats_xmldesc,
+ dns_resstatscounter_max,
+ resstats_index,
+ resstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ result = dumparg.result;
+ goto error;
+ }
+
+ json_object_object_add(res, "stats",
+ counters);
+ }
+
+ dstats = view->resquerystats;
+ if (dstats != NULL) {
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ dumparg.arg = counters;
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatatypestats_dump(dstats,
+ rdtypestat_dump,
+ &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ result = dumparg.result;
+ goto error;
+ }
+
+ json_object_object_add(res, "qtypes",
+ counters);
+ }
+
+ dstats = dns_db_getrrsetstats(view->cachedb);
+ if (dstats != NULL) {
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ dumparg.arg = counters;
+ dumparg.result = ISC_R_SUCCESS;
+ dns_rdatasetstats_dump(dstats,
+ rdatasetstats_dump,
+ &dumparg, 0);
+ if (dumparg.result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ result = dumparg.result;
+ goto error;
+ }
+
+ json_object_object_add(res,
+ "cache",
+ counters);
+ }
+
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ result = dns_cache_renderjson(view->cache,
+ counters);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ json_object_object_add(res, "cachestats",
+ counters);
+
+ istats = view->adbstats;
+ if (istats != NULL) {
+ counters = json_object_new_object();
+ CHECKMEM(counters);
+
+ result = dump_counters(istats,
+ isc_statsformat_json,
+ counters, NULL,
+ adbstats_xmldesc,
+ dns_adbstats_max,
+ adbstats_index,
+ adbstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ result = dumparg.result;
+ goto error;
+ }
+
+ json_object_object_add(res, "adb",
+ counters);
+ }
+ }
+
+ view = ISC_LIST_NEXT(view, link);
+ }
+ }
+
+ if ((flags & STATS_JSON_NET) != 0) {
+ /* socket stat counters */
+ json_object *sockets;
+ counters = json_object_new_object();
+
+ dumparg.result = ISC_R_SUCCESS;
+ dumparg.arg = counters;
+
+ result = dump_counters(server->sockstats,
+ isc_statsformat_json, counters,
+ NULL, sockstats_xmldesc,
+ isc_sockstatscounter_max,
+ sockstats_index, sockstat_values, 0);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(counters);
+ goto error;
+ }
+
+ if (json_object_get_object(counters)->count != 0)
+ json_object_object_add(bindstats, "sockstats",
+ counters);
+ else
+ json_object_put(counters);
+
+ sockets = json_object_new_object();
+ CHECKMEM(sockets);
+
+ result = isc_socketmgr_renderjson(ns_g_socketmgr, sockets);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(sockets);
+ goto error;
+ }
+
+ json_object_object_add(bindstats, "socketmgr", sockets);
+ }
+
+ if ((flags & STATS_JSON_TASKS) != 0) {
+ json_object *tasks = json_object_new_object();
+ CHECKMEM(tasks);
+
+ result = isc_taskmgr_renderjson(ns_g_taskmgr, tasks);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(tasks);
+ goto error;
+ }
+
+ json_object_object_add(bindstats, "taskmgr", tasks);
+ }
+
+ if ((flags & STATS_JSON_MEM) != 0) {
+ json_object *memory = json_object_new_object();
+ CHECKMEM(memory);
+
+ result = isc_mem_renderjson(memory);
+ if (result != ISC_R_SUCCESS) {
+ json_object_put(memory);
+ goto error;
+ }
+
+ json_object_object_add(bindstats, "memory", memory);
+ }
+
+ if ((flags & STATS_JSON_TRAFFIC) != 0) {
+
+ traffic = json_object_new_object();
+ CHECKMEM(traffic);
+
+ udpreq4 = json_object_new_object();
+ CHECKMEM(udpreq4);
+
+ udpresp4 = json_object_new_object();
+ CHECKMEM(udpresp4);
+
+ tcpreq4 = json_object_new_object();
+ CHECKMEM(tcpreq4);
+
+ tcpresp4 = json_object_new_object();
+ CHECKMEM(tcpresp4);
+
+ udpreq6 = json_object_new_object();
+ CHECKMEM(udpreq6);
+
+ udpresp6 = json_object_new_object();
+ CHECKMEM(udpresp6);
+
+ tcpreq6 = json_object_new_object();
+ CHECKMEM(tcpreq6);
+
+ tcpresp6 = json_object_new_object();
+ CHECKMEM(tcpresp6);
+
+ CHECK(dump_counters(server->udpinstats4,
+ isc_statsformat_json, udpreq4, NULL,
+ udpinsizestats_xmldesc,
+ dns_sizecounter_in_max,
+ udpinsizestats_index,
+ udpinsizestat_values, 0));
+
+ CHECK(dump_counters(server->udpoutstats4,
+ isc_statsformat_json, udpresp4, NULL,
+ udpoutsizestats_xmldesc,
+ dns_sizecounter_out_max,
+ udpoutsizestats_index,
+ udpoutsizestat_values, 0));
+
+ CHECK(dump_counters(server->tcpinstats4,
+ isc_statsformat_json, tcpreq4, NULL,
+ tcpinsizestats_xmldesc,
+ dns_sizecounter_in_max,
+ tcpinsizestats_index,
+ tcpinsizestat_values, 0));
+
+ CHECK(dump_counters(server->tcpoutstats4,
+ isc_statsformat_json, tcpresp4, NULL,
+ tcpoutsizestats_xmldesc,
+ dns_sizecounter_out_max,
+ tcpoutsizestats_index,
+ tcpoutsizestat_values, 0));
+
+ CHECK(dump_counters(server->udpinstats6,
+ isc_statsformat_json, udpreq6, NULL,
+ udpinsizestats_xmldesc,
+ dns_sizecounter_in_max,
+ udpinsizestats_index,
+ udpinsizestat_values, 0));
+
+ CHECK(dump_counters(server->udpoutstats6,
+ isc_statsformat_json, udpresp6, NULL,
+ udpoutsizestats_xmldesc,
+ dns_sizecounter_out_max,
+ udpoutsizestats_index,
+ udpoutsizestat_values, 0));
+
+ CHECK(dump_counters(server->tcpinstats6,
+ isc_statsformat_json, tcpreq6, NULL,
+ tcpinsizestats_xmldesc,
+ dns_sizecounter_in_max,
+ tcpinsizestats_index,
+ tcpinsizestat_values, 0));
+
+ CHECK(dump_counters(server->tcpoutstats6,
+ isc_statsformat_json, tcpresp6, NULL,
+ tcpoutsizestats_xmldesc,
+ dns_sizecounter_out_max,
+ tcpoutsizestats_index,
+ tcpoutsizestat_values, 0));
+
+ json_object_object_add(traffic,
+ "dns-udp-requests-sizes-received-ipv4",
+ udpreq4);
+ json_object_object_add(traffic,
+ "dns-udp-responses-sizes-sent-ipv4",
+ udpresp4);
+ json_object_object_add(traffic,
+ "dns-tcp-requests-sizes-received-ipv4",
+ tcpreq4);
+ json_object_object_add(traffic,
+ "dns-tcp-responses-sizes-sent-ipv4",
+ tcpresp4);
+ json_object_object_add(traffic,
+ "dns-udp-requests-sizes-received-ipv6",
+ udpreq6);
+ json_object_object_add(traffic,
+ "dns-udp-responses-sizes-sent-ipv6",
+ udpresp6);
+ json_object_object_add(traffic,
+ "dns-tcp-requests-sizes-received-ipv6",
+ tcpreq6);
+ json_object_object_add(traffic,
+ "dns-tcp-responses-sizes-sent-ipv6",
+ tcpresp6);
+ json_object_object_add(bindstats, "traffic", traffic);
+ udpreq4 = NULL;
+ udpresp4 = NULL;
+ tcpreq4 = NULL;
+ tcpresp4 = NULL;
+ udpreq6 = NULL;
+ udpresp6 = NULL;
+ tcpreq6 = NULL;
+ tcpresp6 = NULL;
+ traffic = NULL;
+ }
+
+ *msg = json_object_to_json_string_ext(bindstats,
+ JSON_C_TO_STRING_PRETTY);
+ *msglen = strlen(*msg);
+
+ if (rootp != NULL) {
+ *rootp = bindstats;
+ bindstats = NULL;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ error:
+ if (udpreq4 != NULL)
+ json_object_put(udpreq4);
+ if (udpresp4 != NULL)
+ json_object_put(udpresp4);
+ if (tcpreq4 != NULL)
+ json_object_put(tcpreq4);
+ if (tcpresp4 != NULL)
+ json_object_put(tcpresp4);
+ if (udpreq6 != NULL)
+ json_object_put(udpreq6);
+ if (udpresp6 != NULL)
+ json_object_put(udpresp6);
+ if (tcpreq6 != NULL)
+ json_object_put(tcpreq6);
+ if (tcpresp6 != NULL)
+ json_object_put(tcpresp6);
+ if (traffic != NULL)
+ json_object_put(traffic);
+ if (bindstats != NULL)
+ json_object_put(bindstats);
+
+ return (result);
+}
+
+static isc_result_t
+render_json(uint32_t flags,
+ const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers,
+ void *arg, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ isc_result_t result;
+ json_object *bindstats = NULL;
+ ns_server_t *server = arg;
+ const char *msg = NULL;
+ size_t msglen = 0;
+ char *p;
+
+ UNUSED(url);
+ UNUSED(urlinfo);
+ UNUSED(headers);
+ UNUSED(querystring);
+
+ result = generatejson(server, &msglen, &msg, &bindstats, flags);
+ if (result == ISC_R_SUCCESS) {
+ *retcode = 200;
+ *retmsg = "OK";
+ *mimetype = "application/json";
+ DE_CONST(msg, p);
+ isc_buffer_reinit(b, p, msglen);
+ isc_buffer_add(b, msglen);
+ *freecb = wrap_jsonfree;
+ *freecb_args = bindstats;
+ } else
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed at rendering JSON()");
+
+ return (result);
+}
+
+static isc_result_t
+render_json_all(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_json(STATS_JSON_ALL, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_status(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_json(STATS_JSON_STATUS, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_server(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_json(STATS_JSON_SERVER, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_zones(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_json(STATS_JSON_ZONES, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_mem(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_json(STATS_JSON_MEM, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_tasks(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_json(STATS_JSON_TASKS, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_net(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_json(STATS_JSON_NET, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+static isc_result_t
+render_json_traffic(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers, void *arg,
+ unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ return (render_json(STATS_JSON_TRAFFIC, url, urlinfo,
+ querystring, headers, arg,
+ retcode, retmsg, mimetype, b,
+ freecb, freecb_args));
+}
+
+#endif /* HAVE_JSON */
+
+static isc_result_t
+render_xsl(const char *url, isc_httpdurl_t *urlinfo,
+ const char *querystring, const char *headers,
+ void *args, unsigned int *retcode, const char **retmsg,
+ const char **mimetype, isc_buffer_t *b,
+ isc_httpdfree_t **freecb, void **freecb_args)
+{
+ isc_result_t result;
+
+ UNUSED(url);
+ UNUSED(querystring);
+ UNUSED(args);
+
+ *freecb = NULL;
+ *freecb_args = NULL;
+ *mimetype = "text/xslt+xml";
+
+ if (urlinfo->isstatic) {
+ isc_time_t when;
+ char *p = strcasestr(headers, "If-Modified-Since: ");
+
+ if (p != NULL) {
+ time_t t1, t2;
+ p += strlen("If-Modified-Since: ");
+ result = isc_time_parsehttptimestamp(p, &when);
+ if (result != ISC_R_SUCCESS)
+ goto send;
+
+ result = isc_time_secondsastimet(&when, &t1);
+ if (result != ISC_R_SUCCESS)
+ goto send;
+
+ result = isc_time_secondsastimet(&urlinfo->loadtime,
+ &t2);
+ if (result != ISC_R_SUCCESS)
+ goto send;
+
+ if (t1 < t2)
+ goto send;
+
+ *retcode = 304;
+ *retmsg = "Not modified";
+ return (ISC_R_SUCCESS);
+ }
+ }
+
+ send:
+ *retcode = 200;
+ *retmsg = "OK";
+ isc_buffer_reinit(b, xslmsg, strlen(xslmsg));
+ isc_buffer_add(b, strlen(xslmsg));
+
+ 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 bool
+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 (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 (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, 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_xml_all, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml",
+ render_xml_all, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v3",
+ render_xml_all, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v3/status",
+ render_xml_status, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v3/server",
+ render_xml_server, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v3/zones",
+ render_xml_zones, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v3/net",
+ render_xml_net, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v3/tasks",
+ render_xml_tasks, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v3/mem",
+ render_xml_mem, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/xml/v3/traffic",
+ render_xml_traffic, server);
+#endif
+#ifdef HAVE_JSON
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json",
+ render_json_all, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json/v1",
+ render_json_all, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json/v1/status",
+ render_json_status, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json/v1/server",
+ render_json_server, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json/v1/zones",
+ render_json_zones, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json/v1/tasks",
+ render_json_tasks, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json/v1/net",
+ render_json_net, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json/v1/mem",
+ render_json_mem, server);
+ isc_httpdmgr_addurl(listener->httpdmgr, "/json/v1/traffic",
+ render_json_traffic, server);
+#endif
+ isc_httpdmgr_addurl2(listener->httpdmgr, "/bind9.xsl", true,
+ 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 EXTENDED_STATS
+ 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 and/or JSON library");
+#else /* EXTENDED_STATS */
+#ifndef HAVE_LIBXML2
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "statistics-channels: XML library missing, "
+ "only JSON stats will be available");
+#endif /* !HAVE_LIBXML2 */
+#ifndef HAVE_JSON
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
+ "statistics-channels: JSON library missing, "
+ "only XML stats will be available");
+#endif /* !HAVE_JSON */
+#endif /* EXTENDED_STATS */
+
+ 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;
+ uint64_t nsstat_values[dns_nsstatscounter_max];
+ uint64_t resstat_values[dns_resstatscounter_max];
+ uint64_t adbstat_values[dns_adbstats_max];
+ uint64_t zonestat_values[dns_zonestatscounter_max];
+ uint64_t sockstat_values[isc_sockstatscounter_max];
+
+ RUNTIME_CHECK(isc_once_do(&once, init_desc) == ISC_R_SUCCESS);
+
+ /* Set common fields */
+ dumparg.type = isc_statsformat_file;
+ dumparg.arg = fp;
+
+ 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 Rcodes ++\n");
+ dns_rcodestats_dump(server->rcodestats, rcodestat_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");
+ (void) dump_counters(server->nsstats, isc_statsformat_file, fp, NULL,
+ nsstats_desc, dns_nsstatscounter_max,
+ nsstats_index, nsstat_values, 0);
+
+ fprintf(fp, "++ Zone Maintenance Statistics ++\n");
+ (void) dump_counters(server->zonestats, isc_statsformat_file, fp, NULL,
+ zonestats_desc, dns_zonestatscounter_max,
+ zonestats_index, zonestat_values, 0);
+
+ fprintf(fp, "++ Resolver Statistics ++\n");
+ fprintf(fp, "[Common]\n");
+ (void) dump_counters(server->resolverstats, isc_statsformat_file, fp,
+ NULL, resstats_desc, dns_resstatscounter_max,
+ resstats_index, resstat_values, 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);
+ (void) dump_counters(view->resstats, isc_statsformat_file, fp,
+ NULL, resstats_desc,
+ dns_resstatscounter_max, resstats_index,
+ resstat_values, 0);
+ }
+
+ fprintf(fp, "++ Cache Statistics ++\n");
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (strcmp(view->name, "_default") == 0)
+ fprintf(fp, "[View: default]\n");
+ else
+ fprintf(fp, "[View: %s (Cache: %s)]\n", view->name,
+ dns_cache_getname(view->cache));
+ /*
+ * Avoid dumping redundant statistics when the cache is shared.
+ */
+ if (dns_view_iscacheshared(view))
+ continue;
+ dns_cache_dumpstats(view->cache, fp);
+ }
+
+ fprintf(fp, "++ Cache DB RRsets ++\n");
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ dns_stats_t *cacherrstats;
+
+ cacherrstats = dns_db_getrrsetstats(view->cachedb);
+ if (cacherrstats == NULL)
+ continue;
+ if (strcmp(view->name, "_default") == 0)
+ fprintf(fp, "[View: default]\n");
+ else
+ fprintf(fp, "[View: %s (Cache: %s)]\n", view->name,
+ dns_cache_getname(view->cache));
+ if (dns_view_iscacheshared(view)) {
+ /*
+ * Avoid dumping redundant statistics when the cache is
+ * shared.
+ */
+ continue;
+ }
+ dns_rdatasetstats_dump(cacherrstats, rdatasetstats_dump,
+ &dumparg, 0);
+ }
+
+ fprintf(fp, "++ ADB stats ++\n");
+ for (view = ISC_LIST_HEAD(server->viewlist);
+ view != NULL;
+ view = ISC_LIST_NEXT(view, link)) {
+ if (view->adbstats == NULL)
+ continue;
+ if (strcmp(view->name, "_default") == 0)
+ fprintf(fp, "[View: default]\n");
+ else
+ fprintf(fp, "[View: %s]\n", view->name);
+ (void) dump_counters(view->adbstats, isc_statsformat_file, fp,
+ NULL, adbstats_desc, dns_adbstats_max,
+ adbstats_index, adbstat_values, 0);
+ }
+
+ fprintf(fp, "++ Socket I/O Statistics ++\n");
+ (void) dump_counters(server->sockstats, isc_statsformat_file, fp, NULL,
+ sockstats_desc, isc_sockstatscounter_max,
+ sockstats_index, sockstat_values, 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)
+ {
+ isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL) {
+ char zonename[DNS_NAME_FORMATSIZE];
+
+ view = dns_zone_getview(zone);
+ if (view == NULL)
+ continue;
+
+ dns_name_format(dns_zone_getorigin(zone),
+ zonename, sizeof(zonename));
+ fprintf(fp, "[%s", zonename);
+ if (strcmp(view->name, "_default") != 0)
+ fprintf(fp, " (view: %s)", view->name);
+ fprintf(fp, "]\n");
+
+ (void) dump_counters(zonestats, isc_statsformat_file,
+ fp, NULL, nsstats_desc,
+ dns_nsstatscounter_max,
+ nsstats_index, nsstat_values, 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..6cbf132
--- /dev/null
+++ b/bin/named/tkeyconf.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+
+#include <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;
+ 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_constinit(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ name = dns_fixedname_initname(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, 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_constinit(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ name = dns_fixedname_initname(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, 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_constinit(&b, s, strlen(s));
+ isc_buffer_add(&b, strlen(s));
+ name = dns_fixedname_initname(&fname);
+ RETERR(dns_name_fromtext(name, &b, dns_rootname, 0, NULL));
+ RETERR(dst_gssapi_acquirecred(name, false, &tctx->gsscred));
+ }
+
+ obj = NULL;
+ result = cfg_map_get(options, "tkey-gssapi-keytab", &obj);
+ if (result == ISC_R_SUCCESS) {
+ s = cfg_obj_asstring(obj);
+ tctx->gssapi_keytab = isc_mem_strdup(mctx, s);
+ if (tctx->gssapi_keytab == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failure;
+ }
+ }
+
+ *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..3426f92
--- /dev/null
+++ b/bin/named/tsigconf.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+/*! \file */
+
+#include <config.h>
+
+#include <inttypes.h>
+
+#include <isc/base64.h>
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/string.h>
+#include <isc/util.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;
+ 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_constinit(&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,
+ DNS_NAME_DOWNCASE, &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,
+ 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;
+
+ REQUIRE(ringp != NULL && *ringp == NULL);
+
+ 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_detach(&ring);
+ return (result);
+}
diff --git a/bin/named/unix/Makefile.in b/bin/named/unix/Makefile.in
new file mode 100644
index 0000000..30075e6
--- /dev/null
+++ b/bin/named/unix/Makefile.in
@@ -0,0 +1,29 @@
+# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# See the COPYRIGHT file distributed with this work for additional
+# information regarding copyright ownership.
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+top_srcdir = @top_srcdir@
+
+@BIND9_MAKE_INCLUDES@
+
+CINCLUDES = -I${srcdir}/include -I${srcdir}/../include \
+ ${ISCCFG_INCLUDES} ${ISCCC_INCLUDES} \
+ ${DNS_INCLUDES} ${ISC_INCLUDES} @DST_OPENSSL_INC@
+
+CDEFINES = @CRYPTO@
+CWARNINGS =
+
+OBJS = os.@O@ dlz_dlopen_driver.@O@
+
+SRCS = os.c dlz_dlopen_driver.c
+
+TARGETS = ${OBJS}
+
+@BIND9_MAKE_RULES@
diff --git a/bin/named/unix/dlz_dlopen_driver.c b/bin/named/unix/dlz_dlopen_driver.c
new file mode 100644
index 0000000..f4dc47c
--- /dev/null
+++ b/bin/named/unix/dlz_dlopen_driver.c
@@ -0,0 +1,633 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <config.h>
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include <dns/log.h>
+#include <dns/result.h>
+#include <dns/dlz_dlopen.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/dlz_dlopen_driver.h>
+
+#ifdef ISC_DLZ_DLOPEN
+static dns_sdlzimplementation_t *dlz_dlopen = NULL;
+
+
+typedef struct dlopen_data {
+ isc_mem_t *mctx;
+ char *dl_path;
+ char *dlzname;
+ void *dl_handle;
+ void *dbdata;
+ unsigned int flags;
+ isc_mutex_t lock;
+ int version;
+ bool in_configure;
+
+ dlz_dlopen_version_t *dlz_version;
+ dlz_dlopen_create_t *dlz_create;
+ dlz_dlopen_findzonedb_t *dlz_findzonedb;
+ dlz_dlopen_lookup_t *dlz_lookup;
+ dlz_dlopen_authority_t *dlz_authority;
+ dlz_dlopen_allnodes_t *dlz_allnodes;
+ dlz_dlopen_allowzonexfr_t *dlz_allowzonexfr;
+ dlz_dlopen_newversion_t *dlz_newversion;
+ dlz_dlopen_closeversion_t *dlz_closeversion;
+ dlz_dlopen_configure_t *dlz_configure;
+ dlz_dlopen_ssumatch_t *dlz_ssumatch;
+ dlz_dlopen_addrdataset_t *dlz_addrdataset;
+ dlz_dlopen_subrdataset_t *dlz_subrdataset;
+ dlz_dlopen_delrdataset_t *dlz_delrdataset;
+ dlz_dlopen_destroy_t *dlz_destroy;
+} dlopen_data_t;
+
+/* Modules can choose whether they are lock-safe or not. */
+#define MAYBE_LOCK(cd) \
+ do { \
+ if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
+ cd->in_configure == false) \
+ LOCK(&cd->lock); \
+ } while (0)
+
+#define MAYBE_UNLOCK(cd) \
+ do { \
+ if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
+ cd->in_configure == false) \
+ UNLOCK(&cd->lock); \
+ } while (0)
+
+/*
+ * Log a message at the given level.
+ */
+static void dlopen_log(int level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(level),
+ fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * SDLZ methods
+ */
+
+static isc_result_t
+dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
+ dns_sdlzallnodes_t *allnodes)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_allnodes == NULL) {
+ return (ISC_R_NOPERM);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_allnodes(zone, cd->dbdata, allnodes);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+
+static isc_result_t
+dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+ const char *client)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+
+ if (cd->dlz_allowzonexfr == NULL) {
+ return (ISC_R_NOPERM);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_allowzonexfr(cd->dbdata, name, client);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+static isc_result_t
+dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata,
+ dns_sdlzlookup_t *lookup)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_authority == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_authority(zone, cd->dbdata, lookup);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+static isc_result_t
+dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_findzonedb(cd->dbdata, name, methods, clientinfo);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+
+static isc_result_t
+dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg,
+ void *dbdata, dns_sdlzlookup_t *lookup,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_lookup(zone, name, cd->dbdata, lookup,
+ methods, clientinfo);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+/*
+ * Load a symbol from the library
+ */
+static void *
+dl_load_symbol(dlopen_data_t *cd, const char *symbol, bool mandatory) {
+ void *ptr = dlsym(cd->dl_handle, symbol);
+ if (ptr == NULL && mandatory) {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen: library '%s' is missing "
+ "required symbol '%s'", cd->dl_path, symbol);
+ }
+ return (ptr);
+}
+
+/*
+ * Called at startup for each dlopen zone in named.conf
+ */
+static isc_result_t
+dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
+ void *driverarg, void **dbdata)
+{
+ dlopen_data_t *cd;
+ isc_mem_t *mctx = NULL;
+ isc_result_t result = ISC_R_FAILURE;
+ int dlopen_flags = 0;
+
+ UNUSED(driverarg);
+
+ if (argc < 2) {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen driver for '%s' needs a path to "
+ "the shared library", dlzname);
+ return (ISC_R_FAILURE);
+ }
+
+ result = isc_mem_create(0, 0, &mctx);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+
+ cd = isc_mem_get(mctx, sizeof(*cd));
+ if (cd == NULL) {
+ isc_mem_destroy(&mctx);
+ return (ISC_R_NOMEMORY);
+ }
+ memset(cd, 0, sizeof(*cd));
+
+ cd->mctx = mctx;
+
+ cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
+ if (cd->dl_path == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failed;
+ }
+
+ cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
+ if (cd->dlzname == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failed;
+ }
+
+ /* Initialize the lock */
+ result = isc_mutex_init(&cd->lock);
+ if (result != ISC_R_SUCCESS)
+ goto failed;
+
+ /* Open the library */
+ dlopen_flags = RTLD_NOW|RTLD_GLOBAL;
+
+#ifdef RTLD_DEEPBIND
+ /*
+ * If RTLD_DEEPBIND is available then use it. This can avoid
+ * issues with a module using a different version of a system
+ * library than one that bind9 uses. For example, bind9 may link
+ * to MIT kerberos, but the module may use Heimdal. If we don't
+ * use RTLD_DEEPBIND then we could end up with Heimdal functions
+ * calling MIT functions, which leads to bizarre results (usually
+ * a segfault).
+ */
+ dlopen_flags |= RTLD_DEEPBIND;
+#endif
+
+ cd->dl_handle = dlopen(cd->dl_path, dlopen_flags);
+ if (cd->dl_handle == NULL) {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen failed to open library '%s' - %s",
+ cd->dl_path, dlerror());
+ result = ISC_R_FAILURE;
+ goto failed;
+ }
+
+ /* Find the symbols */
+ cd->dlz_version = (dlz_dlopen_version_t *)
+ dl_load_symbol(cd, "dlz_version", true);
+ cd->dlz_create = (dlz_dlopen_create_t *)
+ dl_load_symbol(cd, "dlz_create", true);
+ cd->dlz_lookup = (dlz_dlopen_lookup_t *)
+ dl_load_symbol(cd, "dlz_lookup", true);
+ cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)
+ dl_load_symbol(cd, "dlz_findzonedb", true);
+
+ if (cd->dlz_create == NULL ||
+ cd->dlz_version == NULL ||
+ cd->dlz_lookup == NULL ||
+ cd->dlz_findzonedb == NULL)
+ {
+ /* We're missing a required symbol */
+ result = ISC_R_FAILURE;
+ goto failed;
+ }
+
+ cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)
+ dl_load_symbol(cd, "dlz_allowzonexfr", false);
+ cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)
+ dl_load_symbol(cd, "dlz_allnodes",
+ (cd->dlz_allowzonexfr != NULL));
+ cd->dlz_authority = (dlz_dlopen_authority_t *)
+ dl_load_symbol(cd, "dlz_authority", false);
+ cd->dlz_newversion = (dlz_dlopen_newversion_t *)
+ dl_load_symbol(cd, "dlz_newversion", false);
+ cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)
+ dl_load_symbol(cd, "dlz_closeversion",
+ (cd->dlz_newversion != NULL));
+ cd->dlz_configure = (dlz_dlopen_configure_t *)
+ dl_load_symbol(cd, "dlz_configure", false);
+ cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)
+ dl_load_symbol(cd, "dlz_ssumatch", false);
+ cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)
+ dl_load_symbol(cd, "dlz_addrdataset", false);
+ cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)
+ dl_load_symbol(cd, "dlz_subrdataset", false);
+ cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)
+ dl_load_symbol(cd, "dlz_delrdataset", false);
+ cd->dlz_destroy = (dlz_dlopen_destroy_t *)
+ dl_load_symbol(cd, "dlz_destroy", false);
+
+ /* Check the version of the API is the same */
+ cd->version = cd->dlz_version(&cd->flags);
+ if (cd->version < (DLZ_DLOPEN_VERSION - DLZ_DLOPEN_AGE) ||
+ cd->version > DLZ_DLOPEN_VERSION)
+ {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen: %s: incorrect driver API version %d, "
+ "requires %d",
+ cd->dl_path, cd->version, DLZ_DLOPEN_VERSION);
+ result = ISC_R_FAILURE;
+ goto failed;
+ }
+
+ /*
+ * Call the library's create function. Note that this is an
+ * extended version of dlz create, with the addition of
+ * named function pointers for helper functions that the
+ * driver will need. This avoids the need for the backend to
+ * link the BIND9 libraries
+ */
+ MAYBE_LOCK(cd);
+ result = cd->dlz_create(dlzname, argc-1, argv+1,
+ &cd->dbdata,
+ "log", dlopen_log,
+ "putrr", dns_sdlz_putrr,
+ "putnamedrr", dns_sdlz_putnamedrr,
+ "writeable_zone", dns_dlz_writeablezone,
+ NULL);
+ MAYBE_UNLOCK(cd);
+ if (result != ISC_R_SUCCESS)
+ goto failed;
+
+ *dbdata = cd;
+
+ return (ISC_R_SUCCESS);
+
+failed:
+ dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
+ if (cd->dl_path != NULL)
+ isc_mem_free(mctx, cd->dl_path);
+ if (cd->dlzname != NULL)
+ isc_mem_free(mctx, cd->dlzname);
+ if (dlopen_flags != 0)
+ (void) isc_mutex_destroy(&cd->lock);
+#ifdef HAVE_DLCLOSE
+ if (cd->dl_handle)
+ dlclose(cd->dl_handle);
+#endif
+ isc_mem_put(mctx, cd, sizeof(*cd));
+ isc_mem_destroy(&mctx);
+ return (result);
+}
+
+/*
+ * Called when bind is shutting down
+ */
+static void
+dlopen_dlz_destroy(void *driverarg, void *dbdata) {
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_mem_t *mctx;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_destroy) {
+ MAYBE_LOCK(cd);
+ cd->dlz_destroy(cd->dbdata);
+ MAYBE_UNLOCK(cd);
+ }
+
+ if (cd->dl_path)
+ isc_mem_free(cd->mctx, cd->dl_path);
+ if (cd->dlzname)
+ isc_mem_free(cd->mctx, cd->dlzname);
+
+#ifdef HAVE_DLCLOSE
+ if (cd->dl_handle)
+ dlclose(cd->dl_handle);
+#endif
+
+ (void) isc_mutex_destroy(&cd->lock);
+
+ mctx = cd->mctx;
+ isc_mem_put(mctx, cd, sizeof(*cd));
+ isc_mem_destroy(&mctx);
+}
+
+/*
+ * Called to start a transaction
+ */
+static isc_result_t
+dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
+ void **versionp)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_newversion == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_newversion(zone, cd->dbdata, versionp);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+/*
+ * Called to end a transaction
+ */
+static void
+dlopen_dlz_closeversion(const char *zone, bool commit,
+ void *driverarg, void *dbdata, void **versionp)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_newversion == NULL) {
+ *versionp = NULL;
+ return;
+ }
+
+ MAYBE_LOCK(cd);
+ cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
+ MAYBE_UNLOCK(cd);
+}
+
+/*
+ * Called on startup to configure any writeable zones
+ */
+static isc_result_t
+dlopen_dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb,
+ void *driverarg, void *dbdata)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_configure == NULL)
+ return (ISC_R_SUCCESS);
+
+ MAYBE_LOCK(cd);
+ cd->in_configure = true;
+ result = cd->dlz_configure(view, dlzdb, cd->dbdata);
+ cd->in_configure = false;
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+
+/*
+ * Check for authority to change a name.
+ */
+static bool
+dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
+ const char *type, const char *key, uint32_t keydatalen,
+ unsigned char *keydata, void *driverarg, void *dbdata)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ bool ret;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_ssumatch == NULL)
+ return (false);
+
+ MAYBE_LOCK(cd);
+ ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
+ keydata, cd->dbdata);
+ MAYBE_UNLOCK(cd);
+
+ return (ret);
+}
+
+
+/*
+ * Add an rdataset.
+ */
+static isc_result_t
+dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
+ void *driverarg, void *dbdata, void *version)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_addrdataset == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+/*
+ * Subtract an rdataset.
+ */
+static isc_result_t
+dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
+ void *driverarg, void *dbdata, void *version)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_subrdataset == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+/*
+ * Delete a rdataset.
+ */
+static isc_result_t
+dlopen_dlz_delrdataset(const char *name, const char *type,
+ void *driverarg, void *dbdata, void *version)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_delrdataset == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+
+static dns_sdlzmethods_t dlz_dlopen_methods = {
+ dlopen_dlz_create,
+ dlopen_dlz_destroy,
+ dlopen_dlz_findzonedb,
+ dlopen_dlz_lookup,
+ dlopen_dlz_authority,
+ dlopen_dlz_allnodes,
+ dlopen_dlz_allowzonexfr,
+ dlopen_dlz_newversion,
+ dlopen_dlz_closeversion,
+ dlopen_dlz_configure,
+ dlopen_dlz_ssumatch,
+ dlopen_dlz_addrdataset,
+ dlopen_dlz_subrdataset,
+ dlopen_dlz_delrdataset
+};
+#endif
+
+/*
+ * Register driver with BIND
+ */
+isc_result_t
+dlz_dlopen_init(isc_mem_t *mctx) {
+#ifndef ISC_DLZ_DLOPEN
+ UNUSED(mctx);
+ return (ISC_R_NOTIMPLEMENTED);
+#else
+ isc_result_t result;
+
+ dlopen_log(2, "Registering DLZ_dlopen driver");
+
+ result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
+ DNS_SDLZFLAG_RELATIVEOWNER |
+ DNS_SDLZFLAG_RELATIVERDATA |
+ DNS_SDLZFLAG_THREADSAFE,
+ mctx, &dlz_dlopen);
+
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "dns_sdlzregister() failed: %s",
+ isc_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ }
+
+ return (result);
+#endif
+}
+
+
+/*
+ * Unregister the driver
+ */
+void
+dlz_dlopen_clear(void) {
+#ifdef ISC_DLZ_DLOPEN
+ dlopen_log(2, "Unregistering DLZ_dlopen driver");
+ if (dlz_dlopen != NULL)
+ dns_sdlzunregister(&dlz_dlopen);
+#endif
+}
diff --git a/bin/named/unix/include/named/os.h b/bin/named/unix/include/named/os.h
new file mode 100644
index 0000000..5415b83
--- /dev/null
+++ b/bin/named/unix/include/named/os.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef NS_OS_H
+#define NS_OS_H 1
+
+/*! \file */
+
+#include <pwd.h>
+#include <stdbool.h>
+
+#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);
+
+uid_t
+ns_os_uid(void);
+
+void
+ns_os_adjustnofile(void);
+
+void
+ns_os_minprivs(void);
+
+FILE *
+ns_os_openfile(const char *filename, mode_t mode, bool switch_user);
+
+void
+ns_os_writepidfile(const char *filename, bool first_time);
+
+bool
+ns_os_issingleton(const char *filename);
+
+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);
+
+char *
+ns_os_uname(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..809cede
--- /dev/null
+++ b/bin/named/unix/os.c
@@ -0,0 +1,1114 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/*! \file */
+
+#include <config.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <sys/types.h> /* dev_t FreeBSD 2.1 */
+#include <sys/stat.h>
+#ifdef HAVE_UNAME
+#include <sys/utsname.h>
+#endif
+
+#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/globals.h>
+#include <named/main.h>
+#include <named/os.h>
+#ifdef HAVE_LIBSCF
+#include <named/ns_smf_globals.h>
+#endif
+
+static char *pidfile = NULL;
+static char *lockfile = NULL;
+static int devnullfd = -1;
+static int singletonfd = -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 bool done_setuid = false;
+static int dfd[2] = { -1, -1 };
+
+#ifdef HAVE_LINUX_CAPABILITY_H
+
+static bool non_root = false;
+static bool non_root_caps = false;
+
+#ifdef HAVE_SYS_CAPABILITY_H
+#include <sys/capability.h>
+#else
+#ifdef HAVE_LINUX_TYPES_H
+#include <linux/types.h>
+#endif
+/*%
+ * 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 { \
+ cap_flag_value_t curval; \
+ capval = (flag); \
+ 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 { \
+ if (curcaps & (1 << (flag))) { \
+ caps |= (1 << (flag)); \
+ } \
+ } while (0)
+#define INIT_CAP do { caps = 0; } while (0)
+#endif /* HAVE_LIBCAP */
+
+#ifndef HAVE_LIBCAP
+/*%
+ * Store the bitmask representing the permitted capability set in 'capsp'. To
+ * match libcap-enabled behavior, capget() syscall errors are not reported,
+ * they just cause 'capsp' to be set to 0, which effectively prevents any
+ * capability from being subsequently requested.
+ */
+static void
+linux_getpermittedcaps(cap_t *capsp) {
+ struct __user_cap_header_struct caphead;
+ struct __user_cap_data_struct curcaps;
+
+ memset(&caphead, 0, sizeof(caphead));
+ caphead.version = _LINUX_CAPABILITY_VERSION;
+ caphead.pid = 0;
+ memset(&curcaps, 0, sizeof(curcaps));
+ syscall(SYS_capget, &caphead, &curcaps);
+
+ *capsp = curcaps.permitted;
+}
+#endif /* HAVE_LIBCAP */
+
+static void
+linux_initialprivs(void) {
+ cap_t curcaps;
+ cap_t caps;
+#ifdef HAVE_LIBCAP
+ cap_value_t capval;
+ char strbuf[ISC_STRERRORSIZE];
+ int err;
+#else
+ linux_getpermittedcaps(&curcaps);
+#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);
+
+ /*
+ * We need to be able to set the ownership of the containing
+ * directory of the pid file when we create it.
+ */
+ SET_CAP(CAP_CHOWN);
+
+ linux_setcaps(caps);
+
+#ifdef HAVE_LIBCAP
+ FREE_CAP;
+#endif
+}
+
+static void
+linux_minprivs(void) {
+ cap_t curcaps;
+ cap_t caps;
+#ifdef HAVE_LIBCAP
+ cap_value_t capval;
+ char strbuf[ISC_STRERRORSIZE];
+ int err;
+#else
+ linux_getpermittedcaps(&curcaps);
+#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 = true;
+ if (getuid() != 0)
+ non_root = 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 && !ns_g_keepstderr) {
+ (void)close(STDERR_FILENO);
+ (void)dup2(devnullfd, STDERR_FILENO);
+ }
+ }
+}
+
+void
+ns_os_started(void) {
+ char buf = 0;
+
+ /*
+ * Signal to the parent that we started successfully.
+ */
+ if (dfd[0] != -1 && dfd[1] != -1) {
+ if (write(dfd[1], &buf, 1) != 1)
+ ns_main_earlyfatal("unable to signal parent that we "
+ "otherwise started successfully.");
+ 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 bool
+all_digits(const char *s) {
+ if (*s == '\0')
+ return (false);
+ while (*s != '\0') {
+ if (!isdigit((*s)&0xff))
+ return (false);
+ s++;
+ }
+ return (true);
+}
+
+void
+ns_os_chroot(const char *root) {
+ char strbuf[ISC_STRERRORSIZE];
+#ifdef HAVE_LIBSCF
+ ns_smf_chroot = 0;
+#endif
+ if (root != NULL) {
+#ifdef HAVE_CHROOT
+ if (chroot(root) < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("chroot(): %s", strbuf);
+ }
+#else
+ ns_main_earlyfatal("chroot(): disabled");
+#endif
+ 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 = 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
+}
+
+uid_t
+ns_os_uid(void) {
+ if (runas_pw == NULL)
+ return (0);
+ return (runas_pw->pw_uid);
+}
+
+void
+ns_os_adjustnofile(void) {
+#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, mode_t mode, bool 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, mode);
+ else {
+ if (unlink(filename) < 0 && errno != ENOENT)
+ return (-1);
+ fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode);
+ }
+ return (fd);
+}
+
+static void
+cleanup_pidfile(void) {
+ int n;
+ if (pidfile != NULL) {
+ n = unlink(pidfile);
+ if (n == -1 && errno != ENOENT)
+ ns_main_earlywarning("unlink '%s': failed", pidfile);
+ free(pidfile);
+ }
+ pidfile = NULL;
+}
+
+static void
+cleanup_lockfile(void) {
+ if (singletonfd != -1) {
+ close(singletonfd);
+ singletonfd = -1;
+ }
+
+ if (lockfile != NULL) {
+ int n = unlink(lockfile);
+ if (n == -1 && errno != ENOENT)
+ ns_main_earlywarning("unlink '%s': failed", lockfile);
+ free(lockfile);
+ lockfile = NULL;
+ }
+}
+
+/*
+ * Ensure that a directory exists.
+ * NOTE: This function overwrites the '/' characters in 'filename' with
+ * nulls. The caller should copy the filename to a fresh buffer first.
+ */
+static int
+mkdirpath(char *filename, void (*report)(const char *, ...)) {
+ char *slash = strrchr(filename, '/');
+ char strbuf[ISC_STRERRORSIZE];
+ unsigned int mode;
+
+ if (slash != NULL && slash != filename) {
+ struct stat sb;
+ *slash = '\0';
+
+ if (stat(filename, &sb) == -1) {
+ if (errno != ENOENT) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't stat '%s': %s", filename,
+ strbuf);
+ goto error;
+ }
+ if (mkdirpath(filename, report) == -1)
+ goto error;
+ /*
+ * Handle "//", "/./" and "/../" in path.
+ */
+ if (!strcmp(slash + 1, "") ||
+ !strcmp(slash + 1, ".") ||
+ !strcmp(slash + 1, "..")) {
+ *slash = '/';
+ return (0);
+ }
+ mode = S_IRUSR | S_IWUSR | S_IXUSR; /* u=rwx */
+ mode |= S_IRGRP | S_IXGRP; /* g=rx */
+ mode |= S_IROTH | S_IXOTH; /* o=rx */
+ if (mkdir(filename, mode) == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't mkdir '%s': %s", filename,
+ strbuf);
+ goto error;
+ }
+ if (runas_pw != NULL &&
+ chown(filename, runas_pw->pw_uid,
+ runas_pw->pw_gid) == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't chown '%s': %s", filename,
+ strbuf);
+ }
+ }
+ *slash = '/';
+ }
+ return (0);
+
+ error:
+ *slash = '/';
+ return (-1);
+}
+
+static void
+setperms(uid_t uid, gid_t gid) {
+#if defined(HAVE_SETEGID) || defined(HAVE_SETRESGID)
+ char strbuf[ISC_STRERRORSIZE];
+#endif
+#if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID)
+ gid_t oldgid, tmpg;
+#endif
+#if !defined(HAVE_SETEUID) && defined(HAVE_SETRESUID)
+ uid_t olduid, tmpu;
+#endif
+#if defined(HAVE_SETEGID)
+ if (getegid() != gid && setegid(gid) == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("unable to set effective gid to %ld: %s",
+ (long)gid, strbuf);
+ }
+#elif defined(HAVE_SETRESGID)
+ if (getresgid(&tmpg, &oldgid, &tmpg) == -1 || oldgid != gid) {
+ if (setresgid(-1, gid, -1) == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("unable to set effective "
+ "gid to %d: %s", gid, strbuf);
+ }
+ }
+#endif
+
+#if defined(HAVE_SETEUID)
+ if (geteuid() != uid && seteuid(uid) == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("unable to set effective uid to %ld: %s",
+ (long)uid, strbuf);
+ }
+#elif defined(HAVE_SETRESUID)
+ if (getresuid(&tmpu, &olduid, &tmpu) == -1 || olduid != uid) {
+ if (setresuid(-1, uid, -1) == -1) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("unable to set effective "
+ "uid to %d: %s", uid, strbuf);
+ }
+ }
+#endif
+}
+
+FILE *
+ns_os_openfile(const char *filename, mode_t mode, bool switch_user) {
+ char strbuf[ISC_STRERRORSIZE], *f;
+ FILE *fp;
+ int fd;
+
+ /*
+ * Make the containing directory if it doesn't exist.
+ */
+ f = strdup(filename);
+ if (f == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("couldn't strdup() '%s': %s",
+ filename, strbuf);
+ return (NULL);
+ }
+ if (mkdirpath(f, ns_main_earlywarning) == -1) {
+ free(f);
+ return (NULL);
+ }
+ free(f);
+
+ if (switch_user && runas_pw != NULL) {
+#ifndef HAVE_LINUXTHREADS
+ gid_t oldgid = getgid();
+#endif
+ /* Set UID/GID to the one we'll be running with eventually */
+ setperms(runas_pw->pw_uid, runas_pw->pw_gid);
+
+ fd = safe_open(filename, mode, false);
+
+#ifndef HAVE_LINUXTHREADS
+ /* Restore UID/GID to root */
+ setperms(0, oldgid);
+#endif /* HAVE_LINUXTHREADS */
+
+ if (fd == -1) {
+#ifndef HAVE_LINUXTHREADS
+ fd = safe_open(filename, mode, false);
+ if (fd != -1) {
+ ns_main_earlywarning("Required root "
+ "permissions to open "
+ "'%s'.", filename);
+ } else {
+ ns_main_earlywarning("Could not open "
+ "'%s'.", filename);
+ }
+ ns_main_earlywarning("Please check file and "
+ "directory permissions "
+ "or reconfigure the filename.");
+#else /* HAVE_LINUXTHREADS */
+ ns_main_earlywarning("Could not open "
+ "'%s'.", filename);
+ ns_main_earlywarning("Please check file and "
+ "directory permissions "
+ "or reconfigure the filename.");
+#endif /* HAVE_LINUXTHREADS */
+ }
+ } else {
+ fd = safe_open(filename, mode, false);
+ }
+
+ if (fd < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("could not open file '%s': %s",
+ filename, strbuf);
+ return (NULL);
+ }
+
+ fp = fdopen(fd, "w");
+ if (fp == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("could not fdopen() file '%s': %s",
+ filename, strbuf);
+ }
+
+ return (fp);
+}
+
+void
+ns_os_writepidfile(const char *filename, bool first_time) {
+ FILE *fh;
+ 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;
+
+ pidfile = strdup(filename);
+ if (pidfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't strdup() '%s': %s", filename, strbuf);
+ return;
+ }
+
+ fh = ns_os_openfile(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
+ first_time);
+ if (fh == NULL) {
+ cleanup_pidfile();
+ return;
+ }
+#ifdef HAVE_LINUXTHREADS
+ pid = mainpid;
+#else
+ pid = getpid();
+#endif
+ if (fprintf(fh, "%ld\n", (long)pid) < 0) {
+ (*report)("fprintf() to pid file '%s' failed", filename);
+ (void)fclose(fh);
+ cleanup_pidfile();
+ return;
+ }
+ if (fflush(fh) == EOF) {
+ (*report)("fflush() to pid file '%s' failed", filename);
+ (void)fclose(fh);
+ cleanup_pidfile();
+ return;
+ }
+ (void)fclose(fh);
+}
+
+bool
+ns_os_issingleton(const char *filename) {
+ char strbuf[ISC_STRERRORSIZE];
+ struct flock lock;
+
+ if (singletonfd != -1)
+ return (true);
+
+ if (strcasecmp(filename, "none") == 0)
+ return (true);
+
+ /*
+ * Make the containing directory if it doesn't exist.
+ */
+ lockfile = strdup(filename);
+ if (lockfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("couldn't allocate memory for '%s': %s",
+ filename, strbuf);
+ } else {
+ int ret = mkdirpath(lockfile, ns_main_earlywarning);
+ if (ret == -1) {
+ ns_main_earlywarning("couldn't create '%s'", filename);
+ cleanup_lockfile();
+ return (false);
+ }
+ }
+
+ /*
+ * ns_os_openfile() uses safeopen() which removes any existing
+ * files. We can't use that here.
+ */
+ singletonfd = open(filename, O_WRONLY | O_CREAT,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ if (singletonfd == -1) {
+ cleanup_lockfile();
+ return (false);
+ }
+
+ memset(&lock, 0, sizeof(lock));
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 1;
+
+ /* Non-blocking (does not wait for lock) */
+ if (fcntl(singletonfd, F_SETLK, &lock) == -1) {
+ close(singletonfd);
+ singletonfd = -1;
+ return (false);
+ }
+
+ return (true);
+}
+
+void
+ns_os_shutdown(void) {
+ closelog();
+ cleanup_pidfile();
+ cleanup_lockfile();
+}
+
+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 > 0 && n < isc_buffer_availablelength(text))
+ isc_buffer_add(text, n);
+}
+
+void
+ns_os_tzset(void) {
+#ifdef HAVE_TZSET
+ tzset();
+#endif
+}
+
+static char unamebuf[BUFSIZ];
+static char *unamep = NULL;
+
+static void
+getuname(void) {
+#ifdef HAVE_UNAME
+ struct utsname uts;
+
+ memset(&uts, 0, sizeof(uts));
+ if (uname(&uts) < 0) {
+ snprintf(unamebuf, sizeof(unamebuf), "unknown architecture");
+ return;
+ }
+
+ snprintf(unamebuf, sizeof(unamebuf),
+ "%s %s %s %s",
+ uts.sysname, uts.machine, uts.release, uts.version);
+#else
+ snprintf(unamebuf, sizeof(unamebuf), "unknown architecture");
+#endif
+ unamep = unamebuf;
+}
+
+char *
+ns_os_uname(void) {
+ if (unamep == NULL)
+ getuname();
+ return (unamep);
+}
diff --git a/bin/named/update.c b/bin/named/update.c
new file mode 100644
index 0000000..973dda5
--- /dev/null
+++ b/bin/named/update.c
@@ -0,0 +1,3501 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/netaddr.h>
+#include <isc/print.h>
+#include <isc/serial.h>
+#include <isc/stats.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/private.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/tsig.h>
+#include <dns/update.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 attributes other FALSE.
+ */
+#define TCPCLIENT(client) (((client)->attributes & NS_CLIENTATTR_TCP) != 0)
+
+/**************************************************************************/
+
+typedef struct rr rr_t;
+
+struct rr {
+ /* dns_name_t name; */
+ 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;
+};
+
+/*%
+ * 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_name_t *oldname;
+ dns_rdata_t *update_rr;
+ dns_ttl_t update_rr_ttl;
+ bool ignore_add;
+ dns_diff_t del_diff;
+ dns_diff_t add_diff;
+} add_rr_prepare_ctx_t;
+
+/**************************************************************************/
+/*
+ * 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 isc_result_t add_rr_prepare_action(void *data, rr_t *rr);
+
+/**************************************************************************/
+
+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) == 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);
+}
+
+static void
+update_log_cb(void *arg, dns_zone_t *zone, int level, const char *message) {
+ update_log(arg, zone, level, "%s", message);
+}
+
+/*%
+ * Increment updated-related statistics counters.
+ */
+static inline void
+inc_stats(dns_zone_t *zone, isc_statscounter_t counter) {
+ isc_stats_increment(ns_g_server->nsstats, counter);
+
+ if (zone != NULL) {
+ isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL)
+ isc_stats_increment(zonestats, counter);
+ }
+}
+
+/*%
+ * Check if we could have queried for the contents of this zone or
+ * if the zone is potentially updateable.
+ * If the zone can potentially be updated and the check failed then
+ * log a error otherwise we log a informational message.
+ */
+static isc_result_t
+checkqueryacl(ns_client_t *client, dns_acl_t *queryacl, dns_name_t *zonename,
+ dns_acl_t *updateacl, dns_ssutable_t *ssutable)
+{
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+ int level;
+ isc_result_t result;
+
+ result = ns_client_checkaclsilent(client, NULL, queryacl, true);
+ if (result != ISC_R_SUCCESS) {
+ dns_name_format(zonename, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(client->view->rdclass, classbuf,
+ sizeof(classbuf));
+
+ level = (updateacl == NULL && ssutable == NULL) ?
+ ISC_LOG_INFO : ISC_LOG_ERROR;
+
+ ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
+ NS_LOGMODULE_UPDATE, level,
+ "update '%s/%s' denied due to allow-query",
+ namebuf, classbuf);
+ } else if (updateacl == NULL && ssutable == NULL) {
+ dns_name_format(zonename, namebuf, sizeof(namebuf));
+ dns_rdataclass_format(client->view->rdclass, classbuf,
+ sizeof(classbuf));
+
+ result = DNS_R_REFUSED;
+ ns_client_log(client, NS_LOGCATEGORY_UPDATE_SECURITY,
+ NS_LOGMODULE_UPDATE, ISC_LOG_INFO,
+ "update '%s/%s' denied", namebuf, classbuf);
+ }
+ return (result);
+}
+
+/*%
+ * 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, bool slave,
+ bool 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, 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);
+ 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;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+ dns_dbversion_t *oldver = NULL;
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+
+ /*
+ * Only set the clientinfo 'versionp' if the new version is
+ * different from the current version
+ */
+ dns_db_currentversion(db, &oldver);
+ dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
+ dns_db_closeversion(db, &oldver, false);
+
+ node = NULL;
+ result = dns_db_findnodeext(db, name, false, &cm, &ci, &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;
+ dns_clientinfomethods_t cm;
+ dns_clientinfo_t ci;
+ dns_dbversion_t *oldver = NULL;
+ dns_fixedname_t fixed;
+
+ dns_clientinfomethods_init(&cm, ns_client_sourceip);
+
+ /*
+ * Only set the clientinfo 'versionp' if the new version is
+ * different from the current version
+ */
+ dns_db_currentversion(db, &oldver);
+ dns_clientinfo_init(&ci, NULL, (ver != oldver) ? ver : NULL);
+ dns_db_closeversion(db, &oldver, false);
+
+ 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, false, &node);
+ else
+ result = dns_db_findnodeext(db, name, false,
+ &cm, &ci, &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;
+
+ if (rr_action == add_rr_prepare_action) {
+ add_rr_prepare_ctx_t *ctx = rr_action_data;
+
+ ctx->oldname = dns_fixedname_initname(&fixed);
+ dns_name_copy(name, ctx->oldname, NULL);
+ dns_rdataset_getownercase(&rdataset, ctx->oldname);
+ }
+
+ 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 bool 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 true or 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 = true, ISC_R_SUCCESS) : \
+ ((result == ISC_R_SUCCESS) ? \
+ (*exists = 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,
+ bool *exists)
+{
+ isc_result_t result;
+ result = foreach_rr(db, ver, name, type, covers,
+ rrset_exists_action, NULL);
+ RETURN_EXISTENCE_FLAG;
+}
+
+/*%
+ * 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 true.
+ * Otherwise, set it to false.
+ */
+static isc_result_t
+cname_incompatible_rrset_exists(dns_db_t *db, dns_dbversion_t *ver,
+ dns_name_t *name, bool *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,
+ bool *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 arguments 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. */
+ isc_netaddr_t *addr;
+
+ /* Whether the request was sent via TCP. */
+ bool tcp;
+
+ /* The ssu table to check against. */
+ dns_ssutable_t *table;
+
+ /* the key used for TKEY requests */
+ dst_key_t *key;
+} ssu_check_t;
+
+static isc_result_t
+ssu_checkrule(void *data, dns_rdataset_t *rrset) {
+ ssu_check_t *ssuinfo = data;
+ bool 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_checkrules2(ssuinfo->table, ssuinfo->signer,
+ ssuinfo->name, ssuinfo->addr,
+ ssuinfo->tcp, &ns_g_server->aclenv,
+ rrset->type, ssuinfo->key);
+ return (result == true ? ISC_R_SUCCESS : ISC_R_FAILURE);
+}
+
+static bool
+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 *addr, bool tcp, dst_key_t *key)
+{
+ isc_result_t result;
+ ssu_check_t ssuinfo;
+
+ ssuinfo.name = name;
+ ssuinfo.table = ssutable;
+ ssuinfo.signer = signer;
+ ssuinfo.addr = addr;
+ ssuinfo.tcp = tcp;
+ ssuinfo.key = key;
+ result = foreach_rrset(db, ver, name, ssu_checkrule, &ssuinfo);
+ return (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-minimal) "dns_diff_t"
+ * where each tuple 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_casecompare(&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_casecompare(&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, 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 if (type == dns_rdatatype_any) {
+ dns_db_detachnode(db, &node);
+ dns_diff_clear(&trash);
+ return (DNS_R_NXRRSET);
+ } 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 bool
+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) ?
+ true : false);
+}
+
+/*%
+ * Return true iff 'db_rr' is neither a RRSIG nor a NSEC.
+ */
+static bool
+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) ?
+ true : false);
+}
+
+/*%
+ * Return true always.
+ */
+static bool
+true_p(dns_rdata_t *update_rr, dns_rdata_t *db_rr) {
+ UNUSED(update_rr);
+ UNUSED(db_rr);
+ return (true);
+}
+
+/*%
+ * Return true iff the two RRs have identical rdata.
+ */
+static bool
+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_casecompare(update_rr, db_rr) == 0 ?
+ true : 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 simplifies key
+ * rollover by only requiring that the new RRSIG be added.
+ */
+static bool
+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 (false);
+ if (db_rr->type == dns_rdatatype_cname)
+ return (true);
+ if (db_rr->type == dns_rdatatype_dname)
+ return (true);
+ if (db_rr->type == dns_rdatatype_soa)
+ return (true);
+ if (db_rr->type == dns_rdatatype_nsec)
+ return (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 (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 ?
+ true : false);
+ }
+
+ if (db_rr->type == dns_rdatatype_nsec3param) {
+ if (db_rr->length != update_rr->length)
+ return (false);
+ INSIST(db_rr->length >= 4 && update_rr->length >= 4);
+ /*
+ * Replace NSEC3PARAM records that only differ by the
+ * flags field.
+ */
+ if (db_rr->data[0] == update_rr->data[0] &&
+ memcmp(db_rr->data+2, update_rr->data+2,
+ update_rr->length - 2) == 0)
+ return (true);
+ }
+ return (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));
+}
+
+/**************************************************************************/
+
+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;
+ bool equal, case_equal, ttl_equal;
+
+ /*
+ * Are the new and old cases equal?
+ */
+ case_equal = dns_name_caseequal(ctx->name, ctx->oldname);
+
+ /*
+ * Are the ttl's equal?
+ */
+ ttl_equal = rr->ttl == ctx->update_rr_ttl;
+
+ /*
+ * If the update RR is a "duplicate" of a existing RR,
+ * the update should be silently ignored.
+ */
+ equal = !dns_rdata_casecompare(&rr->rdata, ctx->update_rr);
+ if (equal && case_equal && ttl_equal) {
+ ctx->ignore_add = 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->oldname, rr->ttl, &rr->rdata,
+ &tuple));
+ dns_diff_append(&ctx->del_diff, &tuple);
+ return (ISC_R_SUCCESS);
+ }
+
+ /*
+ * If this RR differs in TTL or case from the update RR,
+ * its TTL and case must be adjusted.
+ */
+ if (!ttl_equal || !case_equal) {
+ CHECK(dns_difftuple_create(ctx->del_diff.mctx, DNS_DIFFOP_DEL,
+ ctx->oldname, 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
+update_soa_serial(dns_db_t *db, dns_dbversion_t *ver, dns_diff_t *diff,
+ isc_mem_t *mctx, dns_updatemethod_t method)
+{
+ dns_difftuple_t *deltuple = NULL;
+ dns_difftuple_t *addtuple = NULL;
+ 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);
+ serial = dns_update_soaserial(serial, method);
+ 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 true if the update is legal, 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, bool *ok)
+{
+ uint32_t db_serial;
+ 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 = false;
+ } else {
+ *ok = true;
+ }
+
+ return (ISC_R_SUCCESS);
+
+}
+
+/**************************************************************************/
+/*%
+ * 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, 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, *raw = 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");
+
+ /*
+ * If there is a raw (unsigned) zone associated with this
+ * zone then it processes the UPDATE request.
+ */
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL) {
+ dns_zone_detach(&zone);
+ dns_zone_attach(raw, &zone);
+ dns_zone_detach(&raw);
+ }
+
+ switch(dns_zone_gettype(zone)) {
+ case dns_zone_master:
+ case dns_zone_dlz:
+ /*
+ * 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, true,
+ 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;
+ bool ns_exists;
+ dns_difftuple_t *tupple;
+ dns_diff_t temp_diff;
+
+ dns_diff_init(diff->mctx, &temp_diff);
+
+ for (tupple = ISC_LIST_HEAD(diff->tuples);
+ tupple != NULL;
+ tupple = ISC_LIST_NEXT(tupple, link)) {
+ if (!((tupple->op == DNS_DIFFOP_DEL &&
+ tupple->rdata.type == dns_rdatatype_ns) ||
+ (tupple->op == DNS_DIFFOP_ADD &&
+ tupple->rdata.type == dns_rdatatype_ds)))
+ continue;
+ CHECK(rrset_exists(db, newver, &tupple->name,
+ dns_rdatatype_ns, 0, &ns_exists));
+ if (ns_exists &&
+ !dns_name_equal(&tupple->name, dns_db_origin(db)))
+ continue;
+ CHECK(delete_if(true_p, db, newver, &tupple->name,
+ dns_rdatatype_ds, 0, NULL, &temp_diff));
+ }
+ result = ISC_R_SUCCESS;
+
+ failure:
+ for (tupple = ISC_LIST_HEAD(temp_diff.tuples);
+ tupple != NULL;
+ tupple = ISC_LIST_HEAD(temp_diff.tuples)) {
+ ISC_LIST_UNLINK(temp_diff.tuples, tupple, link);
+ dns_diff_appendminimal(diff, &tupple);
+ }
+ 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;
+ bool ok = true;
+ bool isaddress;
+ isc_result_t result;
+ struct in6_addr addr6;
+ struct in_addr addr;
+ unsigned int options;
+
+ foundname = dns_fixedname_initname(&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 = false;
+ if ((options & DNS_ZONEOPT_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 = true;
+ }
+
+ if (isaddress && (options & DNS_ZONEOPT_CHECKMXFAIL) != 0) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX: '%s': %s",
+ ownerbuf, namebuf,
+ dns_result_totext(DNS_R_MXISADDRESS));
+ ok = 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 = false;
+ } else if (result == DNS_R_CNAME) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "%s/MX '%s' is a CNAME (illegal)",
+ ownerbuf, namebuf);
+ ok = 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 = 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, bool *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, false, &node));
+ else
+ CHECK(dns_db_findnode(db, name, false, &node));
+ result = dns_db_findrdataset(db, node, ver, rdata->type, 0,
+ (isc_stdtime_t) 0, &rdataset, NULL);
+ if (result == ISC_R_NOTFOUND) {
+ *flag = 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_casecompare(&myrdata, rdata))
+ break;
+ }
+ dns_rdataset_disassociate(&rdataset);
+ if (result == ISC_R_SUCCESS) {
+ *flag = true;
+ } else if (result == ISC_R_NOMORE) {
+ *flag = 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, dns_rdatatype_t privatetype,
+ 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);
+ if (result == ISC_R_NOTFOUND)
+ goto try_private;
+ 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;
+
+ dns_rdataset_disassociate(&rdataset);
+
+ try_private:
+ if (privatetype == 0)
+ goto success;
+
+ result = dns_db_findrdataset(db, node, ver, privatetype,
+ 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)) {
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_rdata_t private = DNS_RDATA_INIT;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+
+ dns_rdataset_current(&rdataset, &rdata);
+ if (!dns_nsec3param_fromprivate(&private, &rdata,
+ buf, sizeof(buf)))
+ continue;
+ 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 (node != NULL)
+ dns_db_detachnode(db, &node);
+ if (dns_rdataset_isassociated(&rdataset))
+ dns_rdataset_disassociate(&rdataset);
+ return (result);
+}
+
+/*
+ * Prevent the zone entering a inconsistent 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_difftuple_t *tuple;
+ bool nseconly = false, nsec3 = false;
+ isc_result_t result;
+ unsigned int iterations = 0, max;
+ dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
+
+ /* Scan the tuples for an NSEC-only DNSKEY or an NSEC3PARAM */
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL;
+ tuple = ISC_LIST_NEXT(tuple, link)) {
+ if (tuple->op != DNS_DIFFOP_ADD)
+ continue;
+
+ if (tuple->rdata.type == dns_rdatatype_dnskey) {
+ uint8_t alg;
+ alg = tuple->rdata.data[3];
+ if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 ||
+ alg == DST_ALG_DSA || alg == DST_ALG_ECC) {
+ nseconly = true;
+ break;
+ }
+ } else if (tuple->rdata.type == dns_rdatatype_nsec3param) {
+ nsec3 = true;
+ break;
+ }
+ }
+
+ /* Check existing DB for NSEC-only DNSKEY */
+ if (!nseconly) {
+ result = dns_nsec_nseconly(db, ver, &nseconly);
+
+ /*
+ * An NSEC3PARAM update can proceed without a DNSKEY (it
+ * will trigger a delayed change), so we can ignore
+ * ISC_R_NOTFOUND here.
+ */
+ if (result == ISC_R_NOTFOUND)
+ result = ISC_R_SUCCESS;
+
+ CHECK(result);
+ }
+
+ /* Check existing DB for NSEC3 */
+ if (!nsec3)
+ CHECK(dns_nsec3_activex(db, ver, false,
+ privatetype, &nsec3));
+
+ /* Refuse to allow NSEC3 with NSEC-only keys */
+ if (nseconly && nsec3) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "NSEC only DNSKEYs and NSEC3 chains not allowed");
+ result = DNS_R_REFUSED;
+ goto failure;
+ }
+
+ /* Verify NSEC3 params */
+ CHECK(get_iterations(db, ver, privatetype, &iterations));
+ CHECK(dns_nsec3_maxiterations(db, ver, client->mctx, &max));
+ if (max != 0 && iterations > max) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "too many NSEC3 iterations (%u) for "
+ "weakest DNSKEY (%u)", iterations, max);
+ result = DNS_R_REFUSED;
+ goto failure;
+ }
+
+ failure:
+ return (result);
+}
+
+/*
+ * 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_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 + 1];
+ dns_diff_t temp_diff;
+ dns_diffop_t op;
+ bool flag;
+ dns_name_t *name = dns_zone_getorigin(zone);
+ dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
+ uint32_t ttl = 0;
+ bool ttl_good = false;
+
+ 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);
+ }
+
+ /*
+ * Extract TTL changes pairs, we don't need to convert these to
+ * delayed changes.
+ */
+ for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+ tuple != NULL; tuple = next) {
+ if (tuple->op == DNS_DIFFOP_ADD) {
+ if (!ttl_good) {
+ /*
+ * Any adds here will contain the final
+ * NSEC3PARAM RRset TTL.
+ */
+ ttl = tuple->ttl;
+ ttl_good = true;
+ }
+ /*
+ * Walk the temp_diff list looking for the
+ * corresponding delete.
+ */
+ next = ISC_LIST_HEAD(temp_diff.tuples);
+ while (next != NULL) {
+ unsigned char *next_data = next->rdata.data;
+ unsigned char *tuple_data = tuple->rdata.data;
+ if (next->op == DNS_DIFFOP_DEL &&
+ next->rdata.length == tuple->rdata.length &&
+ !memcmp(next_data, tuple_data,
+ next->rdata.length)) {
+ ISC_LIST_UNLINK(temp_diff.tuples, next,
+ link);
+ ISC_LIST_APPEND(diff->tuples, next,
+ link);
+ break;
+ }
+ next = ISC_LIST_NEXT(next, link);
+ }
+ /*
+ * If we have not found a pair move onto the next
+ * tuple.
+ */
+ if (next == NULL) {
+ next = ISC_LIST_NEXT(tuple, link);
+ continue;
+ }
+ /*
+ * Find the next tuple to be processed before
+ * unlinking then complete moving the pair to 'diff'.
+ */
+ next = ISC_LIST_NEXT(tuple, link);
+ ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+ ISC_LIST_APPEND(diff->tuples, tuple, link);
+ } else
+ next = ISC_LIST_NEXT(tuple, link);
+ }
+
+ /*
+ * Preserve any ongoing changes from a BIND 9.6.x upgrade.
+ *
+ * Any NSEC3PARAM records with flags other than OPTOUT named
+ * in managing and should not be touched so revert such changes
+ * taking into account any TTL change of the NSEC3PARAM RRset.
+ */
+ 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) {
+ /*
+ * If we havn't had any adds then the tuple->ttl must
+ * be the original ttl and should be used for any
+ * future changes.
+ */
+ if (!ttl_good) {
+ ttl = tuple->ttl;
+ ttl_good = true;
+ }
+ op = (tuple->op == DNS_DIFFOP_DEL) ?
+ DNS_DIFFOP_ADD : DNS_DIFFOP_DEL;
+ CHECK(dns_difftuple_create(diff->mctx, op, name,
+ 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);
+ }
+ }
+
+ /*
+ * We now have just the actual changes to the NSEC3PARAM RRset.
+ * Convert the adds to delayed adds and the deletions into delayed
+ * deletions.
+ */
+ for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+ tuple != NULL; tuple = next) {
+ /*
+ * If we havn't had any adds then the tuple->ttl must be the
+ * original ttl and should be used for any future changes.
+ */
+ if (!ttl_good) {
+ ttl = tuple->ttl;
+ ttl_good = true;
+ }
+ if (tuple->op == DNS_DIFFOP_ADD) {
+ bool nseconly = false;
+
+ /*
+ * Look for any deletes which match this ADD ignoring
+ * flags. We don't need to explictly remove them as
+ * they will be removed a side effect of processing
+ * the add.
+ */
+ next = ISC_LIST_HEAD(temp_diff.tuples);
+ while (next != NULL) {
+ unsigned char *next_data = next->rdata.data;
+ unsigned char *tuple_data = tuple->rdata.data;
+ if (next->op != DNS_DIFFOP_DEL ||
+ next->rdata.length != tuple->rdata.length ||
+ next_data[0] != tuple_data[0] ||
+ next_data[2] != tuple_data[2] ||
+ next_data[3] != tuple_data[3] ||
+ memcmp(next_data + 4, tuple_data + 4,
+ tuple->rdata.length - 4)) {
+ next = ISC_LIST_NEXT(next, link);
+ continue;
+ }
+ ISC_LIST_UNLINK(temp_diff.tuples, next, link);
+ ISC_LIST_APPEND(diff->tuples, next, link);
+ next = ISC_LIST_HEAD(temp_diff.tuples);
+ }
+
+ /*
+ * Create a private-type record to signal that
+ * we want a delayed NSEC3 chain add/delete
+ */
+ dns_nsec3param_toprivate(&tuple->rdata, &rdata,
+ privatetype, buf, sizeof(buf));
+ buf[2] |= DNS_NSEC3FLAG_CREATE;
+
+ /*
+ * If the zone is not currently capable of
+ * supporting an NSEC3 chain, then we set the
+ * INITIAL flag to indicate that these parameters
+ * are to be used later.
+ */
+ result = dns_nsec_nseconly(db, ver, &nseconly);
+ if (result == ISC_R_NOTFOUND || nseconly)
+ buf[2] |= DNS_NSEC3FLAG_INITIAL;
+
+ /*
+ * See if this CREATE request already exists.
+ */
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+
+ if (!flag) {
+ CHECK(dns_difftuple_create(diff->mctx,
+ DNS_DIFFOP_ADD,
+ name, 0, &rdata,
+ &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ }
+
+ /*
+ * Remove any existing CREATE request to add an
+ * otherwise indentical chain with a reversed
+ * OPTOUT state.
+ */
+ buf[2] ^= DNS_NSEC3FLAG_OPTOUT;
+ 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));
+ }
+
+ /*
+ * Find the next tuple to be processed and remove the
+ * temporary add record.
+ */
+ next = ISC_LIST_NEXT(tuple, link);
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_DEL,
+ name, 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);
+ } else
+ next = ISC_LIST_NEXT(tuple, link);
+ }
+
+ for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+ tuple != NULL; tuple = next) {
+
+ INSIST(ttl_good);
+
+ next = ISC_LIST_NEXT(tuple, link);
+ /*
+ * See if we already have a REMOVE request in progress.
+ */
+ dns_nsec3param_toprivate(&tuple->rdata, &rdata, privatetype,
+ buf, sizeof(buf));
+
+ buf[2] |= DNS_NSEC3FLAG_REMOVE | DNS_NSEC3FLAG_NONSEC;
+
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+ if (!flag) {
+ buf[2] &= ~DNS_NSEC3FLAG_NONSEC;
+ CHECK(rr_exists(db, ver, name, &rdata, &flag));
+ }
+
+ if (!flag) {
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD,
+ name, 0, &rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, diff));
+ }
+ CHECK(dns_difftuple_create(diff->mctx, DNS_DIFFOP_ADD, name,
+ 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);
+}
+
+static isc_result_t
+rollback_private(dns_db_t *db, dns_rdatatype_t privatetype,
+ dns_dbversion_t *ver, dns_diff_t *diff)
+{
+ dns_diff_t temp_diff;
+ dns_diffop_t op;
+ dns_difftuple_t *tuple, *newtuple = NULL, *next;
+ dns_name_t *name = dns_db_origin(db);
+ isc_mem_t *mctx = diff->mctx;
+ isc_result_t result;
+
+ if (privatetype == 0)
+ return (ISC_R_SUCCESS);
+
+ dns_diff_init(mctx, &temp_diff);
+
+ /*
+ * Extract the changes to be rolled back.
+ */
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL; tuple = next) {
+
+ next = ISC_LIST_NEXT(tuple, link);
+
+ if (tuple->rdata.type != privatetype ||
+ !dns_name_equal(name, &tuple->name))
+ continue;
+
+ /*
+ * Allow records which indicate that a zone has been
+ * signed with a DNSKEY to be removed.
+ */
+ if (tuple->op == DNS_DIFFOP_DEL &&
+ tuple->rdata.length == 5 &&
+ tuple->rdata.data[0] != 0 &&
+ tuple->rdata.data[4] != 0)
+ continue;
+
+ ISC_LIST_UNLINK(diff->tuples, tuple, link);
+ ISC_LIST_PREPEND(temp_diff.tuples, tuple, link);
+ }
+
+ /*
+ * Rollback the changes.
+ */
+ while ((tuple = ISC_LIST_HEAD(temp_diff.tuples)) != NULL) {
+ op = (tuple->op == DNS_DIFFOP_DEL) ?
+ DNS_DIFFOP_ADD : DNS_DIFFOP_DEL;
+ CHECK(dns_difftuple_create(mctx, op, name, tuple->ttl,
+ &tuple->rdata, &newtuple));
+ CHECK(do_one_tuple(&newtuple, db, ver, &temp_diff));
+ }
+ result = ISC_R_SUCCESS;
+
+ failure:
+ dns_diff_clear(&temp_diff);
+ return (result);
+}
+
+/*
+ * 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_rdatatype_t privatetype,
+ dns_dbversion_t *ver, dns_diff_t *diff)
+{
+ dns_difftuple_t *tuple, *newtuple = NULL, *next;
+ dns_rdata_dnskey_t dnskey;
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ bool flag;
+ isc_region_t r;
+ isc_result_t result = ISC_R_SUCCESS;
+ uint16_t keyid;
+ unsigned char buf[5];
+ dns_name_t *name = dns_db_origin(db);
+ dns_diff_t temp_diff;
+
+ dns_diff_init(diff->mctx, &temp_diff);
+
+ /*
+ * Extract the DNSKEY tuples from the list.
+ */
+ for (tuple = ISC_LIST_HEAD(diff->tuples);
+ tuple != NULL; tuple = next) {
+
+ next = ISC_LIST_NEXT(tuple, link);
+
+ if (tuple->rdata.type != dns_rdatatype_dnskey)
+ continue;
+
+ ISC_LIST_UNLINK(diff->tuples, tuple, link);
+ ISC_LIST_APPEND(temp_diff.tuples, tuple, link);
+ }
+
+ /*
+ * Extract TTL changes pairs, we don't need signing records for these.
+ */
+ for (tuple = ISC_LIST_HEAD(temp_diff.tuples);
+ tuple != NULL; tuple = next) {
+ if (tuple->op == DNS_DIFFOP_ADD) {
+ /*
+ * Walk the temp_diff list looking for the
+ * corresponding delete.
+ */
+ next = ISC_LIST_HEAD(temp_diff.tuples);
+ while (next != NULL) {
+ unsigned char *next_data = next->rdata.data;
+ unsigned char *tuple_data = tuple->rdata.data;
+ if (next->op == DNS_DIFFOP_DEL &&
+ dns_name_equal(&tuple->name, &next->name) &&
+ next->rdata.length == tuple->rdata.length &&
+ !memcmp(next_data, tuple_data,
+ next->rdata.length)) {
+ ISC_LIST_UNLINK(temp_diff.tuples, next,
+ link);
+ ISC_LIST_APPEND(diff->tuples, next,
+ link);
+ break;
+ }
+ next = ISC_LIST_NEXT(next, link);
+ }
+ /*
+ * If we have not found a pair move onto the next
+ * tuple.
+ */
+ if (next == NULL) {
+ next = ISC_LIST_NEXT(tuple, link);
+ continue;
+ }
+ /*
+ * Find the next tuple to be processed before
+ * unlinking then complete moving the pair to 'diff'.
+ */
+ next = ISC_LIST_NEXT(tuple, link);
+ ISC_LIST_UNLINK(temp_diff.tuples, tuple, link);
+ ISC_LIST_APPEND(diff->tuples, tuple, link);
+ } else
+ next = ISC_LIST_NEXT(tuple, link);
+ }
+
+ /*
+ * Process the remaining DNSKEY entries.
+ */
+ 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);
+ ISC_LIST_APPEND(diff->tuples, tuple, link);
+
+ result = dns_rdata_tostruct(&tuple->rdata, &dnskey, NULL);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ 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:
+ dns_diff_clear(&temp_diff);
+ return (result);
+}
+
+static bool
+isdnssec(dns_db_t *db, dns_dbversion_t *ver, dns_rdatatype_t privatetype) {
+ isc_result_t result;
+ bool build_nsec, build_nsec3;
+
+ if (dns_db_issecure(db))
+ return (true);
+
+ result = dns_private_chains(db, ver, privatetype,
+ &build_nsec, &build_nsec3);
+ RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ return (build_nsec || build_nsec3);
+}
+
+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. */
+ bool soa_serial_changed = 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, options2;
+ dns_difftuple_t *tuple;
+ dns_rdata_dnskey_t dnskey;
+ bool had_dnskey;
+ dns_rdatatype_t privatetype = dns_zone_getprivatetype(zone);
+ dns_ttl_t maxttl = 0;
+ uint32_t maxrecords;
+ uint64_t records;
+
+ 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);
+
+ /*
+ * Update message processing can leak record existance information
+ * so check that we are allowed to query this zone. Additionally
+ * if we would refuse all updates for this zone we bail out here.
+ */
+ CHECK(checkqueryacl(client, dns_zone_getqueryacl(zone), zonename,
+ dns_zone_getupdateacl(zone), ssutable));
+
+ /*
+ * Get old and new versions now that queryacl has been checked.
+ */
+ 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;
+ bool 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");
+
+ tmpname = dns_fixedname_initname(&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.
+ */
+ if (ssutable == NULL)
+ CHECK(checkupdateacl(client, dns_zone_getupdateacl(zone),
+ "update", zonename, false, false));
+ else if (client->signer == NULL && !TCPCLIENT(client))
+ CHECK(checkupdateacl(client, NULL, "update", zonename,
+ false, 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 (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 netaddr;
+ dst_key_t *tsigkey = NULL;
+ isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
+
+ if (client->message->tsigkey != NULL)
+ tsigkey = client->message->tsigkey->key;
+
+ if (rdata.type != dns_rdatatype_any) {
+ if (!dns_ssutable_checkrules2
+ (ssutable, client->signer, name, &netaddr,
+ TCPCLIENT(client),
+ &ns_g_server->aclenv,
+ rdata.type, tsigkey))
+ {
+ FAILC(DNS_R_REFUSED,
+ "rejected by secure update");
+ }
+ } else {
+ if (!ssu_checkall(db, ver, name, ssutable,
+ client->signer,
+ &netaddr,
+ TCPCLIENT(client),
+ tsigkey))
+ {
+ 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);
+ options2 = dns_zone_getoptions2(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;
+ bool 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) {
+ bool 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 = true;
+ }
+
+ if (rdata.type == privatetype) {
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "attempt to add a private type "
+ "(%u) record rejected internal "
+ "use only", privatetype);
+ continue;
+ }
+
+ 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;
+ }
+ }
+
+ 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 ((options2 & DNS_ZONEOPT2_CHECKTTL) != 0) {
+ maxttl = dns_zone_getmaxttl(zone);
+ if (ttl > maxttl) {
+ ttl = maxttl;
+ update_log(client, zone,
+ LOGLEVEL_PROTOCOL,
+ "reducing TTL to the "
+ "configured max-zone-ttl %d",
+ maxttl);
+ }
+ }
+
+ if (isc_log_wouldlog(ns_g_lctx, LOGLEVEL_PROTOCOL)) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char typestr[DNS_RDATATYPE_FORMATSIZE];
+ char rdstr[2048];
+ isc_buffer_t buf;
+ int len = 0;
+ const char *truncated = "";
+
+ dns_name_format(name, namestr, sizeof(namestr));
+ dns_rdatatype_format(rdata.type, typestr,
+ sizeof(typestr));
+ isc_buffer_init(&buf, rdstr, sizeof(rdstr));
+ result = dns_rdata_totext(&rdata, NULL, &buf);
+ if (result == ISC_R_NOSPACE) {
+ len = (int)isc_buffer_usedlength(&buf);
+ truncated = " [TRUNCATED]";
+ } else if (result != ISC_R_SUCCESS) {
+ snprintf(rdstr, sizeof(rdstr), "[dns_"
+ "rdata_totext failed: %s]",
+ dns_result_totext(result));
+ len = strlen(rdstr);
+ } else
+ len = (int)isc_buffer_usedlength(&buf);
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "adding an RR at '%s' %s %.*s%s",
+ namestr, typestr, len, rdstr,
+ truncated);
+ }
+
+ /* 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.oldname = name;
+ ctx.update_rr = &rdata;
+ ctx.update_rr_ttl = ttl;
+ ctx.ignore_add = 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 {
+ result = do_diff(&ctx.del_diff, db, ver,
+ &diff);
+ if (result == ISC_R_SUCCESS) {
+ result = do_diff(&ctx.add_diff,
+ db, ver,
+ &diff);
+ }
+ if (result != ISC_R_SUCCESS) {
+ dns_diff_clear(&ctx.del_diff);
+ dns_diff_clear(&ctx.add_diff);
+ goto failure;
+ }
+ 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));
+ }
+ } 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) {
+ char namestr[DNS_NAME_FORMATSIZE];
+ char typestr[DNS_RDATATYPE_FORMATSIZE];
+
+ /*
+ * 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;
+ }
+ }
+ }
+ dns_name_format(name, namestr, sizeof(namestr));
+ dns_rdatatype_format(rdata.type, typestr,
+ sizeof(typestr));
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "deleting an RR at %s %s", namestr, typestr);
+ 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 (! ISC_LIST_EMPTY(diff.tuples)) {
+ unsigned int errors = 0;
+ CHECK(dns_zone_nscheck(zone, db, ver, &errors));
+ if (errors != 0) {
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "update rejected: post update name server "
+ "sanity check failed");
+ result = DNS_R_REFUSED;
+ goto failure;
+ }
+ }
+ if (! ISC_LIST_EMPTY(diff.tuples)) {
+ result = dns_zone_cdscheck(zone, db, ver);
+ if (result == DNS_R_BADCDS || result == DNS_R_BADCDNSKEY) {
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "update rejected: bad %s RRset",
+ result == DNS_R_BADCDS ? "CDS" : "CDNSKEY");
+ result = DNS_R_REFUSED;
+ goto failure;
+ }
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+
+ }
+
+ /*
+ * 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;
+ bool 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(update_soa_serial(db, ver, &diff, mctx,
+ dns_zone_getserialupdatemethod(zone)));
+ }
+
+ 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));
+
+#define ALLOW_SECURE_TO_INSECURE(zone) \
+ ((dns_zone_getoptions(zone) & DNS_ZONEOPT_SECURETOINSECURE) != 0)
+
+ CHECK(rrset_exists(db, oldver, zonename, dns_rdatatype_dnskey,
+ 0, &had_dnskey));
+ if (!ALLOW_SECURE_TO_INSECURE(zone)) {
+ if (had_dnskey && !has_dnskey) {
+ update_log(client, zone, LOGLEVEL_PROTOCOL,
+ "update rejected: all DNSKEY "
+ "records removed and "
+ "'dnssec-secure-to-insecure' "
+ "not set");
+ result = DNS_R_REFUSED;
+ goto failure;
+ }
+ }
+
+ CHECK(rollback_private(db, privatetype, ver, &diff));
+
+ CHECK(add_signing_records(db, privatetype, ver, &diff));
+
+ CHECK(add_nsec3param_records(client, zone, db, ver, &diff));
+
+ if (had_dnskey && !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.
+ */
+ CHECK(dns_nsec3param_deletechains(db, ver, zone,
+ true, &diff));
+ } else if (has_dnskey && isdnssec(db, ver, privatetype)) {
+ uint32_t interval;
+ dns_update_log_t log;
+
+ interval = dns_zone_getsigvalidityinterval(zone);
+ log.func = update_log_cb;
+ log.arg = client;
+ result = dns_update_signatures(&log, zone, db, oldver,
+ ver, &diff, interval);
+
+ if (result != ISC_R_SUCCESS) {
+ update_log(client, zone,
+ ISC_LOG_ERROR,
+ "RRSIG/NSEC/NSEC3 update failed: %s",
+ isc_result_totext(result));
+ goto failure;
+ }
+ }
+
+ maxrecords = dns_zone_getmaxrecords(zone);
+ if (maxrecords != 0U) {
+ result = dns_db_getsize(db, ver, &records, NULL);
+ if (result == ISC_R_SUCCESS && records > maxrecords) {
+ update_log(client, zone, ISC_LOG_ERROR,
+ "records in zone (%"
+ PRIu64
+ ") exceeds max-records (%u)",
+ records, maxrecords);
+ result = DNS_R_TOOMANYRECORDS;
+ 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,
+ DNS_JOURNAL_CREATE, &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, 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;
+ 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,
+ (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));
+ }
+ }
+
+ /*
+ * Cause the zone to add/delete NSEC3 chains for the
+ * deferred 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)) {
+ unsigned char buf[DNS_NSEC3PARAM_BUFFERSIZE];
+ dns_rdata_t rdata = DNS_RDATA_INIT;
+ dns_rdata_nsec3param_t nsec3param;
+
+ if (tuple->rdata.type != privatetype ||
+ tuple->op != DNS_DIFFOP_ADD)
+ continue;
+
+ if (!dns_nsec3param_fromprivate(&tuple->rdata, &rdata,
+ buf, sizeof(buf)))
+ continue;
+ dns_rdata_tostruct(&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));
+ }
+ }
+ } else {
+ update_log(client, zone, LOGLEVEL_DEBUG, "redundant request");
+ dns_db_closeversion(db, &ver, true);
+ }
+ result = ISC_R_SUCCESS;
+ goto common;
+
+ failure:
+ /*
+ * 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, false);
+ }
+
+ common:
+ dns_diff_clear(&temp);
+ dns_diff_clear(&diff);
+
+ if (oldver != NULL)
+ dns_db_closeversion(db, &oldver, 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(ver == NULL);
+ 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) {
+ char namebuf[DNS_NAME_FORMATSIZE];
+ char classbuf[DNS_RDATACLASS_FORMATSIZE];
+ isc_result_t result = ISC_R_SUCCESS;
+ update_event_t *event = NULL;
+ isc_task_t *zonetask = NULL;
+ ns_client_t *evclient;
+
+ /*
+ * This may take some time so replace this client.
+ */
+ if (!client->mortal && (client->attributes & NS_CLIENTATTR_TCP) == 0)
+ CHECK(ns_client_replace(client));
+
+ 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_name_format(dns_zone_getorigin(zone), namebuf,
+ sizeof(namebuf));
+ dns_rdataclass_format(dns_zone_getclass(zone), classbuf,
+ sizeof(classbuf));
+
+ ns_client_log(client, NS_LOGCATEGORY_UPDATE, NS_LOGMODULE_UPDATE,
+ LOGLEVEL_PROTOCOL, "forwarding update for zone '%s/%s'",
+ namebuf, classbuf);
+
+ 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/dlz_dlopen_driver.c b/bin/named/win32/dlz_dlopen_driver.c
new file mode 100644
index 0000000..bb0eedd
--- /dev/null
+++ b/bin/named/win32/dlz_dlopen_driver.c
@@ -0,0 +1,618 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#include <config.h>
+
+#include <stdbool.h>
+#include <windows.h>
+
+#include <stdio.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dns/log.h>
+#include <dns/result.h>
+#include <dns/dlz_dlopen.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/dlz_dlopen_driver.h>
+
+#ifdef ISC_DLZ_DLOPEN
+static dns_sdlzimplementation_t *dlz_dlopen = NULL;
+
+
+typedef struct dlopen_data {
+ isc_mem_t *mctx;
+ char *dl_path;
+ char *dlzname;
+ HMODULE dl_handle;
+ void *dbdata;
+ unsigned int flags;
+ isc_mutex_t lock;
+ int version;
+ bool in_configure;
+
+ dlz_dlopen_version_t *dlz_version;
+ dlz_dlopen_create_t *dlz_create;
+ dlz_dlopen_findzonedb_t *dlz_findzonedb;
+ dlz_dlopen_lookup_t *dlz_lookup;
+ dlz_dlopen_authority_t *dlz_authority;
+ dlz_dlopen_allnodes_t *dlz_allnodes;
+ dlz_dlopen_allowzonexfr_t *dlz_allowzonexfr;
+ dlz_dlopen_newversion_t *dlz_newversion;
+ dlz_dlopen_closeversion_t *dlz_closeversion;
+ dlz_dlopen_configure_t *dlz_configure;
+ dlz_dlopen_ssumatch_t *dlz_ssumatch;
+ dlz_dlopen_addrdataset_t *dlz_addrdataset;
+ dlz_dlopen_subrdataset_t *dlz_subrdataset;
+ dlz_dlopen_delrdataset_t *dlz_delrdataset;
+ dlz_dlopen_destroy_t *dlz_destroy;
+} dlopen_data_t;
+
+/* Modules can choose whether they are lock-safe or not. */
+#define MAYBE_LOCK(cd) \
+ do { \
+ if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
+ cd->in_configure == false) \
+ LOCK(&cd->lock); \
+ } while (0)
+
+#define MAYBE_UNLOCK(cd) \
+ do { \
+ if ((cd->flags & DNS_SDLZFLAG_THREADSAFE) == 0 && \
+ cd->in_configure == false) \
+ UNLOCK(&cd->lock); \
+ } while (0)
+
+/*
+ * Log a message at the given level.
+ */
+static void dlopen_log(int level, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(level),
+ fmt, ap);
+ va_end(ap);
+}
+
+/*
+ * SDLZ methods
+ */
+
+static isc_result_t
+dlopen_dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
+ dns_sdlzallnodes_t *allnodes)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_allnodes == NULL) {
+ return (ISC_R_NOPERM);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_allnodes(zone, cd->dbdata, allnodes);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+
+static isc_result_t
+dlopen_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+ const char *client)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+
+ if (cd->dlz_allowzonexfr == NULL) {
+ return (ISC_R_NOPERM);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_allowzonexfr(cd->dbdata, name, client);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+static isc_result_t
+dlopen_dlz_authority(const char *zone, void *driverarg, void *dbdata,
+ dns_sdlzlookup_t *lookup)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_authority == NULL) {
+ return (ISC_R_NOTIMPLEMENTED);
+ }
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_authority(zone, cd->dbdata, lookup);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+static isc_result_t
+dlopen_dlz_findzonedb(void *driverarg, void *dbdata, const char *name,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_findzonedb(cd->dbdata, name, methods, clientinfo);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+
+static isc_result_t
+dlopen_dlz_lookup(const char *zone, const char *name, void *driverarg,
+ void *dbdata, dns_sdlzlookup_t *lookup,
+ dns_clientinfomethods_t *methods,
+ dns_clientinfo_t *clientinfo)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_lookup(zone, name, cd->dbdata, lookup,
+ methods, clientinfo);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+/*
+ * Load a symbol from the library
+ */
+static void *
+dl_load_symbol(dlopen_data_t *cd, const char *symbol, bool mandatory) {
+ void *ptr = GetProcAddress(cd->dl_handle, symbol);
+ if (ptr == NULL && mandatory) {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen: library '%s' is missing "
+ "required symbol '%s'", cd->dl_path, symbol);
+ }
+ return (ptr);
+}
+
+/*
+ * Called at startup for each dlopen zone in named.conf
+ */
+static isc_result_t
+dlopen_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
+ void *driverarg, void **dbdata)
+{
+ dlopen_data_t *cd;
+ isc_mem_t *mctx = NULL;
+ isc_result_t result = ISC_R_FAILURE;
+ bool triedload = false;
+
+ UNUSED(driverarg);
+
+ if (argc < 2) {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen driver for '%s' needs a path to "
+ "the shared library", dlzname);
+ return (ISC_R_FAILURE);
+ }
+
+ isc_mem_create(0, 0, &mctx);
+
+ cd = isc_mem_get(mctx, sizeof(*cd));
+ if (cd == NULL) {
+ isc_mem_destroy(&mctx);
+ return (ISC_R_NOMEMORY);
+ }
+ memset(cd, 0, sizeof(*cd));
+
+ cd->mctx = mctx;
+
+ cd->dl_path = isc_mem_strdup(cd->mctx, argv[1]);
+ if (cd->dl_path == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failed;
+ }
+
+ cd->dlzname = isc_mem_strdup(cd->mctx, dlzname);
+ if (cd->dlzname == NULL) {
+ result = ISC_R_NOMEMORY;
+ goto failed;
+ }
+
+ triedload = true;
+
+ /* Initialize the lock */
+ result = isc_mutex_init(&cd->lock);
+ if (result != ISC_R_SUCCESS)
+ goto failed;
+
+ /* Open the library */
+ cd->dl_handle = LoadLibraryA(cd->dl_path);
+ if (cd->dl_handle == NULL) {
+ unsigned int error = GetLastError();
+
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen failed to open library '%s' - %u",
+ cd->dl_path, error);
+ result = ISC_R_FAILURE;
+ goto cleanup_lock;
+ }
+
+ /* Find the symbols */
+ cd->dlz_version = (dlz_dlopen_version_t *)
+ dl_load_symbol(cd, "dlz_version", true);
+ cd->dlz_create = (dlz_dlopen_create_t *)
+ dl_load_symbol(cd, "dlz_create", true);
+ cd->dlz_lookup = (dlz_dlopen_lookup_t *)
+ dl_load_symbol(cd, "dlz_lookup", true);
+ cd->dlz_findzonedb = (dlz_dlopen_findzonedb_t *)
+ dl_load_symbol(cd, "dlz_findzonedb", true);
+
+ if (cd->dlz_create == NULL ||
+ cd->dlz_version == NULL ||
+ cd->dlz_lookup == NULL ||
+ cd->dlz_findzonedb == NULL)
+ {
+ /* We're missing a required symbol */
+ result = ISC_R_FAILURE;
+ goto cleanup_lock;
+ }
+
+ cd->dlz_allowzonexfr = (dlz_dlopen_allowzonexfr_t *)
+ dl_load_symbol(cd, "dlz_allowzonexfr", false);
+ cd->dlz_allnodes = (dlz_dlopen_allnodes_t *)
+ dl_load_symbol(cd, "dlz_allnodes",
+ (cd->dlz_allowzonexfr != NULL));
+ cd->dlz_authority = (dlz_dlopen_authority_t *)
+ dl_load_symbol(cd, "dlz_authority", false);
+ cd->dlz_newversion = (dlz_dlopen_newversion_t *)
+ dl_load_symbol(cd, "dlz_newversion", false);
+ cd->dlz_closeversion = (dlz_dlopen_closeversion_t *)
+ dl_load_symbol(cd, "dlz_closeversion",
+ (cd->dlz_newversion != NULL));
+ cd->dlz_configure = (dlz_dlopen_configure_t *)
+ dl_load_symbol(cd, "dlz_configure", false);
+ cd->dlz_ssumatch = (dlz_dlopen_ssumatch_t *)
+ dl_load_symbol(cd, "dlz_ssumatch", false);
+ cd->dlz_addrdataset = (dlz_dlopen_addrdataset_t *)
+ dl_load_symbol(cd, "dlz_addrdataset", false);
+ cd->dlz_subrdataset = (dlz_dlopen_subrdataset_t *)
+ dl_load_symbol(cd, "dlz_subrdataset", false);
+ cd->dlz_delrdataset = (dlz_dlopen_delrdataset_t *)
+ dl_load_symbol(cd, "dlz_delrdataset", false);
+
+ /* Check the version of the API is the same */
+ cd->version = cd->dlz_version(&cd->flags);
+ if (cd->version < (DLZ_DLOPEN_VERSION - DLZ_DLOPEN_AGE) ||
+ cd->version > DLZ_DLOPEN_VERSION)
+ {
+ dlopen_log(ISC_LOG_ERROR,
+ "dlz_dlopen: %s: incorrect driver API version %d, "
+ "requires %d",
+ cd->dl_path, cd->version, DLZ_DLOPEN_VERSION);
+ result = ISC_R_FAILURE;
+ goto cleanup_lock;
+ }
+
+ /*
+ * Call the library's create function. Note that this is an
+ * extended version of dlz create, with the addition of
+ * named function pointers for helper functions that the
+ * driver will need. This avoids the need for the backend to
+ * link the BIND9 libraries
+ */
+ MAYBE_LOCK(cd);
+ result = cd->dlz_create(dlzname, argc-1, argv+1,
+ &cd->dbdata,
+ "log", dlopen_log,
+ "putrr", dns_sdlz_putrr,
+ "putnamedrr", dns_sdlz_putnamedrr,
+ "writeable_zone", dns_dlz_writeablezone,
+ NULL);
+ MAYBE_UNLOCK(cd);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup_lock;
+
+ *dbdata = cd;
+
+ return (ISC_R_SUCCESS);
+
+cleanup_lock:
+ DESTROYLOCK(&cd->lock);
+failed:
+ dlopen_log(ISC_LOG_ERROR, "dlz_dlopen of '%s' failed", dlzname);
+ if (cd->dl_path)
+ isc_mem_free(mctx, cd->dl_path);
+ if (cd->dlzname)
+ isc_mem_free(mctx, cd->dlzname);
+ if (triedload)
+ (void) isc_mutex_destroy(&cd->lock);
+ if (cd->dl_handle)
+ FreeLibrary(cd->dl_handle);
+ isc_mem_put(mctx, cd, sizeof(*cd));
+ isc_mem_destroy(&mctx);
+ return (result);
+}
+
+
+/*
+ * Called when bind is shutting down
+ */
+static void
+dlopen_dlz_destroy(void *driverarg, void *dbdata) {
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_mem_t *mctx;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_destroy) {
+ MAYBE_LOCK(cd);
+ cd->dlz_destroy(cd->dbdata);
+ MAYBE_UNLOCK(cd);
+ }
+
+ if (cd->dl_path)
+ isc_mem_free(cd->mctx, cd->dl_path);
+ if (cd->dlzname)
+ isc_mem_free(cd->mctx, cd->dlzname);
+
+ if (cd->dl_handle)
+ FreeLibrary(cd->dl_handle);
+
+ DESTROYLOCK(&cd->lock);
+
+ mctx = cd->mctx;
+ isc_mem_put(mctx, cd, sizeof(*cd));
+ isc_mem_destroy(&mctx);
+}
+
+/*
+ * Called to start a transaction
+ */
+static isc_result_t
+dlopen_dlz_newversion(const char *zone, void *driverarg, void *dbdata,
+ void **versionp)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_newversion == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_newversion(zone, cd->dbdata, versionp);
+ MAYBE_UNLOCK(cd);
+ return (result);
+}
+
+/*
+ * Called to end a transaction
+ */
+static void
+dlopen_dlz_closeversion(const char *zone, bool commit,
+ void *driverarg, void *dbdata, void **versionp)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_newversion == NULL) {
+ *versionp = NULL;
+ return;
+ }
+
+ MAYBE_LOCK(cd);
+ cd->dlz_closeversion(zone, commit, cd->dbdata, versionp);
+ MAYBE_UNLOCK(cd);
+}
+
+/*
+ * Called on startup to configure any writeable zones
+ */
+static isc_result_t
+dlopen_dlz_configure(dns_view_t *view, dns_dlzdb_t *dlzdb,
+ void *driverarg, void *dbdata)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_configure == NULL)
+ return (ISC_R_SUCCESS);
+
+ MAYBE_LOCK(cd);
+ cd->in_configure = true;
+ result = cd->dlz_configure(view, dlzdb, cd->dbdata);
+ cd->in_configure = false;
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+
+/*
+ * Check for authority to change a name
+ */
+static bool
+dlopen_dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr,
+ const char *type, const char *key, uint32_t keydatalen,
+ unsigned char *keydata, void *driverarg, void *dbdata)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ bool ret;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_ssumatch == NULL)
+ return (false);
+
+ MAYBE_LOCK(cd);
+ ret = cd->dlz_ssumatch(signer, name, tcpaddr, type, key, keydatalen,
+ keydata, cd->dbdata);
+ MAYBE_UNLOCK(cd);
+
+ return (ret);
+}
+
+
+/*
+ * Add an rdataset
+ */
+static isc_result_t
+dlopen_dlz_addrdataset(const char *name, const char *rdatastr,
+ void *driverarg, void *dbdata, void *version)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_addrdataset == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_addrdataset(name, rdatastr, cd->dbdata, version);
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+/*
+ * Subtract an rdataset
+ */
+static isc_result_t
+dlopen_dlz_subrdataset(const char *name, const char *rdatastr,
+ void *driverarg, void *dbdata, void *version)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_subrdataset == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_subrdataset(name, rdatastr, cd->dbdata, version);
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+/*
+ delete a rdataset
+ */
+static isc_result_t
+dlopen_dlz_delrdataset(const char *name, const char *type,
+ void *driverarg, void *dbdata, void *version)
+{
+ dlopen_data_t *cd = (dlopen_data_t *) dbdata;
+ isc_result_t result;
+
+ UNUSED(driverarg);
+
+ if (cd->dlz_delrdataset == NULL)
+ return (ISC_R_NOTIMPLEMENTED);
+
+ MAYBE_LOCK(cd);
+ result = cd->dlz_delrdataset(name, type, cd->dbdata, version);
+ MAYBE_UNLOCK(cd);
+
+ return (result);
+}
+
+
+static dns_sdlzmethods_t dlz_dlopen_methods = {
+ dlopen_dlz_create,
+ dlopen_dlz_destroy,
+ dlopen_dlz_findzonedb,
+ dlopen_dlz_lookup,
+ dlopen_dlz_authority,
+ dlopen_dlz_allnodes,
+ dlopen_dlz_allowzonexfr,
+ dlopen_dlz_newversion,
+ dlopen_dlz_closeversion,
+ dlopen_dlz_configure,
+ dlopen_dlz_ssumatch,
+ dlopen_dlz_addrdataset,
+ dlopen_dlz_subrdataset,
+ dlopen_dlz_delrdataset
+};
+#endif
+
+/*
+ * Register driver with BIND
+ */
+isc_result_t
+dlz_dlopen_init(isc_mem_t *mctx) {
+#ifndef ISC_DLZ_DLOPEN
+ UNUSED(mctx);
+ return (ISC_R_NOTIMPLEMENTED);
+#else
+ isc_result_t result;
+
+ dlopen_log(2, "Registering DLZ_dlopen driver");
+
+ result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
+ DNS_SDLZFLAG_RELATIVEOWNER |
+ DNS_SDLZFLAG_RELATIVERDATA |
+ DNS_SDLZFLAG_THREADSAFE,
+ mctx, &dlz_dlopen);
+
+ if (result != ISC_R_SUCCESS) {
+ UNEXPECTED_ERROR(__FILE__, __LINE__,
+ "dns_sdlzregister() failed: %s",
+ isc_result_totext(result));
+ result = ISC_R_UNEXPECTED;
+ }
+
+ return (result);
+#endif
+}
+
+
+/*
+ * Unregister the driver
+ */
+void
+dlz_dlopen_clear(void) {
+#ifdef ISC_DLZ_DLOPEN
+ dlopen_log(2, "Unregistering DLZ_dlopen driver");
+ if (dlz_dlopen != NULL)
+ dns_sdlzunregister(&dlz_dlopen);
+#endif
+}
diff --git a/bin/named/win32/include/named/ntservice.h b/bin/named/win32/include/named/ntservice.h
new file mode 100644
index 0000000..38732fb
--- /dev/null
+++ b/bin/named/win32/include/named/ntservice.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+
+#ifndef 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..cac89a4
--- /dev/null
+++ b/bin/named/win32/include/named/os.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#ifndef NS_OS_H
+#define NS_OS_H 1
+
+#include <stdbool.h>
+
+#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);
+
+unsigned int
+ns_os_uid(void);
+
+void
+ns_os_adjustnofile(void);
+
+void
+ns_os_minprivs(void);
+
+FILE *
+ns_os_openfile(const char *filename, int mode, bool switch_user);
+
+void
+ns_os_writepidfile(const char *filename, bool first_time);
+
+bool
+ns_os_issingleton(const char *filename);
+
+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);
+
+char *
+ns_os_uname(void);
+
+#endif /* NS_OS_H */
diff --git a/bin/named/win32/named.dsp.in b/bin/named/win32/named.dsp.in
new file mode 100644
index 0000000..354b88b
--- /dev/null
+++ b/bin/named/win32/named.dsp.in
@@ -0,0 +1,341 @@
+# Microsoft Developer Studio Project File - Name="named" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "@PLATFORM@ (x86) Console Application" 0x0103
+
+CFG=named - @PLATFORM@ Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "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 - @PLATFORM@ Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "named - @PLATFORM@ Release" (based on "@PLATFORM@ (x86) Console Application")
+!MESSAGE "named - @PLATFORM@ Debug" (based on "@PLATFORM@ (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 @COPTX@ @COPTI@ /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" @COPTY@ /FD /c
+# ADD CPP /nologo /MD /W3 @COPTX@ @COPTI@ /O2 @OPENSSL_INC@ @GSSAPI_INC@ @GEOIP_INC@ /I "./" /I "../../../" @LIBXML2_INC@ /I "../win32/include" /I "../include" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/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" @CRYPTO@ @USE_GSSAPI@ /D "BUILDER=\"old Visual Studio\"" /D "WIN32" /D "NDEBUG" /D "__STDC__" /D "_CONSOLE" /D "_MBCS" @COPTY@ /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@
+# ADD LINK32 @LIBXML2_LIB@ @OPENSSL_LIB@ @GSSAPI_LIB@ @GEOIP_LIB@ user32.lib advapi32.lib kernel32.lib version.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@ /out:"../../../Build/Release/named.exe"
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm @COPTX@ @COPTI@ /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" @COPTY@ /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm @COPTX@ @COPTI@ /ZI /Od @OPENSSL_INC@ @GSSAPI_INC@ @GEOIP_INC@ /I "./" /I "../../../" @LIBXML2_INC@ /I "../win32/include" /I "../include" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/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" @CRYPTO@ @USE_GSSAPI@ /D "BUILDER=\"old Visual Studio\"" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "i386" /FR /FD /GZ /c
+# SUBTRACT CPP /X @COPTY@
+# 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@ /pdbtype:sept
+# ADD LINK32 @LIBXML2_LIB@ @OPENSSL_LIB@ @GSSAPI_LIB@ @GEOIP_LIB@ user32.lib advapi32.lib kernel32.lib version.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@ /out:"../../../Build/Debug/named.exe" /pdbtype:sept
+
+!ENDIF
+
+# Begin Target
+
+# Name "named - @PLATFORM@ Release"
+# Name "named - @PLATFORM@ 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=.\dlz_dlopen_driver.c
+# End Source File
+@IF GEOIP
+# Begin Source File
+
+SOURCE=..\geoip.c
+# End Source File
+@END GEOIP
+# 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\seccomp.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.in b/bin/named/win32/named.mak.in
new file mode 100644
index 0000000..9e73888
--- /dev/null
+++ b/bin/named/win32/named.mak.in
@@ -0,0 +1,1233 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on named.dsp
+!IF "$(CFG)" == ""
+CFG=named - @PLATFORM@ Debug
+!MESSAGE No configuration specified. Defaulting to named - @PLATFORM@ Debug.
+!ENDIF
+
+!IF "$(CFG)" != "named - @PLATFORM@ Release" && "$(CFG)" != "named - @PLATFORM@ Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "named.mak" CFG="named - @PLATFORM@ Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "named - @PLATFORM@ Release" (based on "@PLATFORM@ (x86) Console Application")
+!MESSAGE "named - @PLATFORM@ Debug" (based on "@PLATFORM@ (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
+LIBXML=@LIBXML2_LIB@
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+_VC_MANIFEST_INC=0
+_VC_MANIFEST_BASENAME=__VC80
+!ELSE
+_VC_MANIFEST_INC=1
+_VC_MANIFEST_BASENAME=__VC80.Debug
+!ENDIF
+
+####################################################
+# Specifying name of temporary resource file used only in incremental builds:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+_VC_MANIFEST_AUTO_RES=$(_VC_MANIFEST_BASENAME).auto.res
+!else
+_VC_MANIFEST_AUTO_RES=
+!endif
+
+####################################################
+# _VC_MANIFEST_EMBED_EXE - command to embed manifest in EXE:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+#MT_SPECIAL_RETURN=1090650113
+#MT_SPECIAL_SWITCH=-notify_resource_update
+MT_SPECIAL_RETURN=0
+MT_SPECIAL_SWITCH=
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
+if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
+rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
+link $** /out:$@ $(LFLAGS)
+
+!else
+
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;1
+
+!endif
+
+####################################################
+# _VC_MANIFEST_EMBED_DLL - command to embed manifest in DLL:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+#MT_SPECIAL_RETURN=1090650113
+#MT_SPECIAL_SWITCH=-notify_resource_update
+MT_SPECIAL_RETURN=0
+MT_SPECIAL_SWITCH=
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \
+if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \
+rc /r $(_VC_MANIFEST_BASENAME).auto.rc & \
+link $** /out:$@ $(LFLAGS)
+
+!else
+
+_VC_MANIFEST_EMBED_EXE= \
+if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$@;2
+
+!endif
+####################################################
+# _VC_MANIFEST_CLEAN - command to clean resources files generated temporarily:
+
+!if "$(_VC_MANIFEST_INC)" == "1"
+
+_VC_MANIFEST_CLEAN=-del $(_VC_MANIFEST_BASENAME).auto.res \
+ $(_VC_MANIFEST_BASENAME).auto.rc \
+ $(_VC_MANIFEST_BASENAME).auto.manifest
+
+!else
+
+_VC_MANIFEST_CLEAN=
+
+!endif
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "..\..\..\Build\Release\named.exe"
+
+!ELSE
+
+ALL : "libisccfg - @PLATFORM@ Release" "libisccc - @PLATFORM@ Release" "liblwres - @PLATFORM@ Release" "libbind9 - @PLATFORM@ Release" "libisc - @PLATFORM@ Release" "libdns - @PLATFORM@ Release" "..\..\..\Build\Release\named.exe"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libdns - @PLATFORM@ ReleaseCLEAN" "libisc - @PLATFORM@ ReleaseCLEAN" "libbind9 - @PLATFORM@ ReleaseCLEAN" "liblwres - @PLATFORM@ ReleaseCLEAN" "libisccc - @PLATFORM@ ReleaseCLEAN" "libisccfg - @PLATFORM@ 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)\dlz_dlopen_driver.obj"
+@IF GEOIP
+ -@erase "$(INTDIR)\geoip.obj"
+@END GEOIP
+ -@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 @COPTX@ @COPTI@ /O2 @OPENSSL_INC@ @GSSAPI_INC@ /I "./" /I "../../../" @LIBXML2_INC@ /I "../win32/include" /I "../include" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/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" @CRYPTO@ @USE_GSSAPI@ /D "BUILDER=\"nmake\"" /D "WIN32" /D "NDEBUG" /D "__STDC__" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\named.pch" @COPTY@ /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 version.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 $(LIBXML) @OPENSSL_LIB@ @GSSAPI_LIB@ @GEOIP_LIB@ /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\named.pdb" @MACHINE@ /out:"../../../Build/Release/named.exe"
+LINK32_OBJS= \
+ "$(INTDIR)\client.obj" \
+ "$(INTDIR)\config.obj" \
+ "$(INTDIR)\control.obj" \
+ "$(INTDIR)\controlconf.obj" \
+ "$(INTDIR)\dlz_dlopen_driver.obj" \
+@IF GEOIP
+ "$(INTDIR)\geoip.obj" \
+@END GEOIP
+ "$(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 - @PLATFORM@ 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 - @PLATFORM@ Debug" "libisccc - @PLATFORM@ Debug" "liblwres - @PLATFORM@ Debug" "libbind9 - @PLATFORM@ Debug" "libisc - @PLATFORM@ Debug" "libdns - @PLATFORM@ Debug" "..\..\..\Build\Debug\named.exe" "$(OUTDIR)\named.bsc"
+
+!ENDIF
+
+!IF "$(RECURSE)" == "1"
+CLEAN :"libdns - @PLATFORM@ DebugCLEAN" "libisc - @PLATFORM@ DebugCLEAN" "libbind9 - @PLATFORM@ DebugCLEAN" "liblwres - @PLATFORM@ DebugCLEAN" "libisccc - @PLATFORM@ DebugCLEAN" "libisccfg - @PLATFORM@ 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)\dlz_dlopen_driver.obj"
+ -@erase "$(INTDIR)\dlz_dlopen_driver.sbr"
+@IF GEOIP
+ -@erase "$(INTDIR)\geoip.obj"
+ -@erase "$(INTDIR)\geoip.sbr"
+@END GEOIP
+ -@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 @COPTX@ @COPTI@ /ZI /Od @OPENSSL_INC@ @GSSAPI_INC@ /I "./" /I "../../../" @LIBXML2_INC@ /I "../win32/include" /I "../include" /I "../../../lib/isc/win32" /I "../../../lib/isc/win32/include" /I "../../../lib/isc/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" @CRYPTO@ @USE_GSSAPI@ /D "BUILDER=\"nmake\"" /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)\dlz_dlopen_driver.sbr" \
+@IF GEOIP
+ "$(INTDIR)\geoip.sbr" \
+@END GEOIP
+ "$(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 version.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 $(LIBXML) @OPENSSL_LIB@ @GSSAPI_LIB@ @GEOIP_LIB@ /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\named.pdb" /map:"$(INTDIR)\named.map" /debug @MACHINE@ /out:"../../../Build/Debug/named.exe" /pdbtype:sept
+LINK32_OBJS= \
+ "$(INTDIR)\client.obj" \
+ "$(INTDIR)\config.obj" \
+ "$(INTDIR)\control.obj" \
+ "$(INTDIR)\controlconf.obj" \
+ "$(INTDIR)\dlz_dlopen_driver.obj" \
+@IF GEOIP
+ "$(INTDIR)\geoip.obj" \
+@END GEOIP
+ "$(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 - @PLATFORM@ Release" || "$(CFG)" == "named - @PLATFORM@ Debug"
+SOURCE=..\builtin.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\builtin.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\builtin.obj" "$(INTDIR)\builtin.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\client.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\client.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\client.obj" "$(INTDIR)\client.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\config.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\config.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\config.obj" "$(INTDIR)\config.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\control.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\control.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\control.obj" "$(INTDIR)\control.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\controlconf.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\controlconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\controlconf.obj" "$(INTDIR)\controlconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\dlz_dlopen_driver.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\dlz_dlopen_driver.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\dlz_dlopen_driver.obj" "$(INTDIR)\dlz_dlopen_driver.sbr" : $(SOURCE) "$(INTDIR)"
+
+
+!ENDIF
+
+@IF GEOIP
+SOURCE=..\geoip.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\geoip.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\geoip.obj" "$(INTDIR)\geoip.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+@END GEOIP
+
+SOURCE=..\interfacemgr.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\interfacemgr.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\interfacemgr.obj" "$(INTDIR)\interfacemgr.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\listenlist.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\listenlist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\listenlist.obj" "$(INTDIR)\listenlist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\log.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\log.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\log.obj" "$(INTDIR)\log.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\logconf.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\logconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\logconf.obj" "$(INTDIR)\logconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwaddr.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lwaddr.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lwaddr.obj" "$(INTDIR)\lwaddr.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdclient.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lwdclient.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lwdclient.obj" "$(INTDIR)\lwdclient.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwderror.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lwderror.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lwderror.obj" "$(INTDIR)\lwderror.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdgabn.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lwdgabn.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lwdgabn.obj" "$(INTDIR)\lwdgabn.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdgnba.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lwdgnba.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lwdgnba.obj" "$(INTDIR)\lwdgnba.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdgrbn.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lwdgrbn.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lwdgrbn.obj" "$(INTDIR)\lwdgrbn.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwdnoop.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lwdnoop.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lwdnoop.obj" "$(INTDIR)\lwdnoop.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwresd.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lwresd.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lwresd.obj" "$(INTDIR)\lwresd.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\lwsearch.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\lwsearch.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\lwsearch.obj" "$(INTDIR)\lwsearch.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\main.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\main.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\main.obj" "$(INTDIR)\main.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\notify.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\notify.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\notify.obj" "$(INTDIR)\notify.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=.\ntservice.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\ntservice.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\ntservice.obj" "$(INTDIR)\ntservice.sbr" : $(SOURCE) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=.\os.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\os.obj" : $(SOURCE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\os.obj" "$(INTDIR)\os.sbr" : $(SOURCE) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=..\query.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\query.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\query.obj" "$(INTDIR)\query.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\server.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\server.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\server.obj" "$(INTDIR)\server.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\sortlist.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\sortlist.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\sortlist.obj" "$(INTDIR)\sortlist.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\statschannel.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\statschannel.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\statschannel.obj" "$(INTDIR)\statschannel.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tkeyconf.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\tkeyconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\tkeyconf.obj" "$(INTDIR)\tkeyconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\tsigconf.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\tsigconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\tsigconf.obj" "$(INTDIR)\tsigconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\update.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\update.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\update.obj" "$(INTDIR)\update.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\xfrout.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\xfrout.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\xfrout.obj" "$(INTDIR)\xfrout.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+SOURCE=..\zoneconf.c
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+
+"$(INTDIR)\zoneconf.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+
+"$(INTDIR)\zoneconf.obj" "$(INTDIR)\zoneconf.sbr" : $(SOURCE) "$(INTDIR)"
+ $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ENDIF
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+"libdns - @PLATFORM@ Release" :
+ cd "..\..\..\lib\dns\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - @PLATFORM@ Release"
+ cd "..\..\..\bin\named\win32"
+
+"libdns - @PLATFORM@ ReleaseCLEAN" :
+ cd "..\..\..\lib\dns\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - @PLATFORM@ Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+"libdns - @PLATFORM@ Debug" :
+ cd "..\..\..\lib\dns\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - @PLATFORM@ Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libdns - @PLATFORM@ DebugCLEAN" :
+ cd "..\..\..\lib\dns\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libdns.mak" CFG="libdns - @PLATFORM@ Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+"libisc - @PLATFORM@ Release" :
+ cd "..\..\..\lib\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Release"
+ cd "..\..\..\bin\named\win32"
+
+"libisc - @PLATFORM@ ReleaseCLEAN" :
+ cd "..\..\..\lib\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+"libisc - @PLATFORM@ Debug" :
+ cd "..\..\..\lib\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libisc - @PLATFORM@ DebugCLEAN" :
+ cd "..\..\..\lib\isc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisc.mak" CFG="libisc - @PLATFORM@ Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+"libbind9 - @PLATFORM@ Release" :
+ cd "..\..\..\lib\bind9\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - @PLATFORM@ Release"
+ cd "..\..\..\bin\named\win32"
+
+"libbind9 - @PLATFORM@ ReleaseCLEAN" :
+ cd "..\..\..\lib\bind9\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - @PLATFORM@ Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+"libbind9 - @PLATFORM@ Debug" :
+ cd "..\..\..\lib\bind9\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - @PLATFORM@ Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libbind9 - @PLATFORM@ DebugCLEAN" :
+ cd "..\..\..\lib\bind9\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libbind9.mak" CFG="libbind9 - @PLATFORM@ Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+"liblwres - @PLATFORM@ Release" :
+ cd "..\..\..\lib\lwres\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\liblwres.mak" CFG="liblwres - @PLATFORM@ Release"
+ cd "..\..\..\bin\named\win32"
+
+"liblwres - @PLATFORM@ ReleaseCLEAN" :
+ cd "..\..\..\lib\lwres\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\liblwres.mak" CFG="liblwres - @PLATFORM@ Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+"liblwres - @PLATFORM@ Debug" :
+ cd "..\..\..\lib\lwres\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\liblwres.mak" CFG="liblwres - @PLATFORM@ Debug"
+ cd "..\..\..\bin\named\win32"
+
+"liblwres - @PLATFORM@ DebugCLEAN" :
+ cd "..\..\..\lib\lwres\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\liblwres.mak" CFG="liblwres - @PLATFORM@ Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+"libisccc - @PLATFORM@ Release" :
+ cd "..\..\..\lib\isccc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccc.mak" CFG="libisccc - @PLATFORM@ Release"
+ cd "..\..\..\bin\named\win32"
+
+"libisccc - @PLATFORM@ ReleaseCLEAN" :
+ cd "..\..\..\lib\isccc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccc.mak" CFG="libisccc - @PLATFORM@ Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+"libisccc - @PLATFORM@ Debug" :
+ cd "..\..\..\lib\isccc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccc.mak" CFG="libisccc - @PLATFORM@ Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libisccc - @PLATFORM@ DebugCLEAN" :
+ cd "..\..\..\lib\isccc\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccc.mak" CFG="libisccc - @PLATFORM@ Debug" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ENDIF
+
+!IF "$(CFG)" == "named - @PLATFORM@ Release"
+
+"libisccfg - @PLATFORM@ Release" :
+ cd "..\..\..\lib\isccfg\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccfg.mak" CFG="libisccfg - @PLATFORM@ Release"
+ cd "..\..\..\bin\named\win32"
+
+"libisccfg - @PLATFORM@ ReleaseCLEAN" :
+ cd "..\..\..\lib\isccfg\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccfg.mak" CFG="libisccfg - @PLATFORM@ Release" RECURSE=1 CLEAN
+ cd "..\..\..\bin\named\win32"
+
+!ELSEIF "$(CFG)" == "named - @PLATFORM@ Debug"
+
+"libisccfg - @PLATFORM@ Debug" :
+ cd "..\..\..\lib\isccfg\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccfg.mak" CFG="libisccfg - @PLATFORM@ Debug"
+ cd "..\..\..\bin\named\win32"
+
+"libisccfg - @PLATFORM@ DebugCLEAN" :
+ cd "..\..\..\lib\isccfg\win32"
+ $(MAKE) /$(MAKEFLAGS) /F ".\libisccfg.mak" CFG="libisccfg - @PLATFORM@ 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/named.vcxproj.filters.in b/bin/named/win32/named.vcxproj.filters.in
new file mode 100644
index 0000000..8cc6a7b
--- /dev/null
+++ b/bin/named/win32/named.vcxproj.filters.in
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+ <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+ <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dlz_dlopen_driver.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ntservice.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="os.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\builtin.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\client.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\config.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\control.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\controlconf.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+@IF GEOIP
+ <ClCompile Include="..\geoip.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+@END GEOIP
+ <ClCompile Include="..\interfacemgr.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\listenlist.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\log.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\logconf.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lwaddr.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lwdclient.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lwderror.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lwdgabn.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lwdgnba.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lwdgrbn.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lwdnoop.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lwresd.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\lwsearch.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\main.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\notify.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\query.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\server.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\sortlist.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\statschannel.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\tkeyconf.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\tsigconf.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\update.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\xfrout.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\zoneconf.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="include\named\ntservice.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="include\named\os.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\builtin.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\client.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\control.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+@IF GEOIP
+ <ClInclude Include="..\include\named\geoip.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+@END GEOIP
+ <ClInclude Include="..\include\named\globals.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\interfacemgr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\listenlist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\log.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\logconf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\lwaddr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\lwdclient.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\lwresd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\lwsearch.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\notify.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\query.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\seccomp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\server.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\sortlist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\statschannel.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\tkeyconf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\tsigconf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\types.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\update.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\xfrout.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\include\named\zoneconf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/bin/named/win32/named.vcxproj.in b/bin/named/win32/named.vcxproj.in
new file mode 100644
index 0000000..f2d70cc
--- /dev/null
+++ b/bin/named/win32/named.vcxproj.in
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|@PLATFORM@">
+ <Configuration>Debug</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|@PLATFORM@">
+ <Configuration>Release</Configuration>
+ <Platform>@PLATFORM@</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{723C65DA-A96C-4BA3-A34E-44F11CA346F9}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>named</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>..\..\..\Build\$(Configuration)\</OutDir>
+ <IntDir>.\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|@PLATFORM@'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;@CRYPTO@@USE_GSSAPI@BUILDER="Visual Studio";_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <AdditionalIncludeDirectories>@OPENSSL_INC@@GSSAPI_INC@@GEOIP_INC@.\;..\..\..\;@LIBXML2_INC@..\win32\include;..\include;..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;..\..\..\lib\isccc\include;..\..\..\lib\lwres\win32\include;..\..\..\lib\lwres\include;..\..\..\lib\isccfg\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <AdditionalLibraryDirectories>..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\isccc\win32\$(Configuration);..\..\..\lib\lwres\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@LIBXML2_LIB@@OPENSSL_LIB@@GSSAPI_LIB@@GEOIP_LIB@libisc.lib;libdns.lib;libisccc.lib;liblwres.lib;libisccfg.lib;libbind9.lib;version.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|@PLATFORM@'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>@INTRINSIC@</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;@CRYPTO@@USE_GSSAPI@BUILDER="Visual Studio";NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <WholeProgramOptimization>false</WholeProgramOptimization>
+ <StringPooling>true</StringPooling>
+ <PrecompiledHeaderOutputFile>.\$(Configuration)\$(TargetName).pch</PrecompiledHeaderOutputFile>
+ <AssemblerListingLocation>.\$(Configuration)\</AssemblerListingLocation>
+ <ObjectFileName>.\$(Configuration)\</ObjectFileName>
+ <ProgramDataBaseFileName>$(OutDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <AdditionalIncludeDirectories>@OPENSSL_INC@@GSSAPI_INC@@GEOIP_INC@.\;..\..\..\;@LIBXML2_INC@..\win32\include;..\include;..\..\..\lib\isc\win32;..\..\..\lib\isc\win32\include;..\..\..\lib\isc\include;..\..\..\lib\dns\include;..\..\..\lib\isccc\include;..\..\..\lib\lwres\win32\include;..\..\..\lib\lwres\include;..\..\..\lib\isccfg\include;..\..\..\lib\bind9\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <CompileAs>CompileAsC</CompileAs>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <OutputFile>..\..\..\Build\$(Configuration)\$(TargetName)$(TargetExt)</OutputFile>
+ <LinkTimeCodeGeneration>Default</LinkTimeCodeGeneration>
+ <AdditionalLibraryDirectories>..\..\..\lib\isc\win32\$(Configuration);..\..\..\lib\dns\win32\$(Configuration);..\..\..\lib\isccc\win32\$(Configuration);..\..\..\lib\lwres\win32\$(Configuration);..\..\..\lib\isccfg\win32\$(Configuration);..\..\..\lib\bind9\win32\$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <AdditionalDependencies>@LIBXML2_LIB@@OPENSSL_LIB@@GSSAPI_LIB@@GEOIP_LIB@libisc.lib;libdns.lib;libisccc.lib;liblwres.lib;libisccfg.lib;libbind9.lib;version.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\builtin.c" />
+ <ClCompile Include="..\client.c" />
+ <ClCompile Include="..\config.c" />
+ <ClCompile Include="..\control.c" />
+ <ClCompile Include="..\controlconf.c" />
+@IF GEOIP
+ <ClCompile Include="..\geoip.c" />
+@END GEOIP
+ <ClCompile Include="..\interfacemgr.c" />
+ <ClCompile Include="..\listenlist.c" />
+ <ClCompile Include="..\log.c" />
+ <ClCompile Include="..\logconf.c" />
+ <ClCompile Include="..\lwaddr.c" />
+ <ClCompile Include="..\lwdclient.c" />
+ <ClCompile Include="..\lwderror.c" />
+ <ClCompile Include="..\lwdgabn.c" />
+ <ClCompile Include="..\lwdgnba.c" />
+ <ClCompile Include="..\lwdgrbn.c" />
+ <ClCompile Include="..\lwdnoop.c" />
+ <ClCompile Include="..\lwresd.c" />
+ <ClCompile Include="..\lwsearch.c" />
+ <ClCompile Include="..\main.c" />
+ <ClCompile Include="..\notify.c" />
+ <ClCompile Include="..\query.c" />
+ <ClCompile Include="..\server.c" />
+ <ClCompile Include="..\sortlist.c" />
+ <ClCompile Include="..\statschannel.c" />
+ <ClCompile Include="..\tkeyconf.c" />
+ <ClCompile Include="..\tsigconf.c" />
+ <ClCompile Include="..\update.c" />
+ <ClCompile Include="..\xfrout.c" />
+ <ClCompile Include="..\zoneconf.c" />
+ <ClCompile Include="dlz_dlopen_driver.c" />
+ <ClCompile Include="ntservice.c" />
+ <ClCompile Include="os.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\include\named\builtin.h" />
+ <ClInclude Include="..\include\named\client.h" />
+ <ClInclude Include="..\include\named\config.h" />
+ <ClInclude Include="..\include\named\control.h" />
+@IF GEOIP
+ <ClInclude Include="..\include\named\geoip.h" />
+@END GEOIP
+ <ClInclude Include="..\include\named\globals.h" />
+ <ClInclude Include="..\include\named\interfacemgr.h" />
+ <ClInclude Include="..\include\named\listenlist.h" />
+ <ClInclude Include="..\include\named\log.h" />
+ <ClInclude Include="..\include\named\logconf.h" />
+ <ClInclude Include="..\include\named\lwaddr.h" />
+ <ClInclude Include="..\include\named\lwdclient.h" />
+ <ClInclude Include="..\include\named\lwresd.h" />
+ <ClInclude Include="..\include\named\lwsearch.h" />
+ <ClInclude Include="..\include\named\main.h" />
+ <ClInclude Include="..\include\named\notify.h" />
+ <ClInclude Include="..\include\named\query.h" />
+ <ClInclude Include="..\include\named\seccomp.h" />
+ <ClInclude Include="..\include\named\server.h" />
+ <ClInclude Include="..\include\named\sortlist.h" />
+ <ClInclude Include="..\include\named\statschannel.h" />
+ <ClInclude Include="..\include\named\tkeyconf.h" />
+ <ClInclude Include="..\include\named\tsigconf.h" />
+ <ClInclude Include="..\include\named\types.h" />
+ <ClInclude Include="..\include\named\update.h" />
+ <ClInclude Include="..\include\named\xfrout.h" />
+ <ClInclude Include="..\include\named\zoneconf.h" />
+ <ClInclude Include="include\named\ntservice.h" />
+ <ClInclude Include="include\named\os.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/bin/named/win32/named.vcxproj.user b/bin/named/win32/named.vcxproj.user
new file mode 100644
index 0000000..695b5c7
--- /dev/null
+++ b/bin/named/win32/named.vcxproj.user
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+</Project> \ No newline at end of file
diff --git a/bin/named/win32/ntservice.c b/bin/named/win32/ntservice.c
new file mode 100644
index 0000000..23a5dc4
--- /dev/null
+++ b/bin/named/win32/ntservice.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include <isc/app.h>
+#include <isc/commandline.h>
+#include <isc/log.h>
+#include <isc/print.h>
+#include <isc/string.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);
+int bindmain(int, char *[]); /* From main.c */
+
+/*
+ * Initialize the Service by registering it.
+ */
+void
+ntservice_init(void) {
+ 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_RUNNING);
+ } else {
+ strlcpy(ConsoleTitle, "BIND Version ", sizeof(ConsoleTitle));
+ strlcat(ConsoleTitle, VERSION, sizeof(ConsoleTitle));
+ SetConsoleTitle(ConsoleTitle);
+ }
+}
+
+void
+ntservice_shutdown(void) {
+ UpdateSCM(SERVICE_STOPPED);
+}
+/*
+ * Routine to check if this is a service or a foreground program
+ */
+BOOL
+ntservice_isservice(void) {
+ 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, 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);
+ }
+ }
+}
+
+/* unhook main */
+
+#undef main
+
+/*
+ * This is the entry point for the executable
+ * We can now call bindmain() explicitly or via StartServiceCtrlDispatcher()
+ * as we need to.
+ */
+int main(int argc, char *argv[])
+{
+ int rc, ch;
+
+ /* Command line users should put -f in the options. */
+ isc_commandline_errprint = false;
+ while ((ch = isc_commandline_parse(argc, argv, NS_MAIN_ARGS)) != -1) {
+ switch (ch) {
+ case 'f':
+ case 'g':
+ case 'v':
+ case 'V':
+ foreground = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ isc_commandline_reset = true;
+
+ if (foreground) {
+ /* run in console window */
+ exit(bindmain(argc, argv));
+ } else {
+ /* Start up as service */
+ char *SERVICE_NAME = BIND_SERVICE_NAME;
+
+ SERVICE_TABLE_ENTRY dispatchTable[] = {
+ { TEXT(SERVICE_NAME),
+ (LPSERVICE_MAIN_FUNCTION)bindmain },
+ { NULL, NULL }
+ };
+
+ rc = StartServiceCtrlDispatcher(dispatchTable);
+ if (!rc) {
+ fprintf(stderr,
+ "Use -f to run from the command line.\n");
+ /* will be 1063 when launched as a console app */
+ exit(GetLastError());
+ }
+ }
+ exit(0);
+}
diff --git a/bin/named/win32/os.c b/bin/named/win32/os.c
new file mode 100644
index 0000000..f89c50d
--- /dev/null
+++ b/bin/named/win32/os.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <sys/types.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 *lockfile = NULL;
+static char *pidfile = NULL;
+static int devnullfd = -1;
+static int lockfilefd = -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(void) {
+ if (!Initialized)
+ isc_ntpaths_init();
+
+ 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_defaultlockfile = isc_ntpaths_get(NAMED_LOCK_PATH);
+ ns_g_keyfile = isc_ntpaths_get(RNDC_KEY_PATH);
+ ns_g_defaultsessionkeyfile = isc_ntpaths_get(SESSION_KEY_PATH);
+ ns_g_defaultdnstap = NULL;
+
+ 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_versioncheck(4, 0, 0, 0) >= 0) &&
+ (isc_win32os_versioncheck(5, 0, 0, 0) < 0))
+ 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) {
+}
+
+unsigned int
+ns_os_uid(void) {
+ return (0);
+}
+
+void
+ns_os_adjustnofile(void) {
+}
+
+void
+ns_os_minprivs(void) {
+}
+
+static int
+safe_open(const char *filename, int mode, bool 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, mode);
+ else {
+ (void)unlink(filename);
+ fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, mode);
+ }
+ return (fd);
+}
+
+static void
+cleanup_pidfile(void) {
+ if (pidfile != NULL) {
+ (void)unlink(pidfile);
+ free(pidfile);
+ }
+ pidfile = NULL;
+}
+
+static void
+cleanup_lockfile(void) {
+ if (lockfilefd != -1) {
+ close(lockfilefd);
+ lockfilefd = -1;
+ }
+
+ if (lockfile != NULL) {
+ int n = unlink(lockfile);
+ if (n == -1 && errno != ENOENT)
+ ns_main_earlywarning("unlink '%s': failed", lockfile);
+ free(lockfile);
+ lockfile = NULL;
+ }
+}
+
+FILE *
+ns_os_openfile(const char *filename, int mode, bool switch_user) {
+ char strbuf[ISC_STRERRORSIZE];
+ FILE *fp;
+ int fd;
+
+ UNUSED(switch_user);
+ fd = safe_open(filename, mode, false);
+ if (fd < 0) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("could not open file '%s': %s",
+ filename, strbuf);
+ return (NULL);
+ }
+
+ fp = fdopen(fd, "w");
+ if (fp == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlywarning("could not fdopen() file '%s': %s",
+ filename, strbuf);
+ close(fd);
+ }
+
+ return (fp);
+}
+
+void
+ns_os_writepidfile(const char *filename, bool first_time) {
+ FILE *pidlockfile;
+ 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;
+
+ pidfile = strdup(filename);
+ if (pidfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ (*report)("couldn't strdup() '%s': %s", filename, strbuf);
+ return;
+ }
+
+ pidlockfile = ns_os_openfile(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
+ false);
+ if (pidlockfile == NULL) {
+ free(pidfile);
+ pidfile = NULL;
+ return;
+ }
+
+ pid = getpid();
+
+ if (fprintf(pidlockfile, "%ld\n", (long)pid) < 0) {
+ (*report)("fprintf() to pid file '%s' failed", filename);
+ (void)fclose(pidlockfile);
+ cleanup_pidfile();
+ return;
+ }
+ if (fflush(pidlockfile) == EOF) {
+ (*report)("fflush() to pid file '%s' failed", filename);
+ (void)fclose(pidlockfile);
+ cleanup_pidfile();
+ return;
+ }
+ (void)fclose(pidlockfile);
+}
+
+bool
+ns_os_issingleton(const char *filename) {
+ char strbuf[ISC_STRERRORSIZE];
+ OVERLAPPED o;
+
+ if (lockfilefd != -1)
+ return (true);
+
+ if (strcasecmp(filename, "none") == 0)
+ return (true);
+
+ lockfile = strdup(filename);
+ if (lockfile == NULL) {
+ isc__strerror(errno, strbuf, sizeof(strbuf));
+ ns_main_earlyfatal("couldn't allocate memory for '%s': %s",
+ filename, strbuf);
+ }
+
+ /*
+ * ns_os_openfile() uses safeopen() which removes any existing
+ * files. We can't use that here.
+ */
+ lockfilefd = open(filename, O_WRONLY | O_CREAT,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+ if (lockfilefd == -1) {
+ cleanup_lockfile();
+ return (false);
+ }
+
+ memset(&o, 0, sizeof(o));
+ /* Expect ERROR_LOCK_VIOLATION if already locked */
+ if (!LockFileEx((HANDLE) _get_osfhandle(lockfilefd),
+ LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
+ 0, 0, 1, &o)) {
+ cleanup_lockfile();
+ return (false);
+ }
+
+ return (true);
+}
+
+
+void
+ns_os_shutdown(void) {
+ closelog();
+ cleanup_pidfile();
+
+ if (lockfilefd != -1) {
+ (void) UnlockFile((HANDLE) _get_osfhandle(lockfilefd),
+ 0, 0, 0, 1);
+ close(lockfilefd);
+ lockfilefd = -1;
+ }
+ 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, (int)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();
+}
+
+static char unamebuf[BUFSIZ];
+static char *unamep = NULL;
+
+static void
+getuname(void) {
+ DWORD fvilen;
+ char *fvi;
+ VS_FIXEDFILEINFO *ffi;
+ UINT ffilen;
+ SYSTEM_INFO sysinfo;
+ char *arch;
+
+ fvi = NULL;
+ fvilen = GetFileVersionInfoSize("kernel32.dll", 0);
+ if (fvilen == 0) {
+ goto err;
+ }
+ fvi = (char *)malloc(fvilen);
+ if (fvi == NULL) {
+ goto err;
+ }
+ memset(fvi, 0, fvilen);
+ if (GetFileVersionInfo("kernel32.dll", 0, fvilen, fvi) == 0) {
+ goto err;
+ }
+ ffi = NULL;
+ ffilen = 0;
+ if ((VerQueryValue(fvi, "\\", &ffi, &ffilen) == 0) ||
+ (ffi == NULL) || (ffilen == 0)) {
+ goto err;
+ }
+ memset(&sysinfo, 0, sizeof(sysinfo));
+ GetSystemInfo(&sysinfo);
+ switch (sysinfo.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ arch = "x86";
+ break;
+ case PROCESSOR_ARCHITECTURE_ARM:
+ arch = "arm";
+ break;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ arch = "ia64";
+ break;
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ arch = "x64";
+ break;
+ default:
+ arch = "unknown architecture";
+ break;
+ }
+
+ snprintf(unamebuf, sizeof(unamebuf),
+ "Windows %d %d build %d %d for %s\n",
+ (ffi->dwProductVersionMS >> 16) & 0xffff,
+ ffi->dwProductVersionMS & 0xffff,
+ (ffi->dwProductVersionLS >> 16) & 0xffff,
+ ffi->dwProductVersionLS & 0xffff,
+ arch);
+
+ err:
+ if (fvi != NULL) {
+ free(fvi);
+ }
+ unamep = unamebuf;
+}
+
+/*
+ * GetVersionEx() returns 6.2 (aka Windows 8.1) since it was obsoleted
+ * so we had to switch to the recommended way to get the Windows version.
+ */
+char *
+ns_os_uname(void) {
+ if (unamep == NULL)
+ getuname();
+ return (unamep);
+}
diff --git a/bin/named/xfrout.c b/bin/named/xfrout.c
new file mode 100644
index 0000000..2da7e70
--- /dev/null
+++ b/bin/named/xfrout.c
@@ -0,0 +1,1714 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+/* $Id$ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/formatcheck.h>
+#include <isc/mem.h>
+#include <isc/timer.h>
+#include <isc/print.h>
+#include <isc/stats.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/dlz.h>
+#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/rriterator.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)
+
+/**************************************************************************/
+
+static inline void
+inc_stats(dns_zone_t *zone, isc_statscounter_t counter) {
+ isc_stats_increment(ns_g_server->nsstats, counter);
+ if (zone != NULL) {
+ isc_stats_t *zonestats = dns_zone_getrequeststats(zone);
+ if (zonestats != NULL)
+ isc_stats_increment(zonestats, counter);
+ }
+}
+
+/**************************************************************************/
+
+/*% Log an RR (for debugging) */
+
+static void
+log_rr(dns_name_t *name, dns_rdata_t *rdata, 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;
+
+ dns_rdatalist_init(&rdl);
+ 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;
+ 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,
+ false, 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 **,
+ 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,
+ uint32_t begin_serial,
+ 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 = NULL;
+ isc_mem_attach(mctx, &s->common.mctx);
+ s->common.methods = &ixfr_rrstream_methods;
+ s->journal = NULL;
+
+ CHECK(dns_journal_open(mctx, journal_filename,
+ DNS_JOURNAL_READ, &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, 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_putanddetach(&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;
+ dns_rriterator_t it;
+ bool 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 = NULL;
+ isc_mem_attach(mctx, &s->common.mctx);
+ s->common.methods = &axfr_rrstream_methods;
+ s->it_valid = false;
+
+ CHECK(dns_rriterator_init(&s->it, db, ver, 0));
+ s->it_valid = 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 = dns_rriterator_first(&s->it);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ /* Skip SOA records. */
+ for (;;) {
+ dns_name_t *name_dummy = NULL;
+ uint32_t ttl_dummy;
+ dns_rdata_t *rdata = NULL;
+ dns_rriterator_current(&s->it, &name_dummy,
+ &ttl_dummy, NULL, &rdata);
+ if (rdata->type != dns_rdatatype_soa)
+ break;
+ result = dns_rriterator_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;
+ uint32_t ttl_dummy;
+ dns_rdata_t *rdata = NULL;
+ result = dns_rriterator_next(&s->it);
+ if (result != ISC_R_SUCCESS)
+ break;
+ dns_rriterator_current(&s->it, &name_dummy,
+ &ttl_dummy, NULL, &rdata);
+ if (rdata->type != dns_rdatatype_soa)
+ break;
+ }
+ return (result);
+}
+
+static void
+axfr_rrstream_current(rrstream_t *rs, dns_name_t **name, uint32_t *ttl,
+ dns_rdata_t **rdata)
+{
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ dns_rriterator_current(&s->it, name, ttl, NULL, rdata);
+}
+
+static void
+axfr_rrstream_pause(rrstream_t *rs) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) rs;
+ dns_rriterator_pause(&s->it);
+}
+
+static void
+axfr_rrstream_destroy(rrstream_t **rsp) {
+ axfr_rrstream_t *s = (axfr_rrstream_t *) *rsp;
+ if (s->it_valid)
+ dns_rriterator_destroy(&s->it);
+ isc_mem_putanddetach(&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 = NULL;
+ isc_mem_attach(mctx, &s->common.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, 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_putanddetach(&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 = NULL;
+ isc_mem_attach(mctx, &s->common.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, 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_putanddetach(&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 */
+ bool 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 */
+ bool verified_tsig; /* verified request MAC */
+ bool many_answers;
+ int sends; /* Send in progress */
+ bool 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,
+ bool verified_tsig,
+ unsigned int maxtime,
+ unsigned int idletime,
+ bool 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, *raw = NULL, *mayberaw;
+ 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;
+ bool have_soa = 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];
+ bool is_poll = false;
+ bool is_dlz = false;
+ bool is_ixfr = false;
+ uint32_t begin_serial = 0, current_serial;
+
+ 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 || dns_zone_gettype(zone) == dns_zone_dlz) {
+ /*
+ * The normal zone table does not have a match, or this is
+ * marked in the zone table as a DLZ zone. Check the DLZ
+ * databases for a match.
+ */
+ if (! ISC_LIST_EMPTY(client->view->dlz_searched)) {
+ 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)
+ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
+ question_name, question_class);
+ is_dlz = true;
+ } 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 */
+ switch(dns_zone_gettype(zone)) {
+ /* Master and slave zones are OK for transfer. */
+ case dns_zone_master:
+ case dns_zone_slave:
+ case dns_zone_dlz:
+ break;
+ default:
+ FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
+ question_name, question_class);
+ }
+ CHECK(dns_zone_getdb(zone, &db));
+ dns_db_currentversion(db, &ver);
+ }
+
+ 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 = 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);
+
+ /*
+ * If not a DLZ zone, decide whether to allow this transfer.
+ */
+ if (!is_dlz) {
+ 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),
+ true, ISC_LOG_ERROR));
+ }
+
+ /*
+ * 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.
+ */
+ if (is_dlz)
+ dns_db_currentversion(db, &ver);
+
+ CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
+ &current_soa_tuple));
+
+ current_serial = dns_soa_getserial(&current_soa_tuple->rdata);
+ if (reqtype == dns_rdatatype_ixfr) {
+ bool 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 == false)
+ goto axfr_fallback;
+
+ if (! have_soa)
+ FAILC(DNS_R_FORMERR,
+ "IXFR request missing SOA");
+
+ begin_serial = dns_soa_getserial(&soa_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 = true;
+ goto have_stream;
+ }
+ journalfile = is_dlz ? NULL : 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);
+ is_ixfr = true;
+ } else {
+ axfr_fallback:
+ CHECK(axfr_rrstream_create(mctx, db, ver, &data_stream));
+ }
+
+ /*
+ * Bracket 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.
+ */
+
+
+
+ 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,
+ request->verified_sig,
+ 3600,
+ 3600,
+ (format == dns_many_answers) ?
+ true : false,
+ &xfr));
+ else
+ CHECK(xfrout_ctx_create(mctx, client, request->id,
+ question_name, reqtype, question_class,
+ zone, db, ver, quota, stream,
+ dns_message_gettsigkey(request),
+ tsigbuf,
+ request->verified_sig,
+ dns_zone_getmaxxfrout(zone),
+ dns_zone_getidleout(zone),
+ (format == dns_many_answers) ?
+ true : 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 if (is_ixfr)
+ xfrout_log1(client, question_name, question_class,
+ ISC_LOG_INFO, "%s started%s%s (serial %u -> %u)",
+ mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "",
+ keyname, begin_serial, current_serial);
+ else
+ xfrout_log1(client, question_name, question_class,
+ ISC_LOG_INFO, "%s started%s%s (serial %u)",
+ mnemonic, (xfr->tsigkey != NULL) ? ": TSIG " : "",
+ keyname, current_serial);
+
+
+ if (zone != NULL) {
+ dns_zone_getraw(zone, &raw);
+ mayberaw = (raw != NULL) ? raw : zone;
+ if ((client->attributes & NS_CLIENTATTR_WANTEXPIRE) != 0 &&
+ dns_zone_gettype(mayberaw) == dns_zone_slave) {
+ isc_time_t expiretime;
+ uint32_t secs;
+ dns_zone_getexpiretime(zone, &expiretime);
+ secs = isc_time_seconds(&expiretime);
+ if (secs >= client->now && result == ISC_R_SUCCESS) {
+ client->attributes |= NS_CLIENTATTR_HAVEEXPIRE;
+ client->expire = secs - client->now;
+ }
+ }
+ if (raw != NULL)
+ dns_zone_detach(&raw);
+ }
+
+ /*
+ * 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, 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, bool verified_tsig,
+ unsigned int maxtime, unsigned int idletime,
+ bool 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 = NULL;
+ isc_mem_attach(mctx, &xfr->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 = false;
+ xfr->tsigkey = tsigkey;
+ xfr->lasttsig = lasttsig;
+ xfr->verified_tsig = verified_tsig;
+ xfr->nmsg = 0;
+ xfr->many_answers = many_answers;
+ xfr->sends = 0;
+ xfr->shuttingdown = 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, 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 possibly 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;
+ bool cleanup_cctx = false;
+ bool is_tcp;
+
+ int n_rrs;
+
+ isc_buffer_clear(&xfr->buf);
+ isc_buffer_clear(&xfr->txlenbuf);
+ isc_buffer_clear(&xfr->txbuf);
+
+ is_tcp = (xfr->client->attributes & NS_CLIENTATTR_TCP);
+ if (!is_tcp) {
+ /*
+ * In the UDP case, we put the response data directly into
+ * the client message.
+ */
+ msg = xfr->client->message;
+ CHECK(dns_message_reply(msg, 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);
+ msg->verified_sig = xfr->verified_tsig;
+
+ /*
+ * Add a EDNS option to the message?
+ */
+ if ((xfr->client->attributes & NS_CLIENTATTR_WANTOPT) != 0) {
+ dns_rdataset_t *opt = NULL;
+
+ CHECK(ns_client_addopt(xfr->client, msg, &opt));
+ CHECK(dns_message_setopt(msg, opt));
+ /*
+ * Add to first message only.
+ */
+ xfr->client->attributes &= ~NS_CLIENTATTR_WANTNSID;
+ xfr->client->attributes &= ~NS_CLIENTATTR_HAVEEXPIRE;
+ }
+
+ /*
+ * Account for reserved space.
+ */
+ if (xfr->tsigkey != NULL)
+ INSIST(msg->reserved != 0U);
+ isc_buffer_add(&xfr->buf, msg->reserved);
+
+ /*
+ * 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_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 {
+ /*
+ * Reserve space for the 12-byte message header
+ */
+ isc_buffer_add(&xfr->buf, 12);
+ 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;
+ 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_LIST_APPEND(msgrdl->rdata, msgrdata, link);
+
+ result = dns_message_gettemprdataset(msg, &msgrds);
+ if (result != ISC_R_SUCCESS)
+ goto failure;
+ 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 = true;
+ break;
+ }
+ CHECK(result);
+
+ if (! xfr->many_answers)
+ break;
+ /*
+ * At this stage, at least 1 RR has been rendered into
+ * the message. Check if we want to clamp this message
+ * here (TCP only).
+ */
+ if ((isc_buffer_usedlength(&xfr->buf) >=
+ ns_g_server->transfer_tcp_message_size) && is_tcp)
+ break;
+ }
+
+ if (is_tcp) {
+ CHECK(dns_compress_init(&cctx, -1, xfr->mctx));
+ dns_compress_setsensitive(&cctx, true);
+ cleanup_cctx = 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 = false;
+
+ isc_buffer_usedregion(&xfr->txbuf, &used);
+ isc_buffer_putuint16(&xfr->txlenbuf,
+ (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;
+ ns_client_t *client = NULL;
+
+ 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, false);
+ if (xfr->zone != NULL)
+ dns_zone_detach(&xfr->zone);
+ if (xfr->db != NULL)
+ dns_db_detach(&xfr->db);
+
+ /*
+ * We want to detch the client after we have released the memory
+ * context as ns_client_detach checks the memory reference count.
+ */
+ ns_client_attach(xfr->client, &client);
+ ns_client_detach(&xfr->client);
+ isc_mem_putanddetach(&xfr->mctx, xfr, sizeof(*xfr));
+ ns_client_detach(&client);
+
+ *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 == true) {
+ xfrout_maybe_destroy(xfr);
+ } else if (evresult != ISC_R_SUCCESS) {
+ xfrout_fail(xfr, evresult, "send");
+ } else if (xfr->end_of_stream == 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 = 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 == 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..e237bdb
--- /dev/null
+++ b/bin/named/zoneconf.c
@@ -0,0 +1,1824 @@
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <config.h>
+
+#include <inttypes.h>
+#include <stdbool.h>
+
+#include <isc/buffer.h>
+#include <isc/file.h>
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/stats.h>
+#include <isc/string.h> /* Required for HP/UX (and others?) */
+#include <isc/util.h>
+
+#include <dns/acl.h>
+#include <dns/db.h>
+#include <dns/ipkeylist.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/name.h>
+#include <dns/masterdump.h>
+#include <dns/rdata.h>
+#include <dns/rdatatype.h>
+#include <dns/rdataset.h>
+#include <dns/rdatalist.h>
+#include <dns/result.h>
+#include <dns/sdlz.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_query_on,
+ allow_transfer,
+ allow_update,
+ allow_update_forwarding
+} acl_type_t;
+
+#define RETERR(x) do { \
+ isc_result_t _r = (x); \
+ if (_r != ISC_R_SUCCESS) \
+ return (_r); \
+ } while (0)
+
+#define CHECK(x) do { \
+ result = (x); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } 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_query_on:
+ if (view != NULL)
+ aclp = &view->queryonacl;
+ aclname = "allow-query-on";
+ 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");
+ (void)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) {
+ const cfg_obj_t *options = cfg_tuple_get(vconfig, "options");
+ if (options != NULL)
+ maps[i++] = 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;
+
+ (void)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 char *zname)
+{
+ 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);
+ bool autoddns = false;
+ isc_result_t result;
+
+ (void)cfg_map_get(zconfig, "update-policy", &updatepolicy);
+
+ if (updatepolicy == NULL) {
+ dns_zone_setssutable(zone, NULL);
+ return (ISC_R_SUCCESS);
+ }
+
+ if (cfg_obj_isstring(updatepolicy) &&
+ strcmp("local", cfg_obj_asstring(updatepolicy)) == 0) {
+ autoddns = true;
+ updatepolicy = NULL;
+ }
+
+ 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;
+ bool grant = false;
+ bool usezone = false;
+ dns_ssumatchtype_t 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 = true;
+ else if (strcasecmp(str, "deny") == 0)
+ grant = false;
+ else
+ INSIST(0);
+
+ str = cfg_obj_asstring(matchtype);
+ CHECK(dns_ssu_mtypefromstring(str, &mtype));
+ if (mtype == dns_ssumatchtype_subdomain) {
+ usezone = true;
+ }
+
+ dns_fixedname_init(&fident);
+ str = cfg_obj_asstring(identity);
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
+ dns_rootname, 0, 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);
+ if (usezone) {
+ result = dns_name_copy(dns_zone_getorigin(zone),
+ dns_fixedname_name(&fname),
+ NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
+ "error copying origin: %s",
+ isc_result_totext(result));
+ goto cleanup;
+ }
+ } else {
+ str = cfg_obj_asstring(dname);
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(dns_fixedname_name(&fname),
+ &b, dns_rootname, 0, 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;
+ }
+ }
+
+ /*
+ * If "update-policy local;" and a session key exists,
+ * then use the default policy, which is equivalent to:
+ * update-policy { grant <session-keyname> zonesub any; };
+ */
+ if (autoddns) {
+ dns_rdatatype_t any = dns_rdatatype_any;
+
+ if (ns_g_server->session_keyname == NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "failed to enable auto DDNS policy "
+ "for zone %s: session key not found",
+ zname);
+ result = ISC_R_NOTFOUND;
+ goto cleanup;
+ }
+
+ result = dns_ssutable_addrule(table, true,
+ ns_g_server->session_keyname,
+ DNS_SSUMATCHTYPE_LOCAL,
+ dns_zone_getorigin(zone),
+ 1, &any);
+
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ result = ISC_R_SUCCESS;
+ dns_zone_setssutable(zone, table);
+
+ cleanup:
+ dns_ssutable_detach(&table);
+ return (result);
+}
+
+/*
+ * This is the TTL used for internally generated RRsets for static-stub zones.
+ * The value doesn't matter because the mapping is static, but needs to be
+ * defined for the sake of implementation.
+ */
+#define STATICSTUB_SERVER_TTL 86400
+
+/*%
+ * Configure an apex NS with glues for a static-stub zone.
+ * For example, for the zone named "example.com", the following RRs will be
+ * added to the zone DB:
+ * example.com. NS example.com.
+ * example.com. A 192.0.2.1
+ * example.com. AAAA 2001:db8::1
+ */
+static isc_result_t
+configure_staticstub_serveraddrs(const cfg_obj_t *zconfig, dns_zone_t *zone,
+ dns_rdatalist_t *rdatalist_ns,
+ dns_rdatalist_t *rdatalist_a,
+ dns_rdatalist_t *rdatalist_aaaa)
+{
+ const cfg_listelt_t *element;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ isc_region_t region, sregion;
+ dns_rdata_t *rdata;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (element = cfg_list_first(zconfig);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const isc_sockaddr_t* sa;
+ isc_netaddr_t na;
+ const cfg_obj_t *address = cfg_listelt_value(element);
+ dns_rdatalist_t *rdatalist;
+
+ sa = cfg_obj_assockaddr(address);
+ if (isc_sockaddr_getport(sa) != 0) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "port is not configurable for "
+ "static stub server-addresses");
+ return (ISC_R_FAILURE);
+ }
+ isc_netaddr_fromsockaddr(&na, sa);
+ if (isc_netaddr_getzone(&na) != 0) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "scoped address is not allowed "
+ "for static stub "
+ "server-addresses");
+ return (ISC_R_FAILURE);
+ }
+
+ switch (na.family) {
+ case AF_INET:
+ region.length = sizeof(na.type.in);
+ rdatalist = rdatalist_a;
+ break;
+ default:
+ INSIST(na.family == AF_INET6);
+ region.length = sizeof(na.type.in6);
+ rdatalist = rdatalist_aaaa;
+ break;
+ }
+
+ rdata = isc_mem_get(mctx, sizeof(*rdata) + region.length);
+ if (rdata == NULL)
+ return (ISC_R_NOMEMORY);
+ region.base = (unsigned char *)(rdata + 1);
+ memmove(region.base, &na.type, region.length);
+ dns_rdata_init(rdata);
+ dns_rdata_fromregion(rdata, dns_zone_getclass(zone),
+ rdatalist->type, &region);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ }
+
+ /*
+ * If no address is specified (unlikely in this context, but possible),
+ * there's nothing to do anymore.
+ */
+ if (ISC_LIST_EMPTY(rdatalist_a->rdata) &&
+ ISC_LIST_EMPTY(rdatalist_aaaa->rdata)) {
+ return (ISC_R_SUCCESS);
+ }
+
+ /* Add to the list an apex NS with the ns name being the origin name */
+ dns_name_toregion(dns_zone_getorigin(zone), &sregion);
+ rdata = isc_mem_get(mctx, sizeof(*rdata) + sregion.length);
+ if (rdata == NULL) {
+ /*
+ * Already allocated data will be freed in the caller, so
+ * we can simply return here.
+ */
+ return (ISC_R_NOMEMORY);
+ }
+ region.length = sregion.length;
+ region.base = (unsigned char *)(rdata + 1);
+ memmove(region.base, sregion.base, region.length);
+ dns_rdata_init(rdata);
+ dns_rdata_fromregion(rdata, dns_zone_getclass(zone),
+ dns_rdatatype_ns, &region);
+ ISC_LIST_APPEND(rdatalist_ns->rdata, rdata, link);
+
+ return (result);
+}
+
+/*%
+ * Configure an apex NS with an out-of-zone NS names for a static-stub zone.
+ * For example, for the zone named "example.com", something like the following
+ * RRs will be added to the zone DB:
+ * example.com. NS ns.example.net.
+ */
+static isc_result_t
+configure_staticstub_servernames(const cfg_obj_t *zconfig, dns_zone_t *zone,
+ dns_rdatalist_t *rdatalist, const char *zname)
+{
+ const cfg_listelt_t *element;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ dns_rdata_t *rdata;
+ isc_region_t sregion, region;
+ isc_result_t result = ISC_R_SUCCESS;
+
+ for (element = cfg_list_first(zconfig);
+ element != NULL;
+ element = cfg_list_next(element))
+ {
+ const cfg_obj_t *obj;
+ const char *str;
+ dns_fixedname_t fixed_name;
+ dns_name_t *nsname;
+ isc_buffer_t b;
+
+ obj = cfg_listelt_value(element);
+ str = cfg_obj_asstring(obj);
+
+ nsname = dns_fixedname_initname(&fixed_name);
+
+ isc_buffer_constinit(&b, str, strlen(str));
+ isc_buffer_add(&b, strlen(str));
+ result = dns_name_fromtext(nsname, &b, dns_rootname, 0, NULL);
+ if (result != ISC_R_SUCCESS) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "server-name '%s' is not a valid "
+ "name", str);
+ return (result);
+ }
+ if (dns_name_issubdomain(nsname, dns_zone_getorigin(zone))) {
+ cfg_obj_log(zconfig, ns_g_lctx, ISC_LOG_ERROR,
+ "server-name '%s' must not be a "
+ "subdomain of zone name '%s'",
+ str, zname);
+ return (ISC_R_FAILURE);
+ }
+
+ dns_name_toregion(nsname, &sregion);
+ rdata = isc_mem_get(mctx, sizeof(*rdata) + sregion.length);
+ if (rdata == NULL)
+ return (ISC_R_NOMEMORY);
+ region.length = sregion.length;
+ region.base = (unsigned char *)(rdata + 1);
+ memmove(region.base, sregion.base, region.length);
+ dns_rdata_init(rdata);
+ dns_rdata_fromregion(rdata, dns_zone_getclass(zone),
+ dns_rdatatype_ns, &region);
+ ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+ }
+
+ return (result);
+}
+
+/*%
+ * Configure static-stub zone.
+ */
+static isc_result_t
+configure_staticstub(const cfg_obj_t *zconfig, dns_zone_t *zone,
+ const char *zname, const char *dbtype)
+{
+ int i = 0;
+ const cfg_obj_t *obj;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ dns_db_t *db = NULL;
+ dns_dbversion_t *dbversion = NULL;
+ dns_dbnode_t *apexnode = NULL;
+ dns_name_t apexname;
+ isc_result_t result;
+ dns_rdataset_t rdataset;
+ dns_rdatalist_t rdatalist_ns, rdatalist_a, rdatalist_aaaa;
+ dns_rdatalist_t* rdatalists[] = {
+ &rdatalist_ns, &rdatalist_a, &rdatalist_aaaa, NULL
+ };
+ dns_rdata_t *rdata;
+ isc_region_t region;
+
+ /* Create the DB beforehand */
+ RETERR(dns_db_create(mctx, dbtype, dns_zone_getorigin(zone),
+ dns_dbtype_stub, dns_zone_getclass(zone),
+ 0, NULL, &db));
+ dns_zone_setdb(zone, db);
+
+ dns_rdatalist_init(&rdatalist_ns);
+ rdatalist_ns.rdclass = dns_zone_getclass(zone);
+ rdatalist_ns.type = dns_rdatatype_ns;
+ rdatalist_ns.ttl = STATICSTUB_SERVER_TTL;
+
+ dns_rdatalist_init(&rdatalist_a);
+ rdatalist_a.rdclass = dns_zone_getclass(zone);
+ rdatalist_a.type = dns_rdatatype_a;
+ rdatalist_a.ttl = STATICSTUB_SERVER_TTL;
+
+ dns_rdatalist_init(&rdatalist_aaaa);
+ rdatalist_aaaa.rdclass = dns_zone_getclass(zone);
+ rdatalist_aaaa.type = dns_rdatatype_aaaa;
+ rdatalist_aaaa.ttl = STATICSTUB_SERVER_TTL;
+
+ /* Prepare zone RRs from the configuration */
+ obj = NULL;
+ result = cfg_map_get(zconfig, "server-addresses", &obj);
+ if (result == ISC_R_SUCCESS) {
+ INSIST(obj != NULL);
+ result = configure_staticstub_serveraddrs(obj, zone,
+ &rdatalist_ns,
+ &rdatalist_a,
+ &rdatalist_aaaa);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ obj = NULL;
+ result = cfg_map_get(zconfig, "server-names", &obj);
+ if (result == ISC_R_SUCCESS) {
+ INSIST(obj != NULL);
+ result = configure_staticstub_servernames(obj, zone,
+ &rdatalist_ns,
+ zname);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ /*
+ * Sanity check: there should be at least one NS RR at the zone apex
+ * to trigger delegation.
+ */
+ if (ISC_LIST_EMPTY(rdatalist_ns.rdata)) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "No NS record is configured for a "
+ "static-stub zone '%s'", zname);
+ result = ISC_R_FAILURE;
+ goto cleanup;
+ }
+
+ /*
+ * Now add NS and glue A/AAAA RRsets to the zone DB.
+ * First open a new version for the add operation and get a pointer
+ * to the apex node (all RRs are of the apex name).
+ */
+ result = dns_db_newversion(db, &dbversion);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ dns_name_init(&apexname, NULL);
+ dns_name_clone(dns_zone_getorigin(zone), &apexname);
+ result = dns_db_findnode(db, &apexname, false, &apexnode);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /* Add NS RRset */
+ dns_rdataset_init(&rdataset);
+ RUNTIME_CHECK(dns_rdatalist_tordataset(&rdatalist_ns, &rdataset)
+ == ISC_R_SUCCESS);
+ result = dns_db_addrdataset(db, apexnode, dbversion, 0, &rdataset,
+ 0, NULL);
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+
+ /* Add glue A RRset, if any */
+ if (!ISC_LIST_EMPTY(rdatalist_a.rdata)) {
+ RUNTIME_CHECK(dns_rdatalist_tordataset(&rdatalist_a, &rdataset)
+ == ISC_R_SUCCESS);
+ result = dns_db_addrdataset(db, apexnode, dbversion, 0,
+ &rdataset, 0, NULL);
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ /* Add glue AAAA RRset, if any */
+ if (!ISC_LIST_EMPTY(rdatalist_aaaa.rdata)) {
+ RUNTIME_CHECK(dns_rdatalist_tordataset(&rdatalist_aaaa,
+ &rdataset)
+ == ISC_R_SUCCESS);
+ result = dns_db_addrdataset(db, apexnode, dbversion, 0,
+ &rdataset, 0, NULL);
+ dns_rdataset_disassociate(&rdataset);
+ if (result != ISC_R_SUCCESS)
+ goto cleanup;
+ }
+
+ result = ISC_R_SUCCESS;
+
+ cleanup:
+ if (apexnode != NULL)
+ dns_db_detachnode(db, &apexnode);
+ if (dbversion != NULL)
+ dns_db_closeversion(db, &dbversion, true);
+ if (db != NULL)
+ dns_db_detach(&db);
+ for (i = 0; rdatalists[i] != NULL; i++) {
+ while ((rdata = ISC_LIST_HEAD(rdatalists[i]->rdata)) != NULL) {
+ ISC_LIST_UNLINK(rdatalists[i]->rdata, rdata, link);
+ dns_rdata_toregion(rdata, &region);
+ isc_mem_put(mctx, rdata,
+ sizeof(*rdata) + region.length);
+ }
+ }
+
+ INSIST(dbversion == NULL);
+
+ 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 && obj != NULL);
+ 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 && objp != NULL && *objp != NULL);
+}
+
+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, dns_zone_t *raw)
+{
+ 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 *nodefault[4];
+ const cfg_obj_t *zoptions = NULL;
+ const cfg_obj_t *options = NULL;
+ const cfg_obj_t *obj;
+ const char *filename = NULL;
+ const char *dupcheck;
+ dns_notifytype_t notifytype = dns_notifytype_yes;
+ uint32_t count;
+ unsigned int dbargc;
+ char **dbargv;
+ static char default_dbtype[] = "rbt";
+ static char dlz_dbtype[] = "dlz";
+ char *cpval = default_dbtype;
+ isc_mem_t *mctx = dns_zone_getmctx(zone);
+ dns_dialuptype_t dialup = dns_dialuptype_no;
+ dns_zonetype_t ztype;
+ int i;
+ int32_t journal_size;
+ bool multi;
+ bool alt;
+ dns_view_t *view;
+ bool check = false, fail = false;
+ bool warn = false, ignore = false;
+ bool ixfrdiff;
+ dns_masterformat_t masterformat;
+ const dns_master_style_t *masterstyle = &dns_master_style_default;
+ isc_stats_t *zoneqrystats;
+ dns_stats_t *rcvquerystats;
+ dns_zonestat_level_t statlevel;
+ int seconds;
+ dns_zone_t *mayberaw = (raw != NULL) ? raw : zone;
+ isc_dscp_t dscp;
+
+ i = 0;
+ if (zconfig != NULL) {
+ zoptions = cfg_tuple_get(zconfig, "options");
+ nodefault[i] = maps[i] = zoptions;
+ i++;
+ }
+ if (vconfig != NULL) {
+ nodefault[i] = maps[i] = cfg_tuple_get(vconfig, "options");
+ i++;
+ }
+ if (config != NULL) {
+ (void)cfg_map_get(config, "options", &options);
+ if (options != NULL) {
+ nodefault[i] = maps[i] = options;
+ i++;
+ }
+ }
+ nodefault[i] = NULL;
+ 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);
+ if (raw != NULL)
+ dns_zone_setclass(raw, zclass);
+
+ ztype = zonetype_fromconfig(zoptions);
+ if (raw != NULL) {
+ dns_zone_settype(raw, ztype);
+ dns_zone_settype(zone, dns_zone_master);
+ } else
+ 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));
+ if (cpval == NULL)
+ return(ISC_R_NOMEMORY);
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "dlz", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *dlzname = cfg_obj_asstring(obj);
+ size_t len;
+
+ if (cpval != default_dbtype) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': both 'database' and 'dlz' "
+ "specified", zname);
+ return (ISC_R_FAILURE);
+ }
+
+ len = strlen(dlzname) + 5;
+ cpval = isc_mem_allocate(mctx, len);
+ if (cpval == NULL)
+ return (ISC_R_NOMEMORY);
+ snprintf(cpval, len, "dlz %s", dlzname);
+ }
+
+ 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 && cpval != dlz_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);
+
+ /*
+ * Unless we're using some alternative database, a master zone
+ * will be needing a master file.
+ */
+ if (ztype == dns_zone_master && cpval == default_dbtype &&
+ filename == NULL) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': 'file' not specified",
+ zname);
+ return (ISC_R_FAILURE);
+ }
+
+ if (ztype == dns_zone_slave)
+ masterformat = dns_masterformat_raw;
+ else
+ 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 if (strcasecmp(masterformatstr, "map") == 0)
+ masterformat = dns_masterformat_map;
+ else
+ INSIST(0);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "masterfile-style", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *masterstylestr = cfg_obj_asstring(obj);
+
+ if (masterformat != dns_masterformat_text) {
+ cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
+ "zone '%s': 'masterfile-style' "
+ "can only be used with "
+ "'masterfile-format text'", zname);
+ return (ISC_R_FAILURE);
+ }
+
+ if (strcasecmp(masterstylestr, "full") == 0)
+ masterstyle = &dns_master_style_full;
+ else if (strcasecmp(masterstylestr, "relative") == 0)
+ masterstyle = &dns_master_style_default;
+ else
+ INSIST(0);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-zone-ttl", &obj);
+ if (result == ISC_R_SUCCESS && masterformat == dns_masterformat_map) {
+ isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
+ NS_LOGMODULE_SERVER, ISC_LOG_ERROR,
+ "zone '%s': 'max-zone-ttl' is not compatible "
+ "with 'masterfile-format map'", zname);
+ return (ISC_R_FAILURE);
+ } else if (result == ISC_R_SUCCESS) {
+ dns_ttl_t maxttl = 0; /* unlimited */
+
+ if (cfg_obj_isuint32(obj))
+ maxttl = cfg_obj_asuint32(obj);
+ dns_zone_setmaxttl(zone, maxttl);
+ if (raw != NULL)
+ dns_zone_setmaxttl(raw, maxttl);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-records", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setmaxrecords(mayberaw, cfg_obj_asuint32(obj));
+ if (zone != mayberaw)
+ dns_zone_setmaxrecords(zone, 0);
+
+ if (raw != NULL && filename != NULL) {
+#define SIGNED ".signed"
+ size_t signedlen = strlen(filename) + sizeof(SIGNED);
+ char *signedname;
+
+ RETERR(dns_zone_setfile3(raw, filename,
+ masterformat, masterstyle));
+ signedname = isc_mem_get(mctx, signedlen);
+ if (signedname == NULL)
+ return (ISC_R_NOMEMORY);
+
+ (void)snprintf(signedname, signedlen, "%s" SIGNED, filename);
+ result = dns_zone_setfile3(zone, signedname,
+ dns_masterformat_raw, NULL);
+ isc_mem_put(mctx, signedname, signedlen);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ } else
+ RETERR(dns_zone_setfile3(zone, filename,
+ masterformat, masterstyle));
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "journal", &obj);
+ if (result == ISC_R_SUCCESS)
+ RETERR(dns_zone_setjournal(mayberaw, cfg_obj_asstring(obj)));
+
+ /*
+ * Notify messages are processed by the raw zone if it exists.
+ */
+ if (ztype == dns_zone_slave)
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ allow_notify, ac, mayberaw,
+ 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));
+
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ allow_query_on, ac, zone,
+ dns_zone_setqueryonacl,
+ dns_zone_clearqueryonacl));
+
+ obj = NULL;
+ result = ns_config_get(maps, "dialup", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ 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);
+ }
+ if (raw != NULL)
+ dns_zone_setdialup(raw, dialup);
+ dns_zone_setdialup(zone, dialup);
+
+ obj = NULL;
+ result = ns_config_get(maps, "zone-statistics", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (cfg_obj_isboolean(obj)) {
+ if (cfg_obj_asboolean(obj))
+ statlevel = dns_zonestat_full;
+ else
+ statlevel = dns_zonestat_none;
+ } else {
+ const char *levelstr = cfg_obj_asstring(obj);
+ if (strcasecmp(levelstr, "full") == 0)
+ statlevel = dns_zonestat_full;
+ else if (strcasecmp(levelstr, "terse") == 0)
+ statlevel = dns_zonestat_terse;
+ else if (strcasecmp(levelstr, "none") == 0)
+ statlevel = dns_zonestat_none;
+ else
+ INSIST(0);
+ }
+ dns_zone_setstatlevel(zone, statlevel);
+
+ zoneqrystats = NULL;
+ rcvquerystats = NULL;
+ if (statlevel == dns_zonestat_full) {
+ RETERR(isc_stats_create(mctx, &zoneqrystats,
+ dns_nsstatscounter_max));
+ RETERR(dns_rdatatypestats_create(mctx,
+ &rcvquerystats));
+ }
+ dns_zone_setrequeststats(zone, zoneqrystats);
+ dns_zone_setrcvquerystats(zone, rcvquerystats);
+
+ if (zoneqrystats != NULL)
+ isc_stats_detach(&zoneqrystats);
+
+ if(rcvquerystats != NULL)
+ dns_stats_detach(&rcvquerystats);
+
+ /*
+ * 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 && ztype != dns_zone_staticstub &&
+ ztype != dns_zone_redirect) {
+ obj = NULL;
+ result = ns_config_get(maps, "notify", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ 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);
+ }
+ if (raw != NULL)
+ dns_zone_setnotifytype(raw, dns_notifytype_no);
+ dns_zone_setnotifytype(zone, notifytype);
+
+ obj = NULL;
+ result = ns_config_get(maps, "also-notify", &obj);
+ if (result == ISC_R_SUCCESS &&
+ (notifytype == dns_notifytype_yes ||
+ notifytype == dns_notifytype_explicit ||
+ (notifytype == dns_notifytype_masteronly &&
+ ztype == dns_zone_master)))
+ {
+ dns_ipkeylist_t ipkl;
+ dns_ipkeylist_init(&ipkl);
+
+ RETERR(ns_config_getipandkeylist(config, obj, mctx,
+ &ipkl));
+ result = dns_zone_setalsonotifydscpkeys(zone,
+ ipkl.addrs,
+ ipkl.dscps,
+ ipkl.keys,
+ ipkl.count);
+ dns_ipkeylist_clear(mctx, &ipkl);
+ RETERR(result);
+ } else
+ RETERR(dns_zone_setalsonotify(zone, NULL, 0));
+
+ obj = NULL;
+ result = ns_config_get(maps, "notify-source", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ RETERR(dns_zone_setnotifysrc4(zone, cfg_obj_assockaddr(obj)));
+ dscp = cfg_obj_getdscp(obj);
+ if (dscp == -1)
+ dscp = ns_g_dscp;
+ RETERR(dns_zone_setnotifysrc4dscp(zone, dscp));
+ 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 && obj != NULL);
+ RETERR(dns_zone_setnotifysrc6(zone, cfg_obj_assockaddr(obj)));
+ dscp = cfg_obj_getdscp(obj);
+ if (dscp == -1)
+ dscp = ns_g_dscp;
+ RETERR(dns_zone_setnotifysrc6dscp(zone, dscp));
+ 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 && obj != NULL);
+ 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 && obj != NULL);
+ 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 && obj != NULL);
+ 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 && obj != NULL);
+ if (raw != NULL)
+ dns_zone_setjournalsize(raw, -1);
+ dns_zone_setjournalsize(zone, -1);
+ if (cfg_obj_isstring(obj)) {
+ const char *str = cfg_obj_asstring(obj);
+ INSIST(strcasecmp(str, "unlimited") == 0);
+ journal_size = UINT32_MAX / 2;
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > UINT32_MAX / 2) {
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_ERROR,
+ "'max-journal-size "
+ "%" PRId64 "' "
+ "is too large",
+ value);
+ RETERR(ISC_R_RANGE);
+ }
+ journal_size = (uint32_t)value;
+ }
+ if (raw != NULL)
+ dns_zone_setjournalsize(raw, journal_size);
+ dns_zone_setjournalsize(zone, journal_size);
+
+ obj = NULL;
+ result = ns_config_get(maps, "ixfr-from-differences", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (cfg_obj_isboolean(obj))
+ ixfrdiff = cfg_obj_asboolean(obj);
+ else if (!strcasecmp(cfg_obj_asstring(obj), "master") &&
+ ztype == dns_zone_master)
+ ixfrdiff = true;
+ else if (!strcasecmp(cfg_obj_asstring(obj), "slave") &&
+ ztype == dns_zone_slave)
+ ixfrdiff = true;
+ else
+ ixfrdiff = false;
+ if (raw != NULL) {
+ dns_zone_setoption(raw, DNS_ZONEOPT_IXFRFROMDIFFS,
+ true);
+ dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS,
+ false);
+ } else
+ dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS,
+ ixfrdiff);
+
+ obj = NULL;
+ result = ns_config_get(maps, "request-expire", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setrequestexpire(zone, cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "request-ixfr", &obj);
+ INSIST(result == ISC_R_SUCCESS);
+ dns_zone_setrequestixfr(zone, cfg_obj_asboolean(obj));
+
+ checknames(ztype, maps, &obj);
+ INSIST(obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ fail = false;
+ check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ fail = check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ fail = check = false;
+ } else
+ INSIST(0);
+ if (raw != NULL) {
+ dns_zone_setoption(raw, DNS_ZONEOPT_CHECKNAMES,
+ check);
+ dns_zone_setoption(raw, DNS_ZONEOPT_CHECKNAMESFAIL,
+ fail);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES,
+ false);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL,
+ false);
+ } else {
+ 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 && obj != NULL);
+ dns_zone_setnotifydelay(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-sibling", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKSIBLING,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-spf", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ check = false;
+ } else
+ INSIST(0);
+ dns_zone_setoption(zone, DNS_ZONEOPT_CHECKSPF, check);
+
+ obj = NULL;
+ result = ns_config_get(maps, "zero-no-soa-ttl", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setzeronosoattl(zone, cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "nsec3-test-zone", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_NSEC3TESTZONE,
+ cfg_obj_asboolean(obj));
+ } else if (ztype == dns_zone_redirect) {
+ dns_zone_setnotifytype(zone, dns_notifytype_no);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-journal-size", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setjournalsize(zone, -1);
+ if (cfg_obj_isstring(obj)) {
+ const char *str = cfg_obj_asstring(obj);
+ INSIST(strcasecmp(str, "unlimited") == 0);
+ journal_size = UINT32_MAX / 2;
+ } else {
+ isc_resourcevalue_t value;
+ value = cfg_obj_asuint64(obj);
+ if (value > UINT32_MAX / 2) {
+ cfg_obj_log(obj, ns_g_lctx,
+ ISC_LOG_ERROR,
+ "'max-journal-size "
+ "%" PRId64 "' "
+ "is too large",
+ value);
+ RETERR(ISC_R_RANGE);
+ }
+ journal_size = (uint32_t)value;
+ }
+ dns_zone_setjournalsize(zone, journal_size);
+ }
+
+ /*
+ * 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, mayberaw,
+ dns_zone_setupdateacl,
+ dns_zone_clearupdateacl));
+
+ updateacl = dns_zone_getupdateacl(mayberaw);
+ 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 unsigned updates "
+ "from remote hosts, which is insecure",
+ zname);
+
+ RETERR(configure_zone_ssutable(zoptions, mayberaw, zname));
+ }
+
+ if (ztype == dns_zone_master || raw != NULL) {
+ const cfg_obj_t *validity, *resign;
+ bool allow = false, maint = false;
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-validity-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+
+ validity = cfg_tuple_get(obj, "validity");
+ seconds = cfg_obj_asuint32(validity);
+ if (!ns_g_sigvalinsecs) {
+ seconds *= 86400;
+ }
+ dns_zone_setsigvalidityinterval(zone, seconds);
+
+ resign = cfg_tuple_get(obj, "re-sign");
+ if (cfg_obj_isvoid(resign)) {
+ seconds /= 4;
+ } else if (!ns_g_sigvalinsecs) {
+ if (seconds > 7 * 86400) {
+ seconds = cfg_obj_asuint32(resign) * 86400;
+ } else {
+ seconds = cfg_obj_asuint32(resign) * 3600;
+ }
+ } else {
+ seconds = cfg_obj_asuint32(resign);
+ }
+ 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);
+ RETERR(dns_zone_setkeydirectory(zone, filename));
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-signing-signatures", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setsignatures(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-signing-nodes", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setnodes(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "sig-signing-type", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setprivatetype(zone, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "update-check-ksk", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_UPDATECHECKKSK,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-dnskey-kskonly", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(zone, DNS_ZONEOPT_DNSKEYKSKONLY,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-loadkeys-interval", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ RETERR(dns_zone_setrefreshkeyinterval(zone,
+ cfg_obj_asuint32(obj)));
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "auto-dnssec", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *arg = cfg_obj_asstring(obj);
+ if (strcasecmp(arg, "allow") == 0)
+ allow = true;
+ else if (strcasecmp(arg, "maintain") == 0)
+ allow = maint = true;
+ else if (strcasecmp(arg, "off") == 0)
+ ;
+ else
+ INSIST(0);
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_ALLOW, allow);
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_MAINTAIN, maint);
+ }
+ }
+
+ if (ztype == dns_zone_slave) {
+ RETERR(configure_zone_acl(zconfig, vconfig, config,
+ allow_update_forwarding, ac,
+ mayberaw, 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 = false;
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKWILDCARD, check);
+
+ /*
+ * With map files, the default is ignore duplicate
+ * records. With other master formats, the default is
+ * taken from the global configuration.
+ */
+ obj = NULL;
+ if (masterformat != dns_masterformat_map) {
+ result = ns_config_get(maps, "check-dup-records", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dupcheck = cfg_obj_asstring(obj);
+ } else {
+ result = ns_config_get(nodefault, "check-dup-records",
+ &obj);
+ if (result == ISC_R_SUCCESS)
+ dupcheck = cfg_obj_asstring(obj);
+ else
+ dupcheck = "ignore";
+
+ }
+ if (strcasecmp(dupcheck, "warn") == 0) {
+ fail = false;
+ check = true;
+ } else if (strcasecmp(dupcheck, "fail") == 0) {
+ fail = check = true;
+ } else if (strcasecmp(dupcheck, "ignore") == 0) {
+ fail = check = false;
+ } else
+ INSIST(0);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKDUPRR, check);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKDUPRRFAIL, fail);
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-mx", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ fail = false;
+ check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ fail = check = true;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ fail = check = false;
+ } else
+ INSIST(0);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKMX, check);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKMXFAIL, fail);
+
+ /*
+ * With map files, the default is *not* to check
+ * integrity. With other master formats, the default is
+ * taken from the global configuration.
+ */
+ obj = NULL;
+ if (masterformat != dns_masterformat_map) {
+ result = ns_config_get(maps, "check-integrity", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKINTEGRITY,
+ cfg_obj_asboolean(obj));
+ } else {
+ check = false;
+ result = ns_config_get(nodefault, "check-integrity",
+ &obj);
+ if (result == ISC_R_SUCCESS)
+ check = cfg_obj_asboolean(obj);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_CHECKINTEGRITY,
+ check);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-mx-cname", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ warn = true;
+ ignore = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ warn = ignore = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ warn = ignore = true;
+ } else
+ INSIST(0);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_WARNMXCNAME, warn);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_IGNOREMXCNAME, ignore);
+
+ obj = NULL;
+ result = ns_config_get(maps, "check-srv-cname", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
+ warn = true;
+ ignore = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
+ warn = ignore = false;
+ } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
+ warn = ignore = true;
+ } else
+ INSIST(0);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_WARNSRVCNAME, warn);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_IGNORESRVCNAME,
+ ignore);
+
+ obj = NULL;
+ result = ns_config_get(maps, "dnssec-secure-to-insecure", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_SECURETOINSECURE,
+ cfg_obj_asboolean(obj));
+
+ obj = NULL;
+ result = cfg_map_get(zoptions, "dnssec-update-mode", &obj);
+ if (result == ISC_R_SUCCESS) {
+ const char *arg = cfg_obj_asstring(obj);
+ if (strcasecmp(arg, "no-resign") == 0)
+ dns_zone_setkeyopt(zone, DNS_ZONEKEY_NORESIGN,
+ true);
+ else if (strcasecmp(arg, "maintain") == 0)
+ ;
+ else
+ INSIST(0);
+ }
+
+ obj = NULL;
+ result = ns_config_get(maps, "serial-update-method", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ if (strcasecmp(cfg_obj_asstring(obj), "unixtime") == 0)
+ dns_zone_setserialupdatemethod(zone,
+ dns_updatemethod_unixtime);
+ else if (strcasecmp(cfg_obj_asstring(obj), "date") == 0)
+ dns_zone_setserialupdatemethod(zone,
+ dns_updatemethod_date);
+ else
+ dns_zone_setserialupdatemethod(zone,
+ dns_updatemethod_increment);
+ }
+
+ /*
+ * Configure slave functionality.
+ */
+ switch (ztype) {
+ case dns_zone_slave:
+ case dns_zone_stub:
+ case dns_zone_redirect:
+ count = 0;
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "masters", &obj);
+ if (obj != NULL) {
+ dns_ipkeylist_t ipkl;
+ dns_ipkeylist_init(&ipkl);
+
+ RETERR(ns_config_getipandkeylist(config, obj, mctx,
+ &ipkl));
+ result = dns_zone_setmasterswithkeys(mayberaw,
+ ipkl.addrs,
+ ipkl.keys,
+ ipkl.count);
+ count = ipkl.count;
+ dns_ipkeylist_clear(mctx, &ipkl);
+ RETERR(result);
+ } else
+ result = dns_zone_setmasters(mayberaw, NULL, 0);
+ RETERR(result);
+
+ multi = false;
+ if (count > 1) {
+ obj = NULL;
+ result = ns_config_get(maps, "multi-master", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ multi = cfg_obj_asboolean(obj);
+ }
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_MULTIMASTER, multi);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-time-in", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setmaxxfrin(mayberaw, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-transfer-idle-in", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setidlein(mayberaw, cfg_obj_asuint32(obj) * 60);
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-refresh-time", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setmaxrefreshtime(mayberaw, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "min-refresh-time", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setminrefreshtime(mayberaw, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "max-retry-time", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setmaxretrytime(mayberaw, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "min-retry-time", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ dns_zone_setminretrytime(mayberaw, cfg_obj_asuint32(obj));
+
+ obj = NULL;
+ result = ns_config_get(maps, "transfer-source", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ RETERR(dns_zone_setxfrsource4(mayberaw,
+ cfg_obj_assockaddr(obj)));
+ dscp = cfg_obj_getdscp(obj);
+ if (dscp == -1)
+ dscp = ns_g_dscp;
+ RETERR(dns_zone_setxfrsource4dscp(mayberaw, dscp));
+ 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 && obj != NULL);
+ RETERR(dns_zone_setxfrsource6(mayberaw,
+ cfg_obj_assockaddr(obj)));
+ dscp = cfg_obj_getdscp(obj);
+ if (dscp == -1)
+ dscp = ns_g_dscp;
+ RETERR(dns_zone_setxfrsource6dscp(mayberaw, dscp));
+ 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 && obj != NULL);
+ RETERR(dns_zone_setaltxfrsource4(mayberaw,
+ cfg_obj_assockaddr(obj)));
+ dscp = cfg_obj_getdscp(obj);
+ if (dscp == -1)
+ dscp = ns_g_dscp;
+ RETERR(dns_zone_setaltxfrsource4dscp(mayberaw, dscp));
+
+ obj = NULL;
+ result = ns_config_get(maps, "alt-transfer-source-v6", &obj);
+ INSIST(result == ISC_R_SUCCESS && obj != NULL);
+ RETERR(dns_zone_setaltxfrsource6(mayberaw,
+ cfg_obj_assockaddr(obj)));
+ dscp = cfg_obj_getdscp(obj);
+ if (dscp == -1)
+ dscp = ns_g_dscp;
+ RETERR(dns_zone_setaltxfrsource6dscp(mayberaw, dscp));
+
+ 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 = true;
+ else
+ alt = false;
+ } else
+ alt = cfg_obj_asboolean(obj);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_USEALTXFRSRC, alt);
+
+ obj = NULL;
+ (void)ns_config_get(maps, "try-tcp-refresh", &obj);
+ dns_zone_setoption(mayberaw, DNS_ZONEOPT_TRYTCPREFRESH,
+ cfg_obj_asboolean(obj));
+ break;
+
+ case dns_zone_staticstub:
+ RETERR(configure_staticstub(zoptions, zone, zname,
+ default_dbtype));
+ break;
+
+ default:
+ break;
+ }
+
+ return (ISC_R_SUCCESS);
+}
+
+
+/*
+ * Set up a DLZ zone as writeable
+ */
+isc_result_t
+ns_zone_configure_writeable_dlz(dns_dlzdb_t *dlzdatabase, dns_zone_t *zone,
+ dns_rdataclass_t rdclass, dns_name_t *name)
+{
+ dns_db_t *db = NULL;
+ isc_time_t now;
+ isc_result_t result;
+
+ TIME_NOW(&now);
+
+ dns_zone_settype(zone, dns_zone_dlz);
+ result = dns_sdlz_setdb(dlzdatabase, rdclass, name, &db);
+ if (result != ISC_R_SUCCESS)
+ return (result);
+ result = dns_zone_dlzpostload(zone, db);
+ dns_db_detach(&db);
+ return (result);
+}
+
+bool
+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;
+ dns_zone_t *raw = NULL;
+ bool has_raw;
+ dns_zonetype_t ztype;
+
+ zoptions = cfg_tuple_get(zconfig, "options");
+
+ /*
+ * We always reconfigure a static-stub zone for simplicity, assuming
+ * the amount of data to be loaded is small.
+ */
+ if (zonetype_fromconfig(zoptions) == dns_zone_staticstub) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: staticstub");
+ return (false);
+ }
+
+ /* If there's a raw zone, use that for filename and type comparison */
+ dns_zone_getraw(zone, &raw);
+ if (raw != NULL) {
+ zfilename = dns_zone_getfile(raw);
+ ztype = dns_zone_gettype(raw);
+ dns_zone_detach(&raw);
+ has_raw = true;
+ } else {
+ zfilename = dns_zone_getfile(zone);
+ ztype = dns_zone_gettype(zone);
+ has_raw = false;
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "inline-signing", &obj);
+ if ((obj == NULL || !cfg_obj_asboolean(obj)) && has_raw) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: old zone was inline-signing");
+ return (false);
+ } else if ((obj != NULL && cfg_obj_asboolean(obj)) && !has_raw) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: old zone was not inline-signing");
+ return (false);
+ }
+
+ if (zonetype_fromconfig(zoptions) != ztype) {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: type mismatch");
+ return (false);
+ }
+
+ obj = NULL;
+ (void)cfg_map_get(zoptions, "file", &obj);
+ if (obj != NULL)
+ cfilename = cfg_obj_asstring(obj);
+ else
+ cfilename = NULL;
+ if (!((cfilename == NULL && zfilename == NULL) ||
+ (cfilename != NULL && zfilename != NULL &&
+ strcmp(cfilename, zfilename) == 0)))
+ {
+ dns_zone_log(zone, ISC_LOG_DEBUG(1),
+ "not reusable: filename mismatch");
+ return (false);
+ }
+
+ return (true);
+}