]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #4008 from ahupowerdns/dnsdist-node
authorbert hubert <bert.hubert@netherlabs.nl>
Fri, 24 Jun 2016 09:31:11 +0000 (10:31 +0100)
committerGitHub <noreply@github.com>
Fri, 24 Jun 2016 09:31:11 +0000 (10:31 +0100)
Dnsdist node: infrastructure for querying recent traffic

65 files changed:
build-scripts/build-auth-rpm
configure.ac
contrib/ProtobufLogger.py
docs/Makefile.am
docs/manpages/pdns_control.1.md
docs/markdown/appendix/compiling-powerdns.md
docs/markdown/authoritative/backend-generic-oracle.md
docs/markdown/authoritative/backend-geoip.md
docs/markdown/authoritative/dnssec.md
docs/markdown/authoritative/domainmetadata.md
docs/markdown/authoritative/howtos.md
docs/markdown/recursor/dns64.md
docs/markdown/recursor/dnssec.md
docs/markdown/recursor/scripting.md
docs/secpoll.zone
ext/luawrapper/include/LuaContext.hpp
m4/boost.m4
m4/pdns_check_linkchecker.m4 [deleted file]
m4/pdns_check_lua_hpp.m4 [new file with mode: 0644]
m4/pdns_check_mkdocs.m4 [deleted file]
m4/pdns_check_os.m4
m4/pdns_enable_reproducible.m4
m4/pdns_from_git.m4 [new file with mode: 0644]
m4/pdns_with_lua.m4
modules/bindbackend/bindbackend2.cc
modules/remotebackend/unixconnector.cc
pdns/Makefile.am
pdns/README-dnsdist.md
pdns/dns64.lua
pdns/dnsdist-cache.cc
pdns/dnsdist-lua.cc
pdns/dnsdist-lua2.cc
pdns/dnsdist-tcp.cc
pdns/dnsdist.cc
pdns/dnsdistdist/.gitignore
pdns/dnsdistdist/Makefile.am
pdns/dnsdistdist/configure.ac
pdns/dnsdistdist/dnsrulactions.cc
pdns/dnsdistdist/lua_hpp.mk [new symlink]
pdns/dnsdistdist/m4/pdns_check_lua_hpp.m4 [new symlink]
pdns/dnsdistdist/m4/pdns_check_network_libs.m4 [new symlink]
pdns/dnspcap2protobuf.cc
pdns/dnsrulactions.hh
pdns/dnssecinfra.cc
pdns/lua-recursor4.cc
pdns/lua-recursor4.hh
pdns/lua_hpp.mk [new file with mode: 0644]
pdns/lwres.cc
pdns/pdns_recursor.cc
pdns/recpacketcache.cc
pdns/recpacketcache.hh
pdns/recursordist/.gitignore
pdns/recursordist/Makefile.am
pdns/recursordist/configure.ac
pdns/recursordist/lua_hpp.mk [new symlink]
pdns/recursordist/m4/pdns_check_lua_hpp.m4 [new symlink]
pdns/recursordist/m4/pdns_check_network_libs.m4 [new symlink]
pdns/syncres.cc
pdns/validate-recursor.cc
pdns/validate.cc
regression-tests.api/runtests.py
regression-tests.dnsdist/dnsdisttests.py
regression-tests.dnsdist/test_AXFR.py [new file with mode: 0644]
regression-tests/tests/bind-add-zone/command
regression-tests/tests/bind-add-zone/expected_result.bind

index e9647f1e0cdef582b4f5be0f25f24c4aec4dbcfc..18d8200eb2834f70f31566c302e4cc083d431c03 100755 (executable)
@@ -52,6 +52,219 @@ case "$OS" in
 #      ;;
   CentOS\ *\ 6*)
     RPMBUILD_COMMAND="scl enable devtoolset-3 -- ${RPMBUILD_COMMAND}"
+    cat > $HOME/rpmbuild/SOURCES/pdns.init <<EOF
+#!/bin/sh
+# chkconfig: - 80 75
+# description: PDNS is a versatile high performance authoritative nameserver
+
+### BEGIN INIT INFO
+# Provides:          pdns
+# Required-Start:    \$remote_fs \$network \$syslog
+# Required-Stop:     \$remote_fs \$network \$syslog
+# Should-Start:
+# Should-Stop:
+# Default-Start:     
+# Default-Stop:      0 1 6
+# Short-Description: PowerDNS authoritative server
+# Description:       PowerDNS authoritative server
+### END INIT INFO
+
+set -e
+
+prefix=/usr
+exec_prefix=/usr
+BINARYPATH=/usr/bin
+SBINARYPATH=/usr/sbin
+SOCKETPATH=/var/run
+
+[ -f "\$SBINARYPATH/pdns_server" ] || exit 0
+
+[ -r /etc/default/pdns ] && . /etc/default/pdns
+
+cd \$SOCKETPATH
+suffix=\$(basename \$0 | cut -d- -f2- -s)
+if [ -n "\$suffix" ] 
+then
+       EXTRAOPTS=--config-name=\$suffix
+       PROGNAME=pdns-\$suffix
+else
+       PROGNAME=pdns
+fi
+
+pdns_server="\$SBINARYPATH/pdns_server \$EXTRAOPTS"
+
+doPC()
+{
+       ret=\$(\$BINARYPATH/pdns_control \$EXTRAOPTS \$1 \$2 2> /dev/null)
+}
+
+NOTRUNNING=0
+doPC ping || NOTRUNNING=\$?
+
+case "\$1" in
+       status)
+               if test "\$NOTRUNNING" = "0" 
+               then 
+                       doPC status
+                       echo \$ret
+               else
+                       echo "not running"
+                       exit 3
+               fi 
+       ;;      
+
+       stop)
+               echo -n "Stopping PowerDNS authoritative nameserver: "
+               if test "\$NOTRUNNING" = "0" 
+               then 
+                       doPC quit
+                       rm -f /var/lock/subsys/pdns
+                       echo \$ret
+               else
+                       echo "not running"
+               fi 
+       ;;              
+
+
+       force-stop)
+               echo -n "Stopping PowerDNS authoritative nameserver: "
+               killall -v -9 pdns_server
+               rm -f /var/lock/subsys/pdns
+               echo "killed"
+       ;;
+
+       start)
+               echo -n "Starting PowerDNS authoritative nameserver: "
+               if test "\$NOTRUNNING" = "0" 
+               then 
+                       echo "already running"
+               else
+                       if \$pdns_server --daemon --guardian=yes
+                       then
+                               touch /var/lock/subsys/pdns
+                               echo "started"  
+                       else
+                               echo "starting failed"
+                               exit 1
+                       fi
+               fi 
+       ;;              
+
+       condrestart)
+               if [ -f /var/lock/subsys/pdns ]; 
+               then
+                       echo "running, restarting"
+               \$0 restart
+               else
+                       echo "not running"
+               fi
+       ;;
+
+       force-reload | restart)
+               echo -n "Restarting PowerDNS authoritative nameserver: "
+               if test "\$NOTRUNNING" = "1" 
+               then 
+                       echo "not running, starting"
+               else
+                       
+                       echo -n stopping and waiting.. 
+                       doPC quit
+                       sleep 3
+                       echo done
+               fi
+               \$0 start
+       ;;
+
+       reload) 
+               echo -n "Reloading PowerDNS authoritative nameserver: "
+               if test "\$NOTRUNNING" = "0" 
+               then 
+                       doPC cycle
+                       echo requested reload
+               else
+                       echo not running yet
+                       \$0 start
+               fi 
+       ;;              
+               
+       monitor)
+               if test "\$NOTRUNNING" = "0" 
+               then 
+                       echo "already running"
+               else
+                       \$pdns_server --daemon=no --guardian=no --control-console --loglevel=9
+               fi 
+       ;;              
+
+       dump)
+               if test "\$NOTRUNNING" = "0" 
+               then 
+                       doPC list
+                       echo \$ret
+               else
+                       echo "not running"
+               fi 
+       ;;              
+
+       show)
+               if [ \$# -lt 2 ]
+               then
+                       echo Insufficient parameters
+                       exit
+               fi 
+               if test "\$NOTRUNNING" = "0" 
+               then 
+                       echo -n "\$2="
+                       doPC show \$2 ; echo \$ret
+               else
+                       echo "not running"
+               fi 
+       ;;              
+
+       mrtg)
+               if [ \$# -lt 2 ]
+               then
+                       echo Insufficient parameters
+                       exit
+               fi 
+               if test "\$NOTRUNNING" = "0" 
+               then 
+                       doPC show \$2 ; echo \$ret
+                       if [ "\$3x" != "x" ]
+                       then
+                               doPC show \$3 ; echo \$ret
+                       else
+                               echo 0
+                       fi
+                       doPC uptime ; echo \$ret
+                       echo PowerDNS daemon
+               else
+                       echo "not running"
+               fi 
+       
+       ;;              
+
+       cricket)
+               if [ \$# -lt 2 ]
+               then
+                       echo Insufficient parameters
+                       exit
+               fi 
+               if test "\$NOTRUNNING" = "0" 
+               then 
+                       doPC show \$2 ; echo \$ret
+               else
+                       echo "not running"
+               fi 
+       
+       ;;
+
+       *)
+       echo pdns [start\|stop\|condrestart\|force-reload\|reload\|restart\|status\|dump\|show\|mrtg\|cricket\|monitor]
+
+       ;;
+esac
+EOF
     cat > pdns.spec << EOF
 %global backends %{nil}
 
@@ -62,7 +275,8 @@ Summary: A modern, advanced and high performance authoritative-only nameserver
 Group: System Environment/Daemons
 License: GPLv2
 URL: https://powerdns.com
-Source: ../%{name}-${TARBALLVERSION}.tar.bz2
+Source0: ../%{name}-${TARBALLVERSION}.tar.bz2
+Source1: pdns.init
 
 Requires(pre): shadow-utils
 Requires(post): /sbin/chkconfig
@@ -163,7 +377,7 @@ export CPPFLAGS="-DLDAP_DEPRECATED"
 
 %configure \
        --sysconfdir=%{_sysconfdir}/%{name} \
-       --libdir=%{_libdir}/%{name} \
+       --libdir=%{_libdir} \
        --disable-static \
        --disable-dependency-tracking \
        --disable-silent-rules \
@@ -181,7 +395,7 @@ make %{?_smp_mflags}
 make install DESTDIR=%{buildroot}
 
 %{__rm} -f %{buildroot}%{_libdir}/%{name}/*.la
-%{__install} -p -D -m 0755 pdns/pdns %{buildroot}%{_initrddir}/pdns
+%{__install} -D -p %{SOURCE1} %{buildroot}%{_initrddir}/pdns
 %{buildroot}/usr/sbin/pdns_server --no-config --config > %{buildroot}%{_sysconfdir}/%{name}/pdns.conf
 %{__rm} %{buildroot}%{_sysconfdir}/%{name}/pdns.conf-dist
 %{__rm} %{buildroot}/usr/bin/stubquery
@@ -234,12 +448,10 @@ fi
 
 %files tools
 %{_bindir}/calidns
-%{_bindir}/dnsbulktest
 %{_bindir}/dnsgram
 %{_bindir}/dnsreplay
 %{_bindir}/dnsscan
 %{_bindir}/dnsscope
-%{_bindir}/dnstcpbench
 %{_bindir}/dnswasher
 %{_bindir}/dumresp
 %{_bindir}/ixplore
@@ -249,12 +461,10 @@ fi
 %{_bindir}/saxfr
 %{_bindir}/sdig
 %{_mandir}/man1/calidns.1.gz
-%{_mandir}/man1/dnsbulktest.1.gz
 %{_mandir}/man1/dnsgram.1.gz
 %{_mandir}/man1/dnsreplay.1.gz
 %{_mandir}/man1/dnsscan.1.gz
 %{_mandir}/man1/dnsscope.1.gz
-%{_mandir}/man1/dnstcpbench.1.gz
 %{_mandir}/man1/dnswasher.1.gz
 %{_mandir}/man1/dumresp.1.gz
 %{_mandir}/man1/ixplore.1.gz
@@ -265,13 +475,15 @@ fi
 %{_mandir}/man1/sdig.1.gz
 
 %files backend-mysql
-%doc pdns/dnssec.schema.mysql.sql
-%doc pdns/no-dnssec.schema.mysql.sql
+%doc %{_defaultdocdir}/%{name}/schema.mysql.sql
+%doc %{_defaultdocdir}/%{name}/nodnssec-3.x_to_3.4.0_schema.mysql.sql
+%doc %{_defaultdocdir}/%{name}/dnssec-3.x_to_3.4.0_schema.mysql.sql
 %{_libdir}/%{name}/libgmysqlbackend.so
 
 %files backend-postgresql
-%doc pdns/dnssec.schema.pgsql.sql
-%doc pdns/no-dnssec.schema.pgsql.sql
+%doc %{_defaultdocdir}/%{name}/schema.pgsql.sql
+%doc %{_defaultdocdir}/%{name}/nodnssec-3.x_to_3.4.0_schema.pgsql.sql
+%doc %{_defaultdocdir}/%{name}/dnssec-3.x_to_3.4.0_schema.pgsql.sql
 %{_libdir}/%{name}/libgpgsqlbackend.so
 
 %files backend-pipe
@@ -287,9 +499,9 @@ fi
 %{_libdir}/%{name}/libluabackend.so
 
 %files backend-sqlite
-%doc pdns/dnssec.schema.sqlite3.sql
-%doc pdns/no-dnssec.schema.sqlite3.sql
-%doc pdns/bind-dnssec.schema.sqlite3.sql
+%doc %{_defaultdocdir}/%{name}/schema.sqlite3.sql
+%doc %{_defaultdocdir}/%{name}/nodnssec-3.x_to_3.4.0_schema.sqlite3.sql
+%doc %{_defaultdocdir}/%{name}/dnssec-3.x_to_3.4.0_schema.sqlite3.sql
 %{_libdir}/%{name}/libgsqlite3backend.so
 EOF
     ;;
index 379ec7c40bf46c7df559ebcadaa710f2f6e89bc5..9f7039e9ef36d0a61637d1450197d616910575fe 100644 (file)
@@ -45,6 +45,7 @@ PDNS_WITH_LUAJIT
 AS_IF([test "x$with_luajit" = "xno"], [
   PDNS_WITH_LUA
 ])
+PDNS_CHECK_LUA_HPP
 
 AX_CXX_COMPILE_STDCXX_11
 
@@ -113,8 +114,7 @@ PDNS_ENABLE_REPRODUCIBLE
 PDNS_WITH_SQLITE3
 
 PDNS_CHECK_PANDOC
-PDNS_CHECK_MKDOCS
-PDNS_CHECK_LINKCHECKER
+PDNS_FROM_GIT
 
 dnl Checks for library functions.
 AC_CHECK_FUNCS_ONCE([strcasestr localtime_r recvmmsg])
@@ -332,12 +332,7 @@ AC_MSG_NOTICE([])
 AC_MSG_NOTICE([Configuration summary])
 AC_MSG_NOTICE([=====================])
 AC_MSG_NOTICE([])
-AC_MSG_NOTICE([Configured with:$pdns_configure_args])
-AC_MSG_NOTICE([])
-AC_MSG_NOTICE([Modules: $modules])
-AC_MSG_NOTICE([Dynamic Modules: $dynmodules])
-AC_MSG_NOTICE([])
-AC_MSG_NOTICE([With ZeroMQ connector for remotebackend: $enable_remotebackend_zeromq])
+AC_MSG_NOTICE([Configured with: $pdns_configure_args])
 AC_MSG_NOTICE([])
 AC_MSG_NOTICE([CC: $CC])
 AC_MSG_NOTICE([CXX: $CXX])
@@ -347,22 +342,35 @@ AC_MSG_NOTICE([CPPFLAGS: $CPPFLAGS])
 AC_MSG_NOTICE([CXXFLAGS: $CXXFLAGS])
 AC_MSG_NOTICE([LDFLAGS: $LDFLAGS])
 AC_MSG_NOTICE([LIBS: $LIBS])
-AC_MSG_NOTICE([])
 AC_MSG_NOTICE([BOOST_CPPFLAGS: $BOOST_CPPFLAGS])
 AC_MSG_NOTICE([])
+AC_MSG_NOTICE([Features enabled])
+AC_MSG_NOTICE([----------------])
+AC_MSG_NOTICE([Built-in modules: $modules])
+AC_MSG_NOTICE([Dynamic modules: $dynmodules])
+AC_MSG_NOTICE([])
 AS_IF([test "x$openssl_ecdsa" == "xyes"],
-  [AC_MSG_NOTICE([OpenSSL ecdsa support: yes])],
-  [AC_MSG_NOTICE([OpenSSL ecdsa support: no])]
+  [AC_MSG_NOTICE([OpenSSL ecdsa: yes])],
+  [AC_MSG_NOTICE([OpenSSL ecdsa: no])]
 )
 AS_IF([test "x$needsqlite3" != "x"],
-  [AC_MSG_NOTICE([SQLite3 support: yes])],
-  [AC_MSG_NOTICE([SQLite3 support: no])]
+  [AC_MSG_NOTICE([SQLite3: yes])],
+  [AC_MSG_NOTICE([SQLite3: no])]
 )
 AS_IF([test "x$LUAPC" != "x"],
-  [AC_MSG_NOTICE([Lua support: $LUAPC])],
-  [AC_MSG_NOTICE([Lua support: no])]
-)
+  [AC_MSG_NOTICE([Lua: $LUAPC])],
+  [AS_IF([test "x$LUAJITPC" != "x"],
+    [AC_MSG_NOTICE([LuaJit: $LUAJITPC])],
+    [AC_MSG_NOTICE([Lua/LuaJit: no])])
+])
 AS_IF([test "x$enable_experimental_gss_tsig" == "xyes"],
-  [AC_MSG_NOTICE([GSS-TSIG support: yes])]
+  [AC_MSG_NOTICE([GSS-TSIG: yes])]
+)
+AS_IF([test "x$systemd" != "xn"],
+  [AC_MSG_NOTICE([systemd: yes])],
+  [AC_MSG_NOTICE([systemd: no])]
+)
+AS_IF([test "x$enable_remotebackend_zeromq" != "xno"],
+  [AC_MSG_NOTICE([ZeroMQ connector for remotebackend: yes])]
 )
 AC_MSG_NOTICE([])
index b146e837aa0fd12aab38de48fffcd836bd4c7d2e..848dd63fb964bb237b5b4dbd3994c424fe432d15 100644 (file)
@@ -7,9 +7,8 @@ import struct
 import sys
 import threading
 
-# you need to get the dnsmessage.proto file from the PDNS
-# repository, then run:
-# protoc -I=. --python_out=. dnsmessage.proto
+# run: protoc -I=../pdns/ --python_out=. ../pdns/dnsmessage.proto
+# to generate dnsmessage_pb2
 import dnsmessage_pb2
 
 class PDNSPBConnHandler(object):
@@ -75,11 +74,16 @@ class PDNSPBConnHandler(object):
             if response.HasField('appliedPolicy') and response.appliedPolicy:
                 policystr = ', Applied policy: ' + response.appliedPolicy
 
+            tagsstr = ''
+            if response.tags:
+                tagsstr = ', Tags: ' + ','.join(response.tags)
+
             rrscount = len(response.rrs)
 
-            print("- Response Code: %d, RRs: %d%s" % (response.rcode,
+            print("- Response Code: %d, RRs: %d%s%s" % (response.rcode,
                                                       rrscount,
-                                                      policystr))
+                                                      policystr,
+                                                      tagsstr))
 
             for rr in response.rrs:
                 rrclass = 1
index 2946e583512aef6d9a44cbf0e6f2eea816a78c93..eeeac6984c16ada52995ed38b0952c45fb515502 100644 (file)
@@ -44,12 +44,14 @@ if HAVE_MANPAGES
 dist_man_MANS = $(MANPAGES_TARGET_AUTH) $(MANPAGES_TARGET_TOOLS)
 endif
 
-EXTRA_DIST = manpages \
-       markdown
+EXTRA_DIST = manpages markdown/*.md markdown/appendix markdown/authoritative markdown/common markdown/httpapi markdown/recursor markdown/security markdown/tools
+
 
 .PHONY: html all-manpages
+
 html: html/index.html
 
+if FROM_GIT
 html/index.html: process-md.sh mkdocs.yml markdown/** markdown/*/** manpages/*
        mkdir -p doc-build
        rsync -a --delete markdown/. doc-build/.
@@ -61,6 +63,18 @@ html/index.html: process-md.sh mkdocs.yml markdown/** markdown/*/** manpages/*
 html.tar.bz2: html
        tar cjf html.tar.bz2 html/
 
+check-links: html
+       ./checklinks.sh
+
+publish: html html.tar.bz2
+       rsync -crv --no-p --chmod=g=rwX --exclude '*~' ./html/ web1.powerdns.com:/srv/www/doc.powerdns.com/md
+       rsync -crv --no-p --chmod=g=rwX --exclude '*~' ./html.tar.bz2 web1.powerdns.com:/srv/www/doc.powerdns.com/html.tar.bz2
+else
+html/index.html:
+       @echo "Building the documentation HTML is only"
+       @echo "supported from a git checkout"
+endif
+
 all-manpages: $(MANPAGES_TARGET_ALL)
 
 if HAVE_PANDOC
@@ -72,14 +86,5 @@ $(MANPAGES_TARGET_ALL):
        exit 1
 endif
 
-if HAVE_LINKCHECKER
-check-links: html
-       ./checklinks.sh
-endif
-
 clean:
        rm -rf html html.tar.bz2 *.8 *.1
-
-publish: html html.tar.bz2
-       rsync -crv --no-p --chmod=g=rwX --exclude '*~' ./html/ web1.powerdns.com:/srv/www/doc.powerdns.com/md
-       rsync -crv --no-p --chmod=g=rwX --exclude '*~' ./html.tar.bz2 web1.powerdns.com:/srv/www/doc.powerdns.com/html.tar.bz2
index 0cc96206ee89b5bfc4e178debe010a323301a557..28e24a553d19a3295a3ecb272727a1e341d9c431 100644 (file)
@@ -3,7 +3,7 @@
 % December 2002
 
 # NAME
-**pdns_control** - Contreol the PowerDNS nameserver
+**pdns_control** - Control the PowerDNS nameserver
 
 # SYNOPSIS
 **pdns_control** [*OPTION*]... *COMMAND*
 
 
 # COMMANDS
+bind-add-zone *DOMAIN* *FILENAME*
+:    When using the bindbackend, add a zone. This zone is added in-memory and served
+     immediately. Note that this does not add the zone to the bind-config file.
+     *FILENAME* must be an absolute path.
+
+bind-domain-status [*DOMAIN*...]
+:    When using the bindbackend, list status of all domains. Optionally, append
+     *DOMAIN*s to get the status of specific zones.
+
+bind-list-rejects
+:    When using the bindbackend, get a list of all rejected domains.
+
+bind-reload-now *DOMAIN* [*DOMAIN*...]
+:    When using the bindbackend, immediately reload *DOMAIN* from disk.
+
 ccounts
 :    Show the content of the cache.
 
index 0f62d94134500722da481e6af44dea5e7b9e477b..2bdb0f6a212a22fa00581a278b0140f1e4814cee 100644 (file)
@@ -14,6 +14,11 @@ To compile a module for inclusion at runtime, which is great if you are a unix
 vendor, use `--with-dynmodules='mod1 mod2 mod3'`. These modules then end up as
 .so files in the compiled libdir.
 
+By default, the [bind](../authoritative/backend-bind.md), [mysql](../authoritative/backend-generic-mysql.md)
+and [random](../authoritative/backend-random.md) are compiled into the binary.
+The [pipe](../authoritative/backend-pipe.md) is, by default, compiled as a runtime
+loadable module.
+
 ## Getting the sources
 There are 3 ways of getting the source. If you want the bleeding edge, you can
 clone the repository at [GitHub](https://github.com/PowerDNS/pdns) and run
@@ -61,3 +66,15 @@ make has some issues with some PowerDNS Makefile constructs.
 When compiling, make sure that you have `/usr/ccs/bin` in your path.
 Furthermore, with some versions of MySQL, you may have to add `LDFLAGS=-lz`
 before `./configure`.
+
+### OpenIndiana
+Compiles on OpenIndiana Hipster with `developer/gcc-49`. Other required packages
+are:
+
+ * bison
+ * boost
+ * developer/gcc-49
+ * flex
+ * libtool
+ * pkg-config
+ * system/header
index a7428879e6569e917d25b47e78db14cdf555eddf..d6999965128a94848de91316f12ad8cf4e8696d0 100644 (file)
@@ -37,3 +37,11 @@ in `/etc/tnsnames.ora`.
 
 ## `goracle-dnssec`
 Enable DNSSEC processing for this backend. Default=no.
+
+# Caveats
+## Password Expiry
+When your password is about to expire, and logging into oracle warns about this,
+the Generic Oracle backend can no longer login, and will a OCILogin2 warning.
+
+To work around this, either update the password in time or remove expiration
+from the account used.
index 2366f48dae7a9d59786822d318dee4494f6ec96e..1abb325e43f1260ada213e2fe5573480a770ee67 100644 (file)
@@ -81,7 +81,7 @@ domains:
       - txt: hello world
       - aaaa: 2001:DB8::12:34DE:3
 # this will result first record being handed out 30% of time
-    swe.eu.service.geo.example.com
+    swe.eu.service.geo.example.com:
       - a:
            content: 192.0.2.3
            weight: 50
index fa1e2d16704ff6379e3197edee8e314909b4e7f8..f67324cc575ce11915a6500362fc0165c075067b 100644 (file)
@@ -110,6 +110,10 @@ This corresponds to:
 In order to facilitate interoperability with existing technologies, PowerDNS keys
 can be imported and exported in industry standard formats.
 
+When using OpenSSL for ECDSA signatures (this is default), starting from OpenSSL
+1.1.0, the algorithm used is resilient against PRNG failure, while not
+strictly conforming to [RFC 6979](http://tools.ietf.org/html/rfc6979).
+
 **Note**: Actual supported algorithms depend on the crypto-libraries PowerDNS was
 compiled against. To check the supported DNSSEC algoritms in your build of PowerDNS,
 run `pdnsutil list-algorithms`.
@@ -438,7 +442,7 @@ The quoted part is the content of the NSEC3PARAM records, as defined in [RFC 515
 
 * Hash algorithm, should always be `1` (SHA1)
 * Flags, set to `1` for [NSEC3 Opt-out](https://tools.ietf.org/html/rfc5155#section-6), this best set as `0`
-* Number of iterations of the hash function, read [RFC 5155, Section 10.3](https://tools.ietf.org/html/rfc5155#section-6) for recommendations
+* Number of iterations of the hash function, read [RFC 5155, Section 10.3](https://tools.ietf.org/html/rfc5155#section-10.3) for recommendations
 * Salt (in hexadecimal) to apply during hashing
 
 To convert a zone from NSEC3 to NSEC operations, run:
index 5403fd3640be55294f8f5f1153e6a97d830bf02f..70cc38e78c03e3a99ac37513ed2dcb7c26107575 100644 (file)
@@ -1,11 +1,17 @@
 # Per zone settings aka Domain Metadata
-Starting with the PowerDNS Authoritative Server 3.0, each served zone can have
-"metadata". Such metadata determines how this zone behaves in certain circumstances.
+Each served zone can have "metadata". Such metadata determines how this zone
+behaves in certain circumstances.
 
 **Warning**: Domain metadata is only available for DNSSEC capable backends! Make
 sure to enable the proper '-dnssec' setting to benefit, and to have performed
 the DNSSEC schema update.
 
+For the BIND backend, this information is either stored in the
+[`bind-dnssec-db`](backend-bind.md) or the hybrid database, depending on your
+settings.
+
+For the implementation in non-sql backends, please review your backend's documentation.
+
 ## ALLOW-AXFR-FROM
 Starting with the PowerDNS Authoritative Server 3.1, per-zone AXFR ACLs can be
 stored in the domainmetadata table.
@@ -32,6 +38,12 @@ See the documentation on [Dynamic DNS update](dnsupdate.md)
 
 ## ALSO-NOTIFY
 When notifying this domain, also notify this nameserver (can occur multiple times).
+The nameserver may have contain an optional port number. e.g.:
+
+```
+insert into domainmetadata (domain_id, kind, content) values (7,'ALSO-NOTIFY','192.0.2.1:5300');
+insert into domainmetadata (domain_id, kind, content) values (7,'ALLOW-AXFR-FROM','2001:db8:53::1');
+```
 
 ## AXFR-MASTER-TSIG
 Use this named TSIG key to retrieve this zone from its master, see
@@ -68,6 +80,9 @@ records in the zone. However, if you import a presigned zone using `zone2sql` or
 PowerDNS will not be able to correctly serve the zone if the imported data is
 bogus or incomplete. Also see `set-presigned` in [`pdnsutil`](dnssec.md#pdnsutil).
 
+If a zone is presigned, the content of the metadata must be "1" (without the
+quotes). Any other value will not signal prisignedness.
+
 ## PUBLISH-CDNSKEY, PUBLISH-CDS
 Whether to publish CDNSKEY and/or CDS recording defined in [RFC 7344](https://tools.ietf.org/html/rfc7344).
 
index 5792ff56a71e26195dcef89477427bdf6094cf61..9dd164594dcaaf94e845105d43724d2209d8519a 100644 (file)
@@ -155,12 +155,14 @@ If you have multiple IP addresses on the internet on one machine, UNIX often sen
 The ALIAS record provides a way to have CNAME-like behaviour on the zone apex.
 
 In order to correctly serve ALIAS records, set the [`recursor`](settings.md#recursor)
-setting to an existing resolver and add the ALIAS record to your zone apex. e.g.:
+setting to an existing resolver
 
 ```
 recursor=[::1]:5300
 ```
 
+and add the ALIAS record to your zone apex. e.g.:
+
 ```
 $ORIGIN example.net
 $TTL 1800
@@ -176,6 +178,23 @@ When the authoritative server receives a query for the A-record for `example.net
 it will resolve the A record for `mywebapp.paas-provider.net` and serve an answer
 for `example.net` with that A record.
 
+When a zone containing ALIAS records is transferred over AXFR, the
+[`outgoing-axfr-expand-alias`](settings.md#outgoing-axfr-expand-alias) setting
+controls the behaviour of ALIAS records. When set to 'no' (the default), ALIAS
+records are sent as-is (RRType 65401 and a DNSName in the RDATA) in the AXFR.
+When set to 'yes', PowerDNS will lookup the A and AAAA records of the name in the
+ALIAS-record and send the results in the AXFR.
+
+Set `outgoing-axfr-expand-alias` to 'yes' if your slaves don't understand ALIAS
+or should not look up the addresses themselves. Note that slaves will not
+automatically follow changes in those A/AAAA records unless you AXFR regularly.
+
+## ALIAS and DNSSEC
+Starting with the PowerDNS Authoritative Server 4.0.0, DNSSEC 'washing' of ALIAS
+records is supported on AXFR (**not** on live-siging). Set `outgoing-axfr-expand-alias`
+to 'yes' and enable DNSSEC for the zone on the master. PowerDNS will sign the
+A/AAAA records during the AXFR.
+
 # KSK Rollover
 Before attempting a KSK rollover, please read [RFC 6581 "DNSSEC Operational
 Practices, Version 2", section 4](https://tools.ietf.org/html/rfc6781#section-4)
index a231493053d804b48992f761b048afcf2231403f..140c172acaab99894682dbbc80489c7298e84dec 100644 (file)
@@ -1,42 +1,34 @@
 # DNS64 support in the PowerDNS Recursor
-DNS64 is a technology to allow IPv6-only clients to receive special IPv6 addresses that are proxied to IPv4 addresses. This proxy service is then called NAT64.
+DNS64, described in [RFC 6147](https://tools.ietf.org/html/rfc6147) is a technology
+to allow IPv6-only clients to receive special IPv6 addresses that are proxied to
+IPv4 addresses. This proxy service is then called NAT64.
 
-So, as an example, let's say an IPv6 only client would want to connect to www.example.com, it would request the AAAA records for that name. However, if example.com does not actually have an IPv6 address, what we do is 'fake up' an IPv6 address. We do this by retrieving the A records for www.example.com, and translating them to AAAA records.
+So, as an example, let's say an IPv6 only client would want to connect to
+`www.example.com`, it would request the AAAA records for that name. However, if
+`example.com` does not actually have an IPv6 address, what we do is 'fake up' an
+IPv6 address. We do this by retrieving the A records for `www.example.com`, and
+translating them to AAAA records. Elsewhere, a NAT64 device listens on these IPv6
+addresses, and extracts the IPv4 address from each packet, and proxies it on.
 
-Elsewhere, a NAT64 device listens on these IPv6 addresses, and extracts the IPv4 address from each packet, and proxies it on.
+For maximum flexibility, DNS64 support is included in the [Lua scripting engine](scripting.md).
+This allows for example to hand out custom IPv6 gateway ranges depending on the
+location of the requestor, enabling the use of NAT64 services close to the user.
 
-DNS64 is described in RFC 6147, and is supported by the PowerDNS Recursor since version 3.4.
 
-For maximum flexibility, DNS64 support is included in the Lua scripting engine. This allows for example to hand out custom IPv6 gateway ranges depending on the location of the requestor, enabling the use of NAT64 services close to the user.
+Apart from faking AAAA records, it is also possible to also generate the
+associated PTR records. This makes sure that reverse lookup of DNS64-generated
+IPv6 addresses generate the right name. The procedure is similar, a request for
+an IPv6 PTR is converted into one for the corresponding IPv4 address.
 
-To setup DNS64, create the following Lua script and save it to a file called dns64.lua:
+To setup DNS64, with both forward and reverse records, create the following Lua
+script and save it to a file called `dns64.lua`
 
 ```
-    function nodata ( remoteip, domain, qtype, records )
-             if qtype ~= pdns.AAAA then return pdns.PASS, {} end  --  only AAAA records
-             setvariable()
-             return "getFakeAAAARecords", domain, "fe80::21b:77ff:0:0"
-        end
+!!include ../pdns/dns64.lua
 ```
 
-Where fe80::21b::77ff:0:0 is your "Pref64" translation prefix. Next, make sure your script gets loaded by specifying it with `lua-dns-script=dns64.lua`.
+Where fe80::21b::77ff:0:0 is your "Pref64" translation prefix and the "ip6.arpa"
+string is the reversed form of this Pref64 address. Now ensure your script gets
+loaded by specifying it with [`lua-dns-script=dns64.lua`](#settings.md#lua-dns-script).
 
-In addition, since PowerDNS Recursor 3.6, it is also possible to also generate the associated PTR records. This makes sure that reverse lookup of DNS64-generated IPv6 addresses generate the right name. The procedure is similar, a request for an IPv6 PTR is converted into one for the corresponding IPv4 address.
-
-To hook up the generation of PTR records, include:
-
-```
-      function endswith(s, send)
-             return #s >= #send and s:find(send, #s-#send+1, true) and true or false
-      end
-
-      function preresolve ( remoteip, domain, qtype )
-         if qtype ==pdns.PTR and endswith(domain, "f.f.7.7.b.1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa.")
-             then
-                return "getFakePTRRecords", domain, "fe80::21b::77ff:0:0"
-             end
-         return pdns.PASS, {}
-      end
-```
-
-Where the "ip6.arpa" string is the reversed form of your Pref64 address.
+To enhance DNS64, see the [Lua scripting](scripting.md) documentation.
index 97a322e8e83c58192c530b343a3d3b465c572a56..96664cfd165c38244cec85d5948089a745130431 100644 (file)
@@ -16,7 +16,7 @@ AD bits in queries. In this mode, the behaviour is equal to the PowerDNS Recurso
 ## `process-no-validate`
 The default mode. In this mode the Recursor acts as a "security aware, non-validating"
 nameserver, meaning it will set the DO-bit on outgoing queries and will provide
-DNSSEC related RRsets (NSEC, RRSIG) to clients that ask for it (by means of a
+DNSSEC related RRsets (NSEC, RRSIG) to clients that ask for them (by means of a
 DO-bit in the query). It will not do any validation in this mode, not even when
 requested by the client.
 
@@ -26,7 +26,7 @@ However, when the query has the AD-bit set, the recursor will try to validate th
 data and set the AD-bit in the response when the data is validated and send a
 SERVFAIL on a bogus answer.
 
-## `validate-log`
+## `log-fail`
 In this mode , the recursor will attempt to validate all data it retrieves from
 authoritative servers, regardless of the client's DNSSEC desires, and will log the
 validation result. This mode can be used to determine the extra load and amount
@@ -42,13 +42,17 @@ client's request.
 The descriptions above are a bit terse, here's a table describing different scenarios
 with regards to the `dnssec` mode.
 
-|    | `off` | `process-no-validate` | `process` | `validate-log` | `validate` |
+|    | `off` | `process-no-validate` | `process` | `log-fail` | `validate` |
 |:------------|:-------|:-------------|:-------------|:-------------|:-------------|
 |Perform validation| No | No | Only on +AD from client | Always (logs result) | Always |
 |SERVFAIL on bogus| No | No | Only on +AD from client | Only on +AD from client | Always |
 |AD in response on authenticated data| Never | Never | Only on +AD from client | Only on +AD from client | Only on +AD from client |
 |RRSIGs/NSECs in answer on +DO from client| No | Yes | Yes | Yes | Yes |
 
+**Note**: the `dig` tool sets the AD-bit in the query. This might lead to unexpected
+query results when testing. Set `+noad` on the `dig` commandline when this is the
+case.
+
 # Trust Anchor Management
 In the PowerDNS Recursor, both positive and negative trust anchors can be configured
 during startup (from a persistent configuration file) and at runtime (which is
index f81ff82de7fb15f2134ba86b8d114e3c8f85db4b..9625b4ea85b86e14e38bde18639b447c70dd9d30 100644 (file)
@@ -1,21 +1,25 @@
 # Scripting The Recursor
-As of version 3.1.7 of the PowerDNS Recursor, it is possible to modify
-resolving behaviour using simple scripts written in the
-[Lua](http://www.lua.org) programming language. This page documents the Recursor 4.x and beyond
-version of the scripting API.
+In the PowerDNS recursor, it is possible to modify resolving behaviour using
+simple scripts written in the [Lua](http://www.lua.org) programming language.
+This page documents the Recursor 4.0.0 and beyond version of the scripting API.
+
+**Note**: This describes the Lua scripts as supported by 4.x. They are very
+different than the ones from 3.x, but tend to be faster and more correct.
 
 These scripts can be used to quickly override dangerous domains, fix things
-that are wrong, for load balancing or for legal or commercial purposes. The scripts can also protect 
-you or your users from malicious traffic.
+that are wrong, for load balancing or for legal or commercial purposes. The
+scripts can also protect you or your users from malicious traffic.
 
-Lua is extremely fast and lightweight, easily supporting hundreds of
-thousands of queries per second. The Lua language is explained very
-well in the excellent book [Programming in Lua](http://www.amazon.com/exec/obidos/ASIN/859037985X/lua-pilindex-20). If you already have programming experience, [Learn Lua in 15 Minutes](http://tylerneylon.com/a/learn-lua/) is a great primer.
+Lua is extremely fast and lightweight, easily supporting hundreds of thousands
+of queries per second. The Lua language is explained very well in the excellent
+book [Programming in Lua](http://www.amazon.com/exec/obidos/ASIN/859037985X/lua-pilindex-20).
+If you already have programming experience,
+[Learn Lua in 15 Minutes](http://tylerneylon.com/a/learn-lua/) is a great primer.
 
 For extra performance, a Just In Time compiled version of Lua called
 [LuaJIT](http://luajit.org/) is supported.
 
-Queries can be intercepted in many places: 
+Queries can be intercepted in many places:
 
 * before any packet parsing begins (`ipfilter`)
 * before the resolving logic starts to work (`preresolve`)
@@ -24,13 +28,10 @@ Queries can be intercepted in many places:
 * before an outgoing query is made to an authoritative server (`preoutquery`)
 
 ## Configuring Lua scripts
-
 In order to load scripts, the PowerDNS Recursor must have Lua support built
 in.  The packages distributed from the PowerDNS website have this language
-enabled, other distributions may differ.  To compile with Lua support, use:
-`LUA=1 make` or `LUA=1 gmake` as the case may be.  Paths to the Lua include
-files and binaries may be found near the top of the `Makefile`, or passed 
-to `configure`.
+enabled, other distributions may differ. By default, the Recursor's configure
+script will attempt to detect if Lua is available.
 
 **note**: Only one script can be loaded at the same time. If you load a different
 script, the current one will be replaced (safely)!
@@ -41,7 +42,8 @@ be reloaded or unloaded at runtime with no interruption in operations.  If a
 new script contains syntax errors, the old script remains in force.
 
 On the command line, or in the configuration file, the setting
-`lua-dns-script` can be used to supply a full path to a 'lua' script.
+[`lua-dns-script`](settings.md#lua-dns-script) can be used to supply a full path
+to the Lua script.
 
 At runtime, `rec_control reload-lua-script` can be used to either reload the
 script from its current location, or, when passed a new file name, load one
@@ -54,24 +56,54 @@ also that you'll be informed about syntax errors at compile time.
 
 Finally, `rec_control unload-lua-script` can be used to remove the currently
 installed script, and revert to unmodified behaviour.
-
-## Writing Lua PowerDNS Recursor scripts for version 4.x
-**Note**: This describes the Lua scripts as supported by 4.x. They are very different than the ones from 3.x, but
-tend to be faster and more correct.
 
+# Writing Lua PowerDNS Recursor scripts
 To get a quick start, we have supplied a sample script that showcases all functionality described below. Please
 find it [here](https://github.com/PowerDNS/pdns/blob/master/pdns/powerdns-example-script.lua). 
 
-In the 4.x API, addresses and DNS Names are not passed as strings but as native objects. This allows for
-easy checking against netmasks and domain sets. It also means that to print such names, the `:toString` 
-method must be used (or even `:toStringWithPort` for addresses).
+Addresses and DNS Names are not passed as strings but as native objects. This
+allows for easy checking against [netmasks](#netmask-groups) and [domain sets]().
+It also means that to print such names, the `:toString` method must be used
+(or even `:toStringWithPort` for addresses).
 
 Comparing IP addresses and DNSNames is not done with '==' but with the `:equal` method. 
 
-Once a script is loaded, PowerDNS looks for several functions, as detailed below. All of these functions are optional.
+Once a script is loaded, PowerDNS looks for several functions, as detailed below.
+All of these functions are optional.
+
+## The DNSQuestion (`dq`) object
+Apart from the `ipfilter`-function, all functions work on a `dq` (DNSQuestion)
+object. This object contains details about the current state of the question.
+This state can be modified from the various hooks. If a function returns 'true',
+it will indicate that it handled a query. If it returns false, the Recursor will
+continue processing unchanged (with one minor exception).
+
+The DNSQuestion object contains at least the following fields:
+
+* qname - DNS native version of the name this query is for
+* qtype - type this query is for, can be compared against pdns.A, pdns.AAAA etc
+* rcode - current DNS Result Code, which can be overridden, including to several magical values
+* isTcp - whether the query have been received over TCP or UDP
+* remoteaddr - address of the requestor
+* localaddr - address this query was received on
+* variable - a boolean which, if set, indicates the recursor should not packet cache this answer. Honored even when returning 'false'! Important when providing answers that vary over time or based on sender details.
+* followupFunction - a string that signals the nameserver to take one of the following additional actions:
+  * followCNAMERecords: When adding a CNAME to the answer, this tells the recursor to follow that CNAME. See [CNAME chain resolution](#cname-chain-resolution)
+  * getFakeAAAARecords: Get a fake AAAA record, see [DNS64](#dns64)
+  * getFakePTRRecords: Get a fake PTR record, see [DNS64](#dns64)
+  * udpQueryResponse: Do a UDP query and call a handler, see [`udpQueryResponse`](#udpqueryresponse)
+
+It also supports the following methods:
+
+* `addAnswer(type, content, [ttl, name])`: add an answer to the record of `type` with `content`. Optionally supply TTL and the name of
+  the answer too, which defaults to the name of the question
+* `getRecords()`: get a table of DNS Records in this DNS Question (or answer by now)
+* `setRecords(records)`: after your edits, update the answers of this question
+* `getEDNSOption(num)`: get the EDNS Option with number `num`
+* `getEDNSOptions()`: get a map of all EDNS Options
+* `getEDNSSubnet()`: returns the netmask specified in the EDNSSubnet option, or empty if there was none
 
-### `function ipfilter ( remoteip, localip, dh )`
+## `function ipfilter ( remoteip, localip, dh )`
 This hook gets queried immediately after consulting the packet cache, but before
 parsing the DNS packet. If this hook returns something else than false, the packet is dropped. 
 However, because this check is after the packet cache, the IP address might still receive answers
@@ -99,32 +131,22 @@ function ipfilter(rem, loc, dh)
 end
 ```
 
-This hook does not get the full DNSQuestion object as described below, since filling out the fields
+This hook does not get the full DNSQuestion object, since filling out the fields
 would require packet parsing, which is what we are trying to prevent with `ipfilter`.
 
-### The DNSQuestion object
-The following functions all receive a DNSQuestion object, which contains details about
-the current state of the question. This state can be modified from the various hooks. If
-a function returns 'true', it will indicate that it handled a query. If it returns false,
-the Recursor will continue processing unchanged (with one minor exception).
-
-The DNSQuestion object contains at least the following fields:
-
-* qname - DNS native version of the name this query is for
-* qtype - type this query is for, can be compared against pdns.A, pdns.AAAA etc
-* rcode - current DNS Result Code, which can be overridden, including to several magical values
-* remoteaddr - address of the requestor
-* localaddr - address this query was received on
-* variable - a boolean which, if set, indicates the recursor should not packet cache this answer. Honored even when returning 'false'! Important when providing answers that vary over time or based on sender details.
-
-It also supports the following methods:
+### `function gettag(remote, ednssubnet, local, qname, qtype)`
+The `gettag` function is invoked when `dq.tag` is called on a dq object or when
+the Recursor attempts to discover in which packetcache an answer is available.
+This function must return an integer, which is the tag number of the packetcache.
+In addition to this integer, this function can return a table of policy tags.
 
-* `addAnswer(type, content, [ttl, name])`: add an answer to the record of `type` with `content`. Optionally supply TTL and the name of 
-  the answer too, which defaults to the name of the question
-* `getRecords()`: get a table of DNS Records in this DNS Question (or answer by now)
-* `setRecords(records)`: after your edits, update the answers of this question 
+The tagged packetcache can e.g. be used to answer queries from cache that have
+e.g. been filtered for certain IPs (this logic should be implemented in the
+`gettag` function). This ensure that queries are answered quickly compared to
+setting dq.variable to `true`. In the latter case, repeated queries will pass
+through the entire Lua script.
 
-### `function preresolve ( dq )`
+### `function preresolve(dq)`
 is called before any DNS resolution is attempted, and if this function
 indicates it, it can supply a direct answer to the DNS query, overriding the
 internet.  This is useful to combat botnets, or to disable domains
@@ -133,24 +155,22 @@ unacceptable to an organization for whatever reason.
 The rcode can be set to pdns.DROP to drop the query. Other statuses are normal DNS
 return codes, like no error, NXDOMDAIN etc.
 
-### `function postresolve (dq)`
-
+### `function postresolve(dq)`
 is called right before returning a response to a client (and, unless
 `variable` is set, to the packet cache too).  It allows inspection
-and modification of almost any detail in the return packet.  
-
-### `function nxdomain ( dq )`
+and modification of almost any detail in the return packet.
 
+### `function nxdomain(dq)`
 is called after the DNS resolution process has run its course, but ended in
 an 'NXDOMAIN' situation, indicating that the domain or the specific record
-does not exist.  Works entirely like postresolve, but saves a trip through Lua for 
+does not exist.  Works entirely like postresolve, but saves a trip through Lua for
 answers which are not NXDOMAIN.
 
-### `function nodata ( dq )`
+### `function nodata(dq)`
 is just like `nxdomain`, except it gets called when a domain exists, but the
-requested type does not.  This is where one would implement DNS64. 
+requested type does not.  This is where one would implement DNS64.
 
-### `function preoutquery (dq)`
+### `function preoutquery(dq)`
 This hook is not called in response to a client packet, but fires when the Recursor
 wants to talk to an authoritative server. When this hook sets the special result code -3,
 the whole DNS client query causing this outquery gets dropped.
@@ -158,20 +178,19 @@ the whole DNS client query causing this outquery gets dropped.
 However, this function can also return records like the preresolve query above.
 
 ## Semantics
-
-All these functions are passed the IP address of the requester. Most also
-get passed the name and type being requested.  In return, these functions
-indicate if they have taken over the request, or want to let normal
-proceedings take their course.
+The functions must return `true` if they have taken over the query and wish that
+the nameserver should not proceed with its regular query-processing. When a
+function returns `false`, the nameserver will process the query normally until
+a new function is called.
 
 If a function has taken over a request, it should set an rcode (usually 0),
 and specify a table with records to be put in the answer section of a
 packet.  An interesting rcode is NXDOMAIN (3, or `pdns.NXDOMAIN`), which
-specifies the non-existence of a domain.  Returning false signifies that the
-function chose not to intervene.
+specifies the non-existence of a domain.
 
-The `ipfilter` and `preoutquery` hooks are different, in that `ipfilter` can only return a true of false value, and
-that `preoutquery` can also set rcode -3 to signify that the whole query should be terminated.
+The `ipfilter` and `preoutquery` hooks are different, in that `ipfilter` can
+only return a true of false value, and that `preoutquery` can also set rcode -3
+to signify that the whole query should be terminated.
 
 A minimal sample script:
 
@@ -210,59 +229,71 @@ if nmg:match(dq.remote) then
 end
 ```
 
-### DNSName 
+### IP Addresses
+We move IP addresses around in native format, called ComboAddress within PowerDNS.
+ComboAddresses can be IPv4 or IPv6, and unless you want to know, you don't need
+to. You can make a ComboAddress with: `newCA("::1")`, and you can compare
+it against a NetmaskGroup as described above.
+
+To compare the address (so not the port) of two ComboAddresses, use `:equal`.
+
+To convert an address to human-friendly representation, use `:toString()` or
+`:toStringWithPort()`. To get only the port number, use `:getPort()`.
+
+### DNSName
 DNSNames are passed to various functions, and they sport the following methods:
 
 * `:equal`: use this to compare two DNSNames in DNS native fashion. So 'PoWeRdNs.COM' matches 'powerdns.com'
 * `:isPartOf`: returns true if a is a part of b. So: `newDN("www.powerdns.com"):isPartOf(newDN("CoM."))` returns true
 
-To make your own DNSName, use newDN("domain.name").
+To make your own DNSName, use `newDN("domain.name")`.
 
-### IP Addresses
-We move IP addresses around in native format, called ComboAddress within PowerDNS. ComboAddresses can be IPv4 or IPv6,
-and unless you want to know, you don't need to. You can make a ComboAddress with: `newCA("::1")`, and you can compare 
-it against a NetmaskGroup as described above.
+### DNS Suffix Match groups
+The `newDS` function creates a "Suffix Match group" that allows fast checking if
+a DNSName is part of a group. Add domains to this group with the `:add(domain)`
+function of the object: `myDS:add("example.net")`, or with a list:
+`myDS:add({"example.net", "example.com"}).
 
-To compare the address (so not the port) of two ComboAddresses, use `:equal`. 
+To check e.g. the dq.qname against this list, use `:check(dq.qname)`. This will
+be `true` if dq.qname is part of any of the Suffix Match group domains.
 
-To convert an address to human-friendly representation, use `:toString()` or `:toStringWithPort()`. To 
-get only the port number, use `:getPort()`.
+This could e.g. be used to answer questions for known malware domains.
 
 ### Metrics
-You can custom metrics which will be shown in the output of 'rec_control get-all' and sent to the metrics server over the Carbon protocol,
-and also appear in the JSON HTTP API. 
+You can custom metrics which will be shown in the output of 'rec_control get-all'
+and sent to the metrics server over the Carbon protocol, and also appear in the
+JSON HTTP API.
 
-Create a custom metric with: `myMetric= getMetric("name")`. This metric sports the following metrics:
+Create a custom metric with: `myMetric= getMetric("name")`. This metric sports
+the following metrics:
 
 * `:inc()`: increase metric by 1
 * `:incBy(amount)`: increase metric by amount
 * `:set(to)`: set metric to value to
 * `:get()`: get value of metric
 
-Metrics are shared across all of PowerDNS and are fully atomic and high performance. The myMetric object is effectively a
-pointer to an atomic value. 
+Metrics are shared across all of PowerDNS and are fully atomic and high
+performance. The myMetric object is effectively a pointer to an atomic value.
 
-Note that metrics live in the same namespace as 'system' metrics. So if you generate one that overlaps with a PowerDNS stock
-metric, you will get double output and weird results. 
+Note that metrics live in the same namespace as 'system' metrics. So if you
+generate one that overlaps with a PowerDNS stock metric, you will get double
+output and weird results.
 
 ### Logging
-To log messages with the main PowerDNS Recursor process, use
-`pdnslog(message)`.  Available since version 3.2.  pdnslog can also write
-out to a syslog loglevel if specified.  Use `pdnslog(message,
-pdns.loglevels.LEVEL)` with the correct pdns.loglevels entry.  Entries are
-listed in the following table:
-
-|&nbsp;|&nbsp;|
-|:--||:--|
-|All|pdns.loglevels.All|
-|Alert|pdns.loglevels.Alert|
-|Critical|pdns.loglevels.Critical|
-|Error|pdns.loglevels.Error|
-|Warning|pdns.loglevels.Warning|
-|Notice|pdns.loglevels.Notice|
-|Info|pdns.loglevels.Info|
-|Debug|pdns.loglevels.Debug|
-|None|pdns.loglevels.None|
+To log messages with the main PowerDNS Recursor process, use `pdnslog(message)`.
+pdnslog can also write out to a syslog loglevel if specified.
+Use `pdnslog(message, pdns.loglevels.LEVEL)` with the correct pdns.loglevels
+entry.  Entries are listed in the following table:
+
+* All - `pdns.loglevels.All`
+* Alert - `pdns.loglevels.Alert`
+* Critical - `pdns.loglevels.Critical`
+* Error - `pdns.loglevels.Error`
+* Warning - `pdns.loglevels.Warning`
+* Notice - `pdns.loglevels.Notice`
+* Info - `pdns.loglevels.Info`
+* Debug - `pdns.loglevels.Debug`
+* None - `pdns.loglevels.None`
 
 `pdnslog(message)` will write out to Info by default.
 
@@ -270,19 +301,53 @@ listed in the following table:
 Public Suffix List. In general it will tell you the 'registered domain' for a given
 name.
 
-To get fake AAAA records for DNS64 usage, set dq.followupFunction to "getFakeAAAARecords",
-dq.followupPrefix to (say) "fe80::21b:77ff:0:0" and dq.followupName to the name you want to 
-synthesise an IPv6 address for.
+## DNS64
+The `getFakeAAAARecords` and `getFakePTRRecords` followupFunctions can be used
+to implement DNS64. See [DNS64 support in the PowerDNS Recursor](dns64.md) for
+more information.
+
+To get fake AAAA records for DNS64 usage, set dq.followupFunction to `getFakeAAAARecords`,
+dq.followupPrefix to e.g. "64:ff9b::" and dq.followupName to the name you want to
+synthesize an IPv6 address for.
+
+For fake reverse (PTR) records, set dq.followupFunction to `getFakePTRRecords`
+and set dq.followupName to the name to look up and dq.followupPrefix to the
+same prefix as used with `getFakeAAAARecords`.
 
 ## CNAME chain resolution
 It may be useful to return a CNAME record for Lua, and then have the
 PowerDNS Recursor continue resolving that CNAME.  This can be achieved by
-setting dq.followupFunction to "followCNAMERecords" and followupDomain to 
+setting dq.followupFunction to `followCNAMERecords` and dq.followupDomain to
 "www.powerdns.com". PowerDNS will do the rest.
 
+## `udpQueryResponse`
+The `udpQueryResponse` dq.followupFunction allows you to query a simple key-value
+store over UDP asynchronously.
+
+Several dq variables can be set:
+
+* `udpQueryDest`: destination IP address to send the UDP packet to
+* `udpQuery`: The content of the UDP payload
+* `udpCallback`: The name of the callback function that is called when an answer is received
+
+The callback function must accept the `dq` object and can find the response to
+the UDP query in `dq.udpAnswer`.
+
+In this callback function, `dq.followupFunction` can be set again to any of the
+available functions for further processing.
+
+This example script queries a simple key/value store over UDP to decide on whether
+or not to filter a query:
+
+```
+!!include=../pdns/kv-example-script.lua
+```
 
-## Some sample scripts
-The full sample script can be found [here](https://github.com/PowerDNS/pdns/blob/master/pdns/powerdns-example-script.lua).
+## Example Script
+
+```
+!!include=../pdns/powerdns-example-script.lua
+```
 
 ### Dropping all traffic from botnet-infected users
 Frequently, DoS attacks are performed where specific IP addresses are attacked, 
@@ -310,6 +375,4 @@ function preoutquery(dq)
        end
        return false
 end
-
-
 ```
index 4b4dec668f795fe11579440161cd55ea11c97970..27fdede05f8a308453e4544231ef5f5acfb82a48 100644 (file)
@@ -103,9 +103,11 @@ recursor-4.0.0-rc1.security-status                      60 IN TXT "0 Unknown, pr
 
 ; Recursor Debian
 recursor-3.6.2-2.debian.security-status                 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/md/security/powerdns-advisory-2015-01/"
+recursor-3.6.2-2_bpo70_2.debian.security-status         60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/md/security/powerdns-advisory-2015-01/"
 recursor-3.6.2-2_deb8u1.debian.security-status          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/md/security/powerdns-advisory-2015-01/"
+recursor-3.6.2-2_deb8u1_bpo70_1.debian.security-status  60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/md/security/powerdns-advisory-2015-01/"
 recursor-3.6.2-2_deb8u2.debian.security-status          60 IN TXT "1 OK"
-recursor-3.6.2-2_bpo70_2.debian.security-status         60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/md/security/powerdns-advisory-2015-01/"
+recursor-3.6.2-2_deb8u2_bpo70_1.debian.security-status  60 IN TXT "1 OK"
 
 recursor-3.7.2-1.debian.security-status                 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/md/security/powerdns-advisory-2015-01/"
 recursor-3.7.2-1_bpo8_1.debian.security-status          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/md/security/powerdns-advisory-2015-01/"
index 8efccd41b7a4dac0d5d2532d9f3aced30acce8da..2c4bb30e805c08c1ded1e74b14fc322fdbdaadf2 100644 (file)
@@ -1345,10 +1345,13 @@ private:
                     // an exception_ptr was pushed on the stack
                     // rethrowing it with an additional ExecutionErrorException
                     try {
-                        std::rethrow_exception(readTopAndPop<std::exception_ptr>(state, std::move(errorCode)));
+                        if (const auto exp = readTopAndPop<std::exception_ptr>(state, std::move(errorCode))) {
+                            std::rethrow_exception(exp);
+                        }
                     } catch(...) {
                         std::throw_with_nested(ExecutionErrorException{"Exception thrown by a callback function called by Lua"});
                     }
+                    throw ExecutionErrorException{"Unknown Lua error"};
                 }
             }
         }
index 36109776c9d339288e5f49352c2d8a6775cd56f6..b0d66c9c0935ab4530d78694c7a446f56939f06d 100644 (file)
@@ -22,7 +22,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 m4_define([_BOOST_SERIAL], [m4_translit([
-# serial 25
+# serial 26 PowerDNS modified
 ], [#
 ], [])])
 
@@ -86,9 +86,10 @@ dnl boost-lib-version =
 dnl # 2 "conftest.cc" 3
 dnl                    "1_56"
 dnl
-dnl So get rid of the # lines, and glue the remaining ones together.
+dnl So get rid of the # and empty lines, and glue the remaining ones together.
 (eval "$ac_cpp conftest.$ac_ext") 2>&AS_MESSAGE_LOG_FD |
   grep -v '#' |
+  grep -v '^[[[:space:]]]*$' |
   tr -d '\r' |
   tr -s '\n' ' ' |
   $SED -n -e "$1" >conftest.i 2>&1],
@@ -548,6 +549,13 @@ BOOST_DEFUN([Asio],
 BOOST_FIND_HEADER([boost/asio.hpp])])
 
 
+# BOOST_ASSIGN()
+# -------------
+# Look for Boost.Assign
+BOOST_DEFUN([Assign],
+[BOOST_FIND_HEADER([boost/assign.hpp])])
+
+
 # BOOST_BIND()
 # ------------
 # Look for Boost.Bind.
diff --git a/m4/pdns_check_linkchecker.m4 b/m4/pdns_check_linkchecker.m4
deleted file mode 100644 (file)
index 38d70c8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-AC_DEFUN([PDNS_CHECK_LINKCHECKER], [
-  AC_CHECK_PROG([LINKCHECKER], [linkchecker], [linkchecker], [no])
-
-  AS_IF([test "x$LINKCHECKER" = "xno"], [
-      AC_MSG_WARN([linkchecker is missing, unable to verify links in the documentation.])
-    ])
-  AM_CONDITIONAL([HAVE_LINKCHECKER], [test "x$LINKCHECKER" != "xno"])
-])
-
diff --git a/m4/pdns_check_lua_hpp.m4 b/m4/pdns_check_lua_hpp.m4
new file mode 100644 (file)
index 0000000..7317153
--- /dev/null
@@ -0,0 +1,8 @@
+AC_DEFUN([PDNS_CHECK_LUA_HPP],[
+  AC_REQUIRE([PDNS_WITH_LUA])
+  AC_REQUIRE([PDNS_WITH_LUAJIT])
+  AS_IF([test "x$LUAPC" != "x" -o "x$LUAJITPC" != "x" ], [
+    AC_CHECK_HEADER([lua.hpp], [ have_lua_hpp=y ])
+  ])
+  AM_CONDITIONAL([HAVE_LUA_HPP], [ test x"$have_lua_hpp" = "xy" ])
+])
diff --git a/m4/pdns_check_mkdocs.m4 b/m4/pdns_check_mkdocs.m4
deleted file mode 100644 (file)
index f53aed8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-AC_DEFUN([PDNS_CHECK_MKDOCS], [
-  AC_CHECK_PROG([MKDOCS], [mkdocs], [yes], [no])
-
-  AS_IF([test "x$MKDOCS" = "xno"], [
-    AS_IF([test ! -d "$scrdir/docs/html" ],
-      [AC_MSG_WARN([mkdocs is missing, unable to build documentation.])]
-    )
-  ])
-])
index d18f6ebb34500c28fc6520b855fcb7d792cae0ec..88e71106821d89aec66c7eee715a519da262b19b 100644 (file)
@@ -2,7 +2,7 @@ AC_DEFUN([PDNS_CHECK_OS],[
   THREADFLAGS=""
 
   case "$host_os" in
-  solaris2.10)
+  solaris2.1*)
     LIBS="-lposix4 -lpthread $LIBS"
     CXXFLAGS="-D_REENTRANT $CXXFLAGS"
     have_solaris="yes"
index dfe35f2158d49382eb1c936be545fe93eaa1b1c5..ac65c0c0172de2a751f4a98fb47e53cfa85097b0 100644 (file)
@@ -1,4 +1,5 @@
 AC_DEFUN([PDNS_ENABLE_REPRODUCIBLE], [
+  AC_REQUIRE([PDNS_CHECK_OS])
   AC_MSG_CHECKING([whether to enable reproducible builds.])
   AC_ARG_ENABLE([reproducible],
     AS_HELP_STRING([--enable-reproducible],
@@ -12,13 +13,17 @@ AC_DEFUN([PDNS_ENABLE_REPRODUCIBLE], [
     AC_DEFINE([REPRODUCIBLE], [1], [Define to 1 for reproducible builds])
   ],[
     build_user=$(id -u -n)
-    if [ test x"$host_os" = "xSunOS" ]; then
+
+    case "$host_os" in
+    solaris2.1* | SunOS)
       build_host_host=$(hostname)
       build_host_domain=$(domainname)
       build_host="$build_host_host.$build_host_domain"
-    else
+      ;;
+    *)
       build_host=$(hostname -f || hostname || echo 'localhost')
-    fi
+      ;;
+    esac
     AC_DEFINE_UNQUOTED([BUILD_HOST], ["$build_user@$build_host"], [Set to the user and host that builds PowerDNS])
   ])
 ])
diff --git a/m4/pdns_from_git.m4 b/m4/pdns_from_git.m4
new file mode 100644 (file)
index 0000000..18966a5
--- /dev/null
@@ -0,0 +1,3 @@
+AC_DEFUN([PDNS_FROM_GIT], [
+  AM_CONDITIONAL([FROM_GIT], [test -d "$srcdir/.git"])
+])
index 1956a1538dac00ca1fb69f29473eb4dcd6441f52..18bb1b9d8bd1a4469f1d24c09435e05836cf3b1a 100644 (file)
@@ -28,9 +28,9 @@ AC_DEFUN([PDNS_WITH_LUA],[
       AS_IF([test "x$with_lua" = "xyes"],
         [AC_MSG_ERROR([cannot find lua])],
         [AC_MSG_RESULT([not found])]
-      )],
-      [AC_MSG_RESULT([$LUAPC])]
-    )
-  ])
+      )],[
+        AC_MSG_RESULT([$LUAPC])
+      ])
+    ])
   AM_CONDITIONAL([LUA], [test "x$with_lua" = "xyes"])
 ])
index 79321e0c7a7a487b6912d6f7202de524c95d9bf0..75ccac3aa7c2df44f5ddb0fac7e7d02f42a6a68b 100644 (file)
@@ -576,6 +576,14 @@ string Bind2Backend::DLAddDomainHandler(const vector<string>&parts, Utility::pid
   BB2DomainInfo bbd;
   if(safeGetBBDomainInfo(domainname, &bbd))
     return "Already loaded";
+
+  if (!boost::starts_with(filename, "/") && ::arg()["chroot"].empty())
+    return "Unable to load zone " + domainname.toStringRootDot() + " from " + filename + " as the filename is not absolute.";
+
+  struct stat buf;
+  if (stat(filename.c_str(), &buf) != 0)
+    return "Unable to load zone " + domainname.toStringRootDot() + " from " + filename + ": " + strerror(errno);
+
   Bind2Backend bb2; // createdomainentry needs access to our configuration
   bbd=bb2.createDomainEntry(domainname, filename);
   bbd.d_filename=filename;
index 5c5a7216fcfd7b05d3557c11bba293e9a7e33bb8..f997183e21352754e8666805e72f721aa19714ba 100644 (file)
@@ -89,7 +89,7 @@ ssize_t UnixsocketConnector::read(std::string &data) {
   // just try again later...
   if (nread==-1 && errno == EAGAIN) return 0;
 
-  if (nread==-1) {
+  if (nread==-1 || nread==0) {
     connected = false;
     close(fd);
     return -1;
index 1ade72c65067bc3b7fac8069f8ccabe4061bd512..e2c6a536d7ab698920cf414d5ba540faa0e5b01b 100644 (file)
@@ -1162,6 +1162,14 @@ testrunner_LDADD = \
        $(RT_LIBS) \
        $(LIBDL)
 
+if HAVE_PROTOBUF
+nodist_testrunner_SOURCES = \
+       dnsmessage.pb.cc dnsmessage.pb.h
+
+testrunner_LDADD += \
+       $(PROTOBUF_LIBS)
+endif
+
 if PKCS11
 testrunner_SOURCES += pkcs11signers.cc pkcs11signers.hh
 testrunner_LDADD += $(P11KIT1_LIBS)
@@ -1180,8 +1188,15 @@ pdns_control_SOURCES = \
        dnslabeltext.cc
 
 if UNIT_TESTS
+
+if HAVE_BOOST_GE_148
 TESTS_ENVIRONMENT = env BOOST_TEST_LOG_LEVEL=message SRCDIR='$(srcdir)'
 TESTS=testrunner
+else
+check-local:
+       @echo "Unit tests disabled, boost is too old"
+endif
+
 else
 check-local:
        @echo "Unit tests are not enabled"
index 728bef776e5cd3d12b70caaea478e41e34f4f549..3e3ccfd0e4aaf989bd1416d5f196eff6c5c468d1 100644 (file)
@@ -576,11 +576,13 @@ This is still much in flux, but for now, try:
 For example:
 ```
 > grepq("127.0.0.1/24")
--11.9   127.0.0.1:52599                                 16127 nxdomain.powerdns.com.    A             RD    Question
--11.7   127.0.0.1:52599                                 16127 nxdomain.powerdns.com.    A     175.6    RD    Non-Existent domain
+Time    Client                                          Server       ID    Name                      Type  Lat.   TC RD AA Rcode
+-11.9   127.0.0.1:52599                                              16127 nxdomain.powerdns.com.    A               RD    Question
+-11.7   127.0.0.1:52599                                 127.0.0.1:53 16127 nxdomain.powerdns.com.    A     175.6     RD    Non-Existent domain
 > grepq("powerdns.com")
--38.7   127.0.0.1:52599                                 16127 nxdomain.powerdns.com.    A             RD    Question
--38.6   127.0.0.1:52599                                 16127 nxdomain.powerdns.com.    A     175.6    RD    Non-Existent domain
+Time    Client                                          Server       ID    Name                      Type  Lat.   TC RD AA Rcode
+-38.7   127.0.0.1:52599                                              16127 nxdomain.powerdns.com.    A               RD    Question
+-38.6   127.0.0.1:52599                                 127.0.0.1:53 16127 nxdomain.powerdns.com.    A     175.6     RD    Non-Existent domain
 ```
 
 Live histogram of latency
@@ -987,6 +989,45 @@ If you forgot to write down the provider fingerprint value after generating the
 Provider fingerprint is: E1D7:2108:9A59:BF8D:F101:16FA:ED5E:EA6A:9F6C:C78F:7F91:AF6B:027E:62F4:69C3:B1AA
 ```
 
+AXFR, IXFR and NOTIFY
+---------------------
+When `dnsdist` is deployed in front of a master authoritative server, it might
+receive AXFR or IXFR queries destined to this master. There are two issues
+that can arise in this kind of setup:
+
+ * If the master is part of a pool of servers, the first SOA query can be directed
+   by `dnsdist` to a different server than the following AXFR/IXFR one, which might
+   fail if the servers are not perfectly synchronised.
+ * If the master only allows AXFR/IXFR based on the source address of the requestor,
+   it might be confused by the fact that the source address will be the one from
+   the `dnsdist` server.
+
+The first issue can be solved by routing SOA, AXFR and IXFR requests explicitely
+to the master:
+
+```
+> newServer({address="192.168.1.2", name="master", pool={"master", "otherpool"}})
+> addAction(OrRule({QTypeRule(dnsdist.SOA), QTypeRule(dnsdist.AXFR), QTypeRule(dnsdist.IXFR)}), PoolAction("master"))
+```
+
+The second one might require allowing AXFR/IXFR from the `dnsdist` source address
+and moving the source address check on `dnsdist`'s side:
+
+```
+> addAction(AndRule({OrRule({QTypeRule(dnsdist.AXFR), QTypeRule(dnsdist.IXFR)}), NotRule(makeRule("192.168.1.0/24"))}), RCodeAction(dnsdist.REFUSED))
+```
+
+When `dnsdist` is deployed in front of slaves, however, an issue might arise with NOTIFY
+queries, because the slave will receive a notification coming from the `dnsdist` address,
+and not the master's one. One way to fix this issue is to allow NOTIFY from the `dnsdist`
+address on the slave side (for example with PowerDNS's `trusted-notification-proxy`) and
+move the address check on `dnsdist`'s side:
+
+```
+> addAction(AndRule({OpcodeRule(DNSOpcode.Notify), NotRule(makeRule("192.168.1.0/24"))}), RCodeAction(dnsdist.REFUSED))
+```
+
+
 All functions and types
 -----------------------
 Within `dnsdist` several core object types exist:
@@ -1020,8 +1061,8 @@ Here are all functions:
     * `setACL({netmask, netmask})`: replace the ACL set with these netmasks. Use `setACL({})` to reset the list, meaning no one can use us
     * `showACL()`: show our ACL set
  * Network related:
-    * `addLocal(netmask, [false], [false])`: add to addresses we listen on. Second optional parameter sets TCP/IP or not. Third optional parameter sets SO_REUSEPORT when available.
-    * `setLocal(netmask, [false], [false])`: reset list of addresses we listen on to this address. Second optional parameter sets TCP/IP or not. Third optional parameter sets SO_REUSEPORT when available.
+    * `addLocal(netmask, [true], [false])`: add to addresses we listen on. Second optional parameter sets TCP/IP or not. Third optional parameter sets SO_REUSEPORT when available.
+    * `setLocal(netmask, [true], [false])`: reset list of addresses we listen on to this address. Second optional parameter sets TCP/IP or not. Third optional parameter sets SO_REUSEPORT when available.
  * Blocking related:
     * `addDomainBlock(domain)`: block queries within this domain
  * Carbon/Graphite/Metronome statistics related:
@@ -1118,7 +1159,7 @@ instantiate a server with additional parameters
     * `SpoofAction(ip[, ip])` or `SpoofAction({ip, ip, ..}): forge a response with the specified IPv4 (for an A query) or IPv6 (for an AAAA). If you specify multiple addresses, all that match the query type (A, AAAA or ANY) will get spoofed in
     * `SpoofCNAMEAction(cname)`: forge a response with the specified CNAME value
     * `TCAction()`: create answer to query with TC and RD bits set, to move to TCP/IP
-    * `TeeAction(remote)`: send copy of query to remote, keep stats on responses
+    * `TeeAction(remote[, addECS])`: send copy of query to remote, keep stats on responses. If `addECS` is set to `true`, EDNS Client Subnet information will be added to the query
  * Specialist rule generators
     * `addAnyTCRule()`: generate TC=1 answers to ANY queries received over UDP, moving them to TCP
     * `addDomainSpoof(domain, ip[, ip6])` or `addDomainSpoof(domain, {IP, IP, IP..})`: generate answers for A/AAAA/ANY queries using the ip parameters
index 62a486495554857d16ff37068e34cbc202c440f1..0ab8bb1ec6421897b397b80843a7a4f773227903 100644 (file)
@@ -1,23 +1,24 @@
 -- this small script implements dns64 without any specials or customization
--- the pref64 is "fe80::21b::77ff:0:0", and it appears twice, plus once reversed
+prefix = "fe80::21b:77ff:0:0"
 
-function nodata ( remoteip, domain, qtype, records )
-       if qtype ~= pdns.AAAA then return -1, {} end  --  only AAAA records
-        setvariable()
-        return "getFakeAAAARecords", domain, "fe80::21b:77ff:0:0"
-end     
+function nodata ( dq )
+  if dq.qtype ~= pdns.AAAA then
+    return false
+  end  --  only AAAA records
 
-function endswith(s, send)
-       return #s >= #send and s:find(send, #s-#send+1, true) and true or false
+  dq.followupFunction = "getFakeAAAARecords"
+  dq.followupPrefix = prefix
+  dq.followupName = dq.qname
+  return true
 end
 
--- note that the ip6.arpa string ends on a .
--- it is the reverse of the pref64 address above
-
-function preresolve ( remoteip, domain, qtype )
-       if qtype ==pdns.PTR and endswith(domain, "f.f.7.7.b.1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa.")
-        then
-               return "getFakePTRRecords", domain, "fe80::21b::77ff:0:0"
-       end
-       return -1, {}
+-- the ip6.arpa address is the reverse of the prefix address above
+function preresolve ( dq )
+  if dq.qtype == pdns.PTR and dq.qname:isPartOf(newDN("f.f.7.7.b.1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.e.f.ip6.arpa.")) then
+    dq.followupFunction = "getFakePTRRecords"
+    dq.followupPrefix = prefix
+    dq.followupName = dq.qname
+    return true
+  end
+  return false
 end
index 3c8b1d2eb4454b0aea28f9a83b9ecb39bb37a058..3e085bd7d9ef27eebfb317577e08d1253f829ba4 100644 (file)
@@ -126,7 +126,7 @@ bool DNSDistPacketCache::get(const DNSQuestion& dq, uint16_t consumed, uint16_t
 
     const CacheValue& value = it->second;
     if (value.validity < now) {
-      if ((value.validity + allowExpired) < now) {
+      if ((now - value.validity) >= static_cast<time_t>(allowExpired)) {
         d_misses++;
         return false;
       }
index 45b0ef0714b63247584b28809b9a419fda1d2c78..c2c25d90b423736c649a387b1e8a181f6f4201d6 100644 (file)
@@ -1,3 +1,4 @@
+#include <net/if.h>
 #include "dnsdist.hh"
 #include "dnsrulactions.hh"
 #include <thread>
@@ -7,7 +8,6 @@
 #include <fstream>
 #include "dnswriter.hh"
 #include "lock.hh"
-#include <net/if.h>
 
 #ifdef HAVE_SYSTEMD
 #include <systemd/sd-daemon.h>
index 5f984ad06c5646bb5e652dd66e7088e22dadb6e3..976b8c3699a14d415d14cf0980515f9a5f2d7956 100644 (file)
@@ -677,9 +677,8 @@ void moreLua(bool client)
         return std::make_shared<RemoteLogger>(ComboAddress(remote), timeout ? *timeout : 2, maxQueuedEntries ? *maxQueuedEntries : 100, reconnectWaitTime ? *reconnectWaitTime : 1);
       });
 
-    g_lua.writeFunction("TeeAction", [](const std::string& remote) {
-        setLuaNoSideEffect();
-        return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53)));
+    g_lua.writeFunction("TeeAction", [](const std::string& remote, boost::optional<bool> addECS) {
+        return std::shared_ptr<DNSAction>(new TeeAction(ComboAddress(remote, 53), addECS ? *addECS : false));
       });
 
     g_lua.registerFunction<void(DNSAction::*)()>("printStats", [](const DNSAction& ta) {
index 34e8a14308dc37a2813c9bcc3eca1367599e326a..254dc16b159ec1772ead39d127d0bdd4a83a756c 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "dnsdist.hh"
 #include "dnsdist-ecs.hh"
+#include "dnsparser.hh"
 #include "ednsoptions.hh"
 #include "dolog.hh"
 #include "lock.hh"
@@ -284,9 +285,6 @@ void* tcpClientThread(int pipefd)
          goto drop;
        }
 
-       if(dq.qtype == QType::AXFR || dq.qtype == QType::IXFR)  // XXX fixme we really need to do better
-         break;
-
         std::shared_ptr<ServerPool> serverPool = getPool(*localPools, poolname);
         std::shared_ptr<DNSDistPacketCache> packetCache = nullptr;
        {
@@ -384,6 +382,14 @@ void* tcpClientThread(int pipefd)
           goto retry;
         }
 
+        bool xfrStarted = false;
+        bool isXFR = (dq.qtype == QType::AXFR || dq.qtype == QType::IXFR);
+        if (isXFR) {
+          dq.skipCache = true;
+        }
+
+      getpacket:;
+
         if(!getNonBlockingMsgLen(dsock, &rlen, ds->tcpRecvTimeout)) {
          vinfolog("Downstream connection to %s died on us phase 2, getting a new one!", ds->getName());
           close(dsock);
@@ -391,6 +397,9 @@ void* tcpClientThread(int pipefd)
           sockets.erase(ds->remote);
           sockets[ds->remote]=dsock=setupTCPDownstream(ds);
           downstream_failures++;
+          if(xfrStarted) {
+            goto drop;
+          }
           goto retry;
         }
 
@@ -443,6 +452,18 @@ void* tcpClientThread(int pipefd)
           break;
         }
 
+        if (isXFR && dh->rcode == 0 && dh->ancount != 0) {
+          if (xfrStarted == false) {
+            xfrStarted = true;
+            if (getRecordsOfTypeCount(response, responseLen, 1, QType::SOA) == 1) {
+              goto getpacket;
+            }
+          }
+          else if (getRecordsOfTypeCount(response, responseLen, 1, QType::SOA) == 0) {
+            goto getpacket;
+          }
+        }
+
         g_stats.responses++;
         struct timespec answertime;
         gettime(&answertime);
index 120f98d7e031f297d114953729d1a81c9c288ca5..1ccc762408ca5d26ccf9968b1b67c70cc6ee9b39 100644 (file)
@@ -1566,7 +1566,24 @@ try
       g_verbose=true;
       break;
     case 'V':
-      cout<<"dnsdist "<<VERSION<<endl;
+      cout<<"dnsdist "<<VERSION<<" ("<<LUA_VERSION<<")"<<endl;
+      cout<<"Enabled features: ";
+#ifdef HAVE_DNSCRYPT
+      cout<<"dnscrypt ";
+#endif
+#ifdef HAVE_LIBSODIUM
+      cout<<"libsodium ";
+#endif
+#ifdef HAVE_PROTOBUF
+      cout<<"protobuf ";
+#endif
+#ifdef HAVE_RE2
+      cout<<"re2 ";
+#endif
+#ifdef HAVE_SYSTEMD
+      cout<<"systemd";
+#endif
+      cout<<endl;
       exit(EXIT_SUCCESS);
       break;
     }
index 6a62959c6ee9fdd2b633e9b6e5d618e6aac38cfd..aff08cea58eefef4fe2bdc74479da650e4b205e3 100644 (file)
@@ -32,3 +32,4 @@
 /dnsmessage.pb.cc
 /dnsmessage.pb.h
 /dnsdist.service
+/lua.hpp
index 01565fc91b0ccdd049abf76978cfc04334b0b133..2c946f6cf3344a9f1b31f3fc56e4d05d59ccc0af 100644 (file)
@@ -40,7 +40,8 @@ EXTRA_DIST=dnslabeltext.rl \
           ext/incbin/UNLICENSE \
           incfiles \
           src_js \
-          dnsdist.service.in
+          dnsdist.service.in \
+          lua_hpp.mk
 
 bin_PROGRAMS = dnsdist
 
@@ -117,6 +118,11 @@ if HAVE_RE2
 dnsdist_LDADD += $(RE2_LIBS)
 endif
 
+if !HAVE_LUA_HPP
+BUILT_SOURCES += lua.hpp
+nodist_dnsdist_SOURCES = lua.hpp
+CLEANFILES += lua.hpp
+endif
 
 if HAVE_PROTOBUF
 if HAVE_PROTOC
@@ -200,3 +206,7 @@ systemdsystemunitdir = $(SYSTEMD_DIR)
 systemdsystemunit_DATA = \
        dnsdist.service
 endif
+
+if !HAVE_LUA_HPP
+include lua_hpp.mk
+endif
index e98e7c5b5d17e34b381d09dfaa2f55faec4d608d..6b36cd18fad5da6c42f86171c1be8576656d33df 100644 (file)
@@ -7,6 +7,7 @@ AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])
 AC_PROG_CC
 AC_PROG_CXX
+AC_LANG([C++])
 
 LT_PREREQ([2.2.2])
 LT_INIT([disable-static])
@@ -16,12 +17,16 @@ DNSDIST_CHECK_RAGEL
 PDNS_CHECK_LIBEDIT
 PDNS_CHECK_CLOCK_GETTIME
 
+PDNS_CHECK_OS
+PDNS_CHECK_NETWORK_LIBS
+
 boost_required_version=1.35
 
 PDNS_WITH_PROTOBUF
 AS_IF([test "x$PROTOBUF_LIBS" != "x" -a x"$PROTOC" != "x"],
   # The protobuf code needs boost::uuid, which is available from 1.42 onward
-  [boost_required_version=1.42]
+  [AC_MSG_WARN([Bumping minimal Boost requirement to 1.42. To keep the requirement at 1.35, disable protobuf support])
+  boost_required_version=1.42]
 )
 
 BOOST_REQUIRE([$boost_required_version])
@@ -44,6 +49,7 @@ AS_IF([test "x$with_luajit" = "xno"], [
 AS_IF([test "x$LUAPC" = "x" -a "x$LUAJITPC" = "x"], [
   AC_MSG_ERROR([Neither Lua nor LuaJIT found, Lua support is not optional])
 ])
+PDNS_CHECK_LUA_HPP
 
 AX_CXX_COMPILE_STDCXX_11([ext], [mandatory])
 
@@ -63,8 +69,6 @@ AS_IF([test "x$enable_hardening" != "xno"], [
   AC_LD_RELRO
 ])
 
-PDNS_CHECK_OS
-
 PDNS_ENABLE_SANITIZERS
 
 PDNS_CHECK_PANDOC
@@ -85,3 +89,53 @@ AC_CONFIG_FILES([Makefile
        ext/yahttp/yahttp/Makefile])
 
 AC_OUTPUT
+
+AC_MSG_NOTICE([])
+AC_MSG_NOTICE([Configuration summary])
+AC_MSG_NOTICE([=====================])
+AC_MSG_NOTICE([])
+AS_IF([test "x$ac_configure_args" != "x"],
+  [summary_conf_opts=$ac_configure_args],
+  [summary_conf_opts="(no options)"]
+)
+AC_MSG_NOTICE([dnsdist configured with: $summary_conf_opts])
+AC_MSG_NOTICE([])
+AC_MSG_NOTICE([CC: $CC])
+AC_MSG_NOTICE([CXX: $CXX])
+AC_MSG_NOTICE([LD: $LD])
+AC_MSG_NOTICE([CFLAGS: $CFLAGS])
+AC_MSG_NOTICE([CPPFLAGS: $CPPFLAGS])
+AC_MSG_NOTICE([CXXFLAGS: $CXXFLAGS])
+AC_MSG_NOTICE([LDFLAGS: $LDFLAGS])
+AC_MSG_NOTICE([LIBS: $LIBS])
+AC_MSG_NOTICE([BOOST_CPPFLAGS: $BOOST_CPPFLAGS])
+AC_MSG_NOTICE([])
+AC_MSG_NOTICE([Features enabled])
+AC_MSG_NOTICE([----------------])
+AS_IF([test "x$LUAPC" != "x"],
+  [AC_MSG_NOTICE([Lua: $LUAPC])],
+  [AS_IF([test "x$LUAJITPC" != "x"],
+    [AC_MSG_NOTICE([LuaJit: $LUAJITPC])],
+    [AC_MSG_NOTICE([Lua/LuaJit: no])])
+])
+AS_IF([test "x$PROTOBUF_LIBS" != "x" -a x"$PROTOC" != "x"],
+  [AC_MSG_NOTICE([Protobuf: yes])],
+  [AC_MSG_NOTICE([Protobuf: no])]
+)
+AS_IF([test "x$systemd" != "xn"],
+  [AC_MSG_NOTICE([systemd: yes])],
+  [AC_MSG_NOTICE([systemd: no])]
+)
+AS_IF([test "x$LIBSODIUM_LIBS" != "x"],
+  [AC_MSG_NOTICE([libsodium: yes])],
+  [AC_MSG_NOTICE([libsodium: no])]
+)
+AS_IF([test "x$enable_dnscrypt" != "xno"],
+  [AC_MSG_NOTICE([DNSCrypt: yes])],
+  [AC_MSG_NOTICE([DNSCrypt: no])]
+)
+AS_IF([test "x$RE2_LIBS" != "x"],
+  [AC_MSG_NOTICE([re2: yes])],
+  [AC_MSG_NOTICE([re2: no])]
+)
+AC_MSG_NOTICE([])
index aa40c5a381efca4b59f6bc3ddbdf9b83759fc20e..88e76c295a1c6b8fd01d9cbe34759c432fac6adb 100644 (file)
@@ -3,7 +3,7 @@
 
 using namespace std;
 
-TeeAction::TeeAction(const ComboAddress& ca) : d_remote(ca)
+TeeAction::TeeAction(const ComboAddress& ca, bool addECS) : d_remote(ca), d_addECS(addECS)
 {
   d_fd=SSocket(d_remote.sin4.sin_family, SOCK_DGRAM, 0);
   SConnect(d_fd, d_remote);
@@ -23,8 +23,32 @@ DNSAction::Action TeeAction::operator()(DNSQuestion* dq, string* ruleresult) con
   if(dq->tcp) 
     d_tcpdrops++;
   else {
+    ssize_t res;
     d_queries++;
-    if(send(d_fd, (char*)dq->dh, dq->len, 0) <= 0) 
+
+    if(d_addECS) {
+      std::string query;
+      std::string larger;
+      uint16_t len = dq->len;
+      bool ednsAdded = false;
+      bool ecsAdded = false;
+      query.reserve(dq->size);
+      query.assign((char*) dq->dh, len);
+
+      handleEDNSClientSubnet((char*) query.c_str(), query.size(), dq->qname->wirelength(), &len, larger, &ednsAdded, &ecsAdded, *dq->remote);
+
+      if (larger.empty()) {
+        res = send(d_fd, query.c_str(), len, 0);
+      }
+      else {
+        res = send(d_fd, larger.c_str(), larger.length(), 0);
+      }
+    }
+    else {
+      res = send(d_fd, (char*)dq->dh, dq->len, 0);
+    }
+
+    if (res <= 0)
       d_senderrors++;
   }
   return DNSAction::Action::None;
diff --git a/pdns/dnsdistdist/lua_hpp.mk b/pdns/dnsdistdist/lua_hpp.mk
new file mode 120000 (symlink)
index 0000000..52778c2
--- /dev/null
@@ -0,0 +1 @@
+../lua_hpp.mk
\ No newline at end of file
diff --git a/pdns/dnsdistdist/m4/pdns_check_lua_hpp.m4 b/pdns/dnsdistdist/m4/pdns_check_lua_hpp.m4
new file mode 120000 (symlink)
index 0000000..4ed8acd
--- /dev/null
@@ -0,0 +1 @@
+../../../m4/pdns_check_lua_hpp.m4
\ No newline at end of file
diff --git a/pdns/dnsdistdist/m4/pdns_check_network_libs.m4 b/pdns/dnsdistdist/m4/pdns_check_network_libs.m4
new file mode 120000 (symlink)
index 0000000..ec197ee
--- /dev/null
@@ -0,0 +1 @@
+../../../m4/pdns_check_network_libs.m4
\ No newline at end of file
index 1a1e8d1ec6df6cc0fdc897109739a59a7916cc64..b66ef44bbd4b9675c31bfad92d7ef78d48d89456 100644 (file)
@@ -11,7 +11,7 @@
 #include "statbag.hh"
 StatBag S;
 
-static void addRRs(const char* packet, const size_t len, PBDNSMessage_DNSResponse& response)
+static void addRRs(const char* packet, const size_t len, PBDNSMessage_DNSResponse* response)
 try
 {
   if (len < sizeof(struct dnsheader))
@@ -60,7 +60,7 @@ try
 
     pr.xfrBlob(blob);
     if (ah.d_type == QType::A || ah.d_type == QType::AAAA) {
-      PBDNSMessage_DNSResponse_DNSRR* rr = response.add_rrs();
+      PBDNSMessage_DNSResponse_DNSRR* rr = response->add_rrs();
       if (rr) {
         rr->set_name(rrname.toString());
         rr->set_type(ah.d_type);
@@ -145,8 +145,9 @@ int main(int argc, char **argv)
     }
     message.set_inbytes(pr.d_len);
 
-    PBDNSMessage_DNSQuestion question;
-    PBDNSMessage_DNSResponse response;
+    PBDNSMessage_DNSQuestion* question = message.mutable_question();
+    PBDNSMessage_DNSResponse* response = message.mutable_response();
+
     if (!dh->qr) {
       boost::uuids::uuid uniqueId = uuidGenerator();
       ids[dh->id] = uniqueId;
@@ -162,27 +163,20 @@ int main(int argc, char **argv)
         std::copy(it->second.begin(), it->second.end(), messageId->begin());
       }
 
-      response.set_rcode(dh->rcode);
+      response->set_rcode(dh->rcode);
       addRRs((const char*) dh, pr.d_len, response);
-      message.set_allocated_response(&response);
     }
 
-    question.set_qname(qname.toString());
-    question.set_qtype(qtype);
-    question.set_qclass(qclass);
-    message.set_allocated_question(&question);
+    question->set_qname(qname.toString());
+    question->set_qtype(qtype);
+    question->set_qclass(qclass);
+
     std::string str;
     //cerr<<message.DebugString()<<endl;
     message.SerializeToString(&str);
     uint16_t mlen = htons(str.length());
     fwrite(&mlen, 1, sizeof(mlen), fp);
     fwrite(str.c_str(), 1, str.length(), fp);
-    if (!dh->qr) {
-      message.release_question();
-    }
-    else {
-      message.release_response();
-    }
   }
   fclose(fp);
 }
index 899e7d64b474b9f12f3738a9d9f7cd126e31cbf8..9d47c4dd52720f51d9e77baf6054cd0205873761 100644 (file)
@@ -557,7 +557,7 @@ private:
 class TeeAction : public DNSAction
 {
 public:
-  TeeAction(const ComboAddress& ca);
+  TeeAction(const ComboAddress& ca, bool addECS=false);
   ~TeeAction();
   DNSAction::Action operator()(DNSQuestion* dq, string* ruleresult) const override;
   string toString() const override;
@@ -581,6 +581,7 @@ private:
   mutable unsigned long d_tcpdrops{0};
   unsigned long d_otherrcode{0};
   std::atomic<bool> d_pleaseQuit{false};
+  bool d_addECS{false};
 };
 
 
index d9f2770447958f0417433eb61c4bbf137ddd7cfc..6695a890b9529438f7835891a75978f10e3b1eee 100644 (file)
@@ -365,7 +365,8 @@ string getMessageForRRSET(const DNSName& qname, const RRSIGRecordContent& rrc, v
     toHash.append((char*)&tmp, 2);
     uint32_t ttl=htonl(rrc.d_originalttl);
     toHash.append((char*)&ttl, 4);
-    string rdata=add->serialize(DNSName("."), true, true); 
+    // for NSEC signatures, we should not lowercase the rdata section
+    string rdata=add->serialize(DNSName("."), true, (add->getType() == QType::NSEC) ? false : true);  // RFC 6840, 5.1
     tmp=htons(rdata.length());
     toHash.append((char*)&tmp, 2);
     toHash.append(rdata);
index 1f48e499706dbecda18ad00ace7b6caae90fc0a8..f3b9ace731901b6317bf50b54864dfe7cb519638 100644 (file)
@@ -14,28 +14,27 @@ RecursorLua4::RecursorLua4(const std::string &fname)
   throw std::runtime_error("Attempt to load a Lua script in a PowerDNS binary without Lua support");
 }
 
-bool RecursorLua4::nxdomain(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& ret, int& res, bool* variable)
+bool RecursorLua4::nxdomain(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& ret, int& res, bool* variable)
 {
   return false;
 }
 
-bool RecursorLua4::nodata(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& ret, int& res, bool* variable)
+bool RecursorLua4::nodata(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& ret, int& res, bool* variable)
 {
   return false;
 }
 
-bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& ret, int& res, bool* variable)
+bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& ret, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& res, bool* variable)
 {
   return false;
 }
 
-
-bool RecursorLua4::preresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& ret, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, int& res, bool* variable)
+bool RecursorLua4::preresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& ret, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& res, bool* variable)
 {
   return false;
 }
 
-bool RecursorLua4::preoutquery(const ComboAddress& remote, const ComboAddress& local,const DNSName& query, const QType& qtype, vector<DNSRecord>& ret, int& res)
+bool RecursorLua4::preoutquery(const ComboAddress& remote, const ComboAddress& local,const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& ret, int& res)
 {
   return false;
 }
@@ -45,7 +44,7 @@ bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& loca
   return false;
 }
 
-int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype)
+int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags)
 {
   return 0;
 }
@@ -303,6 +302,7 @@ RecursorLua4::RecursorLua4(const std::string& fname)
   d_lw->registerFunction<bool(DNSName::*)()>("chopOff", [](DNSName&dn ) { return dn.chopOff(); });
   d_lw->registerMember("qname", &DNSQuestion::qname);
   d_lw->registerMember("qtype", &DNSQuestion::qtype);
+  d_lw->registerMember("isTcp", &DNSQuestion::isTcp);
   d_lw->registerMember("localaddr", &DNSQuestion::local);
   d_lw->registerMember("remoteaddr", &DNSQuestion::remote);
   d_lw->registerMember("rcode", &DNSQuestion::rcode);
@@ -343,18 +343,22 @@ RecursorLua4::RecursorLua4(const std::string& fname)
   d_lw->registerFunction("getRecords", &DNSQuestion::getRecords);
   d_lw->registerFunction("setRecords", &DNSQuestion::setRecords);
 
-  d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { dq.policyTags.push_back(tag); });
+  d_lw->registerFunction<void(DNSQuestion::*)(const std::string&)>("addPolicyTag", [](DNSQuestion& dq, const std::string& tag) { if (dq.policyTags) { dq.policyTags->push_back(tag); } });
   d_lw->registerFunction<void(DNSQuestion::*)(const std::vector<std::pair<int, std::string> >&)>("setPolicyTags", [](DNSQuestion& dq, const std::vector<std::pair<int, std::string> >& tags) {
-      dq.policyTags.clear();
-      for (const auto& tag : tags) {
-        dq.policyTags.push_back(tag.second);
+      if (dq.policyTags) {
+        dq.policyTags->clear();
+        for (const auto& tag : tags) {
+          dq.policyTags->push_back(tag.second);
+        }
       }
     });
   d_lw->registerFunction<std::vector<std::pair<int, std::string> >(DNSQuestion::*)()>("getPolicyTags", [](const DNSQuestion& dq) {
       std::vector<std::pair<int, std::string> > ret;
-      int count = 1;
-      for (const auto& tag : dq.policyTags) {
-        ret.push_back({count++, tag});
+      if (dq.policyTags) {
+        int count = 1;
+        for (const auto& tag : *dq.policyTags) {
+          ret.push_back({count++, tag});
+        }
       }
       return ret;
     });
@@ -448,29 +452,29 @@ RecursorLua4::RecursorLua4(const std::string& fname)
   d_gettag = d_lw->readVariable<boost::optional<gettag_t>>("gettag").get_value_or(0);
 }
 
-bool RecursorLua4::preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable)
+bool RecursorLua4::preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable)
 {
-  return genhook(d_preresolve, remote, local, query, qtype, res, ednsOpts, tag, appliedPolicy, policyTags, ret, variable);
+  return genhook(d_preresolve, remote, local, query, qtype, isTcp, res, ednsOpts, tag, appliedPolicy, policyTags, ret, variable);
 }
 
-bool RecursorLua4::nxdomain(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, int& ret, bool* variable)
+bool RecursorLua4::nxdomain(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret, bool* variable)
 {
-  return genhook(d_nxdomain, remote, local, query, qtype, res, 0, 0, nullptr, nullptr, ret, variable);
+  return genhook(d_nxdomain, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable);
 }
 
-bool RecursorLua4::nodata(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, int& ret, bool* variable)
+bool RecursorLua4::nodata(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret, bool* variable)
 {
-  return genhook(d_nodata, remote, local, query, qtype, res, 0, 0, nullptr, nullptr, ret, variable);
+  return genhook(d_nodata, remote, local, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, variable);
 }
 
-bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable)
+bool RecursorLua4::postresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable)
 {
-  return genhook(d_postresolve, remote, local, query, qtype, res, 0, 0, appliedPolicy, policyTags, ret, variable);
+  return genhook(d_postresolve, remote, local, query, qtype, isTcp, res, 0, 0, appliedPolicy, policyTags, ret, variable);
 }
 
-bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, int& ret)
+bool RecursorLua4::preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret)
 {
-  return genhook(d_preoutquery, ns, requestor, query, qtype, res, 0, 0, nullptr, nullptr, ret, 0);
+  return genhook(d_preoutquery, ns, requestor, query, qtype, isTcp, res, 0, 0, nullptr, nullptr, ret, 0);
 }
 
 bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader& dh)
@@ -480,14 +484,25 @@ bool RecursorLua4::ipfilter(const ComboAddress& remote, const ComboAddress& loca
   return false; // don't block
 }
 
-int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype)
+int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& qname, uint16_t qtype, std::vector<std::string>* policyTags)
 {
-  if(d_gettag)
-    return d_gettag(remote, ednssubnet, local, qname, qtype);
+  if(d_gettag) {
+    auto ret = d_gettag(remote, ednssubnet, local, qname, qtype);
+
+    if (policyTags) {
+      const auto& tags = std::get<1>(ret);
+      if (tags) {
+        for (const auto& tag : *tags) {
+          policyTags->push_back(tag.second);
+        }
+      }
+    }
+    return std::get<0>(ret);
+  }
   return 0;
 }
 
-bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype,  vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable)
+bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable)
 {
   if(!func)
     return false;
@@ -500,8 +515,10 @@ bool RecursorLua4::genhook(luacall_t& func, const ComboAddress& remote,const Com
   dq->records = res;
   dq->tag = tag;
   dq->ednsOptions = ednsOpts;
+  dq->isTcp = isTcp;
+  dq->policyTags = policyTags;
   bool handled=func(dq);
-  if(variable) *variable |= dq->variable; // could still be set to indicate this *name* is variable
+  if(variable) *variable |= dq->variable; // could still be set to indicate this *name* is variable, even if not 'handled'
 
   if(handled) {
 loop:;
@@ -536,9 +553,6 @@ loop:;
     if (appliedPolicy) {
       *appliedPolicy=dq->appliedPolicy;
     }
-    if (policyTags) {
-      *policyTags = dq->policyTags;
-    }
   }
 
 
index 377015a5d661239659203d3e101316b21055854a..cbf8087841fb615b8e937139ee66bdc4c40ec181 100644 (file)
@@ -4,6 +4,10 @@
 #include "namespaces.hh"
 #include "dnsrecords.hh"
 #include <unordered_map>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
 string GenUDPQueryResponse(const ComboAddress& dest, const string& query);
 
 class LuaContext;
@@ -11,22 +15,24 @@ class LuaContext;
 class RecursorLua4 : public boost::noncopyable
 {
 private:
+#ifdef HAVE_LUA
   std::unique_ptr<LuaContext> d_lw; // this is way on top because it must get destroyed _last_
+#endif
 
 public:
   explicit RecursorLua4(const std::string& fname);
   ~RecursorLua4(); // this is so unique_ptr works with an incomplete type
-  bool preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable);
-  bool nxdomain(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, int& ret, bool* variable);
-  bool nodata(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, int& ret, bool* variable);
-  bool postresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable);
+  bool preresolve(const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable);
+  bool nxdomain(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret, bool* variable);
+  bool nodata(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret, bool* variable);
+  bool postresolve(const ComboAddress& remote, const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable);
 
-  bool preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, vector<DNSRecord>& res, int& ret);
+  bool preoutquery(const ComboAddress& ns, const ComboAddress& requestor, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res, int& ret);
   bool ipfilter(const ComboAddress& remote, const ComboAddress& local, const struct dnsheader&);
 
-  int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& query, uint16_t qtype);
+  int gettag(const ComboAddress& remote, const Netmask& ednssubnet, const ComboAddress& local, const DNSName& query, uint16_t qtype, std::vector<std::string>* policyTags);
 
-  typedef std::function<int(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t)> gettag_t;
+  typedef std::function<std::tuple<int,boost::optional<std::unordered_map<int,string> > >(ComboAddress, Netmask, ComboAddress, DNSName, uint16_t)> gettag_t;
   gettag_t d_gettag; // public so you can query if we have this hooked
 
 private:
@@ -61,12 +67,13 @@ private:
     DNSName followupName;
 
     string appliedPolicy;
-    std::vector<std::string> policyTags;
+    std::vector<std::string>* policyTags;
+    bool isTcp;
   };
 
   typedef std::function<bool(std::shared_ptr<DNSQuestion>)> luacall_t;
   luacall_t d_preresolve, d_nxdomain, d_nodata, d_postresolve, d_preoutquery, d_postoutquery;
-  bool genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, vector<DNSRecord>& res,  const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable);
+  bool genhook(luacall_t& func, const ComboAddress& remote,const ComboAddress& local, const DNSName& query, const QType& qtype, bool isTcp, vector<DNSRecord>& res,  const vector<pair<uint16_t,string> >* ednsOpts, unsigned int tag, std::string* appliedPolicy, std::vector<std::string>* policyTags, int& ret, bool* variable);
   typedef std::function<bool(ComboAddress,ComboAddress, struct dnsheader)> ipfilter_t;
   ipfilter_t d_ipfilter;
 };
diff --git a/pdns/lua_hpp.mk b/pdns/lua_hpp.mk
new file mode 100644 (file)
index 0000000..70502f4
--- /dev/null
@@ -0,0 +1,6 @@
+lua.hpp:
+       $(AM_V_GEN)echo 'extern "C" {' > $@
+       @echo '#include "lua.h"' >> $@
+       @echo '#include "lualib.h"' >> $@
+       @echo '#include "lauxlib.h"' >> $@
+       @echo '}' >> $@
index 758296f8d01b7af12c7ad3cb4f4742cb61af5790..fa2ee83fdb337b083f8cbb8be8410bf90343a142 100644 (file)
@@ -79,7 +79,7 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d
 
   string ping;
   bool weWantEDNSSubnet=false;
-  if(EDNS0Level && !doTCP) {
+  if(EDNS0Level) { 
     DNSPacketWriter::optvect_t opts;
     if(srcmask) {
       EDNSSubnetOpts eo;
index c64261c8c1cc2821b7458b2fd589f031764847c1..66b12aaffa7505070561b3e193de95a281ef244b 100644 (file)
@@ -199,7 +199,7 @@ struct DNSComboWriter {
   ComboAddress d_remote, d_local;
 #ifdef HAVE_PROTOBUF
   boost::uuids::uuid d_uuid;
-  Netmask ednssubnet;
+  Netmask d_ednssubnet;
 #endif
   bool d_tcp;
   int d_socket;
@@ -207,6 +207,7 @@ struct DNSComboWriter {
   string d_query;
   shared_ptr<TCPConnection> d_tcpConnection;
   vector<pair<uint16_t, string> > d_ednsOpts;
+  std::vector<std::string> d_policyTags;
 };
 
 
@@ -613,28 +614,29 @@ catch(...)
 }
 
 #ifdef HAVE_PROTOBUF
-static void protobufFillMessageFromDC(PBDNSMessage& message, const DNSComboWriter* dc)
+static void protobufUpdateMessage(PBDNSMessage& message, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id)
 {
   std::string* messageId = message.mutable_messageid();
-  messageId->resize(dc->d_uuid.size());
-  std::copy(dc->d_uuid.begin(), dc->d_uuid.end(), messageId->begin());
+  messageId->resize(uniqueId.size());
+  std::copy(uniqueId.begin(), uniqueId.end(), messageId->begin());
 
-  message.set_socketfamily(dc->d_remote.sin4.sin_family == AF_INET ? PBDNSMessage_SocketFamily_INET : PBDNSMessage_SocketFamily_INET6);
-  message.set_socketprotocol(dc->d_tcp ? PBDNSMessage_SocketProtocol_TCP : PBDNSMessage_SocketProtocol_UDP);
-  if (dc->d_local.sin4.sin_family == AF_INET) {
-    message.set_to(&dc->d_local.sin4.sin_addr.s_addr, sizeof(dc->d_local.sin4.sin_addr.s_addr));
+  message.set_socketfamily(remote.sin4.sin_family == AF_INET ? PBDNSMessage_SocketFamily_INET : PBDNSMessage_SocketFamily_INET6);
+  message.set_socketprotocol(tcp ? PBDNSMessage_SocketProtocol_TCP : PBDNSMessage_SocketProtocol_UDP);
+
+  if (local.sin4.sin_family == AF_INET) {
+    message.set_to(&local.sin4.sin_addr.s_addr, sizeof(local.sin4.sin_addr.s_addr));
   }
-  else if (dc->d_local.sin4.sin_family == AF_INET6) {
-    message.set_to(&dc->d_local.sin6.sin6_addr.s6_addr, sizeof(dc->d_local.sin6.sin6_addr.s6_addr));
+  else if (local.sin4.sin_family == AF_INET6) {
+    message.set_to(&local.sin6.sin6_addr.s6_addr, sizeof(local.sin6.sin6_addr.s6_addr));
   }
-  if (dc->d_remote.sin4.sin_family == AF_INET) {
-    message.set_from(&dc->d_remote.sin4.sin_addr.s_addr, sizeof(dc->d_remote.sin4.sin_addr.s_addr));
+  if (remote.sin4.sin_family == AF_INET) {
+    message.set_from(&remote.sin4.sin_addr.s_addr, sizeof(remote.sin4.sin_addr.s_addr));
   }
-  else if (dc->d_remote.sin4.sin_family == AF_INET6) {
-    message.set_from(&dc->d_remote.sin6.sin6_addr.s6_addr, sizeof(dc->d_remote.sin6.sin6_addr.s6_addr));
+  else if (remote.sin4.sin_family == AF_INET6) {
+    message.set_from(&remote.sin6.sin6_addr.s6_addr, sizeof(remote.sin6.sin6_addr.s6_addr));
   }
-  if (!dc->ednssubnet.empty()) {
-    const ComboAddress ca = dc->ednssubnet.getNetwork();
+  if (!ednssubnet.empty()) {
+    const ComboAddress ca = ednssubnet.getNetwork();
     if (ca.sin4.sin_family == AF_INET) {
       message.set_originalrequestorsubnet(&ca.sin4.sin_addr.s_addr, sizeof(ca.sin4.sin_addr.s_addr));
     }
@@ -647,42 +649,62 @@ static void protobufFillMessageFromDC(PBDNSMessage& message, const DNSComboWrite
   gettime(&ts, true);
   message.set_timesec(ts.tv_sec);
   message.set_timeusec(ts.tv_nsec / 1000);
-  message.set_id(ntohs(dc->d_mdp.d_header.id));
+
+  message.set_id(ntohs(id));
+}
+
+static void protobufFillMessage(PBDNSMessage& message, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, const DNSName& qname, uint16_t qtype, uint16_t qclass)
+{
+  protobufUpdateMessage(message, uniqueId, remote, local, ednssubnet, tcp, id);
 
   PBDNSMessage_DNSQuestion* question = message.mutable_question();
-  question->set_qname(dc->d_mdp.d_qname.toString());
-  question->set_qtype(dc->d_mdp.d_qtype);
-  question->set_qclass(dc->d_mdp.d_qclass);
+  question->set_qname(qname.toString());
+  question->set_qtype(qtype);
+  question->set_qclass(qclass);
 }
 
-static void protobufLogQuery(const std::shared_ptr<RemoteLogger>& logger, const DNSComboWriter* dc)
+static void protobufFillMessageFromDC(PBDNSMessage& message, const DNSComboWriter* dc)
+{
+  protobufFillMessage(message, dc->d_uuid, dc->d_remote, dc->d_local, dc->d_ednssubnet, dc->d_tcp, dc->d_mdp.d_header.id, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_mdp.d_qclass);
+}
+
+static void protobufLogQuery(const std::shared_ptr<RemoteLogger>& logger, const boost::uuids::uuid& uniqueId, const ComboAddress& remote, const ComboAddress& local, const Netmask& ednssubnet, bool tcp, uint16_t id, size_t len, const DNSName& qname, uint16_t qtype, uint16_t qclass, const std::string appliedPolicy, const std::vector<std::string>& policyTags)
 {
   PBDNSMessage message;
   message.set_type(PBDNSMessage_Type_DNSQueryType);
-  message.set_inbytes(dc->d_query.length());
-  protobufFillMessageFromDC(message, dc);
+  message.set_inbytes(len);
+  protobufFillMessage(message, uniqueId, remote, local, ednssubnet, tcp, id, qname, qtype, qclass);
+
+  /* just fake it up for now */
+  PBDNSMessage_DNSResponse* response = message.mutable_response();
+  if (!appliedPolicy.empty()) {
+    response->set_appliedpolicy(appliedPolicy);
+  }
+  if (!policyTags.empty()) {
+    for(const auto tag : policyTags) {
+      response->add_tags(tag);
+    }
+  }
 
 //  cerr <<message.DebugString()<<endl;
   std::string str;
   message.SerializeToString(&str);
   logger->queueData(str);
-  message.release_question();
 }
 
-static void protobufLogResponse(const std::shared_ptr<RemoteLogger>& logger, const DNSComboWriter* dc, size_t responseSize, PBDNSMessage_DNSResponse& protobufResponse)
+static void protobufFillResponseFromDC(PBDNSMessage& message, const DNSComboWriter* dc, size_t responseSize)
 {
-  PBDNSMessage message;
   message.set_type(PBDNSMessage_Type_DNSResponseType);
   message.set_inbytes(responseSize);
   protobufFillMessageFromDC(message, dc);
+}
 
-  message.set_allocated_response(&protobufResponse);
-
+static void protobufLogResponse(const std::shared_ptr<RemoteLogger>& logger, const PBDNSMessage& message)
+{
 //  cerr <<message.DebugString()<<endl;
   std::string str;
   message.SerializeToString(&str);
   logger->queueData(str);
-  message.release_response();
 }
 #endif
 
@@ -706,12 +728,9 @@ void startDoResolve(void *p)
 
     auto luaconfsLocal = g_luaconfs.getLocal();
     std::string appliedPolicy;
-    std::vector<std::string> policyTags;
 #ifdef HAVE_PROTOBUF
-    PBDNSMessage_DNSResponse protobufResponse;
-    if(luaconfsLocal->protobufServer) {
-      protobufLogQuery(luaconfsLocal->protobufServer, dc);
-    }
+    PBDNSMessage pbMessage;
+    PBDNSMessage_DNSResponse* protobufResponse = pbMessage.mutable_response();
 #endif
 
     DNSPacketWriter pw(packet, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_mdp.d_qclass);
@@ -773,8 +792,8 @@ void startDoResolve(void *p)
       L<<Logger::Warning<<t_id<<" ["<<MT->getTid()<<"/"<<MT->numProcesses()<<"] " << (dc->d_tcp ? "TCP " : "") << "question for '"<<dc->d_mdp.d_qname<<"|"
        <<DNSRecordContent::NumberToType(dc->d_mdp.d_qtype)<<"' from "<<dc->getRemote();
 #ifdef HAVE_PROTOBUF
-      if(!dc->ednssubnet.empty()) {
-        L<<" (ecs "<<dc->ednssubnet.toString()<<")";
+      if(!dc->d_ednssubnet.empty()) {
+        L<<" (ecs "<<dc->d_ednssubnet.toString()<<")";
       }
 #endif
       L<<endl;
@@ -831,7 +850,7 @@ void startDoResolve(void *p)
     }
 
 
-    if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, dc->d_ednsOpts.empty() ? 0 : &dc->d_ednsOpts, dc->d_tag, &appliedPolicy, &policyTags, res, &variableAnswer)) {
+    if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, dc->d_ednsOpts.empty() ? 0 : &dc->d_ednsOpts, dc->d_tag, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer)) {
       try {
         res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
       }
@@ -893,14 +912,13 @@ void startDoResolve(void *p)
                   if(i->d_type == dc->d_mdp.d_qtype && i->d_place == DNSResourceRecord::ANSWER)
                           break;
                 if(i == ret.cend())
-                  (*t_pdl)->nodata(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
+                  (*t_pdl)->nodata(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, res, &variableAnswer);
        }
        else if(res == RCode::NXDomain)
-         (*t_pdl)->nxdomain(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, res, &variableAnswer);
+         (*t_pdl)->nxdomain(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, res, &variableAnswer);
        
 
-       (*t_pdl)->postresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), ret, &appliedPolicy, &policyTags, res, &variableAnswer);
-       
+       (*t_pdl)->postresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer);
       }
     }
   haveAnswer:;
@@ -955,7 +973,7 @@ void startDoResolve(void *p)
           pw.getHeader()->ad=0;
         }
         else if(state == Bogus) {
-          if(sr.doLog()) {
+          if(sr.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
             L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<" for "<<dc->d_remote.toStringWithPort()<<" validates as Bogus"<<endl;
           }
 
@@ -1003,8 +1021,8 @@ void startDoResolve(void *p)
          goto sendit; // need to jump over pw.commit
        }
 #ifdef HAVE_PROTOBUF
-        if(luaconfsLocal->protobufServer && (i->d_type == QType::A || i->d_type == QType::AAAA)) {
-          PBDNSMessage_DNSResponse_DNSRR* pbRR = protobufResponse.add_rrs();
+        if(luaconfsLocal->protobufServer && protobufResponse && (i->d_type == QType::A || i->d_type == QType::AAAA)) {
+          PBDNSMessage_DNSResponse_DNSRR* pbRR = protobufResponse->add_rrs();
           if(pbRR) {
             pbRR->set_name(i->d_name.toString());
             pbRR->set_type(i->d_type);
@@ -1033,16 +1051,19 @@ void startDoResolve(void *p)
     updateResponseStats(res, dc->d_remote, packet.size(), &dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
 #ifdef HAVE_PROTOBUF
     if (luaconfsLocal->protobufServer) {
-      protobufResponse.set_rcode(pw.getHeader()->rcode);
-      if (!appliedPolicy.empty()) {
-        protobufResponse.set_appliedpolicy(appliedPolicy);
-      }
-      if (!policyTags.empty()) {
-        for(const auto tag : policyTags) {
-          protobufResponse.add_tags(tag);
+      if (protobufResponse) {
+        protobufResponse->set_rcode(pw.getHeader()->rcode);
+        if (!appliedPolicy.empty()) {
+          protobufResponse->set_appliedpolicy(appliedPolicy);
+        }
+        if (!dc->d_policyTags.empty()) {
+          for(const auto tag : dc->d_policyTags) {
+            protobufResponse->add_tags(tag);
+          }
         }
       }
-      protobufLogResponse(luaconfsLocal->protobufServer, dc, packet.size(), protobufResponse);
+      protobufFillResponseFromDC(pbMessage, dc, packet.size());
+      protobufLogResponse(luaconfsLocal->protobufServer, pbMessage);
     }
 #endif
     if(!dc->d_tcp) {
@@ -1057,12 +1078,25 @@ void startDoResolve(void *p)
       if(sendmsg(dc->d_socket, &msgh, 0) < 0 && g_logCommonErrors) 
         L<<Logger::Warning<<"Sending UDP reply to client "<<dc->d_remote.toStringWithPort()<<" failed with: "<<strerror(errno)<<endl;
       if(!SyncRes::s_nopacketcache && !variableAnswer && !sr.wasVariable() ) {
-
+#ifdef HAVE_PROTOBUF
+        if (luaconfsLocal->protobufServer) {
+          t_packetCache->insertResponsePacket(dc->d_tag, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_query,
+                                              string((const char*)&*packet.begin(), packet.size()),
+                                              g_now.tv_sec,
+                                              pw.getHeader()->rcode == RCode::ServFail ? SyncRes::s_packetcacheservfailttl :
+                                              min(minTTL,SyncRes::s_packetcachettl),
+                                              &pbMessage);
+        }
+        else {
+#endif
         t_packetCache->insertResponsePacket(dc->d_tag, dc->d_mdp.d_qname, dc->d_mdp.d_qtype, dc->d_query, 
                                             string((const char*)&*packet.begin(), packet.size()),
                                             g_now.tv_sec,
                                             pw.getHeader()->rcode == RCode::ServFail ? SyncRes::s_packetcacheservfailttl :
                                             min(minTTL,SyncRes::s_packetcachettl));
+#ifdef HAVE_PROTOBUF
+        }
+#endif
       }
       //      else cerr<<"Not putting in packet cache: "<<sr.wasVariable()<<endl;
     }
@@ -1180,6 +1214,32 @@ void makeControlChannelSocket(int processNum=-1)
   }
 }
 
+static void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, uint16_t* qclass, Netmask* ednssubnet)
+{
+  const struct dnsheader* dh = (struct dnsheader*)question.c_str();
+  size_t questionLen = question.length();
+  unsigned int consumed=0;
+  *dnsname=DNSName(question.c_str(), questionLen, sizeof(dnsheader), false, qtype, qclass, &consumed);
+
+  size_t pos= sizeof(dnsheader)+consumed+4;
+  /* at least OPT root label (1), type (2), class (2) and ttl (4) + OPT RR rdlen (2)
+     = 11 */
+  if(ntohs(dh->arcount) == 1 && questionLen > pos + 11) { // this code can extract one (1) EDNS Subnet option
+    /* OPT root label (1) followed by type (2) */
+    if(question.at(pos)==0 && question.at(pos+1)==0 && question.at(pos+2)==QType::OPT) {
+      char* ecsStart = nullptr;
+      size_t ecsLen = 0;
+      int res = getEDNSOption((char*)question.c_str()+pos+9, questionLen - pos - 9, EDNSOptionCode::ECS, &ecsStart, &ecsLen);
+      if (res == 0 && ecsLen > 4) {
+        EDNSSubnetOpts eso;
+        if(getEDNSSubnetOptsFromString(ecsStart + 4, ecsLen - 4, &eso)) {
+          *ednssubnet=eso.source;
+        }
+      }
+    }
+  }
+}
+
 void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
 {
   shared_ptr<TCPConnection> conn=any_cast<shared_ptr<TCPConnection> >(var);
@@ -1244,7 +1304,28 @@ void handleRunningTCPQuestion(int fd, FDMultiplexer::funcparam_t& var)
       getsockname(conn->getFD(), (sockaddr*)&dest, &len); // if this fails, we're ok with it
       dc->setLocal(dest);
 #ifdef HAVE_PROTOBUF
-      dc->d_uuid = (*t_uuidGenerator)();
+      auto luaconfsLocal = g_luaconfs.getLocal();
+
+      if(luaconfsLocal->protobufServer) {
+        dc->d_uuid = (*t_uuidGenerator)();
+
+        try {
+          DNSName qname;
+          uint16_t qtype;
+          uint16_t qclass;
+          Netmask ednssubnet;
+          const struct dnsheader* dh = (const struct dnsheader*) conn->data;
+
+          getQNameAndSubnet(std::string(conn->data, conn->qlen), &qname, &qtype, &qclass, &ednssubnet);
+          dc->d_ednssubnet = ednssubnet;
+
+          protobufLogQuery(luaconfsLocal->protobufServer, dc->d_uuid, dest, conn->d_remote, ednssubnet, true, dh->id, conn->qlen, qname, qtype, qclass, std::string(), std::vector<std::string>());
+        }
+        catch(std::exception& e) {
+          if(g_logCommonErrors)
+            L<<Logger::Warning<<"Error parsing a TCP query packet for edns subnet: "<<e.what()<<endl;
+        }
+      }
 #endif
       if(dc->d_mdp.d_header.qr) {
         delete dc;
@@ -1309,32 +1390,6 @@ void handleNewTCPQuestion(int fd, FDMultiplexer::funcparam_t& )
   }
 }
 
-void getQNameAndSubnet(const std::string& question, DNSName* dnsname, uint16_t* qtype, Netmask* ednssubnet)
-{
-  const struct dnsheader* dh = (struct dnsheader*)question.c_str();
-  size_t questionLen = question.length();
-  unsigned int consumed=0;
-  *dnsname=DNSName(question.c_str(), questionLen, sizeof(dnsheader), false, qtype, 0, &consumed);
-
-  size_t pos= sizeof(dnsheader)+consumed+4;
-  /* at least OPT root label (1), type (2), class (2) and ttl (4) + OPT RR rdlen (2)
-     = 11 */
-  if(ntohs(dh->arcount) == 1 && questionLen > pos + 11) { // this code can extract one (1) EDNS Subnet option
-    /* OPT root label (1) followed by type (2) */
-    if(question.at(pos)==0 && question.at(pos+1)==0 && question.at(pos+2)==QType::OPT) {
-      char* ecsStart = nullptr;
-      size_t ecsLen = 0;
-      int res = getEDNSOption((char*)question.c_str()+pos+9, questionLen - pos - 9, EDNSOptionCode::ECS, &ecsStart, &ecsLen);
-      if (res == 0 && ecsLen > 4) {
-        EDNSSubnetOpts eso;
-        if(getEDNSSubnetOptsFromString(ecsStart + 4, ecsLen - 4, &eso)) {
-          *ednssubnet=eso.source;
-        }
-      }
-    }
-  }
-}
-
 string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fromaddr, const ComboAddress& destaddr, struct timeval tv, int fd)
 {
   gettimeofday(&g_now, 0);
@@ -1354,11 +1409,21 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr
   const struct dnsheader* dh = (struct dnsheader*)question.c_str();
   unsigned int ctag=0;
   bool needECS = false;
+  std::vector<std::string> policyTags;
 #ifdef HAVE_PROTOBUF
-  needECS = true;
+  boost::uuids::uuid uniqueId;
+  PBDNSMessage pbMessage;
+  auto luaconfsLocal = g_luaconfs.getLocal();
+  if (luaconfsLocal->protobufServer) {
+    needECS = true;
+    uniqueId = (*t_uuidGenerator)();
+  }
 #endif
   Netmask ednssubnet;
   try {
+    DNSName qname;
+    uint16_t qtype=0;
+    uint16_t qclass=0;
     uint32_t age;
 #ifdef MALLOC_TRACE
     /*
@@ -1373,14 +1438,12 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr
 #endif
 
     if(needECS || (t_pdl->get() && (*t_pdl)->d_gettag)) {
-      uint16_t qtype=0;
       try {
-        DNSName qname;
-        getQNameAndSubnet(question, &qname, &qtype, &ednssubnet);
+        getQNameAndSubnet(question, &qname, &qtype, &qclass, &ednssubnet);
 
         if(t_pdl->get() && (*t_pdl)->d_gettag) {
           try {
-            ctag=(*t_pdl)->gettag(fromaddr, ednssubnet, destaddr, qname, qtype);
+            ctag=(*t_pdl)->gettag(fromaddr, ednssubnet, destaddr, qname, qtype, &policyTags);
           }
           catch(std::exception& e)  {
             if(g_logCommonErrors)
@@ -1395,7 +1458,24 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr
       }
     }
 
-    if(!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, g_now.tv_sec, &response, &age)) {
+    bool cacheHit = false;
+#ifdef HAVE_PROTOBUF
+    if(luaconfsLocal->protobufServer) {
+      protobufLogQuery(luaconfsLocal->protobufServer, uniqueId, fromaddr, destaddr, ednssubnet, false, dh->id, question.size(), qname, qtype, qclass, std::string(), policyTags);
+
+      cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, g_now.tv_sec, &response, &age, &pbMessage));
+      if (cacheHit) {
+        protobufUpdateMessage(pbMessage, uniqueId, fromaddr, destaddr, ednssubnet, false, dh->id);
+        protobufLogResponse(luaconfsLocal->protobufServer, pbMessage);
+      }
+    }
+    else {
+#endif
+    cacheHit = (!SyncRes::s_nopacketcache && t_packetCache->getResponsePacket(ctag, question, g_now.tv_sec, &response, &age));
+#ifdef HAVE_PROTOBUF
+    }
+#endif
+    if (cacheHit) {
       if(!g_quiet)
         L<<Logger::Notice<<t_id<< " question answered from packet cache tag="<<ctag<<" from "<<fromaddr.toString()<<endl;
 
@@ -1453,9 +1533,10 @@ string* doProcessUDPQuestion(const std::string& question, const ComboAddress& fr
   dc->setRemote(&fromaddr);
   dc->setLocal(destaddr);
   dc->d_tcp=false;
+  dc->d_policyTags = policyTags;
 #ifdef HAVE_PROTOBUF
-  dc->d_uuid = (*t_uuidGenerator)();
-  dc->ednssubnet = ednssubnet;
+  dc->d_uuid = uniqueId;
+  dc->d_ednssubnet = ednssubnet;
 #endif
 
   MT->makeThread(startDoResolve, (void*) dc); // deletes dc
index 873e36962b6be74ffcb3f6e1977921e64c344d52..8b45f7041af8dbb89aac7f80487f04d0e1683ffb 100644 (file)
@@ -95,8 +95,19 @@ uint32_t RecursorPacketCache::canHashPacket(const std::string& origPacket)
   return ret;
 }
 
+#ifdef HAVE_PROTOBUF
+bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
+                                            std::string* responsePacket, uint32_t* age)
+{
+  return getResponsePacket(tag, queryPacket, now, responsePacket, age, nullptr);
+}
+
+bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now,
+                                            std::string* responsePacket, uint32_t* age, PBDNSMessage* protobufMessage)
+#else
 bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now, 
   std::string* responsePacket, uint32_t* age)
+#endif
 {
   uint32_t h = canHashPacket(queryPacket);
   auto& idx = d_packetCache.get<HashTag>();
@@ -128,6 +139,11 @@ bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string&
 
       d_hits++;
       moveCacheItemToBack(d_packetCache, iter);
+#ifdef HAVE_PROTOBUF
+      if (protobufMessage) {
+        *protobufMessage = iter->d_protobufMessage;
+      }
+#endif
       
       return true;
     }
@@ -142,7 +158,16 @@ bool RecursorPacketCache::getResponsePacket(unsigned int tag, const std::string&
 }
 
 
+#ifdef HAVE_PROTOBUF
+void RecursorPacketCache::insertResponsePacket(unsigned int tag, const DNSName& qname, uint16_t qtype, const std::string& queryPacket, const std::string& responsePacket, time_t now, uint32_t ttl)
+{
+  insertResponsePacket(tag, qname, qtype, queryPacket, responsePacket, now, ttl, nullptr);
+}
+
+void RecursorPacketCache::insertResponsePacket(unsigned int tag, const DNSName& qname, uint16_t qtype, const std::string& queryPacket, const std::string& responsePacket, time_t now, uint32_t ttl, const PBDNSMessage* protobufMessage)
+#else
 void RecursorPacketCache::insertResponsePacket(unsigned int tag, const DNSName& qname, uint16_t qtype, const std::string& queryPacket, const std::string& responsePacket, time_t now, uint32_t ttl)
+#endif
 {
   auto qhash = canHashPacket(queryPacket);
   auto& idx = d_packetCache.get<HashTag>();
@@ -160,6 +185,12 @@ void RecursorPacketCache::insertResponsePacket(unsigned int tag, const DNSName&
     iter->d_packet = responsePacket;
     iter->d_ttd = now + ttl;
     iter->d_creation = now;
+#ifdef HAVE_PROTOBUF
+    if (protobufMessage) {
+      iter->d_protobufMessage = *protobufMessage;
+    }
+#endif
+
     break;
   }
   
@@ -172,6 +203,11 @@ void RecursorPacketCache::insertResponsePacket(unsigned int tag, const DNSName&
     e.d_ttd = now+ttl;
     e.d_creation = now;
     e.d_tag = tag;
+#ifdef HAVE_PROTOBUF
+    if (protobufMessage) {
+      e.d_protobufMessage = *protobufMessage;
+    }
+#endif
     d_packetCache.insert(e);
   }
 }
index 8cf7dd2d2518ca6f6a470ef1d10bb3aa60b3a7ae..e61dfefe3d872fe60ff8740f676412776dbd5bd8 100644 (file)
 #include <boost/tuple/tuple_comparison.hpp>
 #include <boost/multi_index/sequenced_index.hpp>
 
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#ifdef HAVE_PROTOBUF
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_generators.hpp>
+#include "dnsmessage.pb.h"
+#endif
+
 
 using namespace ::boost::multi_index;
 
@@ -26,6 +35,10 @@ public:
   RecursorPacketCache();
   bool getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now, std::string* responsePacket, uint32_t* age);
   void insertResponsePacket(unsigned int tag, const DNSName& qname, uint16_t qtype, const std::string& queryPacket, const std::string& responsePacket, time_t now, uint32_t ttd);
+#ifdef HAVE_PROTOBUF
+  bool getResponsePacket(unsigned int tag, const std::string& queryPacket, time_t now, std::string* responsePacket, uint32_t* age, PBDNSMessage* protobufMessage);
+  void insertResponsePacket(unsigned int tag, const DNSName& qname, uint16_t qtype, const std::string& queryPacket, const std::string& responsePacket, time_t now, uint32_t ttd, const PBDNSMessage* protobufMessage);
+#endif
   void doPruneTo(unsigned int maxSize=250000);
   uint64_t doDump(int fd);
   int doWipePacketCache(const DNSName& name, uint16_t qtype=0xffff, bool subtree=false);
@@ -45,6 +58,9 @@ private:
     DNSName d_name;
     uint16_t d_type;
     mutable std::string d_packet; // "I know what I am doing"
+#ifdef HAVE_PROTOBUF
+    mutable PBDNSMessage d_protobufMessage;
+#endif
     uint32_t d_qhash;
     uint32_t d_tag;
     inline bool operator<(const struct Entry& rhs) const;
index 9d26984e896f04e72bd7df9ef0f20d4833d9d402..35bf6defd5885a38b7b6c6e4982c3cd16b1a274f 100644 (file)
@@ -40,3 +40,4 @@
 /dnsmessage.pb.h
 /pdns-recursor.service
 /pdns-recursor@.service
+/lua.hpp
index 3d3dd22a43ac5b074db3a53b1e1cd885c5d6afd2..0a42a72147de1519700ed721b07ab2aa9ac549cc 100644 (file)
@@ -46,6 +46,7 @@ EXTRA_DIST = \
        effective_tld_names.dat \
        epollmplexer.cc \
        kqueuemplexer.cc \
+       lua_hpp.mk \
        malloctrace.cc malloctrace.hh \
        mtasker.cc \
        mtasker_fcontext.cc mtasker_ucontext.cc \
@@ -133,6 +134,12 @@ pdns_recursor_SOURCES = \
        ws-recursor.cc ws-recursor.hh \
        zoneparser-tng.cc zoneparser-tng.hh
 
+if !HAVE_LUA_HPP
+BUILT_SOURCES += lua.hpp
+nodist_pdns_recursor_SOURCES = lua.hpp
+CLEANFILES += lua.hpp
+endif
+
 pdns_recursor_LDADD = \
        $(YAHTTP_LIBS) \
        $(JSON11_LIBS) \
@@ -250,3 +257,7 @@ systemdsystemunit_DATA = \
        pdns-recursor.service \
        pdns-recursor@.service
 endif
+
+if !HAVE_LUA_HPP
+include lua_hpp.mk
+endif
index 448ec662ea6d9cf97afd7984b8dcc056250dc82b..5fcf6a2c947595b526b5fb06026fed2a0d7ec956 100644 (file)
@@ -38,6 +38,7 @@ AX_CXX_COMPILE_STDCXX_11([ext], [mandatory])
 AC_PROG_LIBTOOL
 
 PDNS_CHECK_OS
+PDNS_CHECK_NETWORK_LIBS
 
 # Boost Context was introduced in 1.51 (Aug 2012), but there was an immediate
 # API break in 1.52 (Nov 2012), so we only support that, and later.
@@ -76,7 +77,8 @@ boost_required_version=1.35
 PDNS_WITH_PROTOBUF
 AS_IF([test "x$PROTOBUF_LIBS" != "x" -a x"$PROTOC" != "x"],
   # The protobuf code needs boost::uuid, which is available from 1.42 onward
-  [boost_required_version=1.42]
+  [AC_MSG_WARN([Bumping minimal Boost requirement to 1.42. To keep the requirement at 1.35, disable protobuf support])
+  boost_required_version=1.42]
 )
 
 BOOST_REQUIRE([$boost_required_version])
@@ -88,6 +90,7 @@ PDNS_WITH_LUAJIT
 AS_IF([test "x$with_luajit" = "xno"], [
   PDNS_WITH_LUA
 ])
+PDNS_CHECK_LUA_HPP
 
 PDNS_ENABLE_VERBOSE_LOGGING
 
@@ -172,19 +175,22 @@ AC_MSG_NOTICE([CPPFLAGS: $CPPFLAGS])
 AC_MSG_NOTICE([CXXFLAGS: $CXXFLAGS])
 AC_MSG_NOTICE([LDFLAGS: $LDFLAGS])
 AC_MSG_NOTICE([LIBS: $LIBS])
-AC_MSG_NOTICE([])
 AC_MSG_NOTICE([BOOST_CPPFLAGS: $BOOST_CPPFLAGS])
 AC_MSG_NOTICE([])
+AC_MSG_NOTICE([Features enabled])
+AC_MSG_NOTICE([----------------])
 AS_IF([test "x$LUAPC" != "x"],
-  [AC_MSG_NOTICE([Lua support: $LUAPC])],
-  [AC_MSG_NOTICE([Lua support: no])]
-)
+  [AC_MSG_NOTICE([Lua: $LUAPC])],
+  [AS_IF([test "x$LUAJITPC" != "x"],
+    [AC_MSG_NOTICE([LuaJit: $LUAJITPC])],
+    [AC_MSG_NOTICE([Lua/LuaJit: no])])
+])
 AS_IF([test "x$PROTOBUF_LIBS" != "x" -a x"$PROTOC" != "x"],
-  [AC_MSG_NOTICE([Protobuf support: yes])],
-  [AC_MSG_NOTICE([Protobuf support: no])]
+  [AC_MSG_NOTICE([Protobuf: yes])],
+  [AC_MSG_NOTICE([Protobuf: no])]
 )
-AS_IF([test "x$systemd" != "x"],
-  [AC_MSG_NOTICE([systemd support: yes])],
-  [AC_MSG_NOTICE([systemd support: no])]
+AS_IF([test "x$systemd" != "xn"],
+  [AC_MSG_NOTICE([systemd: yes])],
+  [AC_MSG_NOTICE([systemd: no])]
 )
 AC_MSG_NOTICE([])
diff --git a/pdns/recursordist/lua_hpp.mk b/pdns/recursordist/lua_hpp.mk
new file mode 120000 (symlink)
index 0000000..52778c2
--- /dev/null
@@ -0,0 +1 @@
+../lua_hpp.mk
\ No newline at end of file
diff --git a/pdns/recursordist/m4/pdns_check_lua_hpp.m4 b/pdns/recursordist/m4/pdns_check_lua_hpp.m4
new file mode 120000 (symlink)
index 0000000..4ed8acd
--- /dev/null
@@ -0,0 +1 @@
+../../../m4/pdns_check_lua_hpp.m4
\ No newline at end of file
diff --git a/pdns/recursordist/m4/pdns_check_network_libs.m4 b/pdns/recursordist/m4/pdns_check_network_libs.m4
new file mode 120000 (symlink)
index 0000000..ec197ee
--- /dev/null
@@ -0,0 +1 @@
+../../../m4/pdns_check_network_libs.m4
\ No newline at end of file
index 9b8a02727c7d93702503546de6391d1c06dd7414..c5147d4431e77715a71611e19bb9697558253432 100644 (file)
@@ -1060,7 +1060,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
            if(s_maxtotusec && d_totUsec > s_maxtotusec)
              throw ImmediateServFailException("Too much time waiting for "+qname.toString()+"|"+qtype.getName()+", timeouts: "+std::to_string(d_timeouts) +", throttles: "+std::to_string(d_throttledqueries) + ", queries: "+std::to_string(d_outqueries)+", "+std::to_string(d_totUsec/1000)+"msec");
 
-           if(d_pdl && d_pdl->preoutquery(*remoteIP, d_requestor, qname, qtype, lwr.d_records, resolveret)) {
+           if(d_pdl && d_pdl->preoutquery(*remoteIP, d_requestor, qname, qtype, doTCP, lwr.d_records, resolveret)) {
              LOG(prefix<<qname.toString()<<": query handled by Lua"<<endl);
            }
            else {
index 227f9ddf8163629858bf0ff874d303bfefff0236..eae00a61d4e064cf787a77bffac2340a2cc6922f 100644 (file)
@@ -52,6 +52,9 @@ vState validateRecords(const vector<DNSRecord>& recs)
         if(state == NTA)
           return Insecure;
         LOG("! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys"<<endl);
+        for(const auto& k : keys) {
+          LOG("Key: "<<k.getZoneRepresentation()<< " {tag="<<k.getTag()<<"}"<<endl);
+        }
         // this sort of charges on and 'state' ends up as the last thing to have been checked
         // maybe not the right idea
       }
@@ -90,9 +93,9 @@ vState validateRecords(const vector<DNSRecord>& recs)
 #endif
   //  cerr<<"Input to validate: "<<endl;
   for(const auto& csp : cspmap) {
-    LOG(csp.first.first<<"|"<<csp.first.second<<" with "<<csp.second.signatures.size()<<" signatures"<<endl);
+    LOG(csp.first.first<<"|"<<DNSRecordContent::NumberToType(csp.first.second)<<" with "<<csp.second.signatures.size()<<" signatures"<<endl);
     if(!csp.second.signatures.empty() && !validrrsets.count(csp.first)) {
-      LOG("Lacks signature, must have one"<<endl);
+      LOG("Lacks signature, must have one, signatures: "<<csp.second.signatures.size()<<", valid rrsets: "<<validrrsets.count(csp.first)<<endl);
       return Bogus;
     }
   }
index 8e322fbd64f1443f1006b7d4905a68ccc58e6627..25b6e9b4d6d82b4d7a24af35c9343e0a16b48094 100644 (file)
@@ -88,12 +88,12 @@ void validateWithKeySet(const cspmap_t& rrsets, cspmap_t& validated, const keyse
   }
   */
   for(auto i=rrsets.begin(); i!=rrsets.end(); i++) {
-    //    cerr<<"validating "<<(i->first.first)<<"/"<<DNSRecordContent::NumberToType(i->first.second)<<" with "<<i->second.signatures.size()<<" sigs: ";
+    LOG("validating "<<(i->first.first)<<"/"<<DNSRecordContent::NumberToType(i->first.second)<<" with "<<i->second.signatures.size()<<" sigs"<<endl);
     for(const auto& signature : i->second.signatures) {
       vector<shared_ptr<DNSRecordContent> > toSign = i->second.records;
       
       if(getByTag(keys,signature->d_tag).empty()) {
-       //      cerr<<"No key provided for "<<signature->d_tag<<endl;
+       LOG("No key provided for "<<signature->d_tag<<endl;);
        continue;
       }
       
@@ -106,6 +106,7 @@ void validateWithKeySet(const cspmap_t& rrsets, cspmap_t& validated, const keyse
          if(signature->d_siginception < now && signature->d_sigexpire > now) {
            std::shared_ptr<DNSCryptoKeyEngine> dke = shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::makeFromPublicKeyString(l.d_algorithm, l.d_key));
            isValid = dke->verify(msg, signature->d_signature);
+            LOG("signature by key with tag "<<signature->d_tag<<" was " << (isValid ? "" : "NOT ")<<"valid"<<endl);
          }
          else {
            LOG("signature is expired/not yet valid"<<endl);
@@ -116,8 +117,9 @@ void validateWithKeySet(const cspmap_t& rrsets, cspmap_t& validated, const keyse
        }
        if(isValid) {
          validated[i->first] = i->second;
+          LOG("Validated "<<i->first.first<<"/"<<DNSRecordContent::NumberToType(signature->d_type)<<endl);
          //      cerr<<"valid"<<endl;
-         //      cerr<<"! validated "<<i->first.first<<"/"<<DNSRecordContent::NumberToType(signature->d_type)<<endl;
+         //      cerr<<"! validated "<<i->first.first<<"/"<<)<<endl;
        }
        else {
           LOG("signature invalid"<<endl);
@@ -239,6 +241,7 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, keyset_t &keyset)
       if(rec.d_type == QType::RRSIG)
       {
         auto rrc=getRR<RRSIGRecordContent> (rec);
+        LOG("Got signature: "<<rrc->getZoneRepresentation()<<" with tag "<<rrc->d_tag<<", for type "<<DNSRecordContent::NumberToType(rrc->d_type)<<endl);
         if(rrc && rrc->d_type != QType::DNSKEY)
           continue;
         sigs.push_back(*rrc);
@@ -248,15 +251,15 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, keyset_t &keyset)
         auto drc=getRR<DNSKEYRecordContent> (rec);
         if(drc) {
           tkeys.insert(*drc);
-          //   cerr<<"Inserting key with tag "<<drc->getTag()<<": "<<drc->getZoneRepresentation()<<endl;
-          dotNode("DNSKEY", qname, std::to_string(drc->getTag()), (boost::format("tag=%d, algo=%d") % drc->getTag() % static_cast<int>(drc->d_algorithm)).str());
+          LOG("Inserting key with tag "<<drc->getTag()<<": "<<drc->getZoneRepresentation()<<endl);
+          //          dotNode("DNSKEY", qname, std::to_string(drc->getTag()), (boost::format("tag=%d, algo=%d") % drc->getTag() % static_cast<int>(drc->d_algorithm)).str());
 
           toSign.push_back(rec.d_content);
           toSignTags.push_back(drc->getTag());
         }
       }
     }
-    //    cerr<<"got "<<tkeys.size()<<" keys and "<<sigs.size()<<" sigs from server"<<endl;
+    LOG("got "<<tkeys.size()<<" keys and "<<sigs.size()<<" sigs from server"<<endl);
 
     for(dsmap_t::const_iterator i=dsmap.begin(); i!=dsmap.end(); i++)
     {
@@ -273,11 +276,11 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, keyset_t &keyset)
          isValid = dsrc == dsrc2;
        } 
        catch(std::exception &e) {
-         //      cerr<<"Unable to make DS from DNSKey: "<<e.what()<<endl;
+         LOG("Unable to make DS from DNSKey: "<<e.what()<<endl);
        }
 
         if(isValid) {
-         LOG("got valid DNSKEY (it matches the DS) for "<<qname<<endl);
+         LOG("got valid DNSKEY (it matches the DS) with tag "<<dsrc.d_tag<<"/"<<i->first<<" for "<<qname<<endl);
          
           validkeys.insert(drc);
          dotNode("DS", qname, "" /*std::to_string(dsrc.d_tag)*/, (boost::format("tag=%d, digest algo=%d, algo=%d") % dsrc.d_tag % static_cast<int>(dsrc.d_digesttype) % static_cast<int>(dsrc.d_algorithm)).str());
@@ -316,7 +319,7 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, keyset_t &keyset)
            }
          }
          catch(std::exception& e) {
-           //      cerr<<"Could not make a validator for signature: "<<e.what()<<endl;
+           LOG("Could not make a validator for signature: "<<e.what()<<endl);
          }
          for(uint16_t tag : toSignTags) {
            dotEdge(qname,
index 1638a78fe77d9b1b0d65de28cdcd5547390b0c79..18189424c8508dbf990756598a5d639fddfa0034 100755 (executable)
@@ -100,7 +100,7 @@ if daemon == 'authoritative':
         named_conf.write(AUTH_CONF_TPL)
 
     subprocess.check_call(["../pdns/pdnsutil", "--config-dir=.", "secure-zone", "powerdnssec.org"])
-    pdnscmd = ("../pdns/pdns_server --daemon=no --local-port=5300 --socket-dir=./ --no-shuffle --dnsupdate=yes --cache-ttl=0 --config-dir=. --api=yes --webserver=yes --webserver-port="+WEBPORT+" --webserver-address=127.0.0.1 --webserver-password=something --api-key="+APIKEY).split()
+    pdnscmd = ("../pdns/pdns_server --daemon=no --local-address=127.0.0.1 --local-port=5300 --socket-dir=./ --no-shuffle --dnsupdate=yes --cache-ttl=0 --config-dir=. --api=yes --webserver=yes --webserver-port="+WEBPORT+" --webserver-address=127.0.0.1 --webserver-password=something --api-key="+APIKEY).split()
 
 else:
     conf_dir = 'rec-conf.d'
@@ -112,7 +112,7 @@ else:
     with open(conf_dir+'/example.com..conf', 'w') as conf_file:
         conf_file.write(REC_EXAMPLE_COM_CONF_TPL)
 
-    pdnscmd = (pdns_recursor + " --daemon=no --socket-dir=. --config-dir=. --allow-from-file=acl.list --local-port=5555 --webserver=yes --webserver-port="+WEBPORT+" --webserver-address=127.0.0.1 --webserver-password=something --api-key="+APIKEY).split()
+    pdnscmd = (pdns_recursor + " --daemon=no --socket-dir=. --config-dir=. --allow-from-file=acl.list --local-address=127.0.0.1 --local-port=5555 --webserver=yes --webserver-port="+WEBPORT+" --webserver-address=127.0.0.1 --webserver-password=something --api-key="+APIKEY).split()
 
 
 # Now run pdns and the tests.
index 8184eed4a0e799a45edc89e4737572f5567f983c..6ba23c672d884a65768e3a5ddd39294b69d065e3 100644 (file)
@@ -162,7 +162,7 @@ class DNSDistTest(unittest.TestCase):
         sock.close()
 
     @classmethod
-    def TCPResponder(cls, port, ignoreTrailing=False):
+    def TCPResponder(cls, port, ignoreTrailing=False, multipleResponses=False):
         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
         try:
@@ -182,12 +182,29 @@ class DNSDistTest(unittest.TestCase):
             response = cls._getResponse(request)
 
             if not response:
+                conn.close()
                 continue
 
             wire = response.to_wire()
             conn.send(struct.pack("!H", len(wire)))
             conn.send(wire)
+
+            while multipleResponses:
+                if cls._toResponderQueue.empty():
+                    break
+
+                response = cls._toResponderQueue.get(True, cls._queueTimeout)
+                if not response:
+                    break
+
+                response = copy.copy(response)
+                response.id = request.id
+                wire = response.to_wire()
+                conn.send(struct.pack("!H", len(wire)))
+                conn.send(wire)
+
             conn.close()
+
         sock.close()
 
     @classmethod
@@ -256,6 +273,46 @@ class DNSDistTest(unittest.TestCase):
             message = dns.message.from_wire(data)
         return (receivedQuery, message)
 
+    @classmethod
+    def sendTCPQueryWithMultipleResponses(cls, query, responses, useQueue=True, timeout=2.0, rawQuery=False):
+        if useQueue:
+            for response in responses:
+                cls._toResponderQueue.put(response, True, timeout)
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        if timeout:
+            sock.settimeout(timeout)
+
+        sock.connect(("127.0.0.1", cls._dnsDistPort))
+        messages = []
+
+        try:
+            if not rawQuery:
+                wire = query.to_wire()
+            else:
+                wire = query
+
+            sock.send(struct.pack("!H", len(wire)))
+            sock.send(wire)
+            while True:
+                data = sock.recv(2)
+                if not data:
+                    break
+                (datalen,) = struct.unpack("!H", data)
+                data = sock.recv(datalen)
+                messages.append(dns.message.from_wire(data))
+
+        except socket.timeout as e:
+            print("Timeout: %s" % (str(e)))
+        except socket.error as e:
+            print("Network error: %s" % (str(e)))
+        finally:
+            sock.close()
+
+        receivedQuery = None
+        if useQueue and not cls._fromResponderQueue.empty():
+            receivedQuery = cls._fromResponderQueue.get(True, timeout)
+        return (receivedQuery, messages)
+
     def setUp(self):
         # This function is called before every tests
 
diff --git a/regression-tests.dnsdist/test_AXFR.py b/regression-tests.dnsdist/test_AXFR.py
new file mode 100644 (file)
index 0000000..6f54dc3
--- /dev/null
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+import threading
+import dns
+from dnsdisttests import DNSDistTest
+
+class TestAXFR(DNSDistTest):
+
+    # this test suite uses a different responder port
+    # because, contrary to the other ones, its
+    # TCP responder allows multiple responses and we don't want
+    # to mix things up.
+    _testServerPort = 5370
+    _config_template = """
+    newServer{address="127.0.0.1:%s"}
+    """
+    @classmethod
+    def startResponders(cls):
+        print("Launching responders..")
+
+        cls._UDPResponder = threading.Thread(name='UDP Responder', target=cls.UDPResponder, args=[cls._testServerPort])
+        cls._UDPResponder.setDaemon(True)
+        cls._UDPResponder.start()
+        cls._TCPResponder = threading.Thread(name='TCP Responder', target=cls.TCPResponder, args=[cls._testServerPort, False, True])
+        cls._TCPResponder.setDaemon(True)
+        cls._TCPResponder.start()
+
+    _config_template = """
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    def testOneMessageAXFR(self):
+        """
+        AXFR: One message
+        """
+        name = 'one.axfr.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'AXFR', 'IN')
+        response = dns.message.make_response(query)
+        soa = dns.rrset.from_text(name,
+                                  60,
+                                  dns.rdataclass.IN,
+                                  dns.rdatatype.SOA,
+                                  'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+        response.answer.append(soa)
+        response.answer.append(dns.rrset.from_text(name,
+                                                   60,
+                                                   dns.rdataclass.IN,
+                                                   dns.rdatatype.A,
+                                                   '192.0.2.1'))
+        response.answer.append(soa)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        receivedQuery.id = query.id
+        self.assertEqual(query, receivedQuery)
+        self.assertEqual(response, receivedResponse)
+
+    def testOneNoSOAAXFR(self):
+        """
+        AXFR: One message, no SOA
+        """
+        name = 'onenosoa.axfr.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'AXFR', 'IN')
+        response = dns.message.make_response(query)
+        response.answer.append(dns.rrset.from_text(name,
+                                                   60,
+                                                   dns.rdataclass.IN,
+                                                   dns.rdatatype.A,
+                                                   '192.0.2.1'))
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        receivedQuery.id = query.id
+        self.assertEqual(query, receivedQuery)
+        self.assertEqual(response, receivedResponse)
+
+    def testFourMessagesAXFR(self):
+        """
+        AXFR: Four messages
+        """
+        name = 'four.axfr.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'AXFR', 'IN')
+        responses = []
+        soa = dns.rrset.from_text(name,
+                                  60,
+                                  dns.rdataclass.IN,
+                                  dns.rdatatype.SOA,
+                                  'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+        response = dns.message.make_response(query)
+        response.answer.append(soa)
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        response.answer.append(dns.rrset.from_text(name,
+                                                   60,
+                                                   dns.rdataclass.IN,
+                                                   dns.rdatatype.A,
+                                                   '192.0.2.1'))
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '2001:DB8::1')
+        response.answer.append(rrset)
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.TXT,
+                                    'dummy')
+        response.answer.append(rrset)
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        response.answer.append(soa)
+        responses.append(response)
+
+        (receivedQuery, receivedResponses) = self.sendTCPQueryWithMultipleResponses(query, responses)
+        receivedQuery.id = query.id
+        self.assertEqual(query, receivedQuery)
+        self.assertEqual(len(receivedResponses), len(responses))
+
+    def testFourNoFinalSOAAXFR(self):
+        """
+        AXFR: Four messages, no final SOA
+        """
+        name = 'fournosoa.axfr.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'AXFR', 'IN')
+        responses = []
+        soa = dns.rrset.from_text(name,
+                                  60,
+                                  dns.rdataclass.IN,
+                                  dns.rdatatype.SOA,
+                                  'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+        response = dns.message.make_response(query)
+        response.answer.append(soa)
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        response.answer.append(dns.rrset.from_text(name,
+                                                   60,
+                                                   dns.rdataclass.IN,
+                                                   dns.rdatatype.A,
+                                                   '192.0.2.1'))
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '2001:DB8::1')
+        response.answer.append(rrset)
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.TXT,
+                                    'dummy')
+        response.answer.append(rrset)
+        responses.append(response)
+
+        (receivedQuery, receivedResponses) = self.sendTCPQueryWithMultipleResponses(query, responses)
+        receivedQuery.id = query.id
+        self.assertEqual(query, receivedQuery)
+        self.assertEqual(len(receivedResponses), len(responses))
+
+    def testFourNoFirstSOAAXFR(self):
+        """
+        AXFR: Four messages, no SOA in the first one
+        """
+        name = 'fournosoainfirst.axfr.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'AXFR', 'IN')
+        responses = []
+        soa = dns.rrset.from_text(name,
+                                  60,
+                                  dns.rdataclass.IN,
+                                  dns.rdatatype.SOA,
+                                  'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+        response = dns.message.make_response(query)
+        response.answer.append(dns.rrset.from_text(name,
+                                                   60,
+                                                   dns.rdataclass.IN,
+                                                   dns.rdatatype.A,
+                                                   '192.0.2.1'))
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '2001:DB8::1')
+        response.answer.append(soa)
+        response.answer.append(rrset)
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text('dummy.' + name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '2001:DB8::1')
+        response.answer.append(rrset)
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.TXT,
+                                    'dummy')
+        response.answer.append(rrset)
+        response.answer.append(soa)
+        responses.append(response)
+
+        (receivedQuery, receivedResponses) = self.sendTCPQueryWithMultipleResponses(query, responses)
+        receivedQuery.id = query.id
+        self.assertEqual(query, receivedQuery)
+        self.assertEqual(len(receivedResponses), 1)
+
+    def testFourLastSOAInSecondAXFR(self):
+        """
+        AXFR: Four messages, SOA in the first one and the second one
+        """
+        name = 'foursecondsoainsecond.axfr.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'AXFR', 'IN')
+        responses = []
+        soa = dns.rrset.from_text(name,
+                                  60,
+                                  dns.rdataclass.IN,
+                                  dns.rdatatype.SOA,
+                                  'ns.' + name + ' hostmaster.' + name + ' 1 3600 3600 3600 60')
+
+        response = dns.message.make_response(query)
+        response.answer.append(soa)
+        response.answer.append(dns.rrset.from_text(name,
+                                                   60,
+                                                   dns.rdataclass.IN,
+                                                   dns.rdatatype.A,
+                                                   '192.0.2.1'))
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        response.answer.append(soa)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '2001:DB8::1')
+        response.answer.append(rrset)
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text('dummy.' + name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.AAAA,
+                                    '2001:DB8::1')
+        response.answer.append(rrset)
+        responses.append(response)
+
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    60,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.TXT,
+                                    'dummy')
+        response.answer.append(rrset)
+        responses.append(response)
+
+        (receivedQuery, receivedResponses) = self.sendTCPQueryWithMultipleResponses(query, responses)
+        receivedQuery.id = query.id
+        self.assertEqual(query, receivedQuery)
+        self.assertEqual(len(receivedResponses), 2)
index c95a8355c6d2b398db7e234681668481bdd1a5be..bb3bc17dba894a27b2b247fc0796f3a64c4045a3 100755 (executable)
@@ -6,10 +6,10 @@ fi
 
 cleandig ns1.addzone.com A
 cleandig ns1.test.com A
-$PDNSCONTROL --config-name=bind --socket-dir=. --no-config bind-add-zone addzone.com zones/addzone.com
+$PDNSCONTROL --config-name=bind --socket-dir=. --no-config bind-add-zone addzone.com ${PWD}/zones/addzone.com >/dev/null 2>&1
 $PDNSCONTROL --config-name=bind --socket-dir=. --no-config purge addzone.com
 sleep 1
-$PDNSCONTROL --config-name=bind --socket-dir=. --no-config bind-add-zone addzone.com zones/addzone.com
+$PDNSCONTROL --config-name=bind --socket-dir=. --no-config bind-add-zone addzone.com ${PWD}/zones/addzone.com
 sleep 1
 cleandig ns1.addzone.com A
 cleandig ns1.test.com A
index 6db3dcff91260e789a95fb2bfe6a307a5789ccd9..845856c4727609f0bb09bf2404e93e2af805367e 100644 (file)
@@ -3,7 +3,6 @@ Reply to question for qname='ns1.addzone.com.', qtype=A
 0      ns1.test.com.   IN      A       3600    1.1.1.1
 Rcode: 0 (No Error), RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
 Reply to question for qname='ns1.test.com.', qtype=A
-Loaded zone addzone.com from zones/addzone.com
 1
 Already loaded
 0      ns1.addzone.com.        IN      A       3600    1.1.1.5