# ;;
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}
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
%configure \
--sysconfdir=%{_sysconfdir}/%{name} \
- --libdir=%{_libdir}/%{name} \
+ --libdir=%{_libdir} \
--disable-static \
--disable-dependency-tracking \
--disable-silent-rules \
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
%files tools
%{_bindir}/calidns
-%{_bindir}/dnsbulktest
%{_bindir}/dnsgram
%{_bindir}/dnsreplay
%{_bindir}/dnsscan
%{_bindir}/dnsscope
-%{_bindir}/dnstcpbench
%{_bindir}/dnswasher
%{_bindir}/dumresp
%{_bindir}/ixplore
%{_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
%{_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
%{_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
;;
AS_IF([test "x$with_luajit" = "xno"], [
PDNS_WITH_LUA
])
+PDNS_CHECK_LUA_HPP
AX_CXX_COMPILE_STDCXX_11
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])
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])
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([])
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):
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
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/.
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
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
% 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.
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
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
## `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.
- 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
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`.
* 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:
# 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.
## 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
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).
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
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)
#Â 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.
## `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.
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
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
# 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`)
* 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)!
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
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
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
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.
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:
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:
-
-| | |
-|:--||:--|
-|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.
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,
end
return false
end
-
-
```
; 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/"
// 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"};
}
}
}
# along with this program. If not, see <http://www.gnu.org/licenses/>.
m4_define([_BOOST_SERIAL], [m4_translit([
-# serial 25
+# serial 26 PowerDNS modified
], [#
], [])])
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],
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.
+++ /dev/null
-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"])
-])
-
--- /dev/null
+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" ])
+])
+++ /dev/null
-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.])]
- )
- ])
-])
THREADFLAGS=""
case "$host_os" in
- solaris2.10)
+ solaris2.1*)
LIBS="-lposix4 -lpthread $LIBS"
CXXFLAGS="-D_REENTRANT $CXXFLAGS"
have_solaris="yes"
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],
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])
])
])
--- /dev/null
+AC_DEFUN([PDNS_FROM_GIT], [
+ AM_CONDITIONAL([FROM_GIT], [test -d "$srcdir/.git"])
+])
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"])
])
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;
// 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;
$(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)
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"
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
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:
* `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:
* `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
-- 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
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;
}
+#include <net/if.h>
#include "dnsdist.hh"
#include "dnsrulactions.hh"
#include <thread>
#include <fstream>
#include "dnswriter.hh"
#include "lock.hh"
-#include <net/if.h>
#ifdef HAVE_SYSTEMD
#include <systemd/sd-daemon.h>
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) {
#include "dnsdist.hh"
#include "dnsdist-ecs.hh"
+#include "dnsparser.hh"
#include "ednsoptions.hh"
#include "dolog.hh"
#include "lock.hh"
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;
{
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);
sockets.erase(ds->remote);
sockets[ds->remote]=dsock=setupTCPDownstream(ds);
downstream_failures++;
+ if(xfrStarted) {
+ goto drop;
+ }
goto retry;
}
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);
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;
}
/dnsmessage.pb.cc
/dnsmessage.pb.h
/dnsdist.service
+/lua.hpp
ext/incbin/UNLICENSE \
incfiles \
src_js \
- dnsdist.service.in
+ dnsdist.service.in \
+ lua_hpp.mk
bin_PROGRAMS = dnsdist
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
systemdsystemunit_DATA = \
dnsdist.service
endif
+
+if !HAVE_LUA_HPP
+include lua_hpp.mk
+endif
AC_CONFIG_HEADERS([config.h])
AC_PROG_CC
AC_PROG_CXX
+AC_LANG([C++])
LT_PREREQ([2.2.2])
LT_INIT([disable-static])
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])
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])
AC_LD_RELRO
])
-PDNS_CHECK_OS
-
PDNS_ENABLE_SANITIZERS
PDNS_CHECK_PANDOC
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([])
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);
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;
--- /dev/null
+../lua_hpp.mk
\ No newline at end of file
--- /dev/null
+../../../m4/pdns_check_lua_hpp.m4
\ No newline at end of file
--- /dev/null
+../../../m4/pdns_check_network_libs.m4
\ No newline at end of file
#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))
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);
}
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;
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);
}
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;
mutable unsigned long d_tcpdrops{0};
unsigned long d_otherrcode{0};
std::atomic<bool> d_pleaseQuit{false};
+ bool d_addECS{false};
};
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);
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;
}
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;
}
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);
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;
});
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)
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;
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:;
if (appliedPolicy) {
*appliedPolicy=dq->appliedPolicy;
}
- if (policyTags) {
- *policyTags = dq->policyTags;
- }
}
#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;
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:
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;
};
--- /dev/null
+lua.hpp:
+ $(AM_V_GEN)echo 'extern "C" {' > $@
+ @echo '#include "lua.h"' >> $@
+ @echo '#include "lualib.h"' >> $@
+ @echo '#include "lauxlib.h"' >> $@
+ @echo '}' >> $@
string ping;
bool weWantEDNSSubnet=false;
- if(EDNS0Level && !doTCP) {
+ if(EDNS0Level) {
DNSPacketWriter::optvect_t opts;
if(srcmask) {
EDNSSubnetOpts eo;
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;
string d_query;
shared_ptr<TCPConnection> d_tcpConnection;
vector<pair<uint16_t, string> > d_ednsOpts;
+ std::vector<std::string> d_policyTags;
};
}
#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));
}
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
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);
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;
}
- 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);
}
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:;
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;
}
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);
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) {
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;
}
}
}
+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);
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;
}
}
-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);
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
/*
#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)
}
}
- 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;
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
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>();
d_hits++;
moveCacheItemToBack(d_packetCache, iter);
+#ifdef HAVE_PROTOBUF
+ if (protobufMessage) {
+ *protobufMessage = iter->d_protobufMessage;
+ }
+#endif
return true;
}
}
+#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>();
iter->d_packet = responsePacket;
iter->d_ttd = now + ttl;
iter->d_creation = now;
+#ifdef HAVE_PROTOBUF
+ if (protobufMessage) {
+ iter->d_protobufMessage = *protobufMessage;
+ }
+#endif
+
break;
}
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);
}
}
#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;
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);
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;
/dnsmessage.pb.h
/pdns-recursor.service
/pdns-recursor@.service
+/lua.hpp
effective_tld_names.dat \
epollmplexer.cc \
kqueuemplexer.cc \
+ lua_hpp.mk \
malloctrace.cc malloctrace.hh \
mtasker.cc \
mtasker_fcontext.cc mtasker_ucontext.cc \
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) \
pdns-recursor.service \
pdns-recursor@.service
endif
+
+if !HAVE_LUA_HPP
+include lua_hpp.mk
+endif
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.
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])
AS_IF([test "x$with_luajit" = "xno"], [
PDNS_WITH_LUA
])
+PDNS_CHECK_LUA_HPP
PDNS_ENABLE_VERBOSE_LOGGING
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([])
--- /dev/null
+../lua_hpp.mk
\ No newline at end of file
--- /dev/null
+../../../m4/pdns_check_lua_hpp.m4
\ No newline at end of file
--- /dev/null
+../../../m4/pdns_check_network_libs.m4
\ No newline at end of file
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 {
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
}
#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;
}
}
}
*/
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;
}
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);
}
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);
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);
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++)
{
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());
}
}
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,
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'
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.
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:
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
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
--- /dev/null
+#!/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)
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
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