]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #9088 from neheb/nbm
authorRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 20 Aug 2020 09:27:34 +0000 (11:27 +0200)
committerGitHub <noreply@github.com>
Thu, 20 Aug 2020 09:27:34 +0000 (11:27 +0200)
replace boost:bind() with lambdas

249 files changed:
.circleci/config.yml
.github/ISSUE_TEMPLATE/config.yml
.github/actions/spell-check/allow.txt [new file with mode: 0644]
.github/actions/spell-check/excludes.txt [new file with mode: 0644]
.github/actions/spell-check/expect.txt [new file with mode: 0644]
.github/actions/spell-check/only.txt [new file with mode: 0644]
.github/actions/spell-check/patterns.txt [new file with mode: 0644]
.github/workflows/spelling.yml [new file with mode: 0644]
README.md
build-scripts/docker/generate-repo-files.sh
build-scripts/test-recursor-bulk
build-scripts/travis.sh
builder-support/debian/authoritative/debian-buster/pdns-backend-sqlite3.docs
builder-support/debian/authoritative/debian-buster/rules
builder-support/debian/authoritative/debian-jessie/pdns-backend-sqlite3.docs
builder-support/debian/authoritative/debian-jessie/rules
builder-support/debian/authoritative/debian-stretch/pdns-backend-sqlite3.docs
builder-support/debian/authoritative/debian-stretch/rules
builder-support/debian/recursor/debian-buster/rules
builder-support/debian/recursor/debian-jessie/rules
builder-support/debian/recursor/debian-stretch/rules
builder-support/specs/pdns.spec
configure.ac
contrib/pdnsutil.bash_completion.d
docs/appendices/EOL.rst
docs/appendices/FAQ.rst
docs/appendices/backend-writers-guide.rst
docs/backends/bind.rst
docs/backends/generic-postgresql.rst
docs/backends/generic-sql.rst
docs/changelog/4.2.rst
docs/dnssec/pkcs11.rst
docs/dnsupdate.rst
docs/domainmetadata.rst
docs/http-api/index.rst
docs/http-api/swagger/authoritative-api-swagger.yaml
docs/http-api/tsigkey.rst
docs/http-api/zone.rst
docs/manpages/pdns_control.1.rst
docs/manpages/pdnsutil.1.rst
docs/modes-of-operation.rst
docs/secpoll.zone
docs/settings.rst
docs/tsig.rst
docs/upgrading.rst
ext/lmdb-safe/lmdb-safe.hh
modules/bindbackend/bindbackend2.cc
modules/geoipbackend/geoipbackend.cc
modules/gmysqlbackend/gmysqlbackend.cc
modules/godbcbackend/godbcbackend.cc
modules/gpgsqlbackend/gpgsqlbackend.cc
modules/gpgsqlbackend/spgsql.cc
modules/gpgsqlbackend/spgsql.hh
modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql [new file with mode: 0644]
modules/gsqlite3backend/Makefile.am
modules/gsqlite3backend/gsqlite3backend.cc
modules/gsqlite3backend/schema.sqlite3.sql
modules/ldapbackend/ldapbackend.cc
modules/lmdbbackend/lmdbbackend.cc
modules/lmdbbackend/lmdbbackend.hh
modules/lua2backend/lua2backend.cc
modules/pipebackend/coprocess.cc
modules/pipebackend/coprocess.hh
modules/pipebackend/pipebackend.cc
modules/randombackend/randombackend.cc
modules/remotebackend/Gemfile.lock
modules/remotebackend/httpconnector.cc
modules/remotebackend/regression-tests/Gemfile.lock
modules/remotebackend/remotebackend.cc
modules/remotebackend/remotebackend.hh
modules/tinydnsbackend/tinydnsbackend.cc
pdns/Makefile.am
pdns/auth-carbon.cc
pdns/auth-packetcache.cc
pdns/auth-querycache.cc
pdns/axfr-retriever.cc [new file with mode: 0644]
pdns/axfr-retriever.hh [new file with mode: 0644]
pdns/backends/gsql/gsqlbackend.cc
pdns/backends/gsql/gsqlbackend.hh
pdns/cachecleaner.hh
pdns/common_startup.cc
pdns/communicator.cc
pdns/communicator.hh
pdns/dbdnsseckeeper.cc
pdns/dnsbackend.cc
pdns/dnsbackend.hh
pdns/dnsdist-cache.cc
pdns/dnsdist-carbon.cc
pdns/dnsdist-console.cc
pdns/dnsdist-ecs.cc
pdns/dnsdist-lbpolicies.hh
pdns/dnsdist-lua-bindings.cc
pdns/dnsdist-lua.cc
pdns/dnsdist-web.cc
pdns/dnsdist.cc
pdns/dnsdist.hh
pdns/dnsdistconf.lua
pdns/dnsdistdist/Makefile.am
pdns/dnsdistdist/configure.ac
pdns/dnsdistdist/dnsdist-backend.cc
pdns/dnsdistdist/dnsdist-healthchecks.cc
pdns/dnsdistdist/dnsdist-kvs.cc
pdns/dnsdistdist/dnsdist-kvs.hh
pdns/dnsdistdist/dnsdist-lbpolicies.cc
pdns/dnsdistdist/dnsdist-lua-bindings-kvs.cc
pdns/dnsdistdist/dnsdist-lua-bindings-packetcache.cc
pdns/dnsdistdist/dnsdist-lua-ffi-interface.h
pdns/dnsdistdist/dnsdist-lua-ffi.cc
pdns/dnsdistdist/dnsdist-proxy-protocol.cc
pdns/dnsdistdist/dnsdist-secpoll.cc
pdns/dnsdistdist/dnsdist-web.hh [new file with mode: 0644]
pdns/dnsdistdist/docs/changelog.rst
pdns/dnsdistdist/docs/guides/dns-over-https.rst
pdns/dnsdistdist/docs/guides/webserver.rst
pdns/dnsdistdist/docs/reference/config.rst
pdns/dnsdistdist/docs/reference/kvs.rst
pdns/dnsdistdist/docs/reference/netmaskgroup.rst
pdns/dnsdistdist/docs/rules-actions.rst
pdns/dnsdistdist/docs/upgrade_guide.rst
pdns/dnsdistdist/doh.cc
pdns/dnsdistdist/test-dnsdistkvs_cc.cc
pdns/dnsdistdist/views.hh
pdns/dnsname.hh
pdns/dnspcap.cc
pdns/dnspcap.hh
pdns/dnspcap2protobuf.cc
pdns/dnsrecords.cc
pdns/dnssecinfra.cc
pdns/dnsseckeeper.hh
pdns/dnstcpbench.cc
pdns/doh.hh
pdns/dynhandler.cc
pdns/filterpo.hh
pdns/iputils.hh
pdns/ixfrdist.cc
pdns/ixfrutils.cc
pdns/ixplore.cc
pdns/lua-auth4.cc
pdns/lua-base4.cc
pdns/lua-record.cc
pdns/lua-recursor4.cc
pdns/lua-recursor4.hh
pdns/lwres.cc
pdns/mastercommunicator.cc
pdns/misc.cc
pdns/misc.hh
pdns/opensslsigners.cc
pdns/packethandler.cc
pdns/pdns.service.in
pdns/pdns_recursor.cc
pdns/pdnsutil.cc
pdns/pkcs11signers.cc
pdns/proxy-protocol.cc
pdns/query-local-address.cc [new file with mode: 0644]
pdns/query-local-address.hh [new file with mode: 0644]
pdns/rec-carbon.cc
pdns/rec-lua-conf.cc
pdns/rec_channel_rec.cc
pdns/receiver.cc
pdns/recpacketcache.cc
pdns/recpacketcache.hh
pdns/recursor_cache.cc
pdns/recursor_cache.hh
pdns/recursordist/Makefile.am
pdns/recursordist/axfr-retriever.cc [new symlink]
pdns/recursordist/axfr-retriever.hh [new symlink]
pdns/recursordist/configure.ac
pdns/recursordist/docs/appendices/EOL.rst
pdns/recursordist/docs/changelog/4.1.rst
pdns/recursordist/docs/changelog/4.2.rst
pdns/recursordist/docs/changelog/4.3.rst
pdns/recursordist/docs/changelog/4.4.rst
pdns/recursordist/docs/dnssec.rst
pdns/recursordist/docs/getting-started.rst
pdns/recursordist/docs/index.rst
pdns/recursordist/docs/lua-config/rpz.rst
pdns/recursordist/docs/lua-scripting/dnsname.rst
pdns/recursordist/docs/lua-scripting/hooks.rst
pdns/recursordist/docs/manpages/pdns_recursor.1.rst
pdns/recursordist/docs/manpages/rec_control.1.rst
pdns/recursordist/docs/metrics.rst
pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-01.rst [new file with mode: 0644]
pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-02.rst [new file with mode: 0644]
pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-03.rst [new file with mode: 0644]
pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-04.rst [new file with mode: 0644]
pdns/recursordist/docs/settings.rst
pdns/recursordist/docs/upgrade.rst
pdns/recursordist/negcache.cc
pdns/recursordist/negcache.hh
pdns/recursordist/query-local-address.cc [new symlink]
pdns/recursordist/query-local-address.hh [new symlink]
pdns/recursordist/test-negcache_cc.cc
pdns/recursordist/test-recursorcache_cc.cc
pdns/recursordist/test-rpzloader_cc.cc
pdns/recursordist/test-secpoll_cc.cc
pdns/recursordist/test-syncres_cc.cc
pdns/recursordist/test-syncres_cc1.cc
pdns/recursordist/test-syncres_cc10.cc [new file with mode: 0644]
pdns/recursordist/test-syncres_cc2.cc
pdns/recursordist/test-syncres_cc3.cc
pdns/recursordist/test-syncres_cc4.cc
pdns/recursordist/test-syncres_cc5.cc
pdns/recursordist/test-syncres_cc6.cc
pdns/recursordist/test-syncres_cc7.cc
pdns/recursordist/test-syncres_cc8.cc
pdns/recursordist/test-syncres_cc9.cc
pdns/reczones.cc
pdns/resolver.cc
pdns/resolver.hh
pdns/rfc2136handler.cc
pdns/rpzloader.cc
pdns/secpoll-auth.cc
pdns/secpoll-recursor.cc
pdns/secpoll.cc
pdns/shuffle.cc
pdns/slavecommunicator.cc
pdns/statbag.cc
pdns/statbag.hh
pdns/syncres.cc
pdns/syncres.hh
pdns/tcpreceiver.cc
pdns/test-misc_hh.cc
pdns/test-recpacketcache_cc.cc
pdns/test-ueberbackend_cc.cc [new file with mode: 0644]
pdns/toysdig.cc
pdns/tsig-tests.cc
pdns/ueberbackend.cc
pdns/ueberbackend.hh
pdns/validate.cc
pdns/validate.hh
pdns/ws-auth.cc
pdns/ws-auth.hh
pdns/ws-recursor.cc
pdns/ws-recursor.hh
regression-tests.api/test_Zones.py
regression-tests.auth-py/test_LuaRecords.py
regression-tests.dnsdist/dnsdisttests.py
regression-tests.dnsdist/test_API.py
regression-tests.dnsdist/test_TCPLimits.py
regression-tests.nobackend/counters/expected_result
regression-tests.recursor-dnssec/recursortests.py
regression-tests.recursor-dnssec/test_API.py [new file with mode: 0644]
regression-tests.recursor-dnssec/test_ECS.py
regression-tests.recursor-dnssec/test_RPZ.py
regression-tests.recursor-dnssec/test_RoutingTag.py
regression-tests/backends/gmysql-slave
regression-tests/recursor-test
regression-tests/recursor-test-freebsd
regression-tests/start-test-stop

index 3f7cc1ea6c1af3ccb89a33df8819cc9e694f2c7a..6af960b74b254dadf10486432fe300ed93b30cb9 100644 (file)
@@ -191,12 +191,14 @@ commands:
             libldap-2.4-2 \
             liblmdb0 \
             libluajit-5.1-2 \
+            libp11-kit0 \
             libpq5 \
             libssl1.1 \
             libsodium23 \
             libsystemd0 \
             default-libmysqlclient-dev \
-            unixodbc
+            unixodbc \
+            softhsm2
 
   install-dnsdist-deps:
     description: "Install all libraries needed for testing dnsdist"
@@ -257,6 +259,7 @@ commands:
               libldap2-dev \
               liblmdb-dev \
               libluajit-5.1-dev \
+              libp11-kit-dev \
               libpq-dev \
               libsodium-dev \
               libsqlite3-dev \
@@ -541,6 +544,7 @@ jobs:
               --enable-unit-tests \
               --enable-backend-unit-tests \
               --enable-fuzz-targets \
+              --enable-experimental-pkcs11 \
               --with-lmdb=/usr \
               --with-libsodium \
               --prefix=/opt/pdns-auth \
@@ -682,7 +686,7 @@ jobs:
     steps:
       - auth-regress-setup
       - run:
-          command: apt-get install -qq -y sqlite3
+          command: apt-get install -qq -y sqlite3 p11-kit softhsm2
       - auth-regress:
           context: bind-both
       - auth-regress:
@@ -693,6 +697,8 @@ jobs:
           context: bind-dnssec-nsec3-optout-both
       - auth-regress:
           context: bind-dnssec-nsec3-narrow
+      - auth-regress:
+          context: bind-dnssec-pkcs11
       - run:
           command: apt-get install -qq -y default-mysql-client
       - run:
index 3ba13e0cec6cbbfd462e9ebf529dd2093148cd69..99f51739252bbfff07424e4d295a39ca365a3494 100644 (file)
@@ -1 +1,6 @@
 blank_issues_enabled: false
+
+contact_links:
+- name: Get help with a question or a problem
+  url: https://www.powerdns.com/opensource.html
+  about: Find us on IRC or our mailing lists to get the answers or help you need!
\ No newline at end of file
diff --git a/.github/actions/spell-check/allow.txt b/.github/actions/spell-check/allow.txt
new file mode 100644 (file)
index 0000000..d0c2ac4
--- /dev/null
@@ -0,0 +1,4752 @@
+aaaarc
+aaaarec
+aaaarr
+aaaaset
+aab
+aabbccddeeff
+aabit
+aac
+aafd
+AAg
+Aand
+Aaq
+Aarbp
+aarch
+abca
+abcabcabcabacabac
+abcb
+abcd
+abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq
+abcdefgh
+abcdefghijklmnopq
+abcdefghijklmnopqrstuv
+abcdefghijklmnopqrstuvwxyz
+abcdefghj
+abq
+ACAABIBg
+acceptspace
+accountname
+aci
+aclocal
+ACLTOK
+Acpvl
+Acu
+adata
+addaction
+addgroup
+addnode
+ADDO
+ADDOCD
+addrbuf
+ADDRCONFIG
+addrinfo
+addrlabel
+addrlen
+addrringbuf
+adduser
+addzone
+adifferentlabel
+advsys
+aerique
+afc
+afe
+Affero
+afilias
+Afio
+afpx
+afternm
+aglu
+ahudns
+aio
+Akkermann
+akxe
+Alexey
+algname
+algonum
+algotype
+alldown
+allemaal
+ALLEXTERNALS
+allmakers
+alloc
+allocflux
+alloptions
+alloutqueries
+allowedips
+allownooptinar
+allowns
+allowonear
+allowtwoan
+allowtwoarnoopt
+allowwnooptinar
+alnum
+alphtype
+alpn
+ALSONOTIFYTOK
+altfrac
+altmeters
+altsign
+Amau
+amazonaws
+amazonlinux
+Amc
+amet
+Amf
+AMFLAGS
+Ampl
+anaccount
+anadns
+aname
+ANANSWER
+ancount
+andnot
+ANDQ
+anl
+anonpdns
+anonscm
+anonymizing
+answercount
+answermask
+answername
+answersslow
+answertime
+ANYId
+ANYNo
+anytruncatetcp
+ao
+aoh
+aorudp
+AOver
+apipassword
+apisecret
+apix
+apk
+apos
+apowerdnscom
+appliedpolicy
+appliedpolicytype
+appname
+APTR
+APublic
+Aqerz
+AQEX
+arcount
+arec
+arecvtcp
+argc
+arglock
+argp
+argparse
+args
+Argumentsv
+argv
+argvec
+arl
+aroot
+arp
+arpos
+arrs
+ARSH
+asan
+asendtcp
+asendto
+aset
+asn
+asnr
+AStats
+athread
+atid
+ATime
+ATj
+Ato
+atof
+atruncatetc
+attodot
+attrany
+attributetype
+attrname
+attronly
+Auous
+authbind
+authcid
+authcmd
+authconfdir
+authdir
+authdomain
+authlog
+authname
+AUTHRUN
+authstorage
+authtests
+authzid
+authzonepath
+authzonequeries
+AUTOBRIEF
+Autobuilder
+autocalculating
+autocommit
+autogen
+autom
+autorev
+autorr
+autosetup
+autosplit
+avect
+AWbqt
+AWIBIQ
+AWith
+awithecs
+AWORD
+aww
+AXFRGET
+axfrqlen
+Ayea
+AZWLVw
+bacf
+BACKENDADDRESS
+backendfactory
+BACKENDID
+BACKENDLATENCY
+backendloader
+backendmaker
+backendname
+BACKENDORDER
+BACKENDOUTSTANDING
+BACKENDPOOLS
+BACKENDQPS
+BACKENDQPSLIMIT
+BACKENDQUERIES
+BACKENDREUSED
+BACKENDSTATE
+BACKENDWEIGHT
+BADALGO
+badmac
+badname
+badrequests
+bak
+BAQEB
+baseurl
+BASICRESP
+batchmode
+baz
+bbnew
+bbold
+bbsv
+bcbsks
+Bchl
+Bdn
+bdr
+bdsqlodbc
+befe
+beforenm
+BEFORENMBYTES
+begiter
+Beh
+bereturnscookiesecscookies
+bereturnscookiesthenecs
+bereturnsecs
+bereturnsecsthencookies
+berval
+bestmatch
+bestns
+bestpos
+Bfcw
+bff
+BFr
+BGs
+bh
+bhartvigsen
+BHQk
+bidir
+BIGNUM
+BINARYPATH
+binascii
+bincount
+BINDANY
+BINDCONF
+binddirectory
+binddnssec
+binderror
+bindir
+bindlexer
+bindmethod
+bindnow
+bindparser
+bindparserclasses
+bindpw
+bindwait
+bitcstr
+bitmembers
+bitpointers
+bitset
+bitsizes
+bitsleft
+bitstr
+Bj
+BKCNF
+bkoz
+BKU
+blablabla
+blahb
+bleh
+blen
+blhc
+blockfor
+blockset
+bloeh
+blxn
+bmc
+bmgul
+BMh
+BNA
+BNEh
+BNX
+bogusqueryring
+bogusremotering
+bogusremotes
+BOIWc
+boostorg
+booststringappend
+Bostock
+BPHYx
+bpowerdns
+bpowerdnscom
+bq
+BREHMDq
+brokeloop
+broot
+BRRRRR
+BRu
+bsqlodbc
+BSTUNE
+BTf
+btoe
+buf
+buffersize
+buflen
+BUGLIST
+bugreport
+builddir
+buildflags
+buildroot
+builtins
+bulktest
+burtle
+burtlemix
+bval
+Bvc
+bvect
+Bvy
+BWdx
+byport
+bytag
+bytearray
+byteorder
+byterate
+bytesread
+BZq
+cacheable
+cachebase
+cachecache
+cachecachevalid
+cachecleaner
+cacheda
+cachedecreasettl
+cachedifferentcase
+cachedifferentedns
+cachedir
+cachedoesntdecreasettl
+cachedqname
+cachedrtag
+cacheecsttl
+cacheentries
+cacheexpiration
+cacheexpirationdifferentsets
+cachefull
+cachehandleiter
+cachehit
+cachehitresponses
+cachehitresprulactions
+cachelimit
+cachemisses
+cachenotfullyet
+cacheonly
+cachettl
+cacheweekno
+CAcreateserial
+caddyfile
+caddyserver
+cafile
+CAINFO
+callbackfunc
+callbackmap
+callee
+callgraph
+CANTOPEN
+capair
+caplen
+carbonname
+carbonthread
+cas
+cassert
+cbe
+cbegin
+cbf
+CBlock
+cbmap
+CBTd
+cbuf
+cbufsize
+ccf
+ccname
+ccontrols
+cctype
+ccv
+cda
+cdbbc
+cdbf
+cdbfile
+cdbinit
+cdbm
+CDBQ
+cdc
+cdd
+cdde
+cdef
+cellpadding
+cend
+cerr
+cerrno
+CERTID
+CERTSTATUS
+Cexternal
+Cfb
+cft
+Cfy
+cgit
+CHACHA
+chartocode
+checkbox
+checkfunc
+checkglue
+checkinterval
+checknow
+Checkof
+checkrr
+checkzone
+childstat
+chkconfig
+chr
+chrono
+cin
+cinttypes
+ciphersuites
+circleci
+citmp
+Cj
+cka
+Ckey
+CKF
+CKK
+CKM
+CKO
+CKR
+cku
+classname
+classnum
+CLASSTOK
+cleandig
+CLEANFILES
+cleaninterval
+cleannsupdate
+cleanskipped
+clearrd
+clearrdviaaction
+cleartext
+clearthensetrules
+clen
+clientdiff
+CLIENTIP
+clientparseerrors
+clientsubnet
+clientsubnetoption
+climits
+clmn
+CLOEXEC
+closedir
+closelog
+cls
+Clvv
+cmake
+cmap
+cmatchlen
+cmath
+cmdline
+cmdomains
+cmp
+cmsgbuf
+Cmu
+cmval
+cnam
+cnameaction
+CNAMEAt
+CNAMENo
+CNAMENTA
+cnamerec
+cnamespoof
+cnamespoofaction
+cnamewildcard
+cnamewildcardnxdomain
+cnt
+Cnxd
+codedocs
+coldata
+colnum
+colspan
+columncount
+combovar
+comfun
+compat
+compgen
+compilerflags
+COMPREPLY
+concat
+concurrentqueries
+condrestart
+confbasename
+confdir
+confdirname
+conffile
+configdir
+configentry
+configpath
+configstring
+conflictor's
+confx
+connectionroom
+connectlogstr
+connectstr
+connstr
+conscli
+consequenses
+contentlen
+contentstr
+contenttype
+cookiesoption
+coproc
+copyable
+copyfile
+correctpackets
+cors
+Covcbz
+cparts
+cpid
+cplusplus
+cpng
+cpnmu
+cpnmuog
+cpnmuoj
+cpos
+CPPFLAGS
+cptr
+cpuchart
+cpugraph
+CPUIO
+cpuload
+cpuset
+cpuy
+Cqg
+CQq
+crbegin
+createdomainentry
+createzones
+creativecommons
+cred
+crend
+Crhs
+crr
+crt
+cruft
+CSAx
+Csemi
+Cserver
+cset
+csignal
+Csmtp
+cso
+cspiter
+cspmap
+csr
+cssfunction
+Cstart
+cstat
+cstddef
+cstdint
+cstdio
+cstdlib
+cstr
+cstring
+csu
+CSVE
+ctag
+ctive
+ctl
+ctlun
+Ctoroot
+ctxarg
+ctype
+CUDJFRFI
+cumul
+cumulstats
+Cunauth
+CURLE
+CURLINFO
+CURLOPT
+CURLPROTO
+curtime
+CVbx
+Cw
+Cwithin
+CWORD
+Cwww
+Cxk
+cxx
+CZENW
+daemonize
+DAFB
+dasharray
+databuffer
+datafmt
+datalen
+datapos
+dataret
+datestr
+datetime
+DBC
+dbd
+dbddb
+dbdnssec
+dbdnsseckeeper
+dbg
+DBHOST
+dbi
+DBIf
+dbkey
+dbkeyset
+dblacka
+dbmode
+Dbolui
+DBp
+DBPASS
+dbpath
+DBPORT
+dbrec
+dbrg
+DBSERVER
+DBT
+DBUSER
+DCBE
+dcec
+Dcg
+DCHVORt
+Dcity
+dcke
+DCMAKE
+Dcode
+Ddata
+ddb
+DDct
+Ddcy
+dddx
+ddeb
+ddepth
+DDWN
+deadbeef
+deadc
+debbuild
+debconf
+DEBEMAIL
+DEBFULLNAME
+debhelper
+debtest
+decafsigners
+decerement
+declarearguments
+declarestats
+decltype
+deconfigure
+deconfigured's
+deduplicate
+deepcopy
+defaultmap
+defaultport
+defaultttl
+defaultvalue
+defport
+defstr
+deinit
+delaypipe
+delcount
+delcounter
+deleteme
+deletetsigkey
+delnonterm
+depcomp
+DEPRECATEDLIST
+deps
+deque
+derp
+descr
+descrip
+deserialize
+destaddr
+destdir
+devent
+deviceid
+devnull
+devpoll
+devpollfd
+devpollmplexer
+devscripts
+dfe
+DFL
+dfsdfsdf
+dgn
+dgram
+DGUX
+DHE
+Dhh
+DHTML
+Dhydb
+dietlibc
+difflib
+diffopts
+difft
+diffto
+digesttype
+dimissing
+dimitri
+directbackendcmd
+DIRECTORYTOK
+dirent
+dirfile
+dirhdl
+dirname
+dirp
+dirs
+dirstamp
+disabledvialua
+diskspace
+distdir
+disthashseed
+distributo
+DIXl
+DIy
+djlr
+DJYNFFq
+dkc
+dke
+dkrc
+DLCC
+DLD
+DLDAP
+dlen
+dlfcn
+dlib
+DLOCALSTATEDIR
+DLOG
+dlopen
+DLQ
+DMd
+DMEd
+dmi
+dmp
+dmq
+DNAM
+DNAMETo
+DNb
+dndist
+dnf
+dni
+dnl
+dnmatch
+DNODCACHEDIR
+DNRSx
+dnsbackend
+dnsclass
+DNSDISTBIN
+dnsdistcache
+dnsdistclient
+dnsdistcmd
+dnsdistdynblocks
+dnsdistkvs
+dnsdistlbpolicies
+dnsdistluarules
+dnsdistpacketcache
+dnsdistrings
+dnsdistrules
+dnsdisttests
+dnserrors
+DNSID
+DNSIP
+dnskeyr
+dnslabeltext
+dnslen
+dnsmasq
+dnsmaster
+dnsnameqtyperings
+dnsnameraw
+dnsp
+dnsperf
+dnspkt
+DNSPORT
+dnsproxy
+dnspython
+dnsq
+dnsrecordcontent
+dnsrecordheader
+DNSRR
+dnsrule
+dnsscript
+DNSSE
+dnssecdb
+dnssecinfra
+dnsseckeeper
+dnssecmode
+dnssecok
+DNSSECOn
+dnssecsigner
+dnsstats
+dnsstrings
+dnstcpb
+dnstext
+dnstype
+dnsutils
+dnswriter
+DNSZR
+docbits
+docblock
+DOCD
+docdir
+Dockerfile
+docstring
+DOCTYPE
+doent
+doesnotmatter
+DOHFFI
+dohlocals
+dohquerypair
+dohresponsepair
+DOIx
+dokill
+dolog
+domaindetails
+domainid
+domainidindex
+domainidmetaindex
+domainmap
+domainmetaidindex
+domainmetanameindex
+domainnameindex
+domainsdone
+domcount
+dononterm
+dontallow
+dontdrop
+dontinclude
+dontqueries
+DONTWAIT
+doquery
+dosec
+dotests
+dotfile
+downcase
+Doxyfile
+doxygen
+doxyrules
+DPGZA
+dpk
+dpkg
+DPKGLIBDIR
+dpm
+dpos
+dprefix
+dptr
+dpw
+dqcount
+dqffi
+drc
+drh
+drillchase
+DRmcx
+dro
+Dropbox
+dropdb
+dropset
+dropwhencached
+drr
+drs
+drsoa
+dsa
+DSANSEC
+dsc
+dsdelegation
+dsdigestalgorithm
+dses
+DSfor
+dskey
+dsmap
+dsn
+dspk
+dsrc
+dsrec
+dss
+dsset
+dstates
+dstportrule
+DSYSCONFDIR
+dtime
+dtn
+dtor
+dtv
+dtxn
+DUPSORT
+DUv
+dval
+DVGN
+dvi
+DVIUi
+dvp
+dvpoll
+dw
+Dwaoc
+DWITH
+DXagbsuz
+dylib
+DYNLINKFLAGS
+dynlistener
+dynloader
+dynmessenger
+dynmetrics
+dynmetricslock
+dzr
+eaaecdabe
+eab
+EABy
+eacdd
+EADDRINUSE
+EADDRNOTAVAIL
+EAFNOSUPPORT
+ebda
+EBERb
+Ebgy
+ebpfblock
+EBRACE
+ebuf
+EBUSY
+EByvht
+ECCGOST
+ecdh
+ECDHE
+ecdsap
+ecf
+ecgroup
+eckey
+ecount
+ECP
+ecparam
+ECSBy
+ecscachelimitttl
+ECSIn
+ECSIP
+ecsipv
+ecso
+ecsqueries
+ecsresponses
+ecsrules
+ECSTo
+ECSTTL
+ectx
+eddsa
+EDEADLK
+editttl
+edkey
+edn
+ednscookies
+ednsdomains
+EDNSECS
+EDNSFFI
+ednsflag
+EDNSIGNORANT
+ednsip
+ednslocalsubnets
+ednsmap
+ednsmask
+ednsnm
+EDNSNo
+EDNSOK
+ednsoptcode
+ednsoption
+ednsoptionrule
+EDNSR
+ednsrcode
+ednsremotesubnets
+EDNSRR
+ednsstatus
+EDNSUDP
+ednsversion
+ednsversionrule
+EDNSZ
+edo
+EDR
+EDSIGNORANT
+eecfe
+Eelapsed
+eevent
+EEXIST
+EEy
+efbd
+EFGH
+efmq
+EFw
+egrep
+EHADv
+Ei
+eid
+EILq
+EINPROGRESS
+EINVAL
+Eips
+EISCONN
+eiter
+Ejf
+ejkmcpqxot
+Ekfq
+Eky
+elabel
+elems
+elif
+Elr
+elseif
+elsif
+emailbx
+emap
+EMD
+EMERG
+EMFILE
+enableval
+Enames
+encaps
+endcode
+endianness
+endnode
+endpos
+endptr
+endswith
+ENETUNREACH
+Enj
+ENOBUFS
+ENODEV
+ENOSPC
+ENOSYS
+Enx
+enzo
+eol
+EOu
+EPIPE
+epita
+eply
+epollfd
+EPOLLIN
+epollmplexer
+EPOLLOUT
+eptr
+EPVHU
+eqd
+eqdnsmessage
+equivs
+Eqy
+Erc
+erca
+ercode
+ercursor
+ERKSs
+errcode
+errfds
+errlen
+errmsg
+Erroring
+errorresponses
+errorresult
+errorstring
+errstr
+esac
+escapedtext
+escdecb
+eso
+esow
+ESRCH
+esyscmd
+Etcu
+ETEQw
+ETIME
+ETIMEDOUT
+Eto
+etree
+etry
+eturn
+eui
+evah
+eventkey
+everytime
+EVFILT
+Evi
+evilapikey
+evilsecret
+evloop
+evp
+ewma
+EWOULDBLOCK
+EWPk
+Ewr
+examplenet
+exe
+execinfo
+execv
+execvp
+EXEEXT
+EXITCODE
+exitvalue
+exky
+Expat
+expectedlen
+expf
+expr
+expungebyname
+expungebynameandtype
+expungebynameother
+extdir
+extfile
+extracontext
+EXTRAOPTS
+extrasmn
+extrcode
+exu
+Eyd
+EZ
+EZg
+fabs
+FAFAFA
+FAHr
+FAILONERROR
+fakeroot
+faketime
+fallthrough
+FASTOPEN
+FBB
+Fbo
+fbr
+Fbx
+fclose
+fcnt
+fcntl
+fctx
+fdb
+fdf
+fdm
+fdmultiplexer
+fdset
+feb
+fernando
+fetchall
+FFEE
+ffilb
+ffipolicyfunc
+fflush
+fgets
+fh
+Fibl
+fieldname
+filebasename
+filenam
+fileno
+Filenum
+filestate
+filesystem
+FILETOK
+FILETYPE
+filtermap
+filtername
+filterpo
+findall
+findinit
+findnext
+FIRSTHDR
+firstquestiontime
+fixme
+fixperms
+FJHL
+Fk
+FKFy
+flawednsset
+FLHY
+FLJ
+flowinfo
+FLrjot
+flt
+flushname
+FLYU
+Fmajor
+fmod
+fmri
+fmter
+fnamearg
+fnd
+fndhemi
+FNHYc
+fno
+fns
+fnum
+followedbyanother
+FOLLOWLOCATION
+foob
+fooba
+fopen
+forcesafesearch
+formask
+formerr
+foundct
+fournosoa
+fournosoainfirst
+foursecondsoainsecond
+fpacket
+Fpb
+fpc
+Fpk
+fprintf
+fptype
+fq
+fqdn
+Fqgo
+FQk
+fread
+freeaddrinfo
+FREEBIND
+fri
+fromaddr
+fromlen
+fromport
+fromportstr
+fromserial
+fromstdin
+fromtimestamp
+fromtosockets
+fromvalue
+frontsbase
+fsanitize
+fsck
+FSDB
+fsf
+fsl
+fslu
+fslutest
+fstream
+fstrmlogger
+fsync
+ftf
+ftp
+FTXp
+funarg
+funcdb
+funcparam
+funcstats
+funkdb
+funkwithusage
+funnytext
+fuzzer
+FVM
+Fvn
+FVr
+FVrip
+Fvuj
+FWG
+fwopt
+fwparams
+fwrite
+fx
+FXc
+fxl
+Fxpgs
+FYd
+Fym
+Fyq
+gai
+gaierror
+gamesgiroll
+garblewarble
+gatewaytype
+gatherwildcard
+gbv
+Gcached
+GCCPACKATTRIBUTE
+gcda
+GClj
+gcno
+gcount
+Gcountry
+gcov
+gdata
+gdate
+gdb
+gecos
+Gemfile
+gencontrol
+GENERALIZEDTIME
+GENERR
+genhook
+genlog
+geoipdatabase
+geoipdosec
+geoipgraphic
+geoipinterface
+geoipkeydir
+geoiploader
+geoiplookup
+geoiprecord
+geoipregion
+geoipregionip
+geoname
+geosec
+getaftername
+getalldomains
+getatomics
+getattr
+getbeforeandafternames
+getbeforename
+getchar
+getcommonlabels
+getcontext
+getcursor
+getcwd
+getdiagrec
+getdn
+getegid
+getent
+getenv
+geteuid
+GETFL
+getgrnam
+gethostbyname
+getinfo
+getlastlabel
+getline
+getlock
+getnameinfo
+GETNEXT
+getopt
+getpagesize
+getpass
+getpeername
+getpid
+GETPIPE
+getpwnam
+getpwuid
+getqueries
+GETQUESTION
+getrawlabel
+getrlimit
+getroot
+getrusage
+getserial
+getsocket
+getsockname
+getsockopt
+getstack
+getstdout
+getupdatedmasters
+getvalue
+getvars
+gf
+GFi
+ghostscript
+gir
+gitlab
+GIZI
+GJPRf
+GK
+GLIBCXX
+globalconf
+globfree
+Glq
+glueless
+Glxb
+gmd
+gmt
+GMTOFF
+GMYSQLDB
+GMYSQLHOST
+gmysqlloader
+GMYSQLPASSWD
+GMYSQLUSER
+Gnomovision
+gnuc
+gnupg
+GOceania
+godbcloader
+godns
+GOMRf
+googledomains
+gost
+gotdomaindetails
+gotipdetails
+gotit
+gotoline
+gotsome
+GPflpm
+gpg
+GPGSQ
+GPGSQLDB
+gpgsqlloader
+GPGSQLUSER
+gpl
+GPLv
+GPOS
+GPRINT
+Gqhx
+Gqxdqt
+grabkeys
+GRAPHOPTS
+Graphviz
+greg
+grok
+groupadd
+groupinstall
+grp
+gruleactions
+gsort
+gsqlbackend
+gsqlitebackend
+gssctx
+gtar
+gtm
+GTNi
+GTXTk
+gtzero
+guido
+GVs
+GWa
+GWTy
+Gwy
+Gx
+GXX
+GYK
+GZha
+haas
+habbie
+halen
+HAPB
+hardlimit
+Hartvigsen
+hasattr
+hashedidx
+hashindex
+hashperturb
+hatype
+havedollarttl
+haveednssection
+haveednssubnet
+HAVENSEC
+havetsig
+HBB
+Hc
+HCID
+HCIEg
+HCNUM
+HCo
+hcode
+HCPD
+hctx
+Hcu
+hdr
+Hdv
+headerfmt
+HEADERFUNCTION
+headersize
+headl
+headr
+healththread
+helpmap
+helpstring
+herokuapp
+Heu
+hexlify
+hextodec
+HFtab
+hhc
+hhp
+hhx
+hiddencryptokeys
+hideinitializer
+hidesoaserial
+hightxt
+hightype
+HIHEe
+hijackme
+hintfile
+Hiso
+histfile
+histo
+histog
+HJpbmcg
+hk
+HKm
+hkw
+hlapi
+Hlatitudeh
+HLEN
+Hlll
+Hlocation
+hmacsha
+hmech
+Hmisix
+HMrc
+HMukilteo
+Hn
+HNk
+holelock
+homedir
+horiz
+horizpre
+hostconf
+hostlist
+hostmastercom
+hoststr
+Hpj
+hppa
+hqp
+HResearch
+hsa
+HSIZE
+HSmu
+hsould
+HSw
+htmlfiles
+htobe
+htole
+htonl
+htons
+htons'ed
+httpconnector
+httpconnects
+httpd
+HTTPHEADER
+httpversion
+HUr
+HVeu
+Hvw
+hwinfo
+Hx
+hxx
+HYrl
+HZFV
+HZXIZh
+iarchive
+IAustralia
+Iav
+ibfk
+IBMR
+IBRw
+ICANON
+ICASE
+ICNOKr
+icontent
+Icontinent
+idindex
+idl
+idmanager
+IDOID
+idonotexist
+IDont
+idpool
+idqueue
+Idret
+idserver
+idstate
+iends
+iequals
+IEUW
+iface
+ifarch
+ifeq
+ifindex
+ifl
+ifndef
+IFSOCK
+ifstream
+ifupport
+ifupurl
+ifw
+Iga
+ignorebogus
+igoy
+IH
+IHOST
+Ihw
+IImrg
+iinfo
+Iinputs
+iix
+IIY
+IJ
+Ilanguages
+ilexicographical
+ILggb
+Ilongitudeh
+imap
+IMEI
+Imiw
+impl
+inbytes
+Incd
+inceptionday
+incfiles
+includedir
+indexfunction
+inescape
+inext
+infd
+inflighter
+infodir
+infostream
+initctl
+initgroups
+initialrequestid
+initialrequestidstr
+initparams
+initrddir
+initval
+inkey
+inl
+inlined
+ino
+inp
+inprogress
+inputkey
+inputlen
+inputname
+inqueries
+insn
+insnonterm
+insserv
+installdeb
+installdox
+installexamples
+installinit
+INTERNETTRAFFIC
+intervalcount
+intptr
+intransaction
+ints
+inttypes
+intxn
+ioctl
+iomanip
+ioqueue
+iostate
+iothr
+iothropt
+iov
+iovec
+iovlen
+IOz
+ipaddress
+ipairs
+ipcipheripcipher
+IPDNS
+ipfromstr
+IPg
+iphdr
+ipi
+iplist
+ipmap
+ipo
+ipp
+ipparts
+ipport
+IPPROTO
+ipsum
+IPTo
+iptostr
+ipunitlist
+iqmp
+Iqw
+irz
+isa
+isalnum
+isalpha
+isatty
+isbase
+isdigit
+ISDIR
+isfile
+isinstance
+islandofsecurity
+isleap
+ISREG
+Isrqzjh
+isru
+isrunning
+iss
+isset
+isspace
+istream
+istreambuf
+istringstream
+isxdigit
+iteritems
+itf
+Itg
+ITIMER
+itimerval
+itmp
+Itni
+itoa
+Iu
+IUIu
+IUt
+ival
+Iwlx
+IWN
+IWR
+ixes
+IXF
+ixfrdiff
+IXFRDISTBIN
+ixfrdistcmd
+ixfrdistdomain
+ixfrdisttests
+ixfrinfo
+ixfrutils
+ixx
+iy
+Izd
+IZws
+JAmk
+jan
+JAVADOC
+Jb
+JBnu
+Jcj
+jcong
+JCUw
+jdnssec
+jdthood
+jelu
+JEQ
+JFo
+Jgeoname
+JGKj
+JGT
+Jhb
+Jhdz
+JHm
+Jhpj
+Jip
+Jiy
+Jjbq
+jlist
+JLQ
+jm
+Jmdll
+Jmj
+Jn
+JNE
+Jnode
+JNTJMMHZDO
+journalmode
+jpg
+JQTNLZDBh
+jquery
+jre
+JSGT
+jsonp
+JSONTSIG
+jsr
+JTf
+Jtv
+Juhf
+jul
+Jungermann
+Jwmtfu
+JWndz
+Jx
+JZ
+JZIA
+JZte
+kamago
+kauq
+kbcafe
+KBo
+Kbuild
+kce
+kdb
+kdbarg
+Kdescription
+Kdismbe
+keepnocd
+keeprd
+keeprecurse
+kevent
+keyalgorithm
+KEYBYTES
+keycache
+keycachelock
+keydata
+keyid
+keylen
+keylog
+keymeta
+keymetadb
+keyout
+keypos
+keyring
+keyserver
+keystr
+keystring
+keytag
+keyv
+Kfq
+Kgvf
+kh
+Khk
+killall
+killproc
+Kj
+KJPKEd
+kk
+Kkfp
+KKl
+KLa
+klen
+KMBT
+kmd
+KMP
+Knj
+Kok
+Kosnik
+KOw
+kq
+kqevent
+KQF
+Kqim
+kqueuefd
+kqueuemplexer
+Krecord
+KRIEGER
+Ksj
+kskds
+kskeys
+KStream
+Ksy
+kvo
+kvresp
+kwargs
+KWw
+kx
+KXu
+kz
+KZR
+labellen
+labelparts
+Labelreverse
+labelscount
+labelscountadvanced
+labeltok
+laddr
+LADIP
+largeanswerremotes
+largernumberofconnections
+largettl
+lastanswer
+lastclean
+lastcounts
+lastget
+lastline
+lastmod
+lastperc
+lastpos
+lastquestion
+lastreport
+lastsec
+lastsum
+lastval
+latdeg
+latdiff
+latexmk
+lathem
+latlonstrptr
+latmin
+latsec
+Launcheable
+lauxlib
+layj
+lber
+lbpolicies
+lcc
+lci
+Lcode
+lcontent
+lcrypto
+lczonename
+LDADD
+ldapadd
+ldapauthenticator
+LDAPBASEDN
+ldapbinddn
+ldapdelete
+LDAPHOST
+ldaploader
+ldapmodify
+LDAPNo
+LDAPPASSWD
+LDAPRELATTR
+ldapsecret
+ldapuris
+LDAPUSER
+ldaputils
+LDAPv
+LDB
+ldd
+ldnsutils
+leftcolumn
+leftiter
+lels
+LEMLD
+lenpos
+lentry
+lflag
+LGCe
+LGeo
+lgnutls
+LGPL
+LHav
+lhead
+LHq
+lhs
+Lhwzi
+LIBADD
+LIBASAN
+libauthbind
+libbindbackend
+libboost
+libbpf
+libcap
+libcdb
+libdl
+libexecdir
+libfaketime
+libgen
+libgeoipbackend
+libgmysqlbackend
+libgnutls
+libgodbcbackend
+libgpgsqlbackend
+libgsqlite
+libipcrypt
+libjson
+libkern
+libkrb
+libldap
+libldapbackend
+liblmdb
+liblmdbbackend
+liblua
+libluajit
+libnacl
+libnet
+libopie
+libp
+libpipebackend
+libprobds
+libprotobuf
+librandombackend
+libremotebackend
+libsnmp
+libsqlite
+libsqliteodbc
+libtestremotebackend
+libtinydnsbackend
+libtolua
+libtool
+libunbound
+libwslay
+libyahttp
+licensedir
+lightuserdata
+lineno
+linespoints
+lineterm
+linktype
+LISi
+listenaddress
+listerner
+listname
+listset
+listx
+Lj
+lk
+lkjhgf
+lld
+LLL
+lltemp
+llu
+LMDBQ
+Lmg
+lnc
+loaderdecaf
+loadersodium
+loadmodule
+locala
+localaddress
+localaddresses
+localbind
+localname
+localsock
+localstatedir
+loctext
+locwild
+lodbc
+logaction
+LOGGINGTOK
+Loginfo
+logline
+logprefix
+logscale
+logstream
+lol
+londeg
+londiff
+longenough
+longindex
+LONGLONG
+longopts
+LONGTEXT
+longttl
+lonhem
+lonmin
+lonsec
+Lookaside
+lookedup
+lookupdomain
+loopcount
+Lorem
+lowercasequery
+lowercasing
+Lpat
+LPi
+lptr
+lpz
+Lq
+Lqcha
+Lqm
+Lqr
+lrde
+lru
+LSA
+lsb
+LSH
+Lsl
+lssl
+lstr
+LString
+Lsubdivisions
+LSzyzd
+LTest
+LTGp
+lthread
+LTLIBRARIES
+ltmain
+ltoptions
+ltsugar
+Ltuxc
+ltversion
+LTvniq
+luacall
+luaconf
+luaconfpath
+luadnsrule
+luaffi
+luaffiactionfunction
+luaffiactionsettag
+luaffirulefunction
+luahooks
+LUAJITPC
+lualib
+luamaintenance
+luamutex
+LUAPC
+luarulefunction
+luaruleparams
+luascript
+luascriptpath
+luasmn
+luaspoof
+luaspoofwithstats
+luatarget
+lw
+lwn
+lwr
+lwres
+lwslay
+LZrd
+lzrp
+LZz
+MACBYTES
+maclen
+madname
+mailinglist
+mailserver
+mainfilter
+mainloop
+mainpage
+mainthread
+maj
+makebackend
+makecontext
+makeindex
+makemetadataonly
+MAKEOPTS
+makerfunc
+MAKEVAR
+mallinfo
+Mallocated
+malloctrace
+malwareset
+manpath
+marvin
+maskl
+maskr
+mastercommunicator
+mastermake
+masterplan
+masterschanged
+MASTERTOK
+matchkey
+matchlen
+matthijs
+maxanswersize
+maxbodysize
+maxbogusttl
+maxcachesize
+maxcachettl
+maxchunkrecords
+maxcleaninterval
+maxconnsperclient
+maxcp
+maxent
+maxevents
+maxfd
+MAXHOSTNAMELEN
+maxminddb
+maxnegttl
+maxnsaddressqperq
+maxobjects
+maxqperq
+maxqueriesperconn
+maxreplylen
+maxrss
+maxthreads
+maxtid
+maxtotusec
+mbox
+MBZ
+mcgrof
+mch
+mcontext
+mcp
+Mcu
+Mdatabase
+mday
+mdb
+MDBIn
+MDBRO
+MDBRW
+mdctx
+mde
+mdiff
+mdname
+mdp
+MDQx
+MDSV
+mediumsizedlabel
+MEMB
+Memcheck
+memchr
+memcmp
+memcpy
+memfree
+memis
+memmove
+memset
+memzero
+MERCHANTAPILITY
+mesg
+mesgsize
+messageid
+messageidstr
+metacache
+metacachelock
+metadatadn
+metamap
+metavar
+MEY
+Mfc
+mflags
+mgmname
+Mgo
+MHd
+mic
+michel
+microsoft
+might've
+migweb
+mincleaninterval
+minicurl
+MINIMIZATON
+mintime
+minttl
+MIPSEB
+mipsel
+mistmatching
+Mixin
+Miy
+Mj
+Mjgw
+mkbindist
+mkdir
+mkinstalldirs
+mkpubsuffixcc
+mkquery
+mkstemp
+mktemp
+mktime
+mlen
+Mlj
+Mlkroefk
+mlock
+mman
+Mmax
+mmc
+mmsghdr
+Mngwcrq
+MNorth
+moadnsparser
+moby
+moduledirs
+modulelibs
+moduleobjects
+mohta
+moreutils
+moz
+MPkb
+Mpl
+Mps
+MPSC
+mq
+mqalatency
+Mqas
+MQlel
+Mqoh
+MRUBY
+MRw
+msdn
+msecmatch
+mseconds
+msgfree
+msgh
+msghdr
+msgid
+msocket
+mstr
+MSVC
+msysmsec
+MTASKERTIMING
+MTest
+mtime
+mtracer
+mul
+multialgo
+MULTIARCH
+multimaint
+multimap
+multiplecookies
+Multiplexermap
+multispoof
+multitext
+munlock
+musermsec
+mustlog
+mutexes
+mval
+MWJu
+MXB
+mxname
+mxtics
+MXZQm
+mydata
+mydb
+myemailhere
+myfile
+myinitlock
+MYMETA
+mynewkey
+mypool
+myproject
+mysecretauthkey
+mysecretenckey
+mysqladmin
+mysqldiff
+mytag
+mytics
+Mzlw
+NAGLE
+namealgoindex
+namebuf
+namecount
+namedconf
+namedfile
+namehash
+nameindex
+namelen
+namemap
+namenum
+namepositions
+Nameret
+nameser
+nameservername
+nameservice
+namesseen
+namestocheck
+nametoindex
+nametype
+namq
+nanosleep
+nargs
+narrowbool
+nbb
+nce
+NCx
+ndays
+NDEBUG
+NDELAY
+ndiff
+NDIy
+NDUx
+needcdb
+needldap
+needlmdb
+needres
+needsqlite
+Negativerustanchor
+negativetrustanchorserver
+negativettl
+negcached
+negcacheentries
+negindic
+NERUUDNz
+netdb
+netflix
+Netscape
+netstat
+netw
+newalgo
+newargv
+newauth
+newconnectioncb
+newdata
+newdh
+newdomains
+newfd
+newgid
+newid
+newkey
+newlate
+newlen
+newlyobserveddomain
+newmap
+newnames
+newnod
+newparams
+newpath
+newpos
+newqtype
+newquery
+newrecord
+newrrs
+newruleaction
+newserial
+newserver
+newsize
+newsock
+newsr
+newstat
+newtarget
+newttl
+newuid
+newval
+nextclean
+nextcloser
+nexthash
+nexthdr
+nextid
+nextpart
+nextupd
+NEZU
+nfc
+nfds
+NFLSd
+NGtr
+nh
+NHo
+nif
+nint
+Njd
+NJKw
+nju
+nk
+NKm
+nlaunch
+Nld
+nlen
+nline
+NLips
+nlnen
+nlnetlabs
+nmasters
+nmemb
+nmgrule
+nmin
+Nml
+nmmatch
+nmp
+nmpair
+nmt
+NNl
+NOACK
+noaddrs
+noalias
+noarch
+noaxfr
+nobackend
+NOBLOCK
+noc
+nocachevialua
+NOCASE
+NOCERTS
+nocheck
+nocn
+nocontext
+nocontinue
+nocreate
+nodcachedir
+noddb
+noddbp
+nodelegated
+nodist
+nodnssec
+NODNSTAPTESTS
+nodot
+nodownstream
+NODUPDATA
+nodyndns
+noecs
+noent
+noerroranswers
+noerrorcount
+noexcept
+NOEXIST
+nof
+nogpgcheck
+NOi
+noinline
+noinput
+noinst
+nologin
+nolua
+nometa
+Nominum
+nonarrow
+NONBLOCK
+NONCEBYTES
+noncnames
+noncopyable
+nonetheripudp
+noneup
+NONINFRINGEMENT
+nonleap
+nonsec
+nop
+nopacketcache
+noparse
+NOQUOTES
+nora
+norbert
+nord
+norecurse
+norecursionavailable
+noreplace
+noreply
+noreturn
+norrsig
+noserver
+noservfailstats
+nosetests
+nostale
+NOSUB
+NOSUCHOBJECT
+nosuchpool
+notallowed
+notdisabled
+NOTFOUND
+notimp
+notimpl
+notincludedir
+notoverridden
+notoverriddenprefixlength
+notpool
+notpowerdns
+NOTRUNNING
+notsetecsaction
+notwcard
+notyouroffset
+nounput
+nowait
+nowildcard
+NOWPLUSTEN
+nowrap
+noyy
+nparams
+npos
+NPxzx
+nq
+nqk
+nqueue
+nrc
+nread
+nrecords
+nrep
+Nro
+nrr
+NRus
+nsa
+nscount
+nsdname
+nsdomain
+nseconds
+nsecrecords
+NSECs
+nsecx
+nsecxrepo
+NSIDTCP
+NSIDUDP
+nsip
+nsiploop
+nsock
+nsone
+nsq
+nsrr
+nss
+nsspeed
+nsspeedsentries
+nst
+nstld
+ntk
+ntohl
+ntohs
+ntop
+nts
+Ntx
+numanswers
+numcores
+numdomains
+numentries
+NUMERICHOST
+numerrors
+numevents
+NUMFAILED
+numloops
+numread
+numreceiveddo
+numrecords
+numsigs
+numsocks
+NUMTESTS
+numthreads
+numwarnings
+numworkers
+Nury
+NUx
+NUxt
+Nvd
+nvect
+NVQ
+nw
+nwaiters
+NWSIW
+Nwv
+Nxbulf
+NXCFg
+nxdo
+NXDOMAI
+nxdomainanswers
+nxdomainme
+nxdomainsuffix
+NXNSECS
+NXQ
+nxqtype
+NXRR
+nxrrset
+nxt
+NXTHDR
+nxwithnorr
+Nyeq
+nz
+nzg
+nztest
+oa
+Oaccuracy
+OAfx
+oarchive
+obf
+OBJECTFILES
+OBJECTLIBS
+OBJECTPROPERTY
+OBJEXT
+OBRACE
+Obsu
+OCc
+ocn
+ocontent
+octetsin
+octetsout
+octx
+ODAx
+ODESC
+ODIy
+odl
+odo
+ODQ
+OEVLOOP
+Oew
+ofc
+ofconf
+Ofdvp
+OFFMASK
+offsetof
+ofile
+OFJJXk
+OFkp
+ofstream
+ofzone
+OGhh
+OGlnb
+OGR
+ohn
+oi
+oid
+OJ
+oknodo
+OKPERCENTAGE
+OKPERCENTAGEINT
+oks
+olc
+oldmode
+oldmsg
+oldnames
+oldrr
+oldsr
+oldt
+olen
+OMG's
+omoerbeek
+OMPQK
+Omqq
+oneline
+onenosoa
+oneshot
+onexit
+ONXJQQ
+Oo
+oob
+oobar
+OOO
+OOOTCP
+oopts
+oor
+Ooutputs
+oowerdns
+opacket
+opcodenotify
+opcodeupdate
+OPEI
+opendir
+opendns
+openf
+openldap
+openlog
+opensource
+openssllocks
+opensslsigners
+opensslv
+operat
+opie
+OPj
+opname
+opos
+optarg
+OPTIn
+optind
+OPTIONSTOK
+optname
+optret
+optsize
+optstring
+optvect
+oq
+oqtaen
+orderindex
+origanswers
+origbetter
+origbetterset
+origctx
+origdnserrors
+origfd
+origid
+originalrequestorsubnet
+originalttl
+origlate
+orignever
+origtimedout
+origunmatched
+osixia
+oss
+ostr
+ostream
+osubgrouping
+OTAy
+ote
+otherdatalen
+otherprovider
+otherrcode
+othersize
+othertag
+Otkjt
+otype
+OUh
+OUHh
+ourcount
+ourdomain
+ourhelpfulservice
+ournum
+ourpos
+oursr
+ourttl
+outerpost
+outfd
+outfile
+outgoingtimeouts
+outlen
+outofzone
+outsigned
+outsock
+overriddenprefixlength
+overriddenprefixlengthvialua
+overriddenvialua
+OWJ
+OXd
+OYna
+ozz
+packetcacheentries
+packetcachehits
+packetcachemisses
+packetcacheservfailttl
+packetcachettl
+packetcaching
+packetheader
+packetperc
+packetsize
+packetwriter
+padawan
+Paf
+pagefaults
+pak
+panix
+paramcount
+PARAMDOC
+paridx
+parm
+parnum
+parsecheck
+parsefail
+parsertest
+passhtru
+passlen
+passwd
+pathbuf
+pathc
+pathconf
+pathv
+patsubst
+PBDNS
+pbegin
+PBh
+Pbi
+PBKDF
+PBpq
+pbtag
+PCas
+PCDNSSEC
+PCKS
+PCmissing
+pcomp
+pcount
+PCPU
+pctx
+pddkcrxy
+pddrw
+pdflatex
+Pdk
+pdnsbackendmysql
+pdnsbackendpgsql
+pdnsconf
+pdnsdbi
+PDNSDEBUG
+pdnsdist
+pdnsdomains
+pdnsexception
+pdnsfeatures
+pdnsinfo
+pdnsload
+PDNSPB
+PDNSRECCONTROL
+PDNSRECURSOR
+PDNSSERVER
+pdnssocket
+pdp
+perc
+periodicall
+PERLMOD
+Perror
+pershard
+pertub
+pevents
+pex
+pfh
+PFm
+pfs
+pfsbox
+PGconn
+PGHOST
+PGPASSWORD
+pgpsigurlmangle
+PGr
+PGRES
+PGresult
+PGUe
+Pgv
+PGw
+pheader
+phitrate
+PICOTLS
+pident
+pidfname
+pipeconnector
+pipefail
+pipefd
+pipefunc
+pipeloader
+PIPESTATUS
+piter
+PKCKQAu
+pkey
+pkgconfig
+pkglib
+pkglibdir
+pkgname
+PKgogeu
+pkgv
+pkill
+pkthdr
+pktinfo
+pktlen
+pkttype
+pldap
+pleasequeryfunc
+pleasequit
+pleaseremotefunc
+pleft
+plen
+plenus
+pline
+plugin
+plumgrid
+pmap
+PMQ
+PMTU
+PMTUDISC
+Pn
+pname
+PNPr
+policyfunc
+policystr
+policytag
+POLLERR
+pollfd
+POLLHUP
+pollin
+pollitem
+POLLOUT
+POLLREMOVE
+polmap
+poolaction
+popen
+popisort
+portev
+portfd
+portsmplexer
+posbegin
+posend
+posix
+postdata
+POSTFIELDS
+POSTFIELDSIZE
+postoutquery
+postpol
+postprepare
+postqueries
+postun
+postvars
+potentialsupermasters
+powerdn
+poweroften
+pparent
+pparts
+ppid
+pply
+pprint
+PQclear
+PQconnectdb
+PQerror
+PQescape
+PQexec
+PQfinish
+PQfreemem
+PQftype
+PQgetisnull
+PQgetvalue
+PQnfields
+PQntuples
+PQreset
+PQresult
+PQsocket
+PQstatus
+prc
+PRecord
+precsize
+PREDEF
+preg
+PRELOAD
+preout
+preparse
+prereq
+presignatures
+presignedcontext
+preun
+prevprev
+prevqname
+prfds
+primev
+printargs
+printf
+printlogs
+printtable
+printvars
+PRIu
+privatedns
+privateoid
+proba
+probds
+probs
+processname
+progname
+PROGRAMLISTING
+programname
+progrm
+progsarray
+promtool
+propol
+PROT
+protbuf
+protoc
+protostr
+proxyheader
+proxymagic
+PROXYMAGICLEN
+PROXYPROTOCOLHEADER
+PRsm
+prv
+psbf
+pseudonymized
+PSEUDOSECTION
+Pstmt
+psy
+ptcp
+Pthv
+pton
+pubkey
+publabel
+publicdomain
+publickey
+PUBLICKEYBYTES
+publicsuffix
+pubsuffix
+pubsuffixloader
+puk
+pullrequest
+pushlightuserdata
+pvars
+pvect
+pw
+pwd
+pwent
+pwgen
+pwtkey
+pyc
+pycache
+pycurl
+pysnmp
+PYTHONUNBUFFERED
+qaint
+qalatency
+qame
+QBD
+qc
+qcachehits
+qcachemisses
+Qccuox
+qckzu
+qclasschaos
+QClasses
+qclassin
+QCmissing
+qcount
+qcounter
+qd
+QDDt
+qdomainwild
+qdomainzone
+qesc
+qf
+qfonh
+QGy
+qhash
+qi
+qids
+Qj
+qk
+qkey
+qla
+qlass
+qlog
+Qlolbd
+Qlq
+QLs
+qmail
+qmin
+Qmsa
+qnamefilter
+qnamelen
+qnamemap
+qnameminfallbacksuccess
+qnameminimization
+qnamewirelength
+QOP
+qoutq
+qowerdns
+qpacket
+qpe
+qpol
+qpos
+qpschart
+qpsgraph
+qpslimit
+qpsnone
+qpspoolaction
+qpsstart
+qpsy
+QPTk
+QPv
+qq
+qrate
+qrateactionnxd
+qrateactionrefused
+qrateactiontruncated
+qraterefused
+qrset
+QSarv
+qsock
+qstats
+qstr
+qstring
+QSvh
+QSy
+QTag
+qtid
+qtnull
+qttl
+qtun
+qtypecounters
+qtypenums
+quantcast
+queryb
+queryfd
+queryring
+querystr
+querytimesec
+querytimeusec
+Queuedo
+queuelength
+queuetimeout
+qufnk
+Qug
+quotedname
+QUOTEDWORD
+quux
+qvalue
+qw
+QWN
+qx
+Qxh
+raddr
+raisd
+randomid
+rarg
+rattr
+RAv
+rawpacket
+rbegin
+RBu
+rca
+rcc
+rclass
+rcodecount
+rcodecounters
+rcontent
+rcounts
+rcp
+rcpgenerator
+rcv
+rcvbuf
+RCVTIMEO
+rdacounts
+rdataclass
+rdataset
+rdatastr
+rdatatype
+rdclass
+rdev
+Rdl
+rdlen
+RDLENGTH
+rdlock
+rdnonra
+rdnonrafs
+rdoc
+rdomains
+RDONLY
+rdqaplot
+rdqcounts
+rds
+rdtype
+RDWR
+readdir
+readlock
+readn
+realinput
+realname
+realnow
+realpath
+realqps
+realreferral
+realrr
+realzone
+Rebm
+RECCONTROL
+reccount
+receiveerrors
+recloc
+recordbuffer
+recordcomment
+recordcontent
+recorddata
+recordheader
+recordlen
+recordname
+recordorder
+recordplace
+recordscount
+recordstart
+recordstorage
+recordttl
+recpacketcache
+recparts
+RECRUN
+recsig
+rectrc
+recursorcache
+recursorcmd
+recursorconf
+recursorlog
+recursortests
+recvbytes
+recvcounter
+RECVDSTADDR
+recvec
+recverrors
+RECVPKTINFO
+recvtv
+reczones
+redirectresponses
+redistributors
+refcnt
+refcursor
+referals
+refferals
+refuseemptyar
+refusefouran
+refusenoan
+refusens
+refuseoptinar
+refusetwoar
+regcomp
+regexec
+regexp
+regexstr
+regfree
+reginfo
+regist
+regm
+regmatch
+reinit
+reldir
+relqname
+remdomains
+remlat
+remlen
+remlong
+remotedosec
+remotelen
+remoteloader
+remotelogger
+remotename
+remotering
+remotesec
+remotetype
+rentry
+reparse
+replen
+reqinfo
+reqs
+requestbuilder
+requestorid
+requestorstr
+requestvb
+requeue
+requeueing
+resanswers
+resetring
+residx
+resizering
+resnum
+reso
+resolvconf
+resolveret
+RESOLVERIP
+resourcelimits
+responsebyterate
+RESPONSEIP
+responsestats
+resprulactions
+respsize
+resquestions
+ress
+restfunc
+restoreflags
+resv
+retargetcount
+retargeted
+retkeyset
+retlen
+retline
+retq
+retre
+returncode
+retval
+reuseaddr
+revents
+revsets
+revzone
+revzonedata
+rfds
+rfind
+Rfv
+RFZJVWl
+rgacogne
+rgba
+rhandle
+rhs
+rhscount
+rhsoopts
+rhspos
+ri
+rightcolumn
+rightiter
+ringmeta
+ringname
+ringsize
+riter
+Rj
+Rjd
+Rjk
+rk
+Rkc
+RKEe
+Rkx
+rl
+rlen
+rlim
+rlimit
+RLNl
+rlocks
+rmailbx
+RMCD
+rmd
+rmdir
+Rminor
+rmtree
+RMz
+rnameservers
+rnow
+RNWc
+rollbackmarker
+romap
+RONLY
+rootdnsname
+roothints
+roothintspath
+rootkey
+rootnodot
+rootoid
+rootptr
+rootupdate
+rootzone
+ROqu
+rotxn
+roundtrip
+roundtripped
+routingtag
+rowid
+roystgnr
+Rpa
+rpacket
+rpc
+rpi
+rplookup
+rpmbuild
+rpmdev
+rpmdevtools
+rpmtest
+Rprj
+Rptim
+rpu
+RPZIXFR
+rpzloader
+RPZNSDNAME
+RPZNSIP
+RPZXFR
+rqclass
+rqname
+rqtype
+RQw
+RRA
+rrc
+rrclass
+rrget
+rrhs
+RRIn
+rrout
+rrscount
+rrsigds
+RRSIGIn
+rrsigkey
+rrsigncomp
+RRSIGTTL
+rrterm
+rrthrowaway
+RRto
+rrudr
+rrvalue
+rsakey
+rsamd
+rsautl
+rset
+RShr
+RSIG
+Rsjs
+rsock
+rstrip
+rtag
+rtf
+rthreads
+RTLD
+rtr
+rttl
+rtype
+ruben
+rubenkerkhof
+rubygems
+RUFM
+rulaction
+ruleparams
+ruleresult
+Rumu
+runcond
+runing
+runlevel
+runtests
+RUNWRAPPER
+rusage
+RUTQ
+rval
+RVARS
+RVe
+RVel
+RVM
+RVpn
+RVSBz
+rwl
+rwtxn
+rwxr
+Rxgrk
+RXXw
+RY
+rz
+rzrp
+SAccept
+saccount
+saddr
+safesearch
+salen
+saltlen
+sanitizerflags
+sanitizers
+sargs
+sasl
+savederrno
+sbf
+SBINARYPATH
+SBind
+sbindir
+Sbn
+sbuf
+scalarmult
+scert
+sched
+schemaversion
+scl
+SCombo
+SConnect
+scontrols
+SCRIPTNAME
+SCSV
+sdata
+sdfdhhgj
+sdfsdfs
+sdfsdfsfd
+SDIGBUFSIZE
+sdist
+sdns
+sdomains
+sdynamic
+searchclass
+SEARCHENGINE
+searchkey
+seckey
+secp
+secpollthread
+secretapikey
+secretbox
+secretcommunity
+SECRETKEYBYTES
+secretpassword
+secretuser
+secsfrac
+sectionname
+SECUREDOFFERS
+securezone
+SEEDBYTES
+seekg
+seenauthsoa
+selfanswered
+selfansweredresprulactions
+selfstat
+sendall
+sendfromto
+sendit
+sendlines
+sendmmsg
+sendout
+SENDSRCADDR
+sendto
+sendupdate
+Sensi
+sepa
+seqinit
+seqnext
+Sequanux
+serialtweaker
+servercmd
+serverdiff
+serverdownmaxfails
+serverdownthrottletime
+serverid
+serveridentity
+serveridstr
+serverlist
+serverparseerrors
+serverproc
+serverset
+servfailanswers
+servfailps
+servfailqueryring
+servfailrate
+servfailratio
+servfailremotering
+servfailremotes
+Servlet
+setbuf
+setcd
+setcdviaaction
+setecsaction
+setenv
+setf
+SETFD
+SETFL
+setgroups
+sethook
+SETID
+setitimer
+setmetatable
+setname
+setnegativeandsoa
+SETOF
+setopt
+setprecision
+setrlimit
+setscheduler
+setsid
+SETSIZE
+setsockopt
+settimeout
+settsigkey
+setuptree
+SEv
+SEZ
+sformat
+SHBt
+SHELTEK
+SHFP
+Shk
+shlibs
+shm
+sholder
+showinitializer
+showserversopts
+showvar
+shrinked
+shuf
+shutil
+Shutterstock
+sideeffect
+sidx
+SIGABRT
+SIGALARM
+SIGALRM
+SIGCHLD
+sigdr
+sigexpire
+sigfigs
+SIGFPE
+SIGHUP
+siginception
+SIGKILL
+siglen
+signaturecache
+signingpipe
+Sigoure
+SIGSEGV
+sigset
+sigterm
+SIGVTALRM
+sillyrecords
+simplea
+simplebind
+siz
+sizecounters
+Sj
+skb
+skeyset
+SKIPIT
+skiplua
+skipreasons
+skiprow
+skiptests
+SKnd
+slavecommunicator
+slaveport
+slaveschanged
+slen
+slist
+SListen
+slo
+Slp
+SLQ
+smallquerylargeresponse
+smaps
+smech
+Smirnov
+smlen
+smokeyjoe
+smp
+smt
+smtarg
+SMy
+smysql
+Smz
+sname
+snaplen
+snd
+SNDTIMEO
+sni
+snmpv
+Snv
+SOAAXFR
+soacount
+SOAIn
+SOANo
+SOANXD
+soaret
+soarr
+soatimes
+SOATTL
+socat
+socketaddress
+socketclose
+socketfamily
+socketname
+socketpair
+SOCKETPATH
+socketprotocol
+sockfd
+sockgroup
+socklen
+sockmode
+sockname
+sockowner
+socktype
+sodbc
+sodcrypto
+sodiumsigners
+sokolov
+somedata
+someiostream
+somekey
+sor
+sortcname
+sourceip
+spacelen
+spacket
+sparam
+sparc
+specifictest
+spectest
+speedtest
+SPg
+spgsql
+splitlines
+splot
+spm
+spongerng
+spoofaction
+spoofedcname
+spoofrawrule
+spoofrule
+spos
+SQda
+SQLCHAR
+sqlcmd
+sqlext
+SQLHANDLE
+SQLHDBC
+SQLHENV
+SQLHSTMT
+SQLINTEGER
+SQLLEN
+SQLPOINTER
+SQLRETURN
+SQLSMALLINT
+sqlstate
+sqlstr
+SQLTCHAR
+SQLULEN
+sqname
+sqt
+sqtype
+srcdir
+srcmask
+SRd
+sresult
+srl
+Sro
+SRPMS
+SRSLY
+SRx
+Ssb
+sscanf
+SSetsockopt
+ssh
+ssize
+sslctx
+SSLECDSADNS
+SSLEDDSADNS
+SSLRSADNS
+sslsock
+SSLTLS
+SSLTLSIO
+SSLv
+SSocket
+ssql
+ssqlite
+ssr
+sstorage
+sstream
+sstuff
+Ssystem
+stackoverflow
+stacktrace
+Starovoitov
+startdir
+startpos
+startqueries
+startrecord
+startrecordpos
+startswith
+starttime
+stateenum
+statemachine
+statesbase
+statfmt
+statfunction
+staticmethod
+statmap
+statnames
+statnode
+statnodesince
+statnumentries
+statnumhit
+statnummiss
+Statsv
+stattid
+statuscode
+statvisitor
+stdcxx
+stddev
+stdev
+stdexcept
+stdint
+stdlib
+stest
+stex
+Stogner
+stoi
+stoll
+stormap
+storvect
+storvector
+stoull
+strart
+strbind
+strcasecmp
+strchr
+strcmp
+strdup
+strerr
+strerror
+strftime
+stringappend
+stringbuffer
+stringerror
+stringfgets
+stringstream
+stringtok
+stringvalue
+STRLIT
+strncasecmp
+strncmp
+strncpy
+strptime
+strptr
+strres
+strsignal
+strstr
+strtod
+strtol
+structs
+sttl
+STX
+stype
+subdir
+subgrouping
+Subnetcheck
+subnetlist
+subqueries
+substr
+SUBSTVARS
+subsys
+suckdomains
+suffixmatch
+suffixother
+suffixtype
+SUh
+SUPERMASTERCONF
+suppliedrecords
+suse
+suseconds
+sval
+svg
+SVj
+svn
+svr
+svstat
+Swfi
+SWUOQ
+SXRFNm
+SYlm
+symlink
+Syq
+sysconf
+sysconfdir
+sysmsec
+sysobjects
+sysperc
+systemdsystemunit
+systemdsystemunitdir
+systm
+sz
+Szd
+Szps
+tac
+TAEw
+tagfile
+tagp
+tagsstr
+tagthis
+tahoe
+taiepoch
+TAMg
+tanhqgv
+TARBALLFILE
+TARBALLPREFIX
+TARBALLVERSION
+tardirname
+targetlen
+tbaggery
+Tbz
+tcache
+tcbit
+TCEDNS
+tcgetattr
+TCO
+tcpa
+tcpaaaa
+tcpavgconnduration
+tcpavgconnectionduration
+tcpavgqueriesperconn
+tcpavgqueriesperconnection
+tcpbench
+tcpbytesanswered
+tcpclient
+tcpclientimeouts
+tcpclientthreads
+tcpcurrentconnections
+tcpdiedreaddingresponse
+tcpdiedreadingquery
+tcpdiedreadingresponse
+tcpdiedsendingquery
+tcpdiedsendingresponse
+tcpdownstreamtimeouts
+tcpdrops
+TCPEDNS
+tcpgaveup
+tcphdr
+TCPIO
+tcpiohandler
+tcpka
+tcpnameser
+TCPNo
+tcpnumanswered
+tcpoutqueries
+tcppacket
+tcpqcounter
+tcpquestions
+tcprange
+tcpreadimeouts
+tcpreadtimeouts
+tcpreceiver
+tcpspeeds
+TCPTLS
+tcpwritetimeouts
+TCSANOW
+tcsetattr
+tdate
+tdi
+TDIBy
+TDja
+TDn
+tdomains
+tdsmap
+Tdsza
+TDt
+TDVXa
+Telekom
+tellg
+tempbf
+tempdi
+tempfailure
+tempfailurettlbinding
+tempfile
+temphash
+templ
+templatecounter
+templateline
+templateparts
+templatestep
+templatestop
+templatize
+termios
+testcase
+testcmd
+TESTDRIVER
+testinstance
+testkeysset
+TESTLIST
+testlock
+testmaster
+testmsg
+testname
+testnonzone
+testnum
+testnz
+testresults
+testring
+testringdnsname
+testschema
+testsuffix
+testsuite
+testt
+testuser
+texlive
+tf
+tfh
+TFILE
+TFN
+tfo
+tfunc
+THandler
+theirserial
+thislock
+thisupd
+thiszone
+thr
+threadcloser
+THREADFLAGS
+threadname
+threadwrapper
+throttledout
+throttledqueries
+throttleentries
+THTM
+thu
+tickinterval
+tidx
+timedelta
+timediff
+timegm
+timeoutsec
+timeoutspec
+timersonly
+timesec
+Timespan
+timespec
+timespent
+timeusec
+tinfo
+TINYDNSDATA
+tinydnsloader
+TINYINT
+TIsy
+Tj
+TJR
+tkdb
+tkey
+TKllk
+tkrc
+tl
+tlen
+Tlh
+TLKA
+tlsdhkeytoosmall
+tlsext
+tlshandshakefailures
+tlsinactiveticketkeys
+tlsinappropriatefallback
+TLSIO
+tlslocals
+tlsnewsessions
+tlsnosharedcipher
+tlsqueries
+tlsresumptions
+TLSSNI
+tlsunknownciphertype
+tlsunknownkeyexchangetype
+tlsunknownprotocol
+tlsunknownticketkeys
+tlsunsupportedec
+tlsunsupportedprotocol
+TLSv
+tlsversion
+tlv
+tmeta
+TMi
+Tmk
+tmout
+tmpbuf
+tmpdh
+tmpdir
+tmpfd
+tmpfile
+tmpkey
+tmpnam
+tmpname
+tmpstr
+tmsg
+tname
+tnameservers
+Tno
+tnode
+tnow
+tns
+toaddr
+tocheck
+TODOLIST
+toh
+tok
+tokenizer
+tokill
+TOLO
+tonumber
+toolong
+toolongtobevalid
+toomuchinfo
+topbar
+toport
+toportstr
+toroot
+TOSBy
+toserial
+toskip
+tosql
+totadd
+totar
+totcount
+totcumul
+totlat
+totmperc
+totpairs
+totpcache
+totrdatalen
+totremove
+tottime
+toupper
+toxml
+toysdig
+tozero
+TPL
+tpos
+tptr
+TQBv
+TQJv
+transactiondomain
+transactiondomainid
+trc
+trecords
+treeview
+trilab
+trillian
+trl
+tro
+trollololoooolll
+Truncateds
+truncatemarker
+trustanchorserver
+trustedkeys
+TRw
+tryrdlock
+tryrwlock
+trysuperdomains
+trywait
+trywrlock
+tscomp
+tsdelta
+tsigalgname
+tsigalgorithm
+tsigkeyname
+tsigprevious
+TSIGTCP
+tsigtimersonly
+tsigutils
+tsigverifier
+tsstorage
+Tstbt
+tsuna
+ttd
+ttdi
+ttdindex
+ttdwatch
+ttllimit
+TTLNo
+TTLNX
+TTLRPZ
+ttltooshort
+ttsig
+Tuk
+TUz
+Tvq
+TVU
+TWk
+TWl
+twopt
+txn
+ty
+typedef
+typedns
+typeenum
+typeid
+typeinfo
+typemap
+typename
+typestr
+TYPETOK
+TYX
+tz
+TZDU
+TZOFF
+TZud
+uapi
+ub
+UBIGINT
+ubsan
+UBXc
+ucf
+ucfq
+ucfr
+uchar
+UCLIBC
+ucontext
+UCPEd
+ucspi
+Uctchk
+udiff
+Udipd
+Udm
+udpanswers
+udpbytesanswered
+udpclientsocks
+UDPECS
+UDPEDNS
+udphdr
+udpin
+udpnumanswered
+udpoverruns
+udpsize
+udpsock
+udpspeeds
+udpv
+udrdbp
+UDRYNm
+UDWORD
+ue
+ueberbackend
+Ueuwr
+ufc
+UFt
+ufx
+uglifyjs
+uhry
+uintptr
+uio
+uitoa
+Ujd
+uki
+Ukj
+Ukvz
+UKyg
+ulen
+ulong
+Ulws
+UML
+ums
+uname
+unauthtcp
+unauthudp
+unavailables
+unboundhost
+uncanon
+uncomment
+uncompress
+unconfigured
+undef
+UNDOC
+unescape
+unhexlify
+uninstall
+uniq
+uniquw
+UNIREGISTRYMARKET
+unitdir
+unittest
+unixconnector
+Unixsocket
+Unknownqueries
+unknownrecordcontent
+unlicense
+unloadable
+unmap
+Unpublishing
+unquotify
+unregist
+unreport
+UNsockaddr
+UNSPEC
+Unthrottling
+UNUTTe
+uo
+UOHk
+uom
+Uor
+uordblks
+UPi
+Uploaders
+UPnhs
+uppercasing
+upq
+upto
+urc
+urljoin
+urllib
+urlmap
+urlparse
+urlsafe
+uroot
+usazzz
+useconds
+uselessdrc
+useradd
+userdata
+userfriendly
+usermsec
+userperc
+USHRT
+usleep
+usm
+USs
+ustar
+utexas
+utils
+utime
+Utmc
+utype
+uupdate
+Uuser
+UVARIABLES
+Uwhjf
+uwisza
+uwopt
+UXsnr
+Uxt
+Uy
+UYx
+vab
+validator
+validkeys
+validns
+validpacket
+validresponses
+validrrsets
+valign
+valiter
+vallid
+valmask
+valr
+valrandom
+vals
+varbinary
+varbind
+varchar
+varlist
+varmap
+varval
+Vbi
+vby
+Vcs
+VDLs
+vec
+vect
+veorq
+verboselog
+verhaaltje
+VERIFYHOST
+VERIFYPEER
+verifyzone
+verisign
+verisignlabs
+VERq
+versionbind
+versioncommand
+versionmangle
+versionpdns
+vertpre
+verylongstringlongerthan
+verysecret
+vf
+vfree
+VInf
+vinfolog
+VIuk
+VIW
+Vj
+Vjda
+VK
+vkuiv
+vla
+vlen
+Vlh
+Vll
+vmbe
+vml
+Vmm
+Vmp
+VPA
+vpacket
+vpos
+VPQ
+vptr
+VQBWQ
+VQda
+Vra
+Vrbp
+Vre
+vrooooom
+vrr
+Vscj
+vstate
+vstring
+vstringtok
+vtable
+VTd
+vtnq
+Vuux
+Vw
+VWlr
+vww
+Vx
+vy
+VYBP
+Vyl
+Vz
+Vzd
+WAITALL
+waitpid
+waitpoint
+waitstatus
+waitstatusenum
+waitval
+WAja
+wantsnsid
+wastcp
+wattr
+Waxyl
+wbaw
+wcard
+wcarddomain
+wcmatch
+WCN
+wcname
+WCOREDUMP
+wcplusencloser
+wctx
+wday
+weanswers
+webetter
+webhndlr
+webkit
+WEBPORT
+webrick
+webserv
+webserveropts
+wednserrors
+weekno
+weirdtxt
+wenever
+Werror
+wetimedout
+weunmatched
+WEV
+WEXITSTATUS
+wfile
+WFQTVE
+Wfxq
+wget
+Wgp
+WGqnuy
+WHg
+Whij
+whitelisting
+whoami
+wichert
+WIFEXITED
+WIFSIGNALED
+wildcarddnsname
+wildcardname
+wildzone
+wiplist
+wirelen
+wireshark
+withecs
+withedns
+withednsecs
+withednsnoecs
+withoutedns
+withport
+withval
+Wj
+WJj
+WJO
+Wklm
+wlat
+wli
+wlist
+Wll
+wlocks
+wlon
+Wmpt
+Wmpx
+WNfpw
+Wno
+WNOHANG
+Woi
+Wor
+wordpad
+workdir
+workflow
+Wowie
+Wpv
+WQ
+wrand
+wret
+Wri
+writeability
+WRITEDATA
+WRITEFUNCTION
+writen
+wrlock
+WRONLY
+Wsg
+wshash
+WSIZE
+Wsqvx
+Wswaps
+WTCSr
+WTERMSIG
+wtest
+Wtl
+Wtqlj
+wv
+WVZHd
+wwa
+wwho
+wwjb
+WWo
+wwv
+wwwds
+wwwezdnsit
+wwwpowerdnscom
+WWWPREFIX
+wwx
+wwz
+WX
+WXA
+Wxb
+Wxe
+Wxm
+WXTAH
+Wyc
+WYjld
+wz
+WZERM
+xa
+xaa
+xaaa
+xad
+XADD
+xaf
+XAPI
+xargs
+Xautonomous
+Xbz
+xca
+xce
+xception
+xchange
+Xcml
+xcrypt
+xcy
+xdigits
+XDqdg
+XEz
+XFDj
+xff
+xffverylongstring
+XFRM
+xfrserver
+XFv
+xfz
+xg
+Xga
+xgp
+XHhl
+xhr
+xib
+xit
+xj
+Xk
+XKpw
+Xkqi
+xlabel
+xluajit
+xm
+XMy
+xn
+XNGCH
+xno
+Xnv
+Xo
+Xof
+xor
+xpfcode
+XPFDATA
+xpfdst
+xpfproto
+xpfsrc
+xpfversion
+Xpk
+xpong
+Xq
+xrange
+XRL
+XRq
+xsalsa
+XSKk
+XSyam
+Xtext
+xtics
+xtrue
+xugtk
+XUgz
+xunit
+xv
+xvf
+XXh
+XXxqrt
+XXXXg
+xy
+xyes
+XYk
+xz
+xzvf
+ybndrfg
+YBSI
+yc
+yday
+Yegst
+YEQRBVK
+yetanother
+Yf
+yfb
+YFLAGS
+YGu
+yh
+Yhh
+yiss
+YJou
+yjxe
+YKMY
+ykyb
+YLa
+ylabel
+YLGg
+Ylh
+ylwrap
+Ymf
+YNBIs
+Yng
+Yogz
+YOia
+yop
+YOUNAMEIT
+Yoyodyne
+yq
+Yqi
+YSa
+YSfp
+YTg
+ytics
+YTyj
+YUTh
+YVs
+Yw
+YWA
+YWJx
+YWJYRXp
+YWls
+YWQ
+ywu
+YWVJf
+yx
+yxdomain
+YXos
+YXRR
+yxrrset
+yydebug
+yyerror
+yyin
+yylex
+yylval
+yyparse
+yyrestart
+YYSTYPE
+yyterminate
+yytext
+yywrap
+yyy
+YYYYMMDDH
+yz
+zackw
+ZAWs
+zbefore
+zc
+Zcdnskey
+ZCLASS
+Zd
+Zdelegated
+zdf
+ZDFi
+Zdnssec
+ZDV
+zeromqrb
+zeroport
+Zexample
+Zg
+ZGq
+Zgta
+Zgw
+Zhc
+ZHE
+ZHJp
+ZHml
+ZIf
+Zinsecure
+ziter
+ZJA
+Zjmco
+Zjq
+zl
+zlib
+Zm
+zmakerfunc
+zmakermap
+Zminimal
+zmq
+zmqconnector
+zname
+Znztest
+ZOMG
+zonecontent
+zonecount
+zonecut
+zonedata
+zonedataline
+zonedomain
+zonefilename
+zoneid
+zoneinfo
+zonekind
+zonelevel
+zonelist
+zonemaster
+zoneparsertng
+zonestring
+ZONETOK
+zp
+zpt
+ZQ
+ZQOZ
+ZQSUOf
+zr
+Zrm
+zrr
+Zsecure
+zskds
+zskeys
+Zstest
+Zsu
+Zsub
+zt
+Ztest
+ZTh
+Ztsig
+ztype
+zu
+zugschlus
+Zuhz
+Zun
+Zv
+ZVNIQn
+Zw
+Zwtest
+ZWTQ
+ZWxz
+Zx
+ZXJETl
+ZZj
+zzz
diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt
new file mode 100644 (file)
index 0000000..7871a32
--- /dev/null
@@ -0,0 +1,60 @@
+(?:^|/)ext/
+(?:^|/)go\.mod$
+(?:^|/)go\.sum$
+(?:^|/)m4/
+(?:^|/)package-lock\.json$
+/expected_result
+/test-dnsrecords_cc\.cc$
+SUMS$
+\.ai$
+\.asc$
+\.bmp$
+\.cer$
+\.class$
+\.crl$
+\.crt$
+\.csr$
+\.dll$
+\.DS_Store$
+\.eot$
+\.eps$
+\.exe$
+\.gif$
+\.graffle$
+\.gz$
+\.icns$
+\.ico$
+\.jar$
+\.jpeg$
+\.jpg$
+\.keys?$
+\.lib$
+\.lock$
+\.map$
+\.min\..
+\.mp3$
+\.mp4$
+\.nsec3(?:-optout|)$
+\.otf$
+\.pdf$
+\.pem$
+\.png$
+\.psd$
+\.sig$
+\.so$
+\.svg$
+\.svgz$
+\.tar$
+\.tgz$
+\.ttf$
+\.woff
+\.xcf$
+\.xls
+\.xpm$
+\.yml$
+\.zip$
+^modules/remotebackend/example\.rb$
+^modules/tinydnsbackend/data\.cdb$
+^pdns/dnsdistdist/src_js/
+^pdns/recursordist/html/js/
+^\.github/actions/spell-check/
diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt
new file mode 100644 (file)
index 0000000..2775eb9
--- /dev/null
@@ -0,0 +1,2240 @@
+aaaarecord
+aadaf
+aadceba
+aae
+aaldering
+ababd
+abbb
+ABCD
+abcde
+abcdef
+abfe
+abi
+aborttransaction
+ABQ
+Abraitis
+abspath
+acb
+acd
+acf
+acl
+ACPI
+activatedomainkey
+ada
+adadab
+adb
+adcb
+addc
+adddomainkey
+addingrecords
+addprefix
+addsuffix
+adfdffa
+Adiscon
+aee
+aeefcf
+aef
+afdf
+Afek
+afl
+afnic
+afsdb
+AFYER
+agentx
+agentxperms
+AHM
+ahupowerdns
+Aips
+Aki
+Alenichev
+alexa
+algo
+aliceblue
+allocs
+Altpeter
+amd
+ANCOUNT
+Anderton
+anewid
+anid
+anonymization
+Anonymize
+anotherid
+ANOTHERIPADDRESS
+anothertype
+ansible
+ANSSI
+Antoin
+anycast
+api
+apikey
+APIv
+AQAB
+AQTQ
+ARCHFLAGS
+ARCOUNT
+arecord
+arecvfrom
+Arentz
+ARGS
+arial
+Arjen
+Arjo
+Arnoud
+arpa
+Arsen
+aruba
+asc
+Ascio
+ASd
+Asenov
+ASEP
+Ashish
+ASIN
+asnum
+aspx
+associateddomain
+asyncresolve
+Atlassian
+atoi
+Atomia
+aton
+attr
+atype
+AUTHIP
+authmethod
+Authoritativedoc
+auths
+authzone
+autobuilt
+autocalculation
+autocomplete
+autoconf
+autodetect
+autodetecting
+autodoc
+autogenerated
+automagically
+automake
+Automattic
+autoptr
+autoreconf
+autoserial
+autoslave
+autotools
+AXF
+axfer
+axfr
+axfrfilter
+Baan
+bacc
+backend
+backgrounding
+backport
+Backtick
+backtraces
+BADALG
+BADCOOKIE
+badips
+BADKEY
+BADMODE
+BADNAME
+badserver
+BADSIG
+BADTIME
+BADTRUNC
+BADVERS
+baf
+Baj
+Bakker
+Baltus
+basedn
+basepath
+Bastiaan
+bayour
+bba
+bbb
+bbc
+bbcbbbe
+bbd
+bc
+bca
+bcb
+bcc
+bccd
+bcce
+bce
+bda
+bdd
+bddd
+bded
+bea
+bearggg
+beb
+beda
+beenthere
+bellis
+Belyshev
+benchmarketing
+Benetasso
+Bernd
+bert
+Besselink
+bestwho
+bfa
+bfb
+bfc
+bfcada
+bfe
+bffa
+bgcolor
+Bheca
+Biege
+bigbank
+bigint
+BIGSERIAL
+Bilik
+bindbackend
+binddn
+BINDTODEVICE
+Binero
+binlog
+bla
+Bleker
+blockfilter
+blockquote
+blog
+blogpost
+blogspot
+bmigrate
+bodyfont
+bodysize
+bodywrapper
+bolditalic
+bonafide
+Bortzmeyer
+botnet
+bpf
+bpo
+Brainspark
+Braunoeder
+breadcrumb
+Bremler
+brendangregg
+Briley
+Broens
+broer
+Bromwich
+Brownworth
+Brynjar
+Brzeski
+bsd
+Btw
+Buf
+bufsize
+bugfix
+bugfixes
+bugzilla
+BUILDDIR
+bulc
+bulletinc
+burstable
+bw
+BXvs
+Byterate
+bytestring
+bzero
+bzip
+caa
+caad
+cachekey
+cae
+Cairney
+calculatesoaserial
+calidns
+Cauquil
+cbb
+cbc
+CBF
+Cbjr
+ccac
+ccache
+ccb
+ccbd
+ccc
+ccd
+cce
+ccounts
+cdb
+cdbe
+CDBKV
+cde
+cdece
+cdeede
+cdnskey
+cds
+ceb
+cec
+cece
+cef
+cefcf
+Cegetel
+Cerb
+certusage
+cfe
+cfea
+cfeb
+CFLAGS
+cgi
+CGroup
+changelog
+changeme
+changeset
+changetype
+charset
+chashed
+chbruyand
+chdir
+Chiavacci
+chmod
+chopoff
+chown
+Chqt
+Christof
+chroot
+chrooting
+CHz
+ci
+CIDR
+cjf
+classmethod
+CLASSNUM
+Cloos
+closesocket
+clusions
+cmouse
+cmsg
+cmsghdr
+cn
+cname
+cnamechainresolution
+CNAMEd
+CNAMEDNS
+cnamerecord
+cnf
+Cnma
+cnn
+cockroachlabs
+Cockroft
+codebgcolor
+codeninja
+codetextcolor
+Colemarcus
+colgroup
+collapsiblesidebar
+colm
+comboaddress
+commandline
+committransaction
+conaxis
+config
+configfile
+configname
+configsetting
+configurability
+conntrack
+Conntracking
+Consolas
+constexpr
+controllen
+controlsocket
+coprocess
+coprocesses
+coredumps
+cornercases
+corpit
+CORS
+costypetrisor
+cout
+coverity
+cpp
+cppcheck
+createdb
+createslavedomain
+Cremers
+CRn
+cron
+Cruft
+crv
+cryptokey
+Cryptoki
+cryptopp
+cryptoshop
+css
+csv
+ctime
+ctor
+ctx
+Cuz
+cve
+cvename
+cvs
+cvstrac
+CWD
+CXXFLAGS
+cz
+daa
+dac
+daee
+daemonizing
+daemontools
+daf
+Daganoto
+Danerklint
+dankamongmen
+Darilion
+darix
+Darron
+dataformat
+datasource
+datastore
+datatracker
+Daugaard
+Davids
+Dayneko
+dbaec
+dbe
+dbedfc
+dbf
+dbfile
+dblfilename
+dbname
+dbpf
+dbr
+DBX
+dccc
+dcd
+dcde
+dce
+DCF
+dcobject
+ddaab
+dde
+ddf
+ddns
+DDo
+deactivatedomainkey
+debian
+deboynepollard
+decls
+ded
+Deduktiva
+dedup
+Deduplicate
+defcontent
+defpol
+defttl
+DENIC
+deref
+descclassname
+descname
+Dessel
+dest
+destname
+Detlef
+devicename
+devtoolset
+df
+dfb
+dfd
+dff
+dh
+DHCID
+DHCP
+dhcpd
+dhcpdupdate
+diffs
+DIGESTALGOS
+Digitalus
+dijk
+dilinger
+Directi
+Disqus
+distro
+djbdns
+DKIM
+dlerror
+dlg
+DLLs
+dlmalloc
+DLV
+dmesg
+Dmitry
+dname
+Dnn
+dns
+dnsapi
+dnsbulktest
+dnscache
+dnscrypt
+dnsdemog
+dnsdist
+dnsdistconf
+dnsdistdist
+dnsdistdoc
+dnsdomain
+dnsext
+dnsgram
+dnsheader
+dnskey
+dnsmessage
+dnsname
+dnsnameset
+dnsop
+dnspacket
+dnsparser
+dnspcap
+DNSQ
+dnsquestion
+DNSR
+dnsrecord
+dnsreplay
+dnsresourcerecord
+dnsscan
+dnsscope
+dnssec
+dnssecfromexisting
+DNSSERVER
+dnsspoof
+dnstap
+dnstcpbench
+dnstree
+dnsttl
+dnsupdate
+dnswasher
+dnszone
+dnt
+Dobrawy
+docnamecachelookup
+doctrees
+documentclass
+documentwrapper
+docutils
+doesnotexist
+dofile
+Dohmen
+domaininfo
+domainmetadata
+domainname
+domainrelatedobject
+Donatas
+dontcare
+downsides
+downstreams
+dport
+dq
+drafiei
+Draschl
+droprate
+DRR
+dscontent
+dsrecord
+DSs
+dst
+DTS
+Dufberg
+dumresp
+dynblock
+dynblocklist
+dynblocksref
+dynbpf
+dyndns
+dynhandler
+dynmodules
+eaa
+eac
+eachother
+EAGAIN
+easydns
+eb
+ebaf
+ebd
+ebe
+ebeb
+ebf
+ebfd
+ebpf
+ebpfblocklist
+EBXN
+ecbf
+ecc
+ECCN
+ecdsa
+ECDSAP
+econds
+ECONNRESET
+ecs
+ECSDA
+ecswho
+eda
+edb
+edc
+ede
+edfa
+editline
+edns
+ednsoptions
+ednsoptionview
+ednssubnet
+EDNSTo
+edu
+eea
+eeb
+eec
+EED
+eef
+efb
+efbf
+efc
+efd
+Eieb
+EINTR
+ejones
+EJUGg
+ek
+Ekkelenkamp
+elgoog
+Emph
+endblock
+Enden
+endian
+endif
+endl
+ENOENT
+ENOTCONN
+ent
+entrypoint
+enum
+envoutput
+EOL
+epel
+epoll
+epub
+eqno
+Eriksson
+errlog
+errno
+errorlevels
+esr
+EUI
+EUips
+evildomain
+EVMu
+EWMA
+examplekey
+exceedfuncs
+execfile
+Exort
+externalrefs
+extrahead
+Ezb
+Ezbu
+ezdns
+fabf
+fadec
+FAEEBC
+Faerch
+faf
+failedservers
+failover
+favicon
+FBAE
+fbc
+fbe
+fbf
+fcbd
+fcc
+fcd
+fcde
+fcf
+fcff
+fcgi
+fcontext
+fd
+fda
+fdc
+fdce
+fdd
+fdde
+fdopen
+fedc
+fedoraproject
+feedents
+feedrecord
+feef
+ffaae
+ffb
+ffd
+ffdd
+fff
+ffi
+ffipolicy
+filedescriptor
+Filesystem
+findclientpolicy
+Firefox
+firewalled
+firewalls
+fixednow
+FIXME
+FJZ
+Fki
+FLln
+Florus
+flto
+FNs
+fontname
+footerbgcolor
+footertextcolor
+forfun
+FORMERR
+Fortiguard
+Fortinet's
+forwardzone
+framestream
+freakshow
+freebsd
+freedesktop
+Freenet
+freesans
+freetds
+freshports
+Froemel
+frontend
+fstrm
+fullname
+fulltoc
+func
+Furnell
+Fusl
+FYhvws
+FZq
+gaba
+gacogne
+gatech
+Gavarret
+gcc
+Gci
+gdpr
+Geijn
+genindex
+geobackend
+geoip
+geoipbackend
+geolocated
+Gergely
+Gerritsen
+Gervai
+Gerwin
+getaddrinfo
+getaddrs
+getalldomainmetadata
+getbeforeandafternamesabsolute
+getdomaininfo
+getdomainkeys
+getdomainmetadata
+gethostname
+getlocaladdress
+getn
+getrandom
+getregisteredname
+gettag
+gettext
+gettime
+gettimeofday
+gettsigkey
+Geuze
+GFm
+gh
+Gibheer
+Gieben
+Gillstrom
+github
+githubusercontent
+Gkey
+Gkkq
+glibc
+gmail
+gmake
+Gmb
+gmtime
+GMy
+gmysql
+gmysqlbackend
+gnutls
+godbc
+godbcbackend
+goodmatch
+google
+googleapis
+goracle
+goraclebackend
+GOST
+gouv
+Goxz
+gpgsql
+gpgsqlbackend
+gprof
+gpsqlbackend
+GQj
+GQNy
+grep
+grepping
+grepq
+GSQ
+gsql
+gsqlite
+gss
+gssapi
+gsub
+gtld
+guilabel
+gy
+Gyh
+Gyselinck
+gz
+gzip
+gzipped
+hackerone
+Hakulinen
+Hannu
+haproxy
+hardcode
+hardcoded
+hardcoding
+hardlink
+Harker
+headbgcolor
+headerlink
+headfont
+headlinkcolor
+headtextcolor
+healthcheck
+Heimhilcher
+Helbekkmo
+HELO
+Hendriks
+Henk
+Hensbergen
+Heredoc
+Heuer
+Hev
+hh
+hidesoadetails
+hidettl
+highlighttable
+Hiljanen
+hinfo
+hitrate
+hkraal
+HKSKRWu
+hll
+hlmann
+hmac
+Hmi
+Hoentjen
+Hofstaedtler
+homepage
+Hooimeijer
+hostmaster
+hostname
+Hotmail
+howto
+hpecorp
+hpiers
+hpp
+HPx
+href
+hsm
+htbp
+htm
+html
+htmlescape
+htmlhelp
+http
+httpapi
+httpdomain
+hubert
+hyperlink
+HZQ
+iana
+icann
+ico
+ict
+idprotect
+idq
+idx
+iers
+ietf
+ifdef
+ifportup
+ifurlup
+IFV
+ihsinme
+IJajghd
+IKOYz
+illumos
+img
+Imhard
+incbin
+includeboilerplate
+includerings
+indexa
+indexassociated
+indextable
+inet
+infolog
+infosecinstitute
+ini
+initscript
+Inno
+innodb
+inode
+installable
+interop
+interoperability
+interoperation
+inzk
+iostream
+iowait
+ip
+IPADDRESS
+IPbackend
+ipc
+ipcipher
+ipcom
+ipcrypt
+ipdecrypt
+ipencrypt
+ipfilter
+IPSECKEY
+iptables
+iputils
+ipv
+IQIT
+IQuery
+irc
+isane
+isc
+ismaster
+isoc
+isp
+ispell
+isql
+isse
+issuecomment
+ixfr
+ixfrdist
+ixplore
+Jakub
+Jakum
+janeczku
+Jatko
+Jaury
+Jauvin
+javascript
+JCf
+Jck
+Jeftovic
+Jelte
+Jermar
+Jeroen
+jessie
+jj
+Joaqu
+Jong
+Jorn
+journalctl
+journald
+jp
+jpmens
+jq
+json
+jsondomain
+JSONP
+jsonstat
+ju
+Juergen
+jumpbox
+Juraj
+jye
+Kaminsky
+Kaseorg
+KCtsq
+kd
+Kdhcp
+Kdhcpdupdate
+Kees
+kerberos
+Kerkhof
+KEYBITS
+keyblock
+keydir
+keyfile
+keygen
+keyname
+keypair
+keypairgen
+keyroll
+keysearch
+keysize
+keytab
+KEYTAG
+keytype
+keywordmatches
+kickdaddy
+Kirill
+Klebermass
+koch
+Kockum
+Kolkman
+kom
+Konqueror
+Koos
+koqv
+Kovacic
+kp
+kqueue
+KQZX
+krb
+Krist
+Krul
+ksk
+kskroll
+kskrollcdnskey
+Kuehrer
+KUXs
+kvs
+KX
+Kxxnux
+Kyc
+Ladot
+Lafon
+Lakkas
+largeanswer
+Laros
+lastcheck
+Lastdrager
+lastnotified
+latexpdf
+latlon
+latlonloc
+latomic
+lauch
+Laurient
+Laursen
+Lbackend
+LCUP
+LDA
+ldap
+ldapbackend
+ldflags
+ldif
+ldns
+LDR
+Leen
+Lemoine
+len
+lessthan
+Lesuisse
+lethalgroup
+letsencrypt
+letterpaper
+LFya
+libatomic
+libc
+libcrypto
+libcryptopp
+libcurl
+libdecaf
+libdir
+libedit
+libfstrm
+libgcc
+libgeoip
+libh
+libmaxminddb
+libmongo
+libmysqlclient
+libnss
+libpcap
+libpq
+libpqpp
+libresolv
+libressl
+librt
+libsodium
+libsofthsm
+libssl
+libsystemd
+libtdsodbc
+libyaml
+libzmq
+Lindqvist
+linenos
+linenum
+linkcolor
+lintian
+linux
+linuxnetworks
+Lior
+listinfo
+literalinclude
+llvm
+lmdb
+lmdbbackend
+LMDBKV
+lnsl
+loadbalancer
+loadbalancing
+localaddr
+localhost
+localip
+LOCALSTATEDIR
+localtime
+localtoc
+locaweb
+lochiiconnectivity
+logfile
+loglevel
+logmessage
+logrotate
+lon
+Loopia
+Lorbach
+lordievader
+Louwers
+loweralpha
+lowerroman
+lresolv
+Lrhazi
+lrt
+lsock
+lsocket
+lte
+lua
+luaaction
+luabackend
+luac
+luajit
+luaroundrobin
+luarule
+luawrapper
+Lutter
+Luuk
+Lwc
+Lwz
+LYg
+lz
+Maik
+Maikel
+MAILA
+MAILB
+Majer
+Makefiles
+malcrafted
+malloc
+malware
+Mamane
+Mandriva
+manpage
+mapasync
+mariadb
+Markmann
+maskv
+Massar
+matchtype
+Matthijs
+maxdepth
+MAXINT
+maxlistdepth
+maxmind
+maxqps
+MAXVALUE
+mbed
+mbedtls
+MBOXFW
+mbytes
+MDB
+Meerwald
+Mekking
+MEMLOCK
+Memusage
+menuselection
+metadata
+metadatabase
+metainformation
+metricnames
+metricscarbon
+Meulen
+Michiel
+Microsoft
+Miek
+Miell
+Mieslinger
+Milas
+Mimimization
+minbody
+mindex
+MINFO
+minipatch
+misconfigured
+mjt
+mkuchar
+mmap
+mmdb
+mname
+mnordhoff
+MOADNS
+Modderman
+modifyingpolicydecisions
+modindex
+monshouwer
+Moq
+motherboards
+mozilla
+mplexer
+Mpqhbg
+MQ
+mrtg
+msdcs
+MSDNS
+msphinx
+mssql
+mtasker
+MTEUl
+mthread
+MUar
+Mulholland
+multiline
+multimaster
+multithreading
+mundsson
+munmap
+Muraro
+musl
+mutex
+Mwaikambo
+mx
+mxrecord
+mybackend
+mycompany
+mydns
+mydnsbackend
+mydomain
+MYec
+myhost
+myinstance
+myname
+mypassword
+mypgsql
+mysecretpassword
+myset
+myspecialmetric
+mysql
+mysqlbackend
+mysqld
+mytsigkey
+myuser
+mywebapp
+NAi
+namedroppers
+nameserver
+nameserving
+namespace
+namserver
+naptr
+Nauck
+Navarrete
+nc
+Ndd
+nearmiss
+nearmisses
+Nederlandse
+nedmalloc
+negativetrustanchor
+negcache
+negquery
+neheb
+Nelless
+neosystem
+Netblock
+netfilter
+netherlabs
+netinet
+netmask
+netmaskgroup
+netsnmp
+NETWORKMASK
+Neue
+Neuf
+newcontent
+nextval
+nf
+nic
+nimber
+Nixu
+nkey
+nmg
+nn
+Nncqx
+noaction
+noad
+noall
+nocache
+nocookie
+nodata
+NODELAY
+noedns
+noerrors
+nometasync
+Nominet
+nonexist
+Nonnekes
+noout
+noping
+noport
+nosniff
+nostrip
+NOSUBDIR
+nosync
+Notaras
+NOTAUTH
+NOTIMP
+NOTPARALLEL
+notrack
+NOTZONE
+Novell
+nproxy
+NPTL
+NSCOUNT
+NSCx
+nsd
+NSECx
+NSes
+nsid
+nsis
+nsname
+NSQ
+nsrecord
+NSS
+nsset
+nsspeeds
+NSTTL
+nsupdate
+nta
+ntei
+ntlworld
+nullptr
+NULs
+NUMA
+numreceived
+nx
+nxd
+NXDATA
+nxdomain
+NXRRSET
+oarc
+oauth
+Obermayer
+obidos
+objectclass
+Obser
+obspm
+ocsp
+odbc
+odbcbackend
+odbcinst
+Oddy
+Odintsov
+Oestreicher
+offsite
+Ofpy
+oftc
+OIDs
+Olafur
+Omroep
+openapi
+openbsd
+opendbx
+openpgpkey
+opensc
+openssl
+opensuse
+openwall
+Opmeer
+optcode
+Opteron
+optmem
+optout
+oraclebackend
+ordername
+orsn
+Oservers
+ostringstream
+OSX
+otherdomain
+otherpool
+ou
+OUHTU
+ourname
+ourserial
+ourtime
+OUTFILE
+outpacket
+outputbuffer
+outqueries
+PACKAGEVERSION
+packetcache
+packethandler
+papersize
+paramater
+PARAMKEYWORDS
+params
+passphrase
+passthrough
+passthru
+PATC
+patchlevels
+pathconfig
+pathto
+pawal
+pb
+Pbackend
+pcap
+PCAPFILE
+pdf
+pdns
+pdnsbackend
+pdnscontrol
+pdnsldap
+pdnslog
+pdnsmgrd
+pdnsodbx
+pdnsrandom
+pdnssec
+pdnstest
+pdnsutil
+Peeters
+Pels
+pem
+Penev
+perl
+Perroud
+Pertubation
+pez
+Pfetzing
+pgmysql
+pgmysqlbackend
+pgp
+pgpsql
+pgsql
+phishing
+phonedph
+php
+pickclosest
+pickrandom
+pickwhashed
+pickwrandom
+pid
+piddir
+pidfile
+pidof
+pieter
+pieterlexis
+pilindex
+Pinski
+pipebackend
+pipermail
+PIV
+pkcs
+PKGLIBDIR
+PKI
+PKTINFO
+placeholders
+PLt
+Plusnet
+plzz
+pmtmr
+pnds
+png
+Poelov
+pointsize
+polarssl
+policyactions
+policykinds
+policyname
+pollmplexer
+Ponomarev
+poolers
+poolname
+portnum
+portnumber
+postgre
+postgresql
+postinst
+postresolve
+powerdns
+powerdnsrecursor
+powerdnssec
+powerldap
+powerpc
+ppc
+pragma
+Predota
+preoutquery
+Preproc
+prequery
+prereleases
+prerpz
+presigned
+presignedness
+PRId
+primetime
+princ
+privatekey
+privs
+PRNG
+Proba
+progid
+protobuf
+PROTOC
+providername
+proxyprotocol
+proxyprotocolvalues
+PROXYv
+pseudonymize
+pseudorecord
+psql
+pthread
+ptr
+ptrrecord
+Publieke
+publishdomainkey
+pullreq
+pwfm
+px
+py
+pygments
+pypi
+Pyry
+PYv
+PZFX
+qa
+Qag
+qclass
+qdcount
+qdomain
+qgen
+Qkj
+qlen
+Qlim
+Qll
+qname
+qperq
+Qpkv
+qps
+QPSIP
+qpslimits
+QRate
+qsize
+QSLj
+qthread
+qtype
+qtypelist
+querycache
+querycount
+quickstart
+QYCIHp
+qytpe
+ragel
+randombackend
+randombit
+randombytes
+randomisation
+randomises
+randomloader
+rapidjson
+raspbian
+rb
+RBL
+RBUb
+rcode
+rcodezero
+Rcvbuf
+rcvmmsg
+rdata
+rdqueries
+rdynamic
+readline
+README
+readonly
+realtime
+reconnections
+recursor
+recursordist
+Recursordoc
+Recuweb
+recv
+recvbuf
+recverr
+recvfrom
+recvmmsg
+recvmsg
+redelegations
+redhat
+redjack
+reentrantly
+refactor
+Refactoring
+refcount
+refman
+refreh
+refuseds
+regex
+reid
+reimplementation
+Reinier
+Rejo
+relbar
+relbarbgcolor
+relbarlinkcolor
+relbartextcolor
+relro
+Remco
+remi
+remoteaddr
+remotebackend
+remoteip
+remoting
+removedomainkey
+replacerrset
+requery
+resolv
+respawn
+respawning
+respout
+respsizes
+Resync
+resynchronise
+retransfering
+reuseds
+reuseport
+Reuwiei
+rfc
+rhel
+Rietz
+rightsidebar
+Rijsdijk
+ringbuffer
+rkey
+RLIMIT
+rname
+rng
+rocommunity
+Roel
+Rosmalen
+roundrobin
+rp
+RPATH
+rping
+RPMS
+rpz
+rpzstatistics
+Rqvg
+rr
+rrcontent
+rrd
+rrdata
+rrdtool
+rrname
+rrset
+rrsig
+rrtype
+rsa
+rsasha
+RSP
+rst
+rsync
+ru
+Rueckert
+Rul
+rulesets
+Ruthensteiner
+rv
+Rvd
+rw
+Rwgj
+rwlock
+Rzs
+Sakaguchi
+saltsa
+sandboxing
+Sangwhan
+SASL
+Saunalahti
+saxfr
+SBF
+sbin
+Sbvka
+scalability
+SCHED
+Scheffler
+Schlich
+Scholten
+Schryver
+Schueler
+schwer
+SCn
+scopebits
+scopemask
+scriptable
+scriptlets
+sdb
+sdfoijdfio
+sdig
+secpoll
+securesphere
+securitypolicy
+securitypolling
+seealso
+segfault
+selectmplexer
+selinux
+senderrors
+Sendetzky
+sendmsg
+sensistive
+Sergey
+servername
+serverpools
+serverselection
+servfail
+setaffinity
+setcontent
+setdomainmetadata
+setgid
+seting
+setkey
+setnotified
+SETPIPE
+settting
+setuptools
+setvariable
+Sgs
+Shafir
+shantikulkarni
+shinsterneck
+Shm
+showdetails
+showflags
+Shukla
+sid
+SIddm
+sidebarbgcolor
+sidebarbtncolor
+sidebarbutton
+sidebarlinkcolor
+sidebarlogo
+sidebarsourcelink
+sidebartextcolor
+sidebarwidth
+sidn
+SIGABR
+sigint
+Sigmod
+signedness
+Signingpiper
+signpipe
+signttl
+signzone
+SIGPIPE
+sigs
+SIGTERM
+SIGUSR
+singlethreaded
+Sinstallation
+Sipek
+sizeof
+Sjoerd
+slapd
+slapindex
+slaveness
+SLES
+smartcard
+smb
+smellyspice
+smfgo
+smimea
+smn
+smtp
+Smurthwaite
+Snarked
+sndbuf
+SNI
+snmp
+snmpd
+SNMPv
+snprintf
+soa
+soadata
+soarecord
+sockaddr
+socketdir
+softhsm
+solaris
+SOLc
+Soldaat
+SOMAXCONN
+somedomain
+Sonix
+Soref
+Soroceanu
+sortlist
+sourcecode
+SOURCEDIR
+sourceforge
+sourceware
+Spaans
+Spackages
+spam
+Sparc
+spf
+SPHINXBUILD
+sphinxcontrib
+sphinxjsondomain
+SPHINXOPTS
+SPHINXPROJ
+sphinxsidebar
+sphinxsidebarwrapper
+splitsetup
+sprezzos
+Spruyt
+SQk
+sql
+sqlite
+srandom
+src
+srcname
+SRecord
+Srule
+srv
+SSH
+sshfp
+ssi
+ssl
+sslmode
+sslrootcert
+SSQ
+SSql
+stacksize
+standalone
+starttls
+starttransaction
+Stasic
+statbag
+statbas
+statisticitem
+statm
+stbuehler
+stderr
+stdin
+stdio
+stdout
+Stef
+Steinbuch
+Stichting
+stickysidebar
+Stillaway
+Stirnimann
+stmt
+Stol
+Storbeck
+stou
+stoul
+strcasestr
+stringmatch
+strlen
+strpos
+stubquery
+stubresolver
+stunnel
+Stussy
+stutiredboy
+stylesheet
+subdomain
+subkey
+subnetmask
+sudo
+suffixmatchtree
+supermaster
+supermasterbackend
+supernotification
+supersecretpassword
+superslave
+superslaving
+supervisord
+Surfnet
+SUSE
+swapcontext
+swe
+swoga
+sx
+Symlink
+syncres
+sys
+sysadmin
+syscall
+SYSCONFDIR
+sysctl
+Sysdream
+syslog
+systemcall
+systemctl
+systemd
+sysv
+SYW
+SZ
+szw
+tarball
+Tarjei
+Tarnell
+tbhandler
+tbody
+tcely
+tcp
+tcpdump
+TCPKEEPALIVE
+TCPv
+Tctk
+td
+teeaction
+Telenet
+testrunner
+testsdir
+texinfo
+textcolor
+tfoot
+Tful
+TGJGVc
+thead
+thel
+thelog
+Thessalonikefs
+Thiago
+thinko
+Thomassen
+threadsafe
+throttlemap
+thrysoee
+Thu
+timedipsetrule
+timedout
+timeframe
+timeline
+timesource
+timeval
+timezone
+tinycdb
+tinydns
+tinydnsbackend
+tisr
+TKEY
+tld
+tls
+tlsa
+tmp
+tmpfs
+tobool
+toc
+toctree
+todo
+toint
+tokenuser
+tolower
+Tolstov
+Toshifumi
+tostring
+Travaille
+tribool
+trunc
+trustanchor
+trusteer
+trx
+trxid
+tsc
+tsig
+tsigalgo
+tsigkey
+tsigname
+tsigsecret
+tstamp
+TSU
+tt
+ttl
+Tuinder
+tunables
+tuomi
+Tushuizen
+Tuu
+Tuxis
+TVJRU
+tw
+tylerneylon
+typedefs
+typenames
+tyu
+TZ
+ualberta
+Ubo
+ubuntu
+udp
+udpqueryresponse
+UDPv
+udr
+Ueber
+Ueli
+uid
+uint
+Uisms
+uj
+uk
+ul
+ulimit
+Umq
+unauth
+unbreak
+uncached
+unescaping
+unfresh
+unhash
+unicode
+uninett
+uninitialised
+Uninstaller
+unistd
+unitialized
+unixodbc
+unixtime
+unparseable
+Unprocessable
+unpublish
+unpublishdomainkey
+unreachables
+unregister
+unshadowing
+untruncated
+unzero
+upd
+updatepolicy
+upperalpha
+upperroman
+urandom
+uri
+url
+urlencoded
+usec
+usecase
+useragent
+userbase
+username
+userspace
+usr
+utf
+UUHJWZg
+uuid
+uwaterloo
+Uwcjbp
+Uwi
+Uyypn
+Valentei
+Valentini
+valgrind
+validationstates
+validators
+Valkenburg
+Vandalon
+vandergaast
+Vandoren
+VARCHAR
+varname
+Vasiliy
+VBG
+Vbt
+VDRAW
+VDz
+Veldhuyzen
+venv
+Verheijen
+Verisign
+Verschuren
+versionadded
+versionchanged
+versionmodified
+vh
+viewcode
+virtualenv
+virtualized
+visitedlinkcolor
+vixie
+vm
+Vn
+Voegeli
+Volker
+voxel
+Vranken
+Vsgoi
+Vwgbclzx
+VY
+vzu
+WAITFORONE
+wal
+wallclock
+warnlog
+Wascwa
+Wbq
+webbased
+webdocs
+webhandler
+webpassword
+webserver
+website
+Webspider
+weightparams
+Weimer
+Welzel
+Wessels
+westes
+Wevers
+wextra
+Wgx
+whashed
+whitelist
+Wia
+Wichert
+Wieger
+Wielicki
+Wijk
+Wijnand
+Wijngaards
+wiki
+wikipedia
+wil
+wildcarded
+wildcards
+Willcott
+windr
+Winfried
+wireformat
+wirelength
+Wisiol
+wmc
+Wmissing
+Wojas
+workaround
+Worldnic
+would've
+wouter
+wpad
+wproduction
+wrandom
+Wrange
+Wredundant
+writev
+Wshadow
+wuh
+ww
+www
+WYbw
+Wzqf
+Wzs
+Xander
+xchacha
+Xchange
+xdb
+Xek
+Xeon
+xf
+xfr
+xh
+xhtml
+XM
+xml
+xorbooter
+xpf
+XPFCODE
+XPFDST
+XPFPROTO
+XPFSRC
+XPFVERSION
+Xpw
+XRecord
+XSalsa
+xss
+XYR
+yahttp
+yaml
+yb
+YDyzc
+Yehuda
+YG
+YHk
+YIBo
+Yiu
+Yj
+yk
+YLCOy
+Ylitalo
+yml
+YMMV
+yourcompany
+yourdomain
+yourorganization
+yoursecret
+yp
+YQ
+yubikey
+Yx
+YXDOMAIN
+YXRRSET
+yy
+YYYYMMD
+YYYYMMDD
+YYYYMMDDSS
+Zealey
+zeha
+Zengers
+Zengin
+zeromq
+Zfndz
+Zhf
+zilopbg
+ZJad
+Zmd
+ZNLY
+Zoag
+zonecryptokey
+zonefile
+zonemetadata
+zonename
+zoneparser
+zonetransfer
+Zonneveld
+ZRev
+zsk
+zskroll
+Zt
+Zumstrull
+Zwane
+zz
+zzyzz
diff --git a/.github/actions/spell-check/only.txt b/.github/actions/spell-check/only.txt
new file mode 100644 (file)
index 0000000..b8e3d53
--- /dev/null
@@ -0,0 +1,2 @@
+/docs/
+^docs/
diff --git a/.github/actions/spell-check/patterns.txt b/.github/actions/spell-check/patterns.txt
new file mode 100644 (file)
index 0000000..cf83b34
--- /dev/null
@@ -0,0 +1,6 @@
+(?:0[Xx]|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]{0,2}\b
+addNSECRecordToLW.*DNSName.*powerdnt.com.*QType::NS.*res->d_records
+data:[a-zA-Z=;,/0-9+]+
+BOOST_CHECK_EQUAL\(b64, "[a-zA-Z=;,/0-9+]+"
+\b([A-Za-z])\1{3,}\b
+C(?:XX|)FLAGS="[^"]*"
diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml
new file mode 100644 (file)
index 0000000..d3dcbfa
--- /dev/null
@@ -0,0 +1,33 @@
+name: Spell checking
+on:
+  push:
+    branches:
+      - "**"
+    tags-ignore:
+      - "**"
+    paths:
+      - "docs/**"
+      - "**/docs/**"
+  pull_request:
+    branches:
+      - "**"
+    tags-ignore:
+      - "**"
+    paths:
+      - "docs/**"
+      - "**/docs/**"
+    types: ['opened', 'reopened', 'synchronize']
+
+jobs:
+  build:
+    name: Spell checking
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2.0.0
+      with:
+        fetch-depth: 5
+    - uses: check-spelling/check-spelling@0.0.16-alpha
+      env:
+        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        bucket: .github/actions
+        project: spell-check
index ffc7603b768543328e422f9567103f3d81f06dc3..7fedb7a197ae370b3c6fd29fb1325d7b736f3c8f 100644 (file)
--- a/README.md
+++ b/README.md
@@ -5,10 +5,10 @@ exact license and exception used).
 All documentation can be found on https://doc.powerdns.com/
 
 This file may lag behind at times. For most recent updates, always check
-https://doc.powerdns.com/md/changelog/.
+https://doc.powerdns.com/authoritative/changelog/
 
 Another good place to look for information is:
-https://doc.powerdns.com/md/appendix/compiling-powerdns/
+https://doc.powerdns.com/authoritative/appendices/compiling.html
 
 To file bugs, head towards:
 https://github.com/PowerDNS/pdns/issues
@@ -42,7 +42,7 @@ This will bring up a USAGE-page which will explain how to build the different re
 
 COMPILING Authoritative Server
 ------------------------------
-The PowerDNS Authoritative Server depends on Boost, OpenSSL and requires a
+The PowerDNS Authoritative Server depends on Boost, OpenSSL and Lua, and requires a
 compiler with C++-2011 support.
 
 On Debian 9, the following is useful:
@@ -119,62 +119,6 @@ Building the HTML documentation
 
 The HTML documentation (as seen [on the PowerDNS docs site](https://doc.powerdns.com/authoritative/)) is built from ReStructured Text (rst) files located in `docs`. They are compiled into HTML files using [Sphinx](http://www.sphinx-doc.org/en/master/index.html), a documentation generator tool which is built in Python.
 
-**Using a normal Python installation**
-
-For those simply contributing to the documentation, this avoids needing to install the various build
-tools and other dependencies.
-
-Install Python 2.7 or Python 3 (preferable) if you don't yet have it installed. On some operating
-systems you may also have to install `python3-pip` or similarly named.
-
-Ubuntu 16.04 / 18.04
-
-```sh
-apt update
-apt install python3 python3-pip python3-venv
-```
-
-macOS (using homebrew)
-
-```sh
-brew install python3
-```
-
-Update your `pip` and install/update `virtualenv` to avoid problems:
-
-```sh
-# for python2, use "pip" instead of "pip3"
-pip3 install -U pip
-pip3 install -U virtualenv
-```
-
-Enter the repository's `docs` folder, set up the virtualenv, and install the requirements
-
-```sh
-cd docs
-# for python2, use "virtualenv .venv" instead
-python3 -m venv .venv
-source .venv/bin/activate
-# The virtualenv may use an older pip, so upgrade it again
-pip3 install -U pip setuptools setuptools-git
-# Now you can install the requirements
-pip3 install -r requirements.txt
-```
-
-Finally, you can build the documentation:
-
-```sh
-sphinx-build . html-docs
-```
-
-Note: If your shell has problems finding sphinx-build, try using `.venv/bin/sphinx-build` instead.
-
-The HTML documentation is now available in `html-docs`.
-
-**Using the build tools**
-
-This method is preferable for those who already have a working build environment for PowerDNS.
-
 Install the dependencies under "COMPILING", and run autoreconf if you haven't already:
 
 ```sh
@@ -190,12 +134,6 @@ make html-docs
 
 The HTML documentation will now be available in `html-docs`.
 
-Solaris Notes
--------------
-Use a recent gcc (and other build tools), possibly from Solaris 11 IPS.
-
-If you encounter problems with the Solaris make, gmake is advised.
-
 FreeBSD Notes
 -------------
 You need to compile using gmake - regular make only appears to work, but doesn't in fact. Use gmake, not make.
index e04f6e134519aea2ec56ec97aa978822dc2d526e..e2852fa8cc4e5741205cc9a7eb7acaaefc9472f4 100755 (executable)
@@ -24,9 +24,9 @@
 if [ "$1" = "" -o "$1" = "-?" -o "$1" = "-h" -o "$1" = "--help" ]; then
     echo "Usage: generate-repo-files.sh RELEASE"
     echo
-    echo "  • RELEASE: [ auth-40 | auth-41 | auth-42 | auth-43 |"
-    echo "               rec-40 | rec-41 | rec-42 | rec-43 | rec-44 |"
-    echo "               dnsdist-15 ]"
+    echo "  • RELEASE: [ auth-40 | auth-41 | auth-42 | auth-43 | auth-master |"
+    echo "               rec-40 | rec-41 | rec-42 | rec-43 | rec-44 | rec-master |"
+    echo "               dnsdist-15 | dnsdist-master ]"
     exit 1
 fi
 
@@ -38,6 +38,12 @@ write_centos()
     PKG=$2
     CMD=$3
 
+    if [ "$VERSION" = "8" ]; then
+        CENTOS8_FLAGS="--nobest"
+    else
+        CENTOS8_FLAGS=""
+    fi
+
     cat <<EOF > Dockerfile.$RELEASE.$OS-$VERSION
 FROM $OS:$VERSION
 
@@ -57,7 +63,7 @@ EOF
 
     cat <<EOF >> Dockerfile.$RELEASE.$OS-$VERSION
 RUN curl -o /etc/yum.repos.d/powerdns-$RELEASE.repo https://repo.powerdns.com/repo-files/$OS-$RELEASE.repo
-RUN yum install -y $PKG
+RUN yum install --assumeyes $CENTOS8_FLAGS $PKG
 EOF
 
     if [ "$RELEASE" = "rec-43"  -o "$RELEASE" = "rec-44" ]; then
@@ -106,13 +112,14 @@ EOF
 FROM $OS:$VERSION
 
 RUN apt-get update
-RUN apt-get install -y curl gnupg dnsutils
+RUN apt-get install -y curl gnupg dnsutils apt-transport-https
 
 COPY dnsdist.debian-and-ubuntu /etc/apt/preferences.d/dnsdist
 COPY pdns.debian-and-ubuntu /etc/apt/preferences.d/pdns
 COPY pdns.list.$RELEASE.$OS-$VERSION /etc/apt/sources.list.d/pdns.list
 
 RUN curl https://repo.powerdns.com/FD380FBB-pub.asc | apt-key add -
+RUN curl https://repo.powerdns.com/CBC8B383-pub.asc | apt-key add -
 RUN apt-get update
 RUN apt-get install -y $PKG
 EOF
@@ -160,7 +167,7 @@ elif [ "$RELEASE" = "auth-41" ]; then
     write_ubuntu trusty pdns-server pdns_server
     write_ubuntu xenial pdns-server pdns_server
     write_ubuntu bionic pdns-server pdns_server
-elif [ "$RELEASE" = "auth-42" -o "$RELEASE" = "auth-43" ]; then
+elif [ "$RELEASE" = "auth-42" -o "$RELEASE" = "auth-43" -o "$RELEASE" = "auth-master" ]; then
     write_centos 6 pdns pdns_server
     write_centos 7 pdns pdns_server
     write_centos 8 pdns pdns_server
@@ -183,7 +190,15 @@ elif [ "$RELEASE" = "rec-41" ]; then
     write_ubuntu trusty pdns-recursor pdns_recursor
     write_ubuntu xenial pdns-recursor pdns_recursor
     write_ubuntu bionic pdns-recursor pdns_recursor
-elif [ "$RELEASE" = "rec-42" -o "$RELEASE" = "rec-43" -o "$RELEASE" = "rec-44" ]; then
+elif [ "$RELEASE" = "rec-42" ]; then
+    write_centos 6 pdns-recursor pdns_recursor
+    write_centos 7 pdns-recursor pdns_recursor
+    write_centos 8 pdns-recursor pdns_recursor
+    write_debian stretch pdns-recursor pdns_recursor
+    write_debian buster pdns-recursor pdns_recursor
+    write_ubuntu xenial pdns-recursor pdns_recursor
+    write_ubuntu bionic pdns-recursor pdns_recursor
+elif [ "$RELEASE" = "rec-43" -o "$RELEASE" = "rec-44" -o "$RELEASE" = "rec-master" ]; then
     write_centos 6 pdns-recursor pdns_recursor
     write_centos 7 pdns-recursor pdns_recursor
     write_centos 8 pdns-recursor pdns_recursor
@@ -191,7 +206,8 @@ elif [ "$RELEASE" = "rec-42" -o "$RELEASE" = "rec-43" -o "$RELEASE" = "rec-44" ]
     write_debian buster pdns-recursor pdns_recursor
     write_ubuntu xenial pdns-recursor pdns_recursor
     write_ubuntu bionic pdns-recursor pdns_recursor
-elif [ "$RELEASE" = "dnsdist-15" ]; then
+    write_ubuntu focal pdns-recursor pdns_recursor
+elif [ "$RELEASE" = "dnsdist-15" -o "$RELEASE" = "dnsdist-master" ]; then
     write_centos 6 dnsdist dnsdist
     write_centos 7 dnsdist dnsdist
     write_centos 8 dnsdist dnsdist
index 9921b9e89645c27f71356e0ac809a956fd372729..da30c02dfb2b6a5d1d44cc55497d5cd818d279ce 100755 (executable)
@@ -17,7 +17,7 @@ wget -c -N http://s3-us-west-1.amazonaws.com/umbrella-static/top-1m.csv.zip
 
 unzip top-1m.csv.zip
 
-numdomains="1000 5000 10000 50000 100000 500000 100000"
+numdomains="1000 5000 10000 50000 100000 500000"
 if [ ! -z "$1" ]; then
   numdomains="$1"
 fi
index 80445cb89485a3e822a29be6e96222ad3b0d564c..f1f10266c7773504c4a1451a1f109c5b0429e172 100755 (executable)
@@ -344,10 +344,9 @@ install_recursor() {
   run "cd .."
   run "wget http://s3-us-west-1.amazonaws.com/umbrella-static/top-1m.csv.zip"
   run "unzip top-1m.csv.zip -d ${TRAVIS_BUILD_DIR}/regression-tests"
-  run 'echo -e "deb [arch=amd64] http://repo.powerdns.com/ubuntu trusty-auth-master main" | sudo tee /etc/apt/sources.list.d/pdns.list'
-  run 'echo -e "Package: pdns-*\nPin: origin repo.powerdns.com\nPin-Priority: 9001" | sudo tee /etc/apt/preferences.d/pdns'
-  run 'curl https://repo.powerdns.com/CBC8B383-pub.asc | sudo apt-key add - '
-  run 'sudo apt-get update'
+  run 'wget https://downloads.powerdns.com/tmp/pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty.tar.bz2'
+  run 'tar xf pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty.tar.bz2'
+  run 'sudo dpkg -i pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty/pdns-server_4.2.0~rc2+master.255.g2bee14438-1pdns.trusty_amd64.deb pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty/pdns-tools_4.2.0~rc2+master.255.g2bee14438-1pdns.trusty_amd64.deb pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty/pdns-backend-bind_4.2.0~rc2+master.255.g2bee14438-1pdns.trusty_amd64.deb ; sudo apt-get -y install -f'
   run 'sudo apt-get -y install pdns-server pdns-tools'
   run "sudo service pdns stop"
   run 'for suffix in {1..40}; do sudo /sbin/ip addr add 10.0.3.$suffix/32 dev lo; done'
index cc1d83f9de747df172cb303b637a05e9a45e1228..8813ea89d99a359821a54338f3bd859a74465940 100644 (file)
@@ -1,6 +1,7 @@
 modules/gsqlite3backend/3.4.0_to_4.0.0_schema.sqlite3.sql
 modules/gsqlite3backend/4.0.0_to_4.2.0_schema.sqlite3.sql
 modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql
+modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql
 modules/gsqlite3backend/dnssec-3.x_to_3.4.0_schema.sqlite3.sql
 modules/gsqlite3backend/nodnssec-3.x_to_3.4.0_schema.sqlite3.sql
 modules/gsqlite3backend/schema.sqlite3.sql
index fd44e419422ee8936dc50f26177175164f0406f7..304916cf0263a82d3e3c73ffa05fa20fe6c5b023 100755 (executable)
@@ -55,7 +55,7 @@ override_dh_installinit:
 
 override_dh_install:
        dh_install
-       ./pdns/pdns_server --no-config --config | sed \
+       ./pdns/pdns_server --config=default | sed \
          -e 's!# module-dir=.*!!' \
          -e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/pdns.d!' \
          -e 's!# launch=.*!&\nlaunch=!' \
index cc1d83f9de747df172cb303b637a05e9a45e1228..8813ea89d99a359821a54338f3bd859a74465940 100644 (file)
@@ -1,6 +1,7 @@
 modules/gsqlite3backend/3.4.0_to_4.0.0_schema.sqlite3.sql
 modules/gsqlite3backend/4.0.0_to_4.2.0_schema.sqlite3.sql
 modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql
+modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql
 modules/gsqlite3backend/dnssec-3.x_to_3.4.0_schema.sqlite3.sql
 modules/gsqlite3backend/nodnssec-3.x_to_3.4.0_schema.sqlite3.sql
 modules/gsqlite3backend/schema.sqlite3.sql
index 95a38b4d1653d57c1492b7074470582b5387ea51..5cc5cbb7c92d5194476907e021c1e06bdf101d17 100755 (executable)
@@ -52,7 +52,7 @@ override_dh_installinit:
 
 override_dh_install:
        dh_install
-       ./pdns/pdns_server --no-config --config | sed \
+       ./pdns/pdns_server --config=default | sed \
          -e 's!# module-dir=.*!!' \
          -e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/pdns.d!' \
          -e 's!# launch=.*!&\nlaunch=!' \
index cc1d83f9de747df172cb303b637a05e9a45e1228..8813ea89d99a359821a54338f3bd859a74465940 100644 (file)
@@ -1,6 +1,7 @@
 modules/gsqlite3backend/3.4.0_to_4.0.0_schema.sqlite3.sql
 modules/gsqlite3backend/4.0.0_to_4.2.0_schema.sqlite3.sql
 modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql
+modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql
 modules/gsqlite3backend/dnssec-3.x_to_3.4.0_schema.sqlite3.sql
 modules/gsqlite3backend/nodnssec-3.x_to_3.4.0_schema.sqlite3.sql
 modules/gsqlite3backend/schema.sqlite3.sql
index 20c8df6e6a617b2ef7f94918cf376c342c77b203..28e42be93b5e535fba3384df67adde3181c33e48 100755 (executable)
@@ -51,7 +51,7 @@ override_dh_installinit:
 
 override_dh_install:
        dh_install
-       ./pdns/pdns_server --no-config --config | sed \
+       ./pdns/pdns_server --config=default | sed \
          -e 's!# module-dir=.*!!' \
          -e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/pdns.d!' \
          -e 's!# launch=.*!&\nlaunch=!' \
index 2956517fe73a1000924df3f5f3ddae56b519febe..1680d6c86e77ea2f86a81cacf056b65273dbce95 100755 (executable)
@@ -44,7 +44,7 @@ override_dh_auto_install:
        install -m 644 -t debian/pdns-recursor/usr/share/pdns-recursor/lua-config debian/lua-config/rootkeys.lua
        install -m 644 -t debian/pdns-recursor/etc/powerdns debian/recursor.lua
        rm -f debian/pdns-recursor/etc/powerdns/recursor.conf-dist
-       ./pdns_recursor --no-config --config | sed \
+       ./pdns_recursor --config=default | sed \
                -e 's!# config-dir=.*!config-dir=/etc/powerdns!' \
                -e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/recursor.d!' \
                -e 's!# local-address=.*!local-address=127.0.0.1!' \
@@ -53,6 +53,7 @@ override_dh_auto_install:
                -e 's!# setgid=.*!setgid=pdns!' \
                -e 's!# setuid=.*!setuid=pdns!' \
                -e 's!# hint-file=.*!&\nhint-file=/usr/share/dns/root.hints!' \
+               -e '/^# version-string=.*/d' \
                > debian/pdns-recursor/etc/powerdns/recursor.conf
 
 override_dh_strip:
index 63ce9f2b5f27cde172ab55bda2cfc7ffa56f5fd9..9f8872b027a6d7e839f10af7ba5143af837fc765 100755 (executable)
@@ -43,7 +43,7 @@ override_dh_auto_install:
        install -m 644 -t debian/pdns-recursor/usr/share/pdns-recursor/lua-config debian/lua-config/rootkeys.lua
        install -m 644 -t debian/pdns-recursor/etc/powerdns debian/recursor.lua
        rm -f debian/tmp/etc/powerdns/recursor.conf-dist
-       ./pdns_recursor --no-config --config | sed \
+       ./pdns_recursor --config=default | sed \
                -e 's!# config-dir=.*!config-dir=/etc/powerdns!' \
                -e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/recursor.d!' \
                -e 's!# local-address=.*!local-address=127.0.0.1!' \
@@ -52,6 +52,7 @@ override_dh_auto_install:
                -e 's!# setgid=.*!setgid=pdns!' \
                -e 's!# setuid=.*!setuid=pdns!' \
                -e 's!# hint-file=.*!&\nhint-file=/usr/share/dns/root.hints!' \
+               -e '/^# version-string=.*/d' \
                > debian/tmp/etc/powerdns/recursor.conf
 
 override_dh_strip:
index 2956517fe73a1000924df3f5f3ddae56b519febe..1680d6c86e77ea2f86a81cacf056b65273dbce95 100755 (executable)
@@ -44,7 +44,7 @@ override_dh_auto_install:
        install -m 644 -t debian/pdns-recursor/usr/share/pdns-recursor/lua-config debian/lua-config/rootkeys.lua
        install -m 644 -t debian/pdns-recursor/etc/powerdns debian/recursor.lua
        rm -f debian/pdns-recursor/etc/powerdns/recursor.conf-dist
-       ./pdns_recursor --no-config --config | sed \
+       ./pdns_recursor --config=default | sed \
                -e 's!# config-dir=.*!config-dir=/etc/powerdns!' \
                -e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/recursor.d!' \
                -e 's!# local-address=.*!local-address=127.0.0.1!' \
@@ -53,6 +53,7 @@ override_dh_auto_install:
                -e 's!# setgid=.*!setgid=pdns!' \
                -e 's!# setuid=.*!setuid=pdns!' \
                -e 's!# hint-file=.*!&\nhint-file=/usr/share/dns/root.hints!' \
+               -e '/^# version-string=.*/d' \
                > debian/pdns-recursor/etc/powerdns/recursor.conf
 
 override_dh_strip:
index 38aaff286ae2bc6d0e70ca4734b0ce5f972e55ba..33d9bf551a0a4b81c41ba9ca171d552d6640cd23 100644 (file)
@@ -238,7 +238,7 @@ make install DESTDIR=%{buildroot}
 %{__install} -D -p %{SOURCE1} %{buildroot}%{_initrddir}/pdns
 %endif
 
-%{buildroot}/usr/sbin/pdns_server --no-config --config | sed \
+%{buildroot}/usr/sbin/pdns_server --config=default | sed \
   -e 's!# daemon=.*!daemon=no!' \
   -e 's!# guardian=.*!guardian=no!' \
   -e 's!# launch=.*!&\\nlaunch=!' \
@@ -412,6 +412,7 @@ fi
 %doc modules/gsqlite3backend/3.4.0_to_4.0.0_schema.sqlite3.sql
 %doc modules/gsqlite3backend/4.0.0_to_4.2.0_schema.sqlite3.sql
 %doc modules/gsqlite3backend/4.2.0_to_4.3.0_schema.sqlite3.sql
+%doc modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql
 %{_libdir}/%{name}/libgsqlite3backend.so
 
 %if 0%{?rhel} >= 7
index aa930841cfd96d451b23b3b96f953490c767a874..077615306909553428d34de66ae435407cee5326 100644 (file)
@@ -19,7 +19,7 @@ AM_SILENT_RULES([yes])
 AC_CANONICAL_HOST
 # Add some default CFLAGS and CXXFLAGS, can be appended to using the environment variables
 CFLAGS="-g -O2 -Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls $CFLAGS"
-CXXFLAGS="-g -O2 -Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls $CXXFLAGS"
+CXXFLAGS="-std=c++11 -g -O2 -Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls $CXXFLAGS"
 
 AC_PROG_CC
 AM_PROG_CC_C_O
@@ -48,7 +48,7 @@ PTHREAD_SET_NAME
 PDNS_WITH_LUA([mandatory])
 PDNS_CHECK_LUA_HPP
 
-AX_CXX_COMPILE_STDCXX_11
+AX_CXX_COMPILE_STDCXX_11([noext], [mandatory])
 
 AC_MSG_CHECKING([whether we will enable compiler security checks])
 AC_ARG_ENABLE([hardening],
index d3405aeda042addf7665fad2c78c9073d6cea52c..7ecf09429214dc86674f699739df5d6fa1cc6b11 100644 (file)
@@ -15,12 +15,12 @@ have pdnsutil && {
   _pdnsutil_helper_local_() {
     local cur prev cmd
 
-    local _PDNSUTIL_ALL_CMDS="activate-tsig-key activate-zone-key add-record add-zone-key backend-cmd b2b-migrate bench-db change-slave-zone-master check-zone check-all-zones clear-zone
-                              create-bind-db create-slave-zone create-zone deactivate-tsig-key deactivate-zone-key delete-rrset delete-tsig-key delete-zone disable-dnssec
-                              edit-zone export-zone-dnskey export-zone-key generate-tsig-key generate-zone-key get-meta hash-zone-record increase-serial import-tsig-key
-                              import-zone-key load-zone list-algorithms list-keys list-zone list-all-zones list-tsig-keys rectify-zone rectify-all-zones remove-zone-key
-                              replace-rrset secure-all-zones secure-zone set-kind set-nsec3 set-presigned set-publish-cdnskey set-publish-cds set-meta show-zone
-                              unset-nsec3 unset-presigned unset-publish-cdnskey unset-publish-cds test-schema"
+    local _PDNSUTIL_ALL_CMDS="activate-tsig-key activate-zone-key add-record add-supermaster add-zone-key backend-cmd b2b-migrate bench-db change-slave-zone-master 
+                              check-zone check-all-zones clear-zone create-bind-db create-slave-zone create-zone deactivate-tsig-key deactivate-zone-key delete-rrset 
+                              delete-tsig-key delete-zone disable-dnssec edit-zone export-zone-dnskey export-zone-key generate-tsig-key generate-zone-key get-meta 
+                              hash-zone-record increase-serial import-tsig-key import-zone-key load-zone list-algorithms list-keys list-zone list-all-zones 
+                              list-tsig-keys rectify-zone rectify-all-zones remove-zone-key replace-rrset secure-all-zones secure-zone set-kind set-nsec3 set-presigned 
+                              set-publish-cdnskey set-publish-cds set-meta show-zone unset-nsec3 unset-presigned unset-publish-cdnskey unset-publish-cds test-schema"
     COMPREPLY=()
     cur="${COMP_WORDS[COMP_CWORD]}"
     prev="${COMP_WORDS[COMP_CWORD-1]}"
index 5386818597e1ae90d8607478d5218a302a2aee0d..c3b04dafb81e49698253b4970c6300b3647b2492 100644 (file)
@@ -1,6 +1,12 @@
 End of life statements
 ======================
 
+We aim to have a release every six months.
+The latest and previous release receive correctness, stability and security updates.
+The release before that gets critical security updates only.
+Older releases are marked end of life and receive no updates at all.
+Pre-releases do not receive immediate security updates.
+
 The currently supported release train of PowerDNS Authoritative Server is 4.3.
 
 PowerDNS Authoritative Server 4.2 will only receive correctness, stability and security updates and will be receiving security updates only after PowerDNS Authoritative Server 4.4 is released.
@@ -15,6 +21,30 @@ Note: Users with a commercial agreement with PowerDNS.COM BV or Open-Xchange
 can receive extended support for releases which are End Of Life. If you are
 such a user, these EOL statements do not apply to you.
 
+.. list-table:: PowerDNS Authoritative Server Release Life Cycle
+   :header-rows: 1
+
+   * - Version
+     - Release date
+     - Security-Only updates
+     - End of Life
+   * - 4.3
+     - April 7 2020
+     - ~ April 2021
+     - ~ October 2021
+   * - 4.2
+     - August 30 2019
+     - ~ October 2020
+     - ~ April 2021
+   * - 4.1
+     - November 30 2017
+     - April 7 2020
+     - ~ October 2020
+   * - 4.0 and older
+     - EOL
+     - EOL
+     - EOL
+
 PowerDNS Authoritative Server 3.x
 ---------------------------------
 1st of December 2017
index e464cd0c888c47305ca90a88bce5472895a6da55..056ad3a0690320ce5c5b269636bf1ac6b95ae918 100644 (file)
@@ -59,7 +59,7 @@ Also, check that the configured backend is master or slave capable and you enter
 My masters won't allow PowerDNS to access zones as it is using the wrong local IP address
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 By default, PowerDNS lets the kernel pick the source address.
-To set an explicit source address, use the :ref:`setting-query-local-address` and :ref:`setting-query-local-address6` settings.
+To set an explicit source address, use the :ref:`setting-query-local-address` setting.
 
 PowerDNS does not answer queries on all my IP addresses (and I've ignored the warning I got about that at startup)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 18a8015e2e3f456c53948d381d1cfdf188e3030e..002ef194dc6f73d550c14b53f525fdd2078b3964 100644 (file)
@@ -2,8 +2,9 @@ Backend writers' guide
 ======================
 
 PowerDNS backends are implemented via a simple yet powerful C++
-interface. If your needs are not met by the PipeBackend, you may want to
-write your own. Before doing any PowerDNS development, please read `this blog
+interface. If your needs are not met by the regular backends, including
+the PipeBackend and the RemoteBackend, you may want to write your own.
+Before doing any PowerDNS development, please read `this blog
 post <https://blog.powerdns.com/2015/06/23/what-is-a-powerdns-backend-and-how-do-i-make-it-send-an-nxdomain/>`__
 which has a FAQ and several pictures that help explain what a backend
 is.
@@ -117,12 +118,26 @@ default ``getSOA()`` method performs a regular lookup on your backend to
 figure out the SOA, so if you have no special treatment for SOA records,
 where is no need to implement your own ``getSOA()``.
 
+Figuring out the Start of Authority can require an important number of
+call to ``getSOA()`` if the name has a lot of labels. For example,
+figuring out that the SOA for ``2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.``
+is ``d.0.1.0.0.2.ip6.arpa.`` might involve 26 calls, chopping off one label
+at a time. If your backend has an efficient way to figure out the
+best SOA it has for a given name, it is possible to override the
+default ``getSOA()`` implementation to immediately return the
+``d.0.1.0.0.2.ip6.arpa.`` SOA record to the first
+``2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.``
+``getSOA()`` call.
+
 Besides direct queries, PowerDNS also needs to be able to list a zone,
 to do zone transfers for example. Each zone has an id which should be
-unique within the backend. To list all records belonging to a zone id,
+unique within the backends. To list all records belonging to a zone id,
 the ``list()`` method is used. Conveniently, the domain_id is also
 available in the ``SOAData`` structure.
 
+.. warning::
+  Each zone should have a unique id, even across backends.
+
 The following lists the contents of a zone called "powerdns.com".
 
 .. code-block:: cpp
@@ -210,9 +225,31 @@ furthermore, only about its A record:
     static RandomLoader randomloader;
 
 This simple backend can be used as an 'overlay'. In other words, it only
-knows about a single record, another loaded backend would have to know
-about the SOA and NS records and such. But nothing prevents us from
-loading it without another backend.
+knows about a single name, ``random.powerdns.com``, another loaded backend
+would have to know about the SOA and NS records for the ``powerdns.com`` zone
+and such.
+
+.. warning::
+  Spreading the content of a zone across multiple backends, described above
+  as 'overlay', makes the zone incompatible with some operations that
+  assume that a single zone is always entirely stored in the same backend.
+  Such operations include zone transfers, listing and editing zone content via
+  the API or ``pdnsutil``.
+
+.. warning::
+  When the content of a zone is spread across multiple backends, all the types
+  for a given name should be delegated to the same backend.
+  For example a backend can know about all the types for ``random.powerdns.com``
+  while another backend knows about all the types for ``random2.powerdns.com``,
+  but it is not possible to let one backend handle only ``AAAA`` queries for
+  all names while another one handles only ``A`` queries, for example.
+  This limitation comes from the fact that PowerDNS uses ``ANY`` queries to fetch
+  all types from the backend in one go and that it assumes that once one backend
+  has returned records the other ones do not need to be called.
+  It is also possible to have two backends providing records for the same name
+  and types, for example if the first one does not support DNSSEC and the second
+  does, but that requires some mechanism outside of PowerDNS to keep records in
+  sync between the two backends.
 
 The first part of the code contains the actual logic and should be
 pretty straightforward. The second part is a boilerplate 'factory' class
@@ -316,7 +353,7 @@ Classes
 Methods
 ~~~~~~~
 
-.. cpp:function:: void DNSBackend::lookup(const QType &qtype, const string &qdomain, DNSPacket *pkt=0, int zoneId=-1)
+.. cpp:function:: void DNSBackend::lookup(const QType &qtype, const string &qdomain, DNSPacket *pkt=nullptr, int zoneId=-1)
 
   This function is used to initiate a straight lookup for a record of name
   'qdomain' and type 'qtype'. A QType can be converted into an integer by
@@ -327,6 +364,11 @@ Methods
   is, you can retrieve information about who asked the question with the
   ``pkt->getRemote()`` method.
 
+  .. note::
+    Since 4.1.0, 'SOA' lookups are not passed this pointer anymore because
+    PowerDNS doesn't support tailoring whether a whole zone exists or not based
+    on who is asking.
+
   Note that **qdomain** can be of any case and that your backend should
   make sure it is in effect case insensitive. Furthermore, the case of the
   original question should be retained in answers returned by ``get()``!
@@ -397,7 +439,7 @@ message won't be visible otherwise.
 To indicate the importance of an error, the standard syslog errorlevels
 are available. They can be set by outputting ``Logger::Critical``,
 ``Logger::Error``, ``Logger::Warning``, ``Logger::Notice``,
-``Logger::Info`` or ``Logger::Debug`` to ``L``, in descending order of
+``Logger::Info`` or ``Logger::Debug`` to ``g_log``, in descending order of
 graveness.
 
 Declaring and reading configuration details
@@ -630,7 +672,7 @@ The actual code in PowerDNS is currently:
 
         while(resolver.axfrChunk(recs)) {
           for(Resolver::res_t::const_iterator i=recs.begin();i!=recs.end();++i) {
-        db->feedRecord(*i);
+            db->feedRecord(*i);
           }
         }
         db->commitTransaction();
@@ -771,6 +813,147 @@ other update/remove functionality at a later stage.
   must be added to the zone. ``rrset`` can be empty in which case the
   method is used to remove a RRset.
 
+Domain metadata support
+-----------------------
+
+As described in :ref:`per-zone-settings-domain-metadata`, each served zone can have “metadata”. Such metadata determines how this zone behaves in certain circumstances.
+In order for a backend to support domain metadata, the following operations have to be implemented:
+
+.. code-block:: cpp
+
+    class DNSBackend {
+    public:
+      /* ... */
+      virtual bool getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta);
+      virtual bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta);
+      virtual bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta);
+      /* ... */
+    }
+
+.. cpp:function:: virtual bool getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta)
+
+  Fills 'meta' with the value(s) of all kinds for zone 'name'. Returns true if the domain metadata operation are supported, regardless
+  of whether there is any data for this zone.
+
+.. cpp:function:: virtual bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta)
+
+  Fills 'meta' with the value(s) of the specified kind for zone 'name'. Returns true if the domain metadata operation are supported, regardless
+  of whether there is any data of this kind for this zone.
+
+.. cpp:function:: virtual bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta)
+
+  Store the values from 'meta' for the specified kind for zone 'name', discarding existing values if any. An empty meta is equivalent to a deletion request.
+  Returns true if the values have been correctly stored, and false otherwise.
+
+TSIG keys
+---------
+
+In order for a backend to support the storage of TSIG keys, the following operations have to be implemented:
+
+.. code-block:: cpp
+
+    class DNSBackend {
+    public:
+      /* ... */
+      virtual bool getTSIGKey(const DNSName& name, DNSName* algorithm, string* content);
+      virtual bool setTSIGKey(const DNSName& name, const DNSName& algorithm, const string& content);
+      virtual bool deleteTSIGKey(const DNSName& name);
+      virtual bool getTSIGKeys(std::vector< struct TSIGKey > &keys);
+      /* ... */
+    }
+
+DNSSEC support
+--------------
+
+In order for a backend to support DNSSEC, quite a few number of additional operations have to be implemented:
+
+.. code-block:: cpp
+
+    struct KeyData {
+      std::string content;
+      unsigned int id;
+      unsigned int flags;
+      bool active;
+      bool published;
+    };
+
+    class DNSBackend {
+    public:
+      /* ... */
+      virtual bool doesDNSSEC();
+      virtual bool getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after);
+
+      /* update operations */
+      virtual bool updateDNSSECOrderNameAndAuth(uint32_t domain_id, const DNSName& qname, const DNSName& ordername, bool auth, const uint16_t qtype=QType::ANY);
+      virtual bool updateEmptyNonTerminals(uint32_t domain_id, set<DNSName>& insert, set<DNSName>& erase, bool remove);
+      virtual bool feedEnts(int domain_id, map<DNSName,bool> &nonterm);
+      virtual bool feedEnts3(int domain_id, const DNSName &domain, map<DNSName,bool> &nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow);
+
+      /* keys management */
+      virtual bool getDomainKeys(const DNSName& name, std::vector<KeyData>& keys);
+      virtual bool removeDomainKey(const DNSName& name, unsigned int id);
+      virtual bool addDomainKey(const DNSName& name, const KeyData& key, int64_t& id);
+      virtual bool activateDomainKey(const DNSName& name, unsigned int id);
+      virtual bool deactivateDomainKey(const DNSName& name, unsigned int id);
+      virtual bool publishDomainKey(const DNSName& name, unsigned int id);
+      virtual bool unpublishDomainKey(const DNSName& name, unsigned int id);
+
+      /* ... */
+    }
+
+.. cpp:function:: virtual bool doesDNSSEC()
+
+  Returns true if that backend supports DNSSEC.
+
+.. cpp:function:: virtual bool getBeforeAndAfterNamesAbsolute(uint32_t id, const DNSName& qname, DNSName& unhashed, DNSName& before, DNSName& after)
+
+  Asks the names before and after qname for NSEC and NSEC3. The qname will be hashed when using NSEC3. Care must be taken to handle wrap-around when qname is the first or last in the ordered list of zone names.
+
+.. cpp:function:: virtual bool updateDNSSECOrderNameAndAuth(uint32_t domain_id, const DNSName& qname, const DNSName& ordername, bool auth, const uint16_t qtype=QType::ANY)
+
+  Updates the ordername and auth fields.
+
+.. cpp:function:: virtual bool updateEmptyNonTerminals(uint32_t domain_id, set<DNSName>& insert, set<DNSName>& erase, bool remove)
+
+  Updates ENT after a zone has been rectified. If 'remove' is false, 'erase' contains a list of ENTs to remove from the zone before adding any. Otherwise all ENTs should be removed from the zone before adding any. 'insert' contains the list of ENTs to add to the zone after the removals have been done.
+
+.. cpp:function:: virtual bool feedEnts(int domain_id, map<DNSName,bool> &nonterm)
+
+  This method is used by ``pdnsutil rectify-zone`` to populate missing non-terminals. This is used when you have, say, record like _sip._upd.example.com, but no _udp.example.com. PowerDNS requires that there exists a non-terminal in between, and this instructs you to add one.
+
+.. cpp:function:: virtual bool feedEnts3(int domain_id, const DNSName &domain, map<DNSName,bool> &nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow)
+
+  Same as feedEnts, but provides NSEC3 hashing parameters.
+
+.. cpp:function:: virtual bool getDomainKeys(const DNSName& name, std::vector<KeyData>& keys)
+
+  Retrieves all DNSSEC keys. Content must be valid key record in format that PowerDNS understands.
+
+.. cpp:function:: virtual bool removeDomainKey(const DNSName& name, unsigned int id)
+
+  Removes this key.
+
+.. cpp:function:: virtual bool addDomainKey(const DNSName& name, const KeyData& key, int64_t& id)
+
+  Adds a new DNSSEC key for this domain.
+
+.. cpp:function:: virtual bool activateDomainKey(const DNSName& name, unsigned int id)
+
+  Activates an inactive DNSSEC key for this domain.
+
+.. cpp:function:: virtual bool deactivateDomainKey(const DNSName& name, unsigned int id)
+
+  Deactivates an active DNSSEC key for this domain.
+
+.. cpp:function:: virtual bool publishDomainKey(const DNSName& name, unsigned int id)
+
+  Publishes a previously hidden DNSSEC key for this domain.
+
+.. cpp:function:: virtual bool unpublishDomainKey(const DNSName& name, unsigned int id)
+
+  Hides a DNSSEC key for this domain. Hidden DNSSEC keys are used for signing but do not appear in the actual zone,
+  and are useful for rollover operations.
+
 Miscellaneous
 -------------
 
index 8ea0d0a260e054135f41bf5d15b39cd3b71e6767..4890668999214b0d137aa2195f93172aa4b642ca 100644 (file)
@@ -75,6 +75,8 @@ slave DNSSEC-enabled domains (where the RRSIGS are in the AXFR), a
 :ref:`metadata-presigned` domain metadata is set
 during the zonetransfer.
 
+You can use ``pdnsutil create-bind-db`` to make this database file for you.
+
 .. warning::
    If this is left empty on slaves and a presigned zone is transferred,
    it will (silently) serve it without DNSSEC. This in turn results in
index f65b4bf970316465e1f73423e7b3803d9233b992..96cb1723adb5a99bb74ed4ac8a9bb35dd9fb49a2 100644 (file)
@@ -91,6 +91,17 @@ parameters are documented `in the PostgreSQL
 documentation <https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PARAMKEYWORDS>`__.
 Default: "".
 
+.. _setting-gpgsql-prepared-statements:
+
+``gpgsql-prepared-statements``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Prepare statements for better performance, instead of sending parameterized queries.
+Might not work with connection poolers.
+Default: yes.
+
+.. versionadded:: 4.4.0
+
 Default schema
 --------------
 
@@ -98,3 +109,70 @@ This is the 4.3 schema. Please find `the 4.2 schema <https://github.com/PowerDNS
 
 .. literalinclude:: ../../modules/gpgsqlbackend/schema.pgsql.sql
    :language: SQL
+
+CockroachDB
+-----------
+
+`CockroachDB <https://www.cockroachlabs.com/docs/stable/architecture/overview.html>`__ is a highly available, resilient database that focuses on scaling and consistency. Specifically: it offers a PostgreSQL like database interface,
+which means that most tools that talk the PostgreSQL protocol can use it.
+
+A few changes are needed on top of the generic PostgreSQL settings. CockroachDB does not natively support the range operators that some PowerDNS database queries use,
+and care must be taken that table index columns do not exceed the internal maximum integer size that PowerDNS uses.
+
+Schema differences
+^^^^^^^^^^^^^^^^^^
+
+Given the normal pgsql schema, change the following:
+
+1. Add explicit SEQUENCEs for all SERIAL columns:
+
+.. code-block:: SQL
+
+  CREATE SEQUENCE domain_id MAXVALUE 2147483648;
+  CREATE SEQUENCE record_id MAXVALUE 2147483648;
+  CREATE SEQUENCE comment_id MAXVALUE 2147483648;
+  CREATE SEQUENCE meta_id MAXVALUE 2147483648;
+  CREATE SEQUENCE key_id MAXVALUE 2147483648;
+  CREATE SEQUENCE tsig_id MAXVALUE 2147483648;
+
+2. Change all SERIAL / BIGSERIAL columns to use the SEQUENCEs. For instance:
+
+.. code-block:: SQL
+
+  -- Before
+  CREATE TABLE domains (
+    id SERIAL PRIMARY KEY,
+    --
+  }
+
+  -- After
+  CREATE TABLE domains (
+    id INT DEFAULT nextval('domain_id') PRIMARY KEY,
+    --
+  );
+
+
+3. Do **not** add the following index to the records table, the text_pattern_ops operator class is not supported:
+
+.. code-block:: SQL
+
+  CREATE INDEX recordorder ON records (domain_id, ordername text_pattern_ops);
+
+
+Configuration changes
+^^^^^^^^^^^^^^^^^^^^^
+
+Four queries must be overridden in the PowerDNS config, because by default they use a range operator that is not supported. These modified queries are actually
+taken from the generic MySQL backend, and modified for syntax:
+
+.. code-block:: ini
+
+  gpgsql-get-order-first-query=select ordername from records where domain_id = $1 and disabled = false and ordername is not null order by 1 asc limit 1
+  gpgsql-get-order-before-query=select ordername, name from records where ordername <= $1 and domain_id = $2 and disabled = false and ordername is not null order by 1 desc limit 1
+  gpgsql-get-order-after-query=select ordername from records where ordername > $1 and domain_id = $2 and disabled = false and ordername is not null order by 1 asc limit 1
+  gpgsql-get-order-last-query=select ordername, name from records where ordername != '' and domain_id = $1 and disabled = false and ordername is not null order by 1 desc limit 1
+
+References
+^^^^^^^^^^
+
+See `this Github issue <https://github.com/PowerDNS/pdns/issues/5375#issuecomment-644771800>`__ for the original tests and a full working schema.
index 66d17725114d01339656b527f4d1925c0891e67f..b056d607ba92f301669f3f5be5e496b0ddbd1310 100644 (file)
@@ -190,7 +190,7 @@ the C function 'snprintf' which implies that substitutions are performed
 on the basis of %-placeholders.
 
 To see the default queries for a backend, run
-``pdns_server --no-config --launch=BACKEND --config``.
+``pdns_server --launch=BACKEND --config=default``.
 
 Regular Queries
 ^^^^^^^^^^^^^^^
index 4b519a0e8cbc06e8f8370b325013f3d5c0b3c486..7212a97ee7fc8c0abe122da63a54e0265f96eb39 100644 (file)
@@ -13,7 +13,7 @@ Changelogs for 4.2.x
     fix records ending up in wrong packet section (Kees Monshouwer)
 
   .. change::
-    :tags: Improvents
+    :tags: Improvements
     :pullreq: 9003, 8736
 
     cache: strictly enforce maximum size, and improve cleanup routine
@@ -81,6 +81,14 @@ Changelogs for 4.2.x
 
     API: reduce number of database connections (Kees Monshouwer)
 
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 8497
+
+    Clear the caches for the entire zone after a patch operation (was apex only).
+    The default default-api-rectify setting was ignored in patchZone(), rectify only took place when the API-RECTIFY metadata was set to "1".
+    (Kees Monshouwer)
+
   .. change::
     :tags: Improvements
     :pullreq: 8546
index 2325d0955377aafc142943a48b6a42de681edfc3..b7ad8da7bcb39f10b388255b8cf0e453965e3aa9 100644 (file)
@@ -4,57 +4,106 @@ PKCS#11 support
 .. note::
   This feature is experimental, use at your own risk!
 
-.. deprecated:: 4.0.0
-  slot IDs are deprecated, and you are expected to use slot label instead
+To enable it, compile PowerDNS Authoritative Server using ``--enable-experimental-pkcs11`` flag on configure.
+This requires you to have the p11-kit libraries and headers.
 
-To enable it, compile PowerDNS Authoritative Server using
-``--enable-experimental-pkcs11`` flag on configure. This requires you to
-have p11-kit libraries and headers.
+You can also log on to the tokens after starting the server, in this case you need to edit your PKCS#11 cryptokey record and remove PIN or set it empty.
+Do this after assigning/creating a key, as the PIN is required for assigning keys to zone.
 
-You can also log on to the tokens after starting server, in this case
-you need to edit your PKCS#11 cryptokey record and remove PIN or set it
-empty. PIN is required for assigning keys to zone.
-
-Using with SoftHSM
-------------------
+Using PKCS#11 with SoftHSM
+--------------------------
 
 .. warning::
-  Due to an interaction between `SoftHSM and Botan <https://github.com/PowerDNS/pdns/issues/2496>`__,
-  the PowerDNS Authoritative Server **will most likely** crash on exit when built with ``--enable-botan1.10 --enable-experimental-pkcs11``.
+  Due to an interaction between `SoftHSM and Botan <https://github.com/PowerDNS/pdns/issues/2496>`__, the PowerDNS Authoritative Server **will most likely** crash on exit when built with ``--enable-botan1.10 --enable-experimental-pkcs11``.
   In 4.2.0, Botan support has been removed and this is no longer an issue.
 
-To test this feature, a software HSM can be used. It is **not
-recommended** to use this in production.
+To test this feature, a software HSM can be used.
+It is **not recommended** to do this in production.
 
-Instructions on how to setup SoftHSM to work with the feature after
-compilation on Ubuntu/Debian (tested with Ubuntu 12.04 and 14.04).
+These instructions have been tested on Debian 10 (Buster).
 
-- ``apt-get install softhsm p11-kit opensc``
-- create directory ``/etc/pkcs11/modules``
-- create a file ``softhsm`` (``softhsm.module`` on newer versions),
-  with contents:::
+- ``apt-get install softhsm p11-kit``
+- Verify that it works: ``p11-kit -l``, you should see ``softhsm2: .....``
+- Create a token::
 
-    module: /home/cmouse/softhsm/lib/softhsm/libsofthsm.so     managed: yes
+    softhsm2-util --init-token --label my-pkcs11-dnskey --free --pin 1234 --so-pin 1234
 
-- Verify that it works: ``p11-kit -l``
-- Create at least two tokens (ksk and zsk) with (slot-number starts from 0)::
+- Assign the token to a zone (it says KSK, but because there is no ZSK, this will become a CSK)::
 
-    sudo softhsm --init-token --slot slot-number --label zone-ksk|zone-zsk --pin some-pin --so-pin another-pin
+    pdnsutil hsm assign example.com ecdsa256 ksk softhsm2 my-pkcs11-dnskey 1234 'my key' 'my pub key'
 
--  Using pkcs11-tool, initialize your new keys.::
+- Create the key (for 25, use the ID shown by the previous command)::
 
-    sudo pkcs11-tool --module=/home/cmouse/softhsm/lib/softhsm/libsofthsm.so -l -p some-pin -k --key-type RSA:2048 -a zone-ksk|zone-zsk --slot-index slot-number
+    pdnsutil hsm create-key example.com 25
 
--  Assign the keys using (note that token label is not necessarily same
-   as object label, see p11-kit -l)::
+-  Verify that everything worked, you should see valid data there::
 
-    pdnsutil hsm assign zone rsasha256 ksk|zsk softhsm token-label pin zone-ksk|zsk
+    pdnsutil show-zone example.com
 
--  Verify that everything worked, you should see valid data there::
+SoftHSM2 with forwarding
+------------------------
 
-    pdnsutil show-zone zone
+Based on https://p11-glue.github.io/p11-glue/p11-kit/manual/remoting.html.
+
+You need to install ``gnutls-bin`` to get token URLs.
+
+You cannot run ``p11-kit server`` as root, so you will need some user for running it. This user must be in the ``softhsm`` group.
+
+These commands need to be run as the non-root user (we shall call it ``tokenuser``).
+
+First, set up your token::
+
+   softhsm2-util --init-token --label "ecdsa#1" --pin 1234 --so-pin 1234 --free
+   pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so --keypairgen --key-type EC:prime256v1 --pin 1234 -a 'my key' --token-label "ecdsa#1"
+
+Ensure it's there::
+
+   pkcs11-tool --module /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so -l -O -p 1234
+
+Get the URL for ``p11-kit server``, which is needed for the server::
+
+   p11tool --provider /usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-client.so --list-tokens
+
+Set up forwarding::
+
+  cat <<EOF > /etc/pkcs11/modules/p11-kit-client.module
+  module: /usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-client.so
+  EOF
+
+  p11-kit server -u pdns --provider /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so "pkcs11:model=SoftHSM%20v2;manufacturer=SoftHSM%20project;serial=29fdc44dc0d61539;token=ecdsa%231"
+  P11_KIT_SERVER_ADDRESS=unix:path=/run/user/1000/p11-kit/pkcs11-5198; export P11_KIT_SERVER_ADDRESS;
+  P11_KIT_SERVER_PID=5199; export P11_KIT_SERVER_PID;
+
+You will need those values in PowerDNS running environment. Now you can verify that the token is reachable as ``pdns`` user with::
+
+  pkcs11-tool --module /usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-client.so -T
+  Available slots:
+  Slot 0 (0x10): SoftHSM slot ID 0x40d61539
+    token label        : ecdsa#1
+    token manufacturer : SoftHSM project
+    token model        : SoftHSM v2
+    token flags        : login required, rng, token initialized, PIN initialized, other flags=0x20
+    hardware version   : 2.5
+    firmware version   : 2.5
+    serial num         : 29fdc44dc0d61539
+    pin min/max        : 4/255
+
+Then assign the HSM token to your zone with::
+
+  pdnsutil hsm assign example.com ecdsa256 ksk p11-kit-client 'ecdsa#1' 1234 'my key'
+
+And then verify with ``show-zone`` that the zone now has a valid key.
+
+You can do this over SSH as well (note that the example connects from token server to DNS server)::
+
+    ssh -R /var/run/pdns/pkcs11:${P11_KIT_SERVER_ADDRESS#*=} pdns@server
+    export P11_KIT_SERVER_ADDRESS=/var/run/pdns/pkcs11
+
+Verify that the token is visible::
+
+   pkcs11-tool --module /usr/lib/x86_64-linux-gnu/pkcs11/p11-kit-client.so -T
 
--  SoftHSM signatures are fast enough to be used in live environment.
+Then use the ``pdnsutil hsm assign`` command like before to assign the key to your zone; now you have DNSSEC over SSH.
 
 Using CryptAS
 -------------
@@ -96,8 +145,8 @@ Smart Card token on Ubuntu 14.04.
   Manager no longer can show your token certificates and keys, at least
   on version v6.23.04. ::
 
-    pkcs11-tool --module=/home/cmouse/softhsm/lib/softhsm/libsofthsm.so -l -p some-pin -k --key-type RSA:2048 -a zone-ksk
-    pkcs11-tool --module=/home/cmouse/softhsm/lib/softhsm/libsofthsm.so -l -p some-pin -k --key-type RSA:2048 -a zone-zsk
+    pkcs11-tool --module=/lib64/libASEP11.so -l -p some-pin -k --key-type RSA:2048 -a zone-ksk
+    pkcs11-tool --module=/lib64/libASEP11.so -l -p some-pin -k --key-type RSA:2048 -a zone-zsk
 
 - Verify that keys are there::
 
index a1392a28a36377590f82cfbe31f526f871ac5d98..55629cc56bb147137fd6f7769b78028b813ba67b 100644 (file)
@@ -518,3 +518,6 @@ Simple example script:
 
       return false
     end
+
+Additional updatepolicy example scripts can be found in our
+`Wiki <https://github.com/PowerDNS/pdns/wiki/Lua-Examples-(Authoritative)>`__.
index 9b3f59e0ba4639a97d421ecfe86474d168010868..2e86567fcf88dd86c0afa4a1058e69916dd28411 100644 (file)
@@ -8,6 +8,11 @@ zone behaves in certain circumstances.
   Domain metadata is only available for DNSSEC capable
   backends! Make sure to enable the proper '-dnssec' setting to benefit.
 
+.. warning::
+  When multiple backends are in use, domain metadata is only retrieved from
+  and written to the first DNSSEC-capable backend, no matter where the related
+  zones live.
+
 For the BIND backend, this information is either stored in the
 :ref:`setting-bind-dnssec-db` or the hybrid database,
 depending on your settings.
index ae62df5c748c9468114822ecdc3d0595f8585506..21b369c94ab42d456b9a8664e9229da9c6645a53 100644 (file)
@@ -21,6 +21,280 @@ The following webserver related configuration items are available:
 * :ref:`setting-webserver-allow-from`: Netmasks that are allowed to connect to the webserver
 * :ref:`setting-webserver-max-bodysize`: Maximum request/response body size in megabytes
 
+
+Metrics Endpoint
+----------------
+
+.. versionadded:: 4.4.0
+
+The webserver exposes a metrics-endpoint that follows the `prometheus exposition-format <https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md>`_ on path ``/metrics``.
+
+The metrics listed are equivalent to the variables section on the index-page of the webserver (prefixed with ``pdns_auth_`` and replacing dashes with underscores).
+
+A simple ``GET`` request will return a response similar to the following:
+
+.. http:get:: /metrics
+
+::
+
+  HTTP/1.1 200 OK
+  Connection: close
+  Content-Length: 12044
+  Content-Type: text/plain
+  Server: PowerDNS/0.0.19015.0.master.ge719aae4e8
+
+  # HELP pdns_auth_corrupt_packets Number of corrupt packets received
+  # TYPE pdns_auth_corrupt_packets counter
+  pdns_auth_corrupt_packets 0
+  # HELP pdns_auth_deferred_cache_inserts Amount of cache inserts that were deferred because of maintenance
+  # TYPE pdns_auth_deferred_cache_inserts counter
+  pdns_auth_deferred_cache_inserts 0
+  # HELP pdns_auth_deferred_cache_lookup Amount of cache lookups that were deferred because of maintenance
+  # TYPE pdns_auth_deferred_cache_lookup counter
+  pdns_auth_deferred_cache_lookup 0
+  # HELP pdns_auth_deferred_packetcache_inserts Amount of packet cache inserts that were deferred because of maintenance
+  # TYPE pdns_auth_deferred_packetcache_inserts counter
+  pdns_auth_deferred_packetcache_inserts 0
+  # HELP pdns_auth_deferred_packetcache_lookup Amount of packet cache lookups that were deferred because of maintenance
+  # TYPE pdns_auth_deferred_packetcache_lookup counter
+  pdns_auth_deferred_packetcache_lookup 0
+  # HELP pdns_auth_dnsupdate_answers DNS update packets successfully answered.
+  # TYPE pdns_auth_dnsupdate_answers counter
+  pdns_auth_dnsupdate_answers 0
+  # HELP pdns_auth_dnsupdate_changes DNS update changes to records in total.
+  # TYPE pdns_auth_dnsupdate_changes counter
+  pdns_auth_dnsupdate_changes 0
+  # HELP pdns_auth_dnsupdate_queries DNS update packets received.
+  # TYPE pdns_auth_dnsupdate_queries counter
+  pdns_auth_dnsupdate_queries 0
+  # HELP pdns_auth_dnsupdate_refused DNS update packets that are refused.
+  # TYPE pdns_auth_dnsupdate_refused counter
+  pdns_auth_dnsupdate_refused 0
+  # HELP pdns_auth_incoming_notifications NOTIFY packets received.
+  # TYPE pdns_auth_incoming_notifications counter
+  pdns_auth_incoming_notifications 0
+  # HELP pdns_auth_overload_drops Queries dropped because backends overloaded
+  # TYPE pdns_auth_overload_drops counter
+  pdns_auth_overload_drops 0
+  # HELP pdns_auth_packetcache_hit Number of hits on the packet cache
+  # TYPE pdns_auth_packetcache_hit counter
+  pdns_auth_packetcache_hit 0
+  # HELP pdns_auth_packetcache_miss Number of misses on the packet cache
+  # TYPE pdns_auth_packetcache_miss counter
+  pdns_auth_packetcache_miss 0
+  # HELP pdns_auth_packetcache_size Number of entries in the packet cache
+  # TYPE pdns_auth_packetcache_size gauge
+  pdns_auth_packetcache_size 0
+  # HELP pdns_auth_query_cache_hit Number of hits on the query cache
+  # TYPE pdns_auth_query_cache_hit counter
+  pdns_auth_query_cache_hit 0
+  # HELP pdns_auth_query_cache_miss Number of misses on the query cache
+  # TYPE pdns_auth_query_cache_miss counter
+  pdns_auth_query_cache_miss 0
+  # HELP pdns_auth_query_cache_size Number of entries in the query cache
+  # TYPE pdns_auth_query_cache_size gauge
+  pdns_auth_query_cache_size 0
+  # HELP pdns_auth_rd_queries Number of recursion desired questions
+  # TYPE pdns_auth_rd_queries counter
+  pdns_auth_rd_queries 0
+  # HELP pdns_auth_recursing_answers Number of recursive answers sent out
+  # TYPE pdns_auth_recursing_answers counter
+  pdns_auth_recursing_answers 0
+  # HELP pdns_auth_recursing_questions Number of questions sent to recursor
+  # TYPE pdns_auth_recursing_questions counter
+  pdns_auth_recursing_questions 0
+  # HELP pdns_auth_recursion_unanswered Number of packets unanswered by configured recursor
+  # TYPE pdns_auth_recursion_unanswered counter
+  pdns_auth_recursion_unanswered 0
+  # HELP pdns_auth_security_status Security status based on regular polling
+  # TYPE pdns_auth_security_status gauge
+  pdns_auth_security_status 0
+  # HELP pdns_auth_servfail_packets Number of times a server-failed packet was sent out
+  # TYPE pdns_auth_servfail_packets counter
+  pdns_auth_servfail_packets 0
+  # HELP pdns_auth_signatures Number of DNSSEC signatures made
+  # TYPE pdns_auth_signatures counter
+  pdns_auth_signatures 0
+  # HELP pdns_auth_tcp_answers Number of answers sent out over TCP
+  # TYPE pdns_auth_tcp_answers counter
+  pdns_auth_tcp_answers 0
+  # HELP pdns_auth_tcp_answers_bytes Total size of answers sent out over TCP
+  # TYPE pdns_auth_tcp_answers_bytes counter
+  pdns_auth_tcp_answers_bytes 0
+  # HELP pdns_auth_tcp_queries Number of TCP queries received
+  # TYPE pdns_auth_tcp_queries counter
+  pdns_auth_tcp_queries 0
+  # HELP pdns_auth_tcp4_answers Number of IPv4 answers sent out over TCP
+  # TYPE pdns_auth_tcp4_answers counter
+  pdns_auth_tcp4_answers 0
+  # HELP pdns_auth_tcp4_answers_bytes Total size of answers sent out over TCPv4
+  # TYPE pdns_auth_tcp4_answers_bytes counter
+  pdns_auth_tcp4_answers_bytes 0
+  # HELP pdns_auth_tcp4_queries Number of IPv4 TCP queries received
+  # TYPE pdns_auth_tcp4_queries counter
+  pdns_auth_tcp4_queries 0
+  # HELP pdns_auth_tcp6_answers Number of IPv6 answers sent out over TCP
+  # TYPE pdns_auth_tcp6_answers counter
+  pdns_auth_tcp6_answers 0
+  # HELP pdns_auth_tcp6_answers_bytes Total size of answers sent out over TCPv6
+  # TYPE pdns_auth_tcp6_answers_bytes counter
+  pdns_auth_tcp6_answers_bytes 0
+  # HELP pdns_auth_tcp6_queries Number of IPv6 TCP queries received
+  # TYPE pdns_auth_tcp6_queries counter
+  pdns_auth_tcp6_queries 0
+  # HELP pdns_auth_timedout_packets Number of packets which weren't answered within timeout set
+  # TYPE pdns_auth_timedout_packets counter
+  pdns_auth_timedout_packets 0
+  # HELP pdns_auth_udp_answers Number of answers sent out over UDP
+  # TYPE pdns_auth_udp_answers counter
+  pdns_auth_udp_answers 0
+  # HELP pdns_auth_udp_answers_bytes Total size of answers sent out over UDP
+  # TYPE pdns_auth_udp_answers_bytes counter
+  pdns_auth_udp_answers_bytes 0
+  # HELP pdns_auth_udp_do_queries Number of UDP queries received with DO bit
+  # TYPE pdns_auth_udp_do_queries counter
+  pdns_auth_udp_do_queries 0
+  # HELP pdns_auth_udp_queries Number of UDP queries received
+  # TYPE pdns_auth_udp_queries counter
+  pdns_auth_udp_queries 0
+  # HELP pdns_auth_udp4_answers Number of IPv4 answers sent out over UDP
+  # TYPE pdns_auth_udp4_answers counter
+  pdns_auth_udp4_answers 0
+  # HELP pdns_auth_udp4_answers_bytes Total size of answers sent out over UDPv4
+  # TYPE pdns_auth_udp4_answers_bytes counter
+  pdns_auth_udp4_answers_bytes 0
+  # HELP pdns_auth_udp4_queries Number of IPv4 UDP queries received
+  # TYPE pdns_auth_udp4_queries counter
+  pdns_auth_udp4_queries 0
+  # HELP pdns_auth_udp6_answers Number of IPv6 answers sent out over UDP
+  # TYPE pdns_auth_udp6_answers counter
+  pdns_auth_udp6_answers 0
+  # HELP pdns_auth_udp6_answers_bytes Total size of answers sent out over UDPv6
+  # TYPE pdns_auth_udp6_answers_bytes counter
+  pdns_auth_udp6_answers_bytes 0
+  # HELP pdns_auth_udp6_queries Number of IPv6 UDP queries received
+  # TYPE pdns_auth_udp6_queries counter
+  pdns_auth_udp6_queries 0
+  # HELP pdns_auth_cpu_iowait Time spent waiting for I/O to complete by the whole system, in units of USER_HZ
+  # TYPE pdns_auth_cpu_iowait counter
+  pdns_auth_cpu_iowait 2739
+  # HELP pdns_auth_cpu_steal Stolen time, which is the time spent by the whole system in other operating systems when running in a virtualized environment, in units of USER_HZ
+  # TYPE pdns_auth_cpu_steal counter
+  pdns_auth_cpu_steal 0
+  # HELP pdns_auth_fd_usage Number of open filedescriptors
+  # TYPE pdns_auth_fd_usage gauge
+  pdns_auth_fd_usage 26
+  # HELP pdns_auth_key_cache_size Number of entries in the key cache
+  # TYPE pdns_auth_key_cache_size gauge
+  pdns_auth_key_cache_size 0
+  # HELP pdns_auth_latency Average number of microseconds needed to answer a question
+  # TYPE pdns_auth_latency gauge
+  pdns_auth_latency 0
+  # HELP pdns_auth_meta_cache_size Number of entries in the metadata cache
+  # TYPE pdns_auth_meta_cache_size gauge
+  pdns_auth_meta_cache_size 0
+  # HELP pdns_auth_open_tcp_connections Number of currently open TCP connections
+  # TYPE pdns_auth_open_tcp_connections gauge
+  pdns_auth_open_tcp_connections 0
+  # HELP pdns_auth_qsize_q Number of questions waiting for database attention
+  # TYPE pdns_auth_qsize_q gauge
+  pdns_auth_qsize_q 0
+  # HELP pdns_auth_real_memory_usage Actual unique use of memory in bytes (approx)
+  # TYPE pdns_auth_real_memory_usage gauge
+  pdns_auth_real_memory_usage 133189632
+  # HELP pdns_auth_ring_logmessages_capacity Maximum number of entries in the logmessages ring
+  # TYPE pdns_auth_ring_logmessages_capacity gauge
+  pdns_auth_ring_logmessages_capacity 10000
+  # HELP pdns_auth_ring_logmessages_size Number of entries in the logmessages ring
+  # TYPE pdns_auth_ring_logmessages_size gauge
+  pdns_auth_ring_logmessages_size 7
+  # HELP pdns_auth_ring_noerror_queries_capacity Maximum number of entries in the noerror-queries ring
+  # TYPE pdns_auth_ring_noerror_queries_capacity gauge
+  pdns_auth_ring_noerror_queries_capacity 10000
+  # HELP pdns_auth_ring_noerror_queries_size Number of entries in the noerror-queries ring
+  # TYPE pdns_auth_ring_noerror_queries_size gauge
+  pdns_auth_ring_noerror_queries_size 0
+  # HELP pdns_auth_ring_nxdomain_queries_capacity Maximum number of entries in the nxdomain-queries ring
+  # TYPE pdns_auth_ring_nxdomain_queries_capacity gauge
+  pdns_auth_ring_nxdomain_queries_capacity 10000
+  # HELP pdns_auth_ring_nxdomain_queries_size Number of entries in the nxdomain-queries ring
+  # TYPE pdns_auth_ring_nxdomain_queries_size gauge
+  pdns_auth_ring_nxdomain_queries_size 0
+  # HELP pdns_auth_ring_queries_capacity Maximum number of entries in the queries ring
+  # TYPE pdns_auth_ring_queries_capacity gauge
+  pdns_auth_ring_queries_capacity 10000
+  # HELP pdns_auth_ring_queries_size Number of entries in the queries ring
+  # TYPE pdns_auth_ring_queries_size gauge
+  pdns_auth_ring_queries_size 0
+  # HELP pdns_auth_ring_remotes_capacity Maximum number of entries in the remotes ring
+  # TYPE pdns_auth_ring_remotes_capacity gauge
+  pdns_auth_ring_remotes_capacity 10000
+  # HELP pdns_auth_ring_remotes_corrupt_capacity Maximum number of entries in the remotes-corrupt ring
+  # TYPE pdns_auth_ring_remotes_corrupt_capacity gauge
+  pdns_auth_ring_remotes_corrupt_capacity 10000
+  # HELP pdns_auth_ring_remotes_corrupt_size Number of entries in the remotes-corrupt ring
+  # TYPE pdns_auth_ring_remotes_corrupt_size gauge
+  pdns_auth_ring_remotes_corrupt_size 0
+  # HELP pdns_auth_ring_remotes_size Number of entries in the remotes ring
+  # TYPE pdns_auth_ring_remotes_size gauge
+  pdns_auth_ring_remotes_size 0
+  # HELP pdns_auth_ring_remotes_unauth_capacity Maximum number of entries in the remotes-unauth ring
+  # TYPE pdns_auth_ring_remotes_unauth_capacity gauge
+  pdns_auth_ring_remotes_unauth_capacity 10000
+  # HELP pdns_auth_ring_remotes_unauth_size Number of entries in the remotes-unauth ring
+  # TYPE pdns_auth_ring_remotes_unauth_size gauge
+  pdns_auth_ring_remotes_unauth_size 0
+  # HELP pdns_auth_ring_servfail_queries_capacity Maximum number of entries in the servfail-queries ring
+  # TYPE pdns_auth_ring_servfail_queries_capacity gauge
+  pdns_auth_ring_servfail_queries_capacity 10000
+  # HELP pdns_auth_ring_servfail_queries_size Number of entries in the servfail-queries ring
+  # TYPE pdns_auth_ring_servfail_queries_size gauge
+  pdns_auth_ring_servfail_queries_size 0
+  # HELP pdns_auth_ring_unauth_queries_capacity Maximum number of entries in the unauth-queries ring
+  # TYPE pdns_auth_ring_unauth_queries_capacity gauge
+  pdns_auth_ring_unauth_queries_capacity 10000
+  # HELP pdns_auth_ring_unauth_queries_size Number of entries in the unauth-queries ring
+  # TYPE pdns_auth_ring_unauth_queries_size gauge
+  pdns_auth_ring_unauth_queries_size 0
+  # HELP pdns_auth_signature_cache_size Number of entries in the signature cache
+  # TYPE pdns_auth_signature_cache_size gauge
+  pdns_auth_signature_cache_size 0
+  # HELP pdns_auth_sys_msec Number of msec spent in system time
+  # TYPE pdns_auth_sys_msec counter
+  pdns_auth_sys_msec 56
+  # HELP pdns_auth_udp_in_errors UDP 'in' errors
+  # TYPE pdns_auth_udp_in_errors counter
+  pdns_auth_udp_in_errors 151
+  # HELP pdns_auth_udp_noport_errors UDP 'noport' errors
+  # TYPE pdns_auth_udp_noport_errors counter
+  pdns_auth_udp_noport_errors 9
+  # HELP pdns_auth_udp_recvbuf_errors UDP 'recvbuf' errors
+  # TYPE pdns_auth_udp_recvbuf_errors counter
+  pdns_auth_udp_recvbuf_errors 0
+  # HELP pdns_auth_udp_sndbuf_errors UDP 'sndbuf' errors
+  # TYPE pdns_auth_udp_sndbuf_errors counter
+  pdns_auth_udp_sndbuf_errors 9
+  # HELP pdns_auth_uptime Uptime of process in seconds
+  # TYPE pdns_auth_uptime counter
+  pdns_auth_uptime 672
+  # HELP pdns_auth_user_msec Number of msec spent in user time
+  # TYPE pdns_auth_user_msec counter
+  pdns_auth_user_msec 48
+
+
+Prometheus can then be configured to scrape metrics from this endpoint using a simple job description like the following:
+
+.. prometheus scrape-job::
+
+  scrape_configs:
+    - job_name: 'pdns_auth'
+      scrape_interval: 1m
+      static_configs:
+        - targets: ['pdns_auth_host:pdns_auth_ws_port'] 
+
+Further details can be gathered from the `prometheus docs <https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config>`_.
+
+
 Enabling the API
 ----------------
 
index d950003b3bb36a408c18614dbf52736efae16f03..7abc117fed6dee8c0c6a3a78415c2999014d6003 100644 (file)
@@ -159,6 +159,11 @@ paths:
           in: path
           required: true
           description: The id of the zone to retrieve
+        - name: rrsets
+          in: query
+          description: '“true” (default) or “false”, whether to include the “rrsets” in the response Zone object.'
+          type: boolean
+          default: true
       responses:
         '200':
           description: A Zone
index dce4448684c70bb51a3ebfd3d259ae93dcf5b72e..596fa2218eed2b5330dc6f130b113ac278d00c93 100644 (file)
@@ -57,3 +57,4 @@ Objects
 
 .. openapi:: swagger/authoritative-api-swagger.yaml
   :definitions: TSIGKey
+
index b8b8b595a8468cceb0ed1e215a87c5bc441dd217..ddda71559fb61fcdf4310ac0c3281d705203f742 100644 (file)
@@ -37,7 +37,7 @@ Changes made through the Zones API will always yield valid zone data, as the API
 
 DNSSEC-enabled zones should be :ref:`rectified <rules-for-filling-out-dnssec-fields>` after changing the zone data.
 This can be done by the API automatically after a change when the :ref:`metadata-api-rectify` metadata is set.
-When creating or updating a zone, the "api_rectify" field of the :json:object:`ZOne` can be set to `true` to enable this behaviour.
+When creating or updating a zone, the "api_rectify" field of the :json:object:`Zone` can be set to `true` to enable this behaviour.
 
 Backends might implement additional features (by coincidence or not).
 These things are not supported through the API.
index 8c1b8d1ff07272eefa34aeb42e8c2573c772128e..d242c020de16a1e3e24c463149397857daf3e286 100644 (file)
@@ -147,10 +147,12 @@ respsizes
 
 Get a histogram of the response sizes.
 
-retrieve *DOMAIN*
+retrieve *DOMAIN* [IP]
 ^^^^^^^^^^^^^^^^^
 
 Retrieve slave *DOMAIN* from its master. Done nearly immediately.
+If IP is specified, then retrieval is forced from the specified IP.
+Port may be specified in AFI specific manner.
 
 set *VARIABLE* *VALUE*
 ^^^^^^^^^^^^^^^^^^^^^^
index 46ad08a2e4230b6aa6c946df064222320bd3e363..dce9405ef3723d2d77c497b3bf982576435f6a6b 100644 (file)
@@ -160,6 +160,8 @@ ZONE MANIPULATION COMMANDS
 add-record *ZONE* *NAME* *TYPE* [*TTL*] *CONTENT*
     Add one or more records of *NAME* and *TYPE* to *ZONE* with *CONTENT* 
     and optional *TTL*. If *TTL* is not set, default will be used. 
+add-supermaster *IP* *NAMESERVER* [*ACCOUNT*]
+    Add a supermaster entry into the backend. This enables receiving zone updates from other servers.
 create-zone *ZONE*
     Create an empty zone named *ZONE*.
 create-slave-zone *ZONE* *MASTER* [*MASTER*]..
index d2f6aabe07059f7787eed13ad478da15983b5394..ff891db5b92796920bc0f43b16269e201f7b7b88 100644 (file)
@@ -222,7 +222,9 @@ So, to benefit from this feature, a backend needs to know about the IP
 address of the supermaster, and how PowerDNS will be listed in the set
 of NS records remotely, and the 'account' name of your supermaster.
 There is no need to fill the account name out but it does help keep
-track of where a domain comes from.
+track of where a domain comes from. 
+Adding a supermaster can be done either directly in the database,
+or by using the 'pdnsutil add-supermaster' command. 
 
 .. note::
   Removal of zones provisioned using the supermaster must be
index cd396d570ba2f878d52f106b7ba37c31145f0000..f933370da766d34c2f9c658a2ed47ee64c099bb2 100644 (file)
@@ -1,4 +1,4 @@
-@       86400   IN  SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020042201 10800 3600 604800 10800
+@       86400   IN  SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020073001 10800 3600 604800 10800
 @       3600    IN  NS  pdns-public-ns1.powerdns.com.
 @       3600    IN  NS  pdns-public-ns2.powerdns.com.
 
@@ -34,8 +34,8 @@ auth-4.0.4.security-status                              60 IN TXT "3 Upgrade now
 auth-4.0.5.security-status                              60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2018-03.html"
 auth-4.0.6.security-status                              60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-03.html"
 auth-4.0.7.security-status                              60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2018-03.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2018-05.html"
-auth-4.0.8.security-status                              60 IN TXT "1 OK"
-auth-4.0.9.security-status                              60 IN TXT "1 OK"
+auth-4.0.8.security-status                              60 IN TXT "2 Unsupported release (EOL)"
+auth-4.0.9.security-status                              60 IN TXT "2 Unsupported release (EOL)"
 auth-4.1.0-rc1.security-status                          60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
 auth-4.1.0-rc2.security-status                          60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
 auth-4.1.0-rc3.security-status                          60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
@@ -92,7 +92,7 @@ auth-3.4.1-4_deb8u4.debian.security-status              60 IN TXT "3 Upgrade now
 auth-3.4.1-4_deb8u5.debian.security-status              60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2016-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
 auth-3.4.1-4_deb8u6.debian.security-status              60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
 auth-3.4.1-4_deb8u7.debian.security-status              60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2017-04.html"
-auth-3.4.1-4_deb8u8.debian.security-status              60 IN TXT "1 OK"
+auth-3.4.1-4_deb8u8.debian.security-status              60 IN TXT "2 Unsupported release (EOL)"
 
 auth-3.4.4-2_bpo8_1.debian.security-status              60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
 auth-3.4.5-1_bpo8_1.debian.security-status              60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
@@ -122,7 +122,7 @@ auth-4.0.1-6.debian.security-status                     60 IN TXT "3 Upgrade now
 auth-4.0.1-7.debian.security-status                     60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-02.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-03.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-04.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-05.html"
 auth-4.0.2-1.debian.security-status                     60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2017-04.html"
 auth-4.0.3-1.debian.security-status                     60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2017-04.html"
-auth-4.0.3-1_deb9u2.debian.security-status              60 IN TXT "1 OK"
+auth-4.0.3-1_deb9u2.debian.security-status              60 IN TXT "2 Unsupported release (EOL)"
 ; From here on security polling has been disabled for this distribution.
 
 ; Auth Ubuntu
@@ -195,28 +195,37 @@ recursor-4.1.5.security-status                          60 IN TXT "3 Upgrade now
 recursor-4.1.6.security-status                          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2018-09.html"
 recursor-4.1.7.security-status                          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2018-09.html"
 recursor-4.1.8.security-status                          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2019-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2019-02.html"
-recursor-4.1.9.security-status                          60 IN TXT "1 OK"
-recursor-4.1.10.security-status                         60 IN TXT "1 OK"
-recursor-4.1.11.security-status                         60 IN TXT "1 OK"
-recursor-4.1.12.security-status                         60 IN TXT "1 OK"
-recursor-4.1.13.security-status                         60 IN TXT "1 OK"
-recursor-4.1.14.security-status                         60 IN TXT "1 OK"
-recursor-4.1.15.security-status                         60 IN TXT "1 OK"
-recursor-4.2.0-alpha1.security-status                   60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0-beta1.security-status                    60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0-rc1.security-status                      60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0-rc2.security-status                      60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0.security-status                          60 IN TXT "1 OK"
-recursor-4.2.1.security-status                          60 IN TXT "1 OK"
-recursor-4.3.0-alpha1.security-status                   60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-alpha2.security-status                   60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-alpha3.security-status                   60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-beta1.security-status                    60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-beta2.security-status                    60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-rc1.security-status                      60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-rc2.security-status                      60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0.security-status                          60 IN TXT "1 OK"
-recursor-4.4.0-alpha1.security-status                   60 IN TXT "1 OK"
+recursor-4.1.9.security-status                          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.10.security-status                         60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.11.security-status                         60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.12.security-status                         60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.13.security-status                         60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.14.security-status                         60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.15.security-status                         60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.16.security-status                         60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-04.html"
+recursor-4.1.17.security-status                         60 IN TXT "1 OK"
+recursor-4.2.0-alpha1.security-status                   60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0-beta1.security-status                    60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0-rc1.security-status                      60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0-rc2.security-status                      60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0.security-status                          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.2.1.security-status                          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.2.2.security-status                          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-04.html"
+recursor-4.2.3.security-status                          60 IN TXT "1 OK"
+recursor-4.2.4.security-status                          60 IN TXT "1 OK"
+recursor-4.3.0-alpha1.security-status                   60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-alpha2.security-status                   60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-alpha3.security-status                   60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-beta1.security-status                    60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-beta2.security-status                    60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-rc1.security-status                      60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-rc2.security-status                      60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0.security-status                          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.3.1.security-status                          60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-04.html"
+recursor-4.3.2.security-status                          60 IN TXT "1 OK"
+recursor-4.3.3.security-status                          60 IN TXT "1 OK"
+recursor-4.4.0-alpha1.security-status                   60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.4.0-alpha2.security-status                   60 IN TXT "1 OK"
 
 ; Recursor Debian
 recursor-3.6.2-2.debian.security-status                 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/"
@@ -345,4 +354,8 @@ dnsdist-1.4.0-rc4.security-status                          60 IN TXT "2 Unsuppor
 dnsdist-1.4.0-rc5.security-status                          60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
 dnsdist-1.4.0.security-status                              60 IN TXT "1 OK"
 dnsdist-1.5.0-alpha1.security-status                       60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-dnsdist-1.5.0-rc1.security-status                          60 IN TXT "1 OK"
+dnsdist-1.5.0-rc1.security-status                          60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc2.security-status                          60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc3.security-status                          60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc4.security-status                          60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0.security-status                              60 IN TXT "1 OK"
index fc9fccd7ebced99a460d03ef0bd1dda0e6068810..58f16aba4be9ed66b89683afa45086e00a5982a0 100644 (file)
@@ -35,7 +35,12 @@ Allow 8 bit DNS queries.
 -  Default: 127.0.0.0/8,::1
 
 If set, only these IP addresses or netmasks will be able to perform
-AXFR.
+AXFR without TSIG.
+
+.. warning::
+   This setting only applies to AXFR without TSIG keys. If you allow a TSIG key to perform an AXFR,
+   this setting will not be checked for that transfer, and the client will be able to perform the AXFR
+   from everywhere.
 
 .. _setting-allow-dnsupdate-from:
 
@@ -770,19 +775,27 @@ available in non-static distributions.
 ``local-address``
 -----------------
 .. versionchanged:: 4.3.0
-  now also takes your IPv6 addresses
+  now also accepts IPv6 addresses
 
 .. versionchanged:: 4.3.0
-  Before 4.3.0, this setting only supported IPv4.
+  Before 4.3.0, this setting only supported IPv4 addresses.
 
--  IPv4 Addresses, separated by commas or whitespace
+-  IPv4/IPv6 Addresses, with optional port numbers, separated by commas or whitespace
 -  Default: ``0.0.0.0, ::``
 
-Local IP addresses to which we bind. It is highly advised to bind to
-specific interfaces and not use the default 'bind to any'. This causes
-big problems if you have multiple IP addresses. Unix does not provide a
-way of figuring out what IP address a packet was sent to when binding to
-any.
+Local IP addresses to which we bind. Each address specified can
+include a port number; if no port is included then the
+:ref:`setting-local-port` port will be used for that address. If a
+port number is specified, it must be separated from the address with a
+':'; for an IPv6 address the address must be enclosed in square
+brackets.
+
+Examples::
+
+  local-address=127.0.0.1 ::1
+  local-address=0.0.0.0:5353
+  local-address=[::]:8053
+  local-address=127.0.0.1:53, [::1]:5353
 
 .. _setting-local-address-nonexist-fail:
 
@@ -834,7 +847,8 @@ addresses do not exist on this server.
 -  Integer
 -  Default: 53
 
-The port on which we listen. Only one port possible.
+Local port to bind to.
+If an address in :ref:`setting-local-address` does not have an explicit port, this port is used.
 
 .. _setting-log-dns-details:
 
@@ -1270,21 +1284,33 @@ Seconds to store queries with an answer in the Query Cache. See :ref:`query-cach
 
 ``query-local-address``
 -----------------------
+.. versionchanged:: 4.4.0
+  Accepts both IPv4 and IPv6 addresses. Also accept more than one address per
+  address family.
 
--  IPv4 Address
--  Default: 0.0.0.0
+-  IP addresses, separated by spaces or commas
+-  Default: 0.0.0.0 ::
 
-The IP address to use as a source address for sending queries. Useful if
+The IP addresses to use as a source address for sending queries. Useful if
 you have multiple IPs and PowerDNS is not bound to the IP address your
 operating system uses by default for outgoing packets.
 
+PowerDNS will pick the correct address family based on the remote's address (v4
+for outgoing v4, v6 for outgoing v6). However, addresses are selected at random
+without taking into account ip subnet reachability. It is highly recommended to
+use the defaults in that case (the kernel will pick the right source address for
+the network).
+
 .. _setting-query-local-address6:
 
 ``query-local-address6``
 ------------------------
+.. deprecated:: 4.4.0
+  Use :ref:`setting-query-local-address`. The default has been changed
+  from '::' to unset.
 
 -  IPv6 Address
--  Default: '::'
+-  Default: unset
 
 Source IP address for sending IPv6 queries.
 
index 0716c9487e0ffee0bd5db71b97e87d28d4b7a881..1f7941c89f59522768b8291613b8c9890e4831c5 100644 (file)
@@ -33,6 +33,10 @@ with the key name in the content field. For example::
 
     $ dig -t axfr powerdnssec.org @127.0.0.1 -y 'test:kp4/24gyYsEzbuTVJRUMoqGFmN3LYgVDzJ/3oRSP7ys='
 
+.. warning::
+  Any host with the correct TSIG key will be able to perform the AXFR, even
+  if the host is not within the defined ``allow-axfr-ips`` ranges.
+
 Another way of importing and activating TSIG keys into the database is using
 :doc:`pdnsutil <manpages/pdnsutil.1>`:
 
index 1e7d7f84ead93149f9ef833ff5a941281b65332a..683bdbcd6c787b7c4ac7a121fc468a1115bb01b2 100644 (file)
@@ -8,6 +8,15 @@ Please upgrade to the PowerDNS Authoritative Server 4.0.0 from 3.4.2+.
 See the `3.X <https://doc.powerdns.com/3/authoritative/upgrading/>`__
 upgrade notes if your version is older than 3.4.2.
 
+4.3.x to 4.4.0
+--------------
+
+``IPSECKEY`` change on secondaries
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The in-database format of the ``IPSECKEY`` has changed from 'generic' format to its specialized format.
+It is recommended to re-transfer, using ``pdns_control retrieve ZONE``, all zones that have ``IPSECKEY`` or ``TYPE45`` records.
+
 4.2.x to 4.3.0
 --------------
 
index 056a6cd823dfb208eab7dae21619e5f781e94e48..16d150fa7d6112e4f1d276f28da6a1d9acd8e4d3 100644 (file)
@@ -12,8 +12,9 @@
 #include <vector>
 #include <algorithm>
 
-// apple compiler somehow has string_view even in c++11!
-#if __cplusplus < 201703L && !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
+#ifdef __cpp_lib_string_view
+using std::string_view;
+#else
 #include <boost/version.hpp>
 #if BOOST_VERSION >= 106100
 #include <boost/utility/string_view.hpp>
@@ -22,8 +23,6 @@ using boost::string_view;
 #include <boost/utility/string_ref.hpp>
 using string_view = boost::string_ref;
 #endif
-#else // C++17
-using std::string_view;
 #endif
 
 
index 03e71e0fbaae1bff7ed126ca88fb4f9b2829bdb5..e7841a570d04c3de7ff4041ba8cf263af7e3a8fa 100644 (file)
@@ -290,7 +290,7 @@ bool Bind2Backend::feedRecord(const DNSResourceRecord &rr, const DNSName &ordern
     throw DBException("out-of-zone data '"+rr.qname.toLogString()+"' during AXFR of zone '"+bbd.d_name.toLogString()+"'");
   }
 
-  shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
+  shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), QClass::IN, rr.content));
   string content = drc->getZoneRepresentation();
 
   // SOA needs stripping too! XXX FIXME - also, this should not be here I think
@@ -1150,7 +1150,7 @@ void Bind2Backend::lookup(const QType &qtype, const DNSName &qname, int zoneId,
 
   if(!found) {
     if(mustlog)
-      g_log<<Logger::Warning<<"Found no authoritative zone for '"<<qname<<"' and/or id "<<bbd.d_id<<endl;
+      g_log<<Logger::Warning<<"Found no authoritative zone for '"<<qname<<"' and/or id "<<zoneId<<endl;
     d_handle.d_list=false;
     return;
   }
@@ -1443,7 +1443,7 @@ class Bind2Factory : public BackendFactory
    public:
       Bind2Factory() : BackendFactory("bind") {}
 
-      void declareArguments(const string &suffix="")
+      void declareArguments(const string &suffix="") override
       {
          declare(suffix,"ignore-broken-records","Ignore records that are out-of-bound for the zone.","no");
          declare(suffix,"config","Location of named.conf","");
@@ -1456,13 +1456,13 @@ class Bind2Factory : public BackendFactory
          declare(suffix,"hybrid","Store DNSSEC metadata in other backend","no");
       }
 
-      DNSBackend *make(const string &suffix="")
+      DNSBackend *make(const string &suffix="") override
       {
          assertEmptySuffix(suffix);
          return new Bind2Backend(suffix);
       }
       
-      DNSBackend *makeMetadataOnly(const string &suffix="")
+      DNSBackend *makeMetadataOnly(const string &suffix="") override
       {
         assertEmptySuffix(suffix);
         return new Bind2Backend(suffix, false);
index 612c9a5298a7605ef1bcce5d784f0ebb7ce092cd..83a86bc06b369036afb261798af8d4b3c643d339 100644 (file)
@@ -263,7 +263,7 @@ void GeoIPBackend::initialize() {
     for(auto &item: dom.records) {
       map<uint16_t, float> weights;
       map<uint16_t, float> sums;
-      map<uint16_t, GeoIPDNSResourceRecord> lasts;
+      map<uint16_t, GeoIPDNSResourceRecord*> lasts;
       bool has_weight=false;
       // first we look for used weight
       for(const auto &rr: item.second) {
@@ -277,13 +277,13 @@ void GeoIPBackend::initialize() {
           rr.weight=static_cast<int>((static_cast<float>(rr.weight) / weights[rr_type])*1000.0);
           sums[rr_type] += rr.weight;
           rr.has_weight = has_weight;
-          lasts[rr_type] = rr;
+          lasts[rr_type] = &rr;
         }
         // remove rounding gap
         for(auto &x: lasts) {
           float sum = sums[x.first];
           if (sum < 1000)
-            x.second.weight += (1000-sum);
+            x.second->weight += (1000-sum);
         }
       }
     }
@@ -314,11 +314,13 @@ GeoIPBackend::~GeoIPBackend() {
 bool GeoIPBackend::lookup_static(const GeoIPDomain &dom, const DNSName &search, const QType &qtype, const DNSName& qdomain, const Netmask& addr, GeoIPNetmask &gl) {
   const auto& i = dom.records.find(search);
   map<uint16_t,int> cumul_probabilities;
+  map<uint16_t,bool> weighted_match;
   int probability_rnd = 1+(dns_random(1000)); // setting probability=0 means it never is used
 
   if (i != dom.records.end()) { // return static value
     for(const auto& rr : i->second) {
-      if (qtype != QType::ANY && rr.qtype != qtype) continue;
+      if ((qtype != QType::ANY && rr.qtype != qtype) || weighted_match[rr.qtype.getCode()])
+        continue;
 
       if (rr.has_weight) {
         gl.netmask = (addr.isIPv6()?128:32);
@@ -332,6 +334,10 @@ bool GeoIPBackend::lookup_static(const GeoIPDomain &dom, const DNSName &search,
       d_result.push_back(rr);
       d_result.back().content = content;
       d_result.back().qname = qdomain;
+      // If we are weighted we only return one resource and we found a matching resource,
+      // so no need to check the other ones.
+      if (rr.has_weight)
+        weighted_match[rr.qtype.getCode()] = true;
     }
     // ensure we get most strict netmask
     for(DNSResourceRecord& rr: d_result) {
@@ -929,13 +935,13 @@ class GeoIPFactory : public BackendFactory{
 public:
   GeoIPFactory() : BackendFactory("geoip") {}
 
-  void declareArguments(const string &suffix = "") {
+  void declareArguments(const string &suffix = "") override {
     declare(suffix, "zones-file", "YAML file to load zone(s) configuration", "");
     declare(suffix, "database-files", "File(s) to load geoip data from ([driver:]path[;opt=value]", "");
     declare(suffix, "dnssec-keydir", "Directory to hold dnssec keys (also turns DNSSEC on)", "");
   }
 
-  DNSBackend *make(const string &suffix) {
+  DNSBackend *make(const string &suffix) override {
     return new GeoIPBackend(suffix);
   }
 };
index ef80c9d88a55ec7c40da17db4ee301fcc61f64ba..7036788bdc7ddcc5dd49ccf02ba10225494f8b3c 100644 (file)
@@ -62,6 +62,7 @@ void gMySQLBackend::reconnect()
                    getArgAsNum("timeout"),
                    mustDo("thread-cleanup"),
                    mustDo("ssl")));
+  allocateStatements();
 }
 
 class gMySQLFactory : public BackendFactory
@@ -69,7 +70,7 @@ class gMySQLFactory : public BackendFactory
 public:
   gMySQLFactory(const string &mode) : BackendFactory(mode),d_mode(mode) {}
 
-  void declareArguments(const string &suffix="")
+  void declareArguments(const string &suffix="") override
   {
     declare(suffix,"dbname","Database name to connect to","powerdns");
     declare(suffix,"user","Database backend user to connect as","powerdns");
@@ -103,6 +104,7 @@ public:
     declare(suffix,"info-all-slaves-query","","select id,name,master,last_check from domains where type='SLAVE'");
     declare(suffix,"supermaster-query","", "select account from supermasters where ip=? and nameserver=?");
     declare(suffix,"supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=? and account=?");
+    declare(suffix,"supermaster-add","", "insert into supermasters (ip, nameserver, account) values (?,?,?)");
 
     declare(suffix,"insert-zone-query","", "insert into domains (type,name,master,account,last_check,notified_serial) values(?,?,?,?,NULL,NULL)");
 
@@ -159,7 +161,7 @@ public:
     declare(suffix, "search-comments-query", "", "SELECT domain_id,name,type,modified_at,account,comment FROM comments WHERE name LIKE ? OR comment LIKE ? LIMIT ?");
   }
 
-  DNSBackend *make(const string &suffix="")
+  DNSBackend *make(const string &suffix="") override
   {
     return new gMySQLBackend(d_mode,suffix);
   }
index 02139c24c4b0cb7fc959b2b828d18cb37c777b39..6ef452702eb9356ad335f3b93da2f180538335a9 100644 (file)
@@ -47,6 +47,8 @@ gODBCBackend::gODBCBackend (const std::string & mode, const std::string & suffix
     throw PDNSException( "Unable to launch " + mode + " connection: " + e.txtReason());
   }
 
+  allocateStatements();
+
   g_log << Logger::Warning << mode << " Connection successful" << std::endl;
 }
 
@@ -61,7 +63,7 @@ public:
   }
 
   //! Declares all needed arguments.
-  void declareArguments( const std::string & suffix = "" )
+  void declareArguments( const std::string & suffix = "" ) override
   {
     declare( suffix, "datasource", "Datasource (DSN) to use","PowerDNS");
     declare( suffix, "username", "User to connect as","powerdns");
@@ -86,6 +88,7 @@ public:
     declare(suffix,"info-all-slaves-query","","select id,name,master,last_check from domains where type='SLAVE'");
     declare(suffix,"supermaster-query","", "select account from supermasters where ip=? and nameserver=?");
     declare(suffix,"supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=? and account=?");
+    declare(suffix,"supermaster-add","", "insert into supermasters (ip, nameserver, account) values (?,?,?)");
 
     declare(suffix,"insert-zone-query","", "insert into domains (type,name,master,account,last_check,notified_serial) values(?,?,?,?,null,null)");
 
@@ -146,7 +149,7 @@ public:
   }
 
   //! Constructs a new gODBCBackend object.
-  DNSBackend *make(const string & suffix = "" )
+  DNSBackend *make(const string & suffix = "" ) override
   {
     return new gODBCBackend( d_mode, suffix );
   }
index c8e38a86073717a4d7a7d8c6a4dd5cfa241727c4..4ea3f69277113f142a2513146f2db6805410abe5 100644 (file)
@@ -40,17 +40,19 @@ gPgSQLBackend::gPgSQLBackend(const string &mode, const string &suffix)  : GSQLBa
 {
   try {
     setDB(new SPgSQL(getArg("dbname"),
-                 getArg("host"),
-                 getArg("port"),
-                 getArg("user"),
-                 getArg("password"),
-                 getArg("extra-connection-parameters")));
+                     getArg("host"),
+                     getArg("port"),
+                     getArg("user"),
+                     getArg("password"),
+                     getArg("extra-connection-parameters"),
+                     mustDo("prepared-statements")));
   }
 
   catch(SSqlException &e) {
     g_log<<Logger::Error<<mode<<" Connection failed: "<<e.txtReason()<<endl;
     throw PDNSException("Unable to launch "+mode+" connection: "+e.txtReason());
   }
+  allocateStatements();
   g_log<<Logger::Info<<mode<<" Connection successful. Connected to database '"<<getArg("dbname")<<"' on '"<<getArg("host")<<"'."<<endl;
 }
 
@@ -79,7 +81,7 @@ class gPgSQLFactory : public BackendFactory
 public:
   gPgSQLFactory(const string &mode) : BackendFactory(mode),d_mode(mode) {}
 
-  void declareArguments(const string &suffix="")
+  void declareArguments(const string &suffix="") override
   {
     declare(suffix,"dbname","Backend database name to connect to","");
     declare(suffix,"user","Database backend user to connect as","");
@@ -87,6 +89,7 @@ public:
     declare(suffix,"port","Database backend port to connect to","");
     declare(suffix,"password","Database backend password to connect with","");
     declare(suffix,"extra-connection-parameters", "Extra parameters to add to connection string","");
+    declare(suffix,"prepared-statements", "Use prepared statements instead of parameterized queries", "yes");
 
     declare(suffix,"dnssec","Enable DNSSEC processing","no");
 
@@ -108,6 +111,7 @@ public:
     declare(suffix,"info-all-slaves-query","","select id,name,master,last_check from domains where type='SLAVE'");
     declare(suffix,"supermaster-query","", "select account from supermasters where ip=$1 and nameserver=$2");
     declare(suffix,"supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=$1 and account=$2");
+    declare(suffix,"supermaster-add","", "insert into supermasters (ip, nameserver, account) values ($1,$2,$3)");
 
     declare(suffix,"insert-zone-query","", "insert into domains (type,name,master,account,last_check, notified_serial) values($1,$2,$3,$4,null,null)");
 
@@ -165,7 +169,7 @@ public:
 
   }
 
-  DNSBackend *make(const string &suffix="")
+  DNSBackend *make(const string &suffix="") override
   {
     return new gPgSQLBackend(d_mode,suffix);
   }
index 68e5be885fc7cf88d576a52ed39185d3481c27c0..fe6e06517aeb54b0dcb372b8e35f941eafc47146 100644 (file)
 class SPgSQLStatement: public SSqlStatement
 {
 public:
-  SPgSQLStatement(const string& query, bool dolog, int nparams, SPgSQL* db) {
+  SPgSQLStatement(const string& query, bool dolog, int nparams, SPgSQL* db, unsigned int nstatement) {
     d_query = query;
     d_dolog = dolog;
     d_parent = db;
-    d_prepared = false;
     d_nparams = nparams;
-    d_res = NULL;
-    d_res_set = NULL;
-    paramValues = NULL;
-    paramLengths = NULL;
-    d_paridx = 0;
-    d_residx = 0;
-    d_resnum = 0;
-    d_fnum = 0;
-    d_cur_set = 0;
+    d_nstatement = nstatement;
   }
 
   SSqlStatement* bind(const string& name, bool value) { return bind(name, string(value ? "t" : "f")); }
@@ -77,10 +68,26 @@ public:
   SSqlStatement* execute() {
     prepareStatement();
     if (d_dolog) {
-      g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": " << d_query << endl;
+      g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": Statement: " << d_query << endl;
+      if (d_paridx) {
+        // Log message is similar, bot not exactly the same as the postgres server log.
+        std::stringstream log_message;
+        log_message<< "Query "<<((long)(void*)this)<<": Parameters: ";
+        for (int i = 0; i < d_paridx; i++) {
+          if (i != 0) {
+            log_message << ", ";
+          }
+          log_message << "$" << (i + 1) << " = '" << paramValues[i] << "'";
+        }
+        g_log<<Logger::Warning<< log_message.str() << endl;
+      }
       d_dtime.set();
     }
-    d_res_set = PQexecParams(d_db(), d_query.c_str(), d_nparams, NULL, paramValues, paramLengths, NULL, 0);
+    if (!d_stmt.empty()) {
+      d_res_set = PQexecPrepared(d_db(), d_stmt.c_str(), d_nparams, paramValues, paramLengths, nullptr, 0);
+    } else {
+      d_res_set = PQexecParams(d_db(), d_query.c_str(), d_nparams, nullptr, paramValues, paramLengths, nullptr, 0);
+    }
     ExecStatusType status = PQresultStatus(d_res_set);
     if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
       string errmsg(PQresultErrorMessage(d_res_set));
@@ -98,10 +105,10 @@ public:
   }
 
   void nextResult() {
-    if (d_res_set == NULL) return; // no refcursor
+    if (d_res_set == nullptr) return; // no refcursor
     if (d_cur_set >= PQntuples(d_res_set)) {
       PQclear(d_res_set);
-      d_res_set = NULL;
+      d_res_set = nullptr;
       return;
     }
     // this code handles refcursors if they are returned
@@ -111,7 +118,7 @@ public:
 #if PG_VERSION_NUM > 90000
       // PQescapeIdentifier was added to libpq in postgresql 9.0
       char *val = PQgetvalue(d_res_set, d_cur_set++, 0);
-      char *portal =  PQescapeIdentifier(d_db(), val, strlen(val));
+      char *portal = PQescapeIdentifier(d_db(), val, strlen(val));
       string cmd = string("FETCH ALL FROM \"") + string(portal) + string("\"");
       PQfreemem(portal);
 #else
@@ -119,17 +126,16 @@ public:
       string cmd = string("FETCH ALL FROM \"") + portal + string("\"");
 #endif
       // execute FETCH
-      if (d_dolog)
+      if (d_dolog) {
          g_log<<Logger::Warning<<"Query: "<<cmd<<endl;
+      }
       d_res = PQexec(d_db(),cmd.c_str());
       d_resnum = PQntuples(d_res);
-      d_fnum = PQnfields(d_res);
       d_residx = 0;
     } else {
       d_res = d_res_set;
-      d_res_set = NULL;
+      d_res_set = nullptr;
       d_resnum = PQntuples(d_res);
-      d_fnum = PQnfields(d_res);
     }
   }
 
@@ -160,7 +166,7 @@ public:
     d_residx++;
     if (d_residx >= d_resnum) {
       PQclear(d_res);
-      d_res = NULL;
+      d_res = nullptr;
       nextResult();
     }
     return this;
@@ -168,7 +174,7 @@ public:
 
   SSqlStatement* getResult(result_t& result) {
     result.clear();
-    if (d_res == NULL) return this;
+    if (d_res == nullptr) return this;
     result.reserve(d_resnum);
     row_t row;
     while(hasNextRow()) { nextRow(row); result.push_back(std::move(row)); }
@@ -177,20 +183,26 @@ public:
 
   SSqlStatement* reset() {
      int i;
-     if (d_res)
+     if (d_res) {
        PQclear(d_res);
-     if (d_res_set)
+     }
+     if (d_res_set) {
        PQclear(d_res_set);
-     d_res_set = NULL;
-     d_res = NULL;
+     }
+     d_res_set = nullptr;
+     d_res = nullptr;
      d_paridx = d_residx = d_resnum = 0;
-     if (paramValues)
-       for(i=0;i<d_nparams;i++)
-         if (paramValues[i]) delete [] paramValues[i];
+     if (paramValues) {
+       for(i=0;i<d_nparams;i++) {
+         if (paramValues[i]) {
+           delete [] paramValues[i];
+         }
+       }
+     }
      delete [] paramValues;
-     paramValues = NULL;
+     paramValues = nullptr;
      delete [] paramLengths;
-     paramLengths = NULL;
+     paramLengths = nullptr;
      return this;
   }
 
@@ -207,20 +219,38 @@ private:
   void releaseStatement() {
     d_prepared = false;
     reset();
+    if (!d_stmt.empty()) {
+      string cmd = string("DEALLOCATE " + d_stmt);
+      PGresult *res = PQexec(d_db(), cmd.c_str());
+      PQclear(res);
+      d_stmt.clear();
+    }
   }
 
   void prepareStatement() {
     if (d_prepared) return;
-    paramValues=NULL;
-    d_cur_set=d_paridx=d_residx=d_resnum=d_fnum=0;
-    paramLengths=NULL;
-    d_res=NULL;
-    d_res_set=NULL;
+    if (d_parent->usePrepared()) {
+      // prepare a statement; name must be unique per session (using d_nstatement to ensure this).
+      this->d_stmt = string("stmt") + std::to_string(d_nstatement);
+      PGresult* res = PQprepare(d_db(), d_stmt.c_str(), d_query.c_str(), d_nparams, nullptr);
+      ExecStatusType status = PQresultStatus(res);
+      string errmsg(PQresultErrorMessage(res));
+      PQclear(res);
+      if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK && status != PGRES_NONFATAL_ERROR) {
+        releaseStatement();
+        throw SSqlException("Fatal error during prePQpreparepare: " + d_query + string(": ") + errmsg);
+      }
+    }
+    paramValues = nullptr;
+    paramLengths = nullptr;
+    d_cur_set = d_paridx = d_residx = d_resnum = 0;
+    d_res = nullptr;
+    d_res_set = nullptr;
     d_prepared = true;
   }
 
   void allocate() {
-     if (paramValues != NULL) return;
+     if (paramValues != nullptr) return;
      paramValues = new char*[d_nparams];
      paramLengths = new int[d_nparams];
      memset(paramValues, 0, sizeof(char*)*d_nparams);
@@ -228,30 +258,32 @@ private:
   }
 
   string d_query;
+  string d_stmt;
   SPgSQL *d_parent;
-  PGresult *d_res_set;
-  PGresult *d_res;
+  PGresult *d_res_set{nullptr};
+  PGresult *d_res{nullptr};
   bool d_dolog;
   DTime d_dtime; // only used if d_dolog is set
-  bool d_prepared;
+  bool d_prepared{false};
   int d_nparams;
-  int d_paridx;
-  char **paramValues;
-  int *paramLengths;
-  int d_residx;
-  int d_resnum;
-  int d_fnum;
-  int d_cur_set;
+  int d_paridx{0};
+  char **paramValues{nullptr};
+  int *paramLengths{nullptr};
+  int d_residx{0};
+  int d_resnum{0};
+  int d_cur_set{0};
+  unsigned int d_nstatement;
 };
 
 bool SPgSQL::s_dolog;
 
 SPgSQL::SPgSQL(const string &database, const string &host, const string& port, const string &user,
-               const string &password, const string &extra_connection_parameters)
+               const string &password, const string &extra_connection_parameters, const bool use_prepared)
 {
-  d_db=0;
+  d_db = nullptr;
   d_in_trx = false;
-  d_connectstr="";
+  d_connectstr = "";
+  d_nstatements = 0;
 
   if (!database.empty())
     d_connectstr+="dbname="+database;
@@ -275,6 +307,8 @@ SPgSQL::SPgSQL(const string &database, const string &host, const string& port, c
     d_connectstr+=" password="+password;
   }
 
+  d_use_prepared = use_prepared;
+
   d_db=PQconnectdb(d_connectstr.c_str());
 
   if (!d_db || PQstatus(d_db)==CONNECTION_BAD) {
@@ -318,7 +352,8 @@ void SPgSQL::execute(const string& query)
 
 std::unique_ptr<SSqlStatement> SPgSQL::prepare(const string& query, int nparams)
 {
-  return std::unique_ptr<SSqlStatement>(new SPgSQLStatement(query, s_dolog, nparams, this));
+  d_nstatements++;
+  return std::unique_ptr<SSqlStatement>(new SPgSQLStatement(query, s_dolog, nparams, this, d_nstatements));
 }
 
 void SPgSQL::startTransaction() {
index c0aab35ca1625c5a59382b9bf4bc99018676c910..aea85c422197457210a664eb18e0b6508eebc02a 100644 (file)
@@ -29,7 +29,7 @@ class SPgSQL : public SSql
 public:
   SPgSQL(const string &database, const string &host="", const string& port="",
          const string &user="", const string &password="",
-         const string &extra_connection_parameters="");
+         const string &extra_connection_parameters="", const bool use_prepared = true);
 
   ~SPgSQL();
   
@@ -47,6 +47,7 @@ public:
 
   PGconn* db() { return d_db; }
   bool in_trx() const { return d_in_trx; }
+  bool usePrepared() { return d_use_prepared; }
 
 private:
   PGconn* d_db;
@@ -54,4 +55,6 @@ private:
   string d_connectlogstr;
   static bool s_dolog;
   bool d_in_trx;
+  bool d_use_prepared;
+  unsigned int d_nstatements;
 };
diff --git a/modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql b/modules/gsqlite3backend/4.3.0_to_4.3.1_schema.sqlite3.sql
new file mode 100644 (file)
index 0000000..26eb997
--- /dev/null
@@ -0,0 +1,15 @@
+CREATE INDEX records_lookup_idx ON records(name, type);
+CREATE INDEX records_lookup_id_idx ON records(domain_id, name, type);
+CREATE INDEX records_order_idx ON records(domain_id, ordername);
+
+DROP INDEX IF EXISTS rec_name_index;
+DROP INDEX IF EXISTS nametype_index;
+DROP INDEX IF EXISTS domain_id;
+DROP INDEX IF EXISTS orderindex;
+
+CREATE INDEX comments_idx ON comments(domain_id, name, type);
+
+DROP INDEX IF EXISTS comments_domain_id_index;
+DROP INDEX IF EXISTS comments_nametype_index;
+
+ANALYZE;
index 2a836773803579f1d4eb534d28275a4f353c9de6..4c5de6dcbec7b59fa29107651500822d04668c31 100644 (file)
@@ -10,6 +10,7 @@ dist_doc_DATA = \
        3.4.0_to_4.0.0_schema.sqlite3.sql \
        4.0.0_to_4.2.0_schema.sqlite3.sql \
        4.2.0_to_4.3.0_schema.sqlite3.sql \
+       4.3.0_to_4.3.1_schema.sqlite3.sql \
        schema.sqlite3.sql
 
 libgsqlite3backend_la_SOURCES = gsqlite3backend.cc gsqlite3backend.hh
index d697b83bd5cc5460bcad96c9945148a89b02494b..92a2ec21e296b27337d3e89ade741a7e91ec30f3 100644 (file)
@@ -45,6 +45,7 @@ gSQLite3Backend::gSQLite3Backend( const std::string & mode, const std::string &
   {
     SSQLite3 *ptr = new SSQLite3( getArg( "database" ), getArg( "pragma-journal-mode") );
     setDB(ptr);
+    allocateStatements();
     if(!getArg("pragma-synchronous").empty()) {
       ptr->execute("PRAGMA synchronous="+getArg("pragma-synchronous"));
     }
@@ -72,7 +73,7 @@ public:
   }
   
   //! Declares all needed arguments.
-  void declareArguments( const std::string & suffix = "" )
+  void declareArguments( const std::string & suffix = "" ) override
   {
     declare(suffix, "database", "Filename of the SQLite3 database", "powerdns.sqlite");
     declare(suffix, "pragma-synchronous", "Set this to 0 for blazing speed", "");
@@ -99,6 +100,7 @@ public:
     declare(suffix, "info-all-slaves-query", "","select id,name,master,last_check from domains where type='SLAVE'");
     declare(suffix, "supermaster-query", "", "select account from supermasters where ip=:ip and nameserver=:nameserver");
     declare(suffix, "supermaster-name-to-ips", "", "select ip,account from supermasters where nameserver=:nameserver and account=:account");
+    declare(suffix,"supermaster-add","", "insert into supermasters (ip, nameserver, account) values (:ip,:nameserver,:account)"); 
 
     declare(suffix, "insert-zone-query", "", "insert into domains (type,name,master,account,last_check,notified_serial) values(:type, :domain, :masters, :account, null, null)");
 
@@ -156,7 +158,7 @@ public:
   }
 
   //! Constructs a new gSQLite3Backend object.
-  DNSBackend *make( const string & suffix = "" )
+  DNSBackend *make( const string & suffix = "" ) override
   {
     return new gSQLite3Backend( d_mode, suffix );
   }
index 64a7da0d20f36fe2fd124fc591d023664cea6e12..08755211855fe5be610fd3463a36fa81574445f3 100644 (file)
@@ -27,10 +27,9 @@ CREATE TABLE records (
   FOREIGN KEY(domain_id) REFERENCES domains(id) ON DELETE CASCADE ON UPDATE CASCADE
 );
 
-CREATE INDEX rec_name_index ON records(name);
-CREATE INDEX nametype_index ON records(name,type);
-CREATE INDEX domain_id ON records(domain_id);
-CREATE INDEX orderindex ON records(ordername);
+CREATE INDEX records_lookup_idx ON records(name, type);
+CREATE INDEX records_lookup_id_idx ON records(domain_id, name, type);
+CREATE INDEX records_order_idx ON records(domain_id, ordername);
 
 
 CREATE TABLE supermasters (
@@ -53,8 +52,7 @@ CREATE TABLE comments (
   FOREIGN KEY(domain_id) REFERENCES domains(id) ON DELETE CASCADE ON UPDATE CASCADE
 );
 
-CREATE INDEX comments_domain_id_index ON comments (domain_id);
-CREATE INDEX comments_nametype_index ON comments (name, type);
+CREATE INDEX comments_idx ON comments(domain_id, name, type);
 CREATE INDEX comments_order_idx ON comments (domain_id, modified_at);
 
 
index 7078bf5513e48fa03426eb0300da7024e8b816f2..f428a1adf0c311a24068dc3cf1dcf7ea2311361e 100644 (file)
@@ -277,7 +277,7 @@ class LdapFactory : public BackendFactory
 
     LdapFactory() : BackendFactory( "ldap" ) {}
 
-    void declareArguments( const string &suffix="" )
+    void declareArguments( const string &suffix="" ) override
     {
       declare( suffix, "host", "One or more LDAP server with ports or LDAP URIs (separated by spaces)","ldap://127.0.0.1:389/" );
       declare( suffix, "starttls", "Use TLS to encrypt connection (unused for LDAP URIs)", "no" );
@@ -297,7 +297,7 @@ class LdapFactory : public BackendFactory
     }
 
 
-    DNSBackend* make( const string &suffix="" )
+    DNSBackend* make( const string &suffix="" ) override
     {
       return new LdapBackend( suffix );
     }
index 56096cd4609c6bc6ff128415aef864737e715526..c7ec4d4a01ec29f324fad6e346adc40d6671463e 100644 (file)
@@ -233,7 +233,7 @@ void serFromString(const string_view& str, DNSResourceRecord& rr)
 
 static std::string serializeContent(uint16_t qtype, const DNSName& domain, const std::string& content)
 {
-  auto drc = DNSRecordContent::mastermake(qtype, 1, content);
+  auto drc = DNSRecordContent::mastermake(qtype, QClass::IN, content);
   return drc->serialize(domain, false);
 }
 
@@ -758,25 +758,14 @@ void LMDBBackend::setNotified(uint32_t domain_id, uint32_t serial)
 }
 
 
-bool LMDBBackend::setMaster(const DNSName &domain, const std::string& ips)
+bool LMDBBackend::setMasters(const DNSName &domain, const vector<ComboAddress> &masters)
 {
-  vector<ComboAddress> masters;
-  vector<string> parts;
-  stringtok(parts, ips, " \t;,");
-  for(const auto& ip : parts) 
-    masters.push_back(ComboAddress(ip, 53));
-  
   return genChangeDomain(domain, [&masters](DomainInfo& di) {
       di.masters = masters;
     });
 }
 
-bool LMDBBackend::createDomain(const DNSName &domain)
-{
-  return createDomain(domain, "NATIVE", "", "");
-}
-          
-bool LMDBBackend::createDomain(const DNSName &domain, const string &type, const string &masters, const string &account)
+bool LMDBBackend::createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account)
 {
   DomainInfo di;
 
@@ -784,16 +773,10 @@ bool LMDBBackend::createDomain(const DNSName &domain, const string &type, const
   if(txn.get<0>(domain, di)) {
     throw DBException("Domain '"+domain.toLogString()+"' exists already");
   }
-  
+
   di.zone = domain;
-  if(pdns_iequals(type, "master"))
-    di.kind = DomainInfo::Master;
-  else if(pdns_iequals(type, "slave"))
-    di.kind = DomainInfo::Slave;
-  else if(pdns_iequals(type, "native"))
-    di.kind = DomainInfo::Native;
-  else
-    throw DBException("Unable to create domain of unknown type '"+type+"'");
+  di.kind = kind;
+  di.masters = masters;
   di.account = account;
 
   txn.put(di);
@@ -1575,7 +1558,7 @@ class LMDBFactory : public BackendFactory
 {
 public:
   LMDBFactory() : BackendFactory("lmdb") {}
-  void declareArguments(const string &suffix="")
+  void declareArguments(const string &suffix="") override
   {
     declare(suffix,"filename","Filename for lmdb","./pdns.lmdb");
     declare(suffix,"sync-mode","Synchronisation mode: nosync, nometasync, mapasync, sync","mapasync");
@@ -1583,7 +1566,7 @@ public:
     declare(suffix,"shards","Records database will be split into this number of shards", (sizeof(long) == 4) ? "2" : "64");
     declare(suffix,"schema-version","Maximum allowed schema version to run on this DB. If a lower version is found, auto update is performed", SCHEMAVERSION_TEXT); 
   }
-  DNSBackend *make(const string &suffix="")
+  DNSBackend *make(const string &suffix="") override
   {
     return new LMDBBackend(suffix);
   }
index 6199f6fdc26d8574018220677f06992262cb207f..d37b497879dfef1be24d841f9b638859b1f30857 100644 (file)
@@ -38,10 +38,8 @@ public:
   bool list(const DNSName &target, int id, bool include_disabled) override;
 
   bool getDomainInfo(const DNSName &domain, DomainInfo &di, bool getSerial=true) override;
-  bool createDomain(const DNSName &domain, const string &type, const string &masters, const string &account);
+  bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account) override;
 
-  bool createDomain(const DNSName &domain) override;
-  
   bool startTransaction(const DNSName &domain, int domain_id=-1) override;
   bool commitTransaction() override;
   bool abortTransaction() override;
@@ -57,7 +55,7 @@ public:
 
   void getUnfreshSlaveInfos(vector<DomainInfo>* domains) override;
   
-  bool setMaster(const DNSName &domain, const string &ip) override;
+  bool setMasters(const DNSName &domain, const vector<ComboAddress> &masters) override;
   bool setKind(const DNSName &domain, const DomainInfo::DomainKind kind) override;
   bool getAllDomainMetadata(const DNSName& name, std::map<std::string, std::vector<std::string> >& meta) override;
   bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta) override
index 378203eccdac2c8418b5f3267678e777463468da..b3ce01e0d5f8e5b2c5485d242aaf22814397f7bd 100644 (file)
@@ -31,14 +31,14 @@ class Lua2Factory : public BackendFactory
 public:
   Lua2Factory() : BackendFactory("lua2") {}
 
-  void declareArguments(const string &suffix="")
+  void declareArguments(const string &suffix="") override
   {
     declare(suffix,"filename","Filename of the script for lua backend","powerdns-luabackend.lua");
     declare(suffix,"query-logging","Logging of the Lua2 Backend","no");
     declare(suffix,"api","Lua backend API version","2");
   }
 
-  DNSBackend *make(const string &suffix="")
+  DNSBackend *make(const string &suffix="") override
   {
     const std::string apiSet = "lua2" + suffix + "-api";
     const int api = ::arg().asNum(apiSet);
index 8a5183977f93caddbeab60f4daf902e1119800d7..52ca5f95a33e9189e813369e965aa6d4a82e7a70 100644 (file)
@@ -232,12 +232,7 @@ UnixRemote::UnixRemote(const string& path, int timeout)
   if(connect(d_fd, (struct sockaddr*)&remote, sizeof(remote)) < 0)
     unixDie("Unable to connect to remote '"+path+"' using UNIX domain socket");
 
-  d_fp = fdopen(d_fd, "r");
-}
-
-UnixRemote::~UnixRemote()
-{
-  fclose(d_fp);
+  d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(d_fd, "r"), fclose);
 }
 
 void UnixRemote::send(const string& line)
@@ -250,7 +245,7 @@ void UnixRemote::send(const string& line)
 void UnixRemote::receive(string& line)
 {
   line.clear();
-  stringfgets(d_fp, line);
+  stringfgets(d_fp.get(), line);
   trim_right(line);
 }
 
index 84c516e3d0eaa64994ae4393e2a6bbd2bd11a560..87099db5667963a9661dfb168cbbb0e6c23210db 100644 (file)
@@ -61,12 +61,11 @@ class UnixRemote : public CoRemote
 {
 public:
   UnixRemote(const string &path, int timeout=0);
-  ~UnixRemote();
   void sendReceive(const string &send, string &receive) override;
   void receive(string &rcv) override;
   void send(const string &send) override;
 private:
   int d_fd;
-  FILE *d_fp;
+  std::unique_ptr<FILE, int(*)(FILE*)> d_fp{nullptr, fclose};
 };
 bool isUnixSocket(const string& fname);
index 103d39e1f1a6bcc7279f7c8c79bbd4e7f52f09dc..208ef2e5d4f0987006db807f19dd98a1ad1a88ec 100644 (file)
@@ -357,7 +357,7 @@ class PipeFactory : public BackendFactory
    public:
       PipeFactory() : BackendFactory("pipe") {}
 
-      void declareArguments(const string &suffix="")
+      void declareArguments(const string &suffix="") override
       {
          declare(suffix,"command","Command to execute for piping questions to","");
          declare(suffix,"timeout","Number of milliseconds to wait for an answer","2000");
@@ -365,7 +365,7 @@ class PipeFactory : public BackendFactory
          declare(suffix,"abi-version","Version of the pipe backend ABI","1");
       }
 
-      DNSBackend *make(const string &suffix="")
+      DNSBackend *make(const string &suffix="") override
       {
          return new PipeBackend(suffix);
       }
index 577ba63904c5ce464dd91353e2892626aa8b7192..20b179b8d3a2df721e8b1a1e658bb52f1299f531 100644 (file)
@@ -102,11 +102,11 @@ class RandomFactory : public BackendFactory
 {
 public:
   RandomFactory() : BackendFactory("random") {}
-  void declareArguments(const string &suffix="")
+  void declareArguments(const string &suffix="") override
   {
     declare(suffix,"hostname","Hostname which is to be random","random.example.com");
   }
-  DNSBackend *make(const string &suffix="")
+  DNSBackend *make(const string &suffix="") override
   {
     return new RandomBackend(suffix);
   }
index d7987a906814a5f286e1dcc569ad1037418d2fe9..cd8c2c98d569f318cd1f843dfd30480444cf1f1b 100644 (file)
@@ -6,7 +6,7 @@ GEM
       ffi-rzmq-core (>= 1.0.1)
     ffi-rzmq-core (1.0.3)
       ffi (~> 1.9)
-    json (1.8.5)
+    json (2.3.0)
     sqlite3 (1.3.9)
     webrick (1.4.2)
     zeromqrb (0.1.3)
index ce42ca7932361013a9c7eb935ab0dd7c0ea4f590..6d5bc3ab0e1af2b39b4483a43f23bd52b70b1bb7 100644 (file)
@@ -384,7 +384,6 @@ int HTTPConnector::recv_message(Json& output) {
     if (d_socket == nullptr ) return -1; // cannot receive :(
     char buffer[4096];
     int rd = -1;
-    bool fail = false;
     time_t t0;
 
     arl.initialize(&resp);
@@ -403,24 +402,18 @@ int HTTPConnector::recv_message(Json& output) {
       if (arl.ready() == false)
         throw NetworkError("timeout");
     } catch (NetworkError &ne) {
-      g_log<<Logger::Error<<"While reading from HTTP endpoint "<<d_addr.toStringWithPort()<<": "<<ne.what()<<std::endl; 
       d_socket.reset();
-      fail = true;
+      throw PDNSException("While reading from HTTP endpoint " + d_addr.toStringWithPort() + ": " + ne.what());
     } catch (...) {
-      g_log<<Logger::Error<<"While reading from HTTP endpoint "<<d_addr.toStringWithPort()<<": exception caught"<<std::endl;
       d_socket.reset();
-      fail = true;
-    }
-
-    if (fail) {
-      return -1;
+      throw PDNSException("While reading from HTTP endpoint " + d_addr.toStringWithPort() + ": unknown error");
     }
 
     arl.finalize();
 
-    if (resp.status < 200 || resp.status >= 400) {
+    if ((resp.status < 200 || resp.status >= 400) && resp.status != 404) {
       // bad. 
-      return -1;
+      throw PDNSException("Received unacceptable HTTP status code " + std::to_string(resp.status) + " from HTTP endpoint " + d_addr.toStringWithPort());
     }
 
     int rv = -1;
index c1be5e3f2919f001a98c7f0b1199a8f1a2c0818c..23db9c50ad1abe5c1f0984e4c68582ac77db8fb4 100644 (file)
@@ -6,7 +6,7 @@ GEM
       ffi-rzmq-core (>= 1.0.1)
     ffi-rzmq-core (1.0.3)
       ffi (~> 1.9)
-    json (1.8.2)
+    json (2.3.0)
     sqlite3 (1.3.9)
     webrick (1.4.2)
     zeromqrb (0.1.3)
index b5862edbc273adf6b5663441fc8691e756992524..220b518f0e0a73a5a820ffc5b54a3121a656335d 100644 (file)
@@ -42,18 +42,27 @@ bool Connector::send(Json& value) {
  * result. Logging is performed here, too.
  */
 bool Connector::recv(Json& value) {
-    if (recv_message(value)>0) {
-       bool rv = true;
-       // check for error
-       if (value["result"] == Json())
-         return false;
-       if (value["result"].is_bool() && boolFromJson(value, "result", false) == false)
-         rv = false;
-       for(const auto& message: value["log"].array_items())
-         g_log<<Logger::Info<<"[remotebackend]: "<< message.string_value() <<std::endl;
-       return rv;
+  if (recv_message(value) > 0) {
+    bool retval = true;
+    if (value["result"] == Json()) {
+      throw PDNSException("No 'result' field in response from remote process");
+    } else if (value["result"].is_bool() && boolFromJson(value, "result", false) == false) {
+      retval = false;
     }
-    return false;
+    for(const auto& message: value["log"].array_items()) {
+      g_log<<Logger::Info<<"[remotebackend]: "<< message.string_value() <<std::endl;
+    }
+    return retval;
+  }
+  throw PDNSException("Unknown error while receiving data");
+}
+
+void RemoteBackend::makeErrorAndThrow(Json &value) {
+  std::string msg = "Remote process indicated a failure";
+  for(const auto& message: value["log"].array_items()) {
+    msg += " '" + message.string_value() + "'";
+  }
+  throw PDNSException(msg);
 }
 
 /**
@@ -74,29 +83,31 @@ RemoteBackend::RemoteBackend(const std::string &suffix)
 RemoteBackend::~RemoteBackend() { }
 
 bool RemoteBackend::send(Json& value) {
-   try {
-     return connector->send(value);
-   } catch (PDNSException &ex) {
-     g_log<<Logger::Error<<"Exception caught when sending: "<<ex.reason<<std::endl;
-   }
-
-   this->connector.reset();
-   build();
-   return false;
+  try {
+    if (!connector->send(value)) {
+      // XXX does this work work even though we throw?
+      this->connector.reset();
+      build();
+      throw DBException("Could not send a message to remote process");
+    }
+  } catch (const PDNSException &ex) {
+    throw DBException("Exception caught when sending: " + ex.reason);
+  }
+  return true;
 }
 
 bool RemoteBackend::recv(Json& value) {
-   try {
-     return connector->recv(value);
-   } catch (PDNSException &ex) {
-     g_log<<Logger::Error<<"Exception caught when receiving: "<<ex.reason<<std::endl;
-   } catch (...) {
-     g_log<<Logger::Error<<"Exception caught when receiving"<<std::endl;;
-   }
-
-   this->connector.reset();
-   build();
-   return false;
+  try {
+    return connector->recv(value);
+  } catch (const PDNSException &ex) {
+    this->connector.reset();
+    build();
+    throw DBException("Exception caught when receiving: " + ex.reason);
+  } catch (const std::exception &e) {
+    this->connector.reset();
+    build();
+    throw DBException("Exception caught when receiving: " + std::string(e.what()));
+  }
 }
 
 
@@ -1025,13 +1036,13 @@ class RemoteBackendFactory : public BackendFactory
   public:
       RemoteBackendFactory() : BackendFactory("remote") {}
 
-      void declareArguments(const std::string &suffix="")
+      void declareArguments(const std::string &suffix="") override
       {
           declare(suffix,"dnssec","Enable dnssec support","no");
           declare(suffix,"connection-string","Connection string","");
       }
 
-      DNSBackend *make(const std::string &suffix="")
+      DNSBackend *make(const std::string &suffix="") override
       {
          return new RemoteBackend(suffix);
       }
index 5f94d223bc4c0864a06768f3a3dde8ac24cedc5d..cadc8eed7e8e0742f9e0c0327c5d2b27b7ba9b97 100644 (file)
@@ -207,6 +207,7 @@ class RemoteBackend : public DNSBackend
 
     bool send(Json &value);
     bool recv(Json &value);
+    void makeErrorAndThrow(Json &value);
  
     string asString(const Json& value) {
       if (value.is_number()) return std::to_string(value.int_value());
index 3f77b0e2f8a76816c771300278e6c21b73be1abe..1de99d5e0232fef5b15401ab76efa1bd43bbd977 100644 (file)
@@ -348,7 +348,7 @@ class TinyDNSFactory: public BackendFactory
 public:
   TinyDNSFactory() : BackendFactory("tinydns") {}
 
-  void declareArguments(const string &suffix="") {
+  void declareArguments(const string &suffix="") override {
     declare(suffix, "notify-on-startup", "Tell the TinyDNSBackend to notify all the slave nameservers on startup. Default is no.", "no");
     declare(suffix, "dbfile", "Location of the cdb data file", "data.cdb");
     declare(suffix, "tai-adjust", "This adjusts the TAI value if timestamps are used. These seconds will be added to the start point (1970) and will allow you to adjust for leap seconds. The default is 11.", "11");
@@ -356,7 +356,7 @@ public:
     declare(suffix, "ignore-bogus-records", "The data.cdb file might have some incorrect record data, this causes PowerDNS to fail, where tinydns would send out truncated data. This option makes powerdns ignore that data!", "no");
   }
 
-  DNSBackend *make(const string &suffix="") {
+  DNSBackend *make(const string &suffix="") override {
     return new TinyDNSBackend(suffix);
   }
 };
index 9c18c801e599fc44989681b04f8b0e63e0287571..e54d7569e70eb7c87d4ea712e201f84b8265a8f1 100644 (file)
@@ -202,9 +202,11 @@ pdns_server_SOURCES = \
        packethandler.cc packethandler.hh \
        pdnsexception.hh \
        qtype.cc qtype.hh \
+       query-local-address.hh query-local-address.cc \
        rcpgenerator.cc \
        receiver.cc \
        resolver.cc resolver.hh \
+       axfr-retriever.cc axfr-retriever.hh \
        responsestats.cc responsestats.hh responsestats-auth.cc \
        rfc2136handler.cc \
        secpoll.cc secpoll.hh \
@@ -633,9 +635,11 @@ ixfrdist_SOURCES = \
        misc.cc misc.hh \
        mplexer.hh \
        nsecrecords.cc \
+       query-local-address.hh query-local-address.cc \
        qtype.cc \
        rcpgenerator.cc rcpgenerator.hh \
        resolver.cc \
+       axfr-retriever.cc \
        pollmplexer.cc \
        sillyrecords.cc \
        sstuff.hh \
@@ -687,9 +691,11 @@ ixplore_SOURCES = \
        logger.cc \
        misc.cc misc.hh \
        nsecrecords.cc \
+       query-local-address.hh query-local-address.cc \
        qtype.cc \
        rcpgenerator.cc rcpgenerator.hh \
        resolver.cc \
+       axfr-retriever.cc \
        ixfr.cc ixfr.hh \
        ixfrutils.cc ixfrutils.hh \
        ixplore.cc \
@@ -823,6 +829,7 @@ toysdig_LDADD += $(P11KIT1_LIBS)
 endif
 
 tsig_tests_SOURCES = \
+       axfr-retriever.cc \
        arguments.cc \
        base32.cc \
        base64.cc base64.hh \
@@ -836,9 +843,11 @@ tsig_tests_SOURCES = \
        dnssecinfra.cc \
        dnswriter.cc dnswriter.hh \
        gss_context.cc gss_context.hh \
+       iputils.cc \
        logger.cc \
        misc.cc misc.hh \
        nsecrecords.cc \
+       query-local-address.cc \
        qtype.cc \
        rcpgenerator.cc rcpgenerator.hh \
        resolver.cc \
@@ -1255,7 +1264,7 @@ endif
 endif
 
 pdns.conf-dist: pdns_server
-       $(AM_V_GEN)./pdns_server --no-config --config=default 2>/dev/null > $@
+       $(AM_V_GEN)./pdns_server --config=default 2>/dev/null > $@
 
 testrunner_SOURCES = \
        arguments.cc \
@@ -1334,11 +1343,12 @@ testrunner_SOURCES = \
        test-sha_hh.cc \
        test-statbag_cc.cc \
        test-tsig.cc \
+       test-ueberbackend_cc.cc \
        test-zoneparser_tng_cc.cc \
        testrunner.cc \
        threadname.hh threadname.cc \
        tsigverifier.cc tsigverifier.hh \
-       ueberbackend.cc \
+       ueberbackend.cc ueberbackend.hh \
        unix_utility.cc \
        zoneparser-tng.cc zoneparser-tng.hh
 
@@ -1615,6 +1625,8 @@ endif
 pdns@.service: pdns.service
        $(AM_V_GEN)sed -e 's!/pdns_server!& --config-name=%i!' \
          -e 's!Authoritative Server!& %i!' \
+         -e 's!RuntimeDirectory=.*!&-%i!' \
+         -e 's!SyslogIdentifier=.*!&-%i!' \
          < $< > $@
 
 systemdsystemunitdir = $(SYSTEMD_DIR)
index 09f776aa7c943d62cd5fcd8c90330efa497bb330..3f839d2b6bd2a7c907264de4291b7b4c11b83635 100644 (file)
@@ -40,14 +40,13 @@ try
 
   string namespace_name=arg()["carbon-namespace"];
   string hostname=arg()["carbon-ourname"];
-  if(hostname.empty()) {
-    char tmp[80];
-    memset(tmp, 0, sizeof(tmp));
-    gethostname(tmp, sizeof(tmp));
-    char *p = strchr(tmp, '.');
-    if(p) *p=0;
-    hostname=tmp;
-    boost::replace_all(hostname, ".", "_");
+  if (hostname.empty()) {
+    try {
+      hostname = getCarbonHostName();
+    }
+    catch(const std::exception& e) {
+      throw std::runtime_error(std::string("The 'carbon-ourname' setting has not been set and we are unable to determine the system's hostname: ") + e.what());
+    }
   }
   string instance_name=arg()["carbon-instance"];
 
index 759864e58888323b83981edccd7e210e9c004ad7..1cb554f44ed4e4513594ecd1527608ba0e9ecc6b 100644 (file)
@@ -34,7 +34,7 @@ AuthPacketCache::AuthPacketCache(size_t mapsCount): d_maps(mapsCount), d_lastcle
 {
   S.declare("packetcache-hit", "Number of hits on the packet cache");
   S.declare("packetcache-miss", "Number of misses on the packet cache");
-  S.declare("packetcache-size", "Number of entries in the packet cache");
+  S.declare("packetcache-size", "Number of entries in the packet cache", StatType::gauge);
   S.declare("deferred-packetcache-inserts","Amount of packet cache inserts that were deferred because of maintenance");
   S.declare("deferred-packetcache-lookup","Amount of packet cache lookups that were deferred because of maintenance");
 
index f98d9b133d875aa8d15222ac17c40d0831ae426a..82489e1e9214a4fafa33fdf1941d8221d1e3d491 100644 (file)
@@ -35,7 +35,7 @@ AuthQueryCache::AuthQueryCache(size_t mapsCount): d_maps(mapsCount), d_lastclean
 {
   S.declare("query-cache-hit","Number of hits on the query cache");
   S.declare("query-cache-miss","Number of misses on the query cache");
-  S.declare("query-cache-size", "Number of entries in the query cache");
+  S.declare("query-cache-size", "Number of entries in the query cache", StatType::gauge);
   S.declare("deferred-cache-inserts","Amount of cache inserts that were deferred because of maintenance");
   S.declare("deferred-cache-lookup","Amount of cache lookups that were deferred because of maintenance");
 
diff --git a/pdns/axfr-retriever.cc b/pdns/axfr-retriever.cc
new file mode 100644 (file)
index 0000000..4d2718e
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "axfr-retriever.hh"
+#include "arguments.hh"
+#include "dns_random.hh"
+#include "utility.hh"
+#include "resolver.hh"
+#include "query-local-address.hh"
+
+using pdns::resolver::parseResult;
+
+AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
+                             const DNSName& domain,
+                             const TSIGTriplet& tt, 
+                             const ComboAddress* laddr,
+                             size_t maxReceivedBytes,
+                             uint16_t timeout)
+  : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
+{
+  ComboAddress local;
+  if (laddr != nullptr) {
+    local = ComboAddress(*laddr);
+  } else {
+    if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
+      throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". Address family is not configured for outgoing queries");
+    }
+    local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
+  }
+  d_sock = -1;
+  try {
+    d_sock = makeQuerySocket(local, false); // make a TCP socket
+    if (d_sock < 0)
+      throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort());
+    d_buf = shared_array<char>(new char[65536]);
+    d_remote = remote; // mostly for error reporting
+    this->connect(timeout);
+    d_soacount = 0;
+  
+    vector<uint8_t> packet;
+    DNSPacketWriter pw(packet, domain, QType::AXFR);
+    pw.getHeader()->id = dns_random_uint16();
+  
+    if(!tt.name.empty()) {
+      if (tt.algo == DNSName("hmac-md5"))
+        d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int");
+      else
+        d_trc.d_algoName = tt.algo;
+      d_trc.d_time = time(0);
+      d_trc.d_fudge = 300;
+      d_trc.d_origID=ntohs(pw.getHeader()->id);
+      d_trc.d_eRcode=0;
+      addTSIG(pw, d_trc, tt.name, tt.secret, "", false);
+    }
+  
+    uint16_t replen=htons(packet.size());
+    Utility::iovec iov[2];
+    iov[0].iov_base=reinterpret_cast<char*>(&replen);
+    iov[0].iov_len=2;
+    iov[1].iov_base=packet.data();
+    iov[1].iov_len=packet.size();
+  
+    int ret=Utility::writev(d_sock, iov, 2);
+    if(ret < 0)
+      throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
+    if(ret != (int)(2+packet.size())) {
+      throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
+    }
+  
+    int res = waitForData(d_sock, timeout, 0);
+    
+    if(!res)
+      throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
+    if(res<0)
+      throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
+  }
+  catch(...) {
+    if(d_sock >= 0)
+      close(d_sock);
+    d_sock = -1;
+    throw;
+  }
+}
+
+AXFRRetriever::~AXFRRetriever()
+{
+  close(d_sock);
+}
+
+
+
+int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed.
+{
+  if(d_soacount > 1)
+    return false;
+
+  // d_sock is connected and is about to spit out a packet
+  int len=getLength(timeout);
+  if(len<0)
+    throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
+
+  if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
+    throw ResolverException("Reached the maximum number of received bytes during AXFR");
+
+  timeoutReadn(len, timeout);
+
+  d_receivedBytes += (uint16_t) len;
+
+  MOADNSParser mdp(false, d_buf.get(), len);
+
+  int err = mdp.d_header.rcode;
+
+  if(err) {
+    throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
+  }
+
+  try {
+    d_tsigVerifier.check(std::string(d_buf.get(), len), mdp);
+  }
+  catch(const std::runtime_error& re) {
+    throw ResolverException(re.what());
+  }
+
+  if(!records) {
+    err = parseResult(mdp, DNSName(), 0, 0, &res);
+
+    if (!err) {
+      for(const auto& answer :  mdp.d_answers)
+        if (answer.first.d_type == QType::SOA)
+          d_soacount++;
+    }
+  }
+  else {
+    records->clear();
+    records->reserve(mdp.d_answers.size());
+
+    for(auto& r: mdp.d_answers) {
+      if (r.first.d_type == QType::SOA) {
+        d_soacount++;
+      }
+
+      records->push_back(std::move(r.first));
+    }
+  }
+
+  return true;
+}
+
+void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec)
+{
+  time_t start=time(nullptr);
+  int n=0;
+  int numread;
+  while(n<bytes) {
+    int res=waitForData(d_sock, timeoutsec-(time(nullptr)-start));
+    if(res<0)
+      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
+    if(!res)
+      throw ResolverException("Timeout while reading data from remote nameserver over TCP");
+
+    numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
+    if(numread<0)
+      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
+    if(numread==0)
+      throw ResolverException("Remote nameserver closed TCP connection");
+    n+=numread;
+  }
+}
+
+void AXFRRetriever::connect(uint16_t timeout)
+{
+  setNonBlocking( d_sock );
+
+  int err;
+
+  if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
+    try {
+      closesocket(d_sock);
+    }
+    catch(const PDNSException& e) {
+      d_sock=-1;
+      throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
+    }
+
+    throw ResolverException("connect: "+stringerror());
+  }
+
+  if(!err)
+    goto done;
+
+  err=waitForRWData(d_sock, false, timeout, 0); // wait for writeability
+  
+  if(!err) {
+    try {
+      closesocket(d_sock); // timeout
+    }
+    catch(const PDNSException& e) {
+      d_sock=-1;
+      throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
+    }
+
+    d_sock=-1;
+    errno=ETIMEDOUT;
+    
+    throw ResolverException("Timeout connecting to server");
+  }
+  else if(err < 0) {
+    throw ResolverException("Error connecting: "+stringerror());
+  }
+  else {
+    Utility::socklen_t len=sizeof(err);
+    if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
+      throw ResolverException("Error connecting: "+stringerror()); // Solaris
+
+    if(err)
+      throw ResolverException("Error connecting: "+string(strerror(err)));
+  }
+  
+ done:
+  setBlocking( d_sock );
+  // d_sock now connected
+}
+
+int AXFRRetriever::getLength(uint16_t timeout)
+{
+  timeoutReadn(2, timeout);
+  return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
+}
+
diff --git a/pdns/axfr-retriever.hh b/pdns/axfr-retriever.hh
new file mode 100644 (file)
index 0000000..63f3324
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+#include <boost/utility.hpp>
+
+#include "iputils.hh"
+#include "dnsname.hh"
+#include "resolver.hh"
+
+class AXFRRetriever : public boost::noncopyable
+{
+  public:
+    AXFRRetriever(const ComboAddress& remote,
+                  const DNSName& zone,
+                  const TSIGTriplet& tt = TSIGTriplet(),
+                  const ComboAddress* laddr = NULL,
+                  size_t maxReceivedBytes=0,
+                  uint16_t timeout=10);
+    ~AXFRRetriever();
+    int getChunk(Resolver::res_t &res, vector<DNSRecord>* records=0, uint16_t timeout=10);
+
+  private:
+    void connect(uint16_t timeout);
+    int getLength(uint16_t timeout);
+    void timeoutReadn(uint16_t bytes, uint16_t timeoutsec=10);
+
+    shared_array<char> d_buf;
+    string d_domain;
+    int d_sock;
+    int d_soacount;
+    ComboAddress d_remote;
+    TSIGTCPVerifier d_tsigVerifier;
+
+    size_t d_receivedBytes;
+    size_t d_maxReceivedBytes;
+    TSIGRecordContent d_trc;
+};
index 3687c61a5a04223ad846f32b98280b0c626a30e5..3f9372fd253de4c5054fe04c68c55fcdceaef399 100644 (file)
@@ -41,7 +41,7 @@
 GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
 {
   setArgPrefix(mode+suffix);
-  d_db=0;
+  d_db = nullptr;
   d_logprefix="["+mode+"Backend"+suffix+"] ";
 
   try
@@ -65,6 +65,7 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
   d_InfoOfAllSlaveDomainsQuery=getArg("info-all-slaves-query");
   d_SuperMasterInfoQuery=getArg("supermaster-query");
   d_GetSuperMasterIPs=getArg("supermaster-name-to-ips");
+  d_AddSuperMaster=getArg("supermaster-add"); 
   d_InsertZoneQuery=getArg("insert-zone-query");
   d_InsertRecordQuery=getArg("insert-record-query");
   d_UpdateMasterOfZoneQuery=getArg("update-master-query");
@@ -134,6 +135,7 @@ GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
   d_InfoOfAllSlaveDomainsQuery_stmt = NULL;
   d_SuperMasterInfoQuery_stmt = NULL;
   d_GetSuperMasterIPs_stmt = NULL;
+  d_AddSuperMaster_stmt = NULL;
   d_InsertZoneQuery_stmt = NULL;
   d_InsertRecordQuery_stmt = NULL;
   d_InsertEmptyNonTerminalOrderQuery_stmt = NULL;
@@ -216,19 +218,26 @@ void GSQLBackend::setFresh(uint32_t domain_id)
   }
 }
 
-bool GSQLBackend::setMaster(const DNSName &domain, const string &ip)
+bool GSQLBackend::setMasters(const DNSName &domain, const vector<ComboAddress> &masters)
 {
+  vector<string> masters_s;
+  for (const auto& master : masters) {
+    masters_s.push_back(master.toStringWithPortExcept(53));
+  }
+
+  auto tmp = boost::join(masters_s, ", ");
+
   try {
     reconnectIfNeeded();
 
     d_UpdateMasterOfZoneQuery_stmt->
-      bind("master", ip)->
+      bind("master", tmp)->
       bind("domain", domain)->
       execute()->
       reset();
   }
   catch (SSqlException &e) {
-    throw PDNSException("GSQLBackend unable to set master of domain '"+domain.toLogString()+"' to IP address " + ip + ": "+e.txtReason());
+    throw PDNSException("GSQLBackend unable to set masters of domain '"+domain.toLogString()+"' to " + tmp + ": "+e.txtReason());
   }
   return true;
 }
@@ -408,11 +417,11 @@ void GSQLBackend::getUnfreshSlaveInfos(vector<DomainInfo> *unfreshDomains)
       }
     }
     catch(const std::exception& exp) {
-      g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone.toLogString()<<"': "<<exp.what()<<endl;
+      g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone<<"': "<<exp.what()<<endl;
       continue;
     }
     catch(...) {
-      g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone.toLogString()<<"', skipping"<<endl;
+      g_log<<Logger::Warning<<"Error while parsing SOA data for slave zone '"<<slave.zone<<"', skipping"<<endl;
       continue;
     }
   }
@@ -1206,6 +1215,26 @@ skiprow:
   return false;
 }
 
+bool GSQLBackend::superMasterAdd(const string &ip, const string &nameserver, const string &account)
+{
+  try{
+    reconnectIfNeeded();
+
+    d_AddSuperMaster_stmt -> 
+      bind("ip",ip)->
+      bind("nameserver",nameserver)->
+      bind("account",account)->
+      execute()->
+      reset();
+
+  }
+  catch (SSqlException &e){
+    throw PDNSException("GSQLBackend unable to insert a supermaster with IP " + ip + " and nameserver name '" + nameserver + "' and account '" + account + "': " + e.txtReason()); 
+  }
+  return true;
+
+}
+
 bool GSQLBackend::superMasterBackend(const string &ip, const DNSName &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **ddb)
 {
   // check if we know the ip/ns couple in the database
@@ -1234,15 +1263,20 @@ bool GSQLBackend::superMasterBackend(const string &ip, const DNSName &domain, co
   return false;
 }
 
-bool GSQLBackend::createDomain(const DNSName &domain, const string &type, const string &masters, const string &account)
+bool GSQLBackend::createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account)
 {
+  vector<string> masters_s;
+  for (const auto& master : masters) {
+    masters_s.push_back(master.toStringWithPortExcept(53));
+  }
+
   try {
     reconnectIfNeeded();
 
     d_InsertZoneQuery_stmt->
-      bind("type", type)->
+      bind("type", toUpper(DomainInfo::getKindString(kind)))->
       bind("domain", domain)->
-      bind("masters", masters)->
+      bind("masters", boost::join(masters_s, ", "))->
       bind("account", account)->
       execute()->
       reset();
@@ -1256,7 +1290,7 @@ bool GSQLBackend::createDomain(const DNSName &domain, const string &type, const
 bool GSQLBackend::createSlaveDomain(const string &ip, const DNSName &domain, const string &nameserver, const string &account)
 {
   string name;
-  string masters(ip);
+  vector<ComboAddress> masters({ComboAddress(ip, 53)});
   try {
     if (!nameserver.empty()) {
       // figure out all IP addresses for the master
@@ -1270,16 +1304,16 @@ bool GSQLBackend::createSlaveDomain(const string &ip, const DNSName &domain, con
         reset();
       if (!d_result.empty()) {
         // collect all IP addresses
-        vector<string> tmp;
+        vector<ComboAddress> tmp;
         for(const auto& row: d_result) {
           if (account == row[1])
-            tmp.push_back(row[0]);
+            tmp.emplace_back(row[0], 53);
         }
         // set them as domain's masters, comma separated
-        masters = boost::join(tmp, ", ");
+        masters = tmp;
       }
     }
-    createDomain(domain, "SLAVE", masters, account);
+    createDomain(domain, DomainInfo::Slave, masters, account);
   }
   catch(SSqlException &e) {
     throw PDNSException("Database error trying to insert new slave domain '"+domain.toLogString()+"': "+ e.txtReason());
index a98b3a83805841dfb91ffc3a42004a3299c9b3f3..69083e01edff9da906cbf11656e7f33d999151b0 100644 (file)
@@ -48,11 +48,11 @@ public:
     d_db=std::unique_ptr<SSql>(db);
     if (d_db) {
       d_db->setLog(::arg().mustDo("query-logging"));
-      allocateStatements();
     }
   }
 
-  void allocateStatements()
+protected:
+  virtual void allocateStatements()
   {
     if (d_db) {
       d_NoIdQuery_stmt = d_db->prepare(d_NoIdQuery, 2);
@@ -66,6 +66,7 @@ public:
       d_InfoOfAllSlaveDomainsQuery_stmt = d_db->prepare(d_InfoOfAllSlaveDomainsQuery, 0);
       d_SuperMasterInfoQuery_stmt = d_db->prepare(d_SuperMasterInfoQuery, 2);
       d_GetSuperMasterIPs_stmt = d_db->prepare(d_GetSuperMasterIPs, 2);
+      d_AddSuperMaster_stmt = d_db->prepare(d_AddSuperMaster, 3); 
       d_InsertZoneQuery_stmt = d_db->prepare(d_InsertZoneQuery, 4);
       d_InsertRecordQuery_stmt = d_db->prepare(d_InsertRecordQuery, 9);
       d_InsertEmptyNonTerminalOrderQuery_stmt = d_db->prepare(d_InsertEmptyNonTerminalOrderQuery, 4);
@@ -117,7 +118,7 @@ public:
     }
   }
 
-  void freeStatements() {
+  virtual void freeStatements() {
     d_NoIdQuery_stmt.reset();
     d_IdQuery_stmt.reset();
     d_ANYNoIdQuery_stmt.reset();
@@ -129,6 +130,7 @@ public:
     d_InfoOfAllSlaveDomainsQuery_stmt.reset();
     d_SuperMasterInfoQuery_stmt.reset();
     d_GetSuperMasterIPs_stmt.reset();
+    d_AddSuperMaster_stmt.reset();
     d_InsertZoneQuery_stmt.reset();
     d_InsertRecordQuery_stmt.reset();
     d_InsertEmptyNonTerminalOrderQuery_stmt.reset();
@@ -179,6 +181,7 @@ public:
     d_SearchCommentsQuery_stmt.reset();
   }
 
+public:
   void lookup(const QType &, const DNSName &qdomain, int zoneId, DNSPacket *p=nullptr) override;
   bool list(const DNSName &target, int domain_id, bool include_disabled=false) override;
   bool get(DNSResourceRecord &r) override;
@@ -190,18 +193,17 @@ public:
   bool feedRecord(const DNSResourceRecord &r, const DNSName &ordername, bool ordernameIsNSEC3=false) override;
   bool feedEnts(int domain_id, map<DNSName,bool>& nonterm) override;
   bool feedEnts3(int domain_id, const DNSName &domain, map<DNSName,bool> &nonterm, const NSEC3PARAMRecordContent& ns3prc, bool narrow) override;
-  bool createDomain(const DNSName &domain) override {
-    return createDomain(domain, "NATIVE", "", "");
-  };
+  bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account) override;
   bool createSlaveDomain(const string &ip, const DNSName &domain, const string &nameserver, const string &account) override;
   bool deleteDomain(const DNSName &domain) override;
+  bool superMasterAdd(const string &ip, const string &nameserver, const string &account); 
   bool superMasterBackend(const string &ip, const DNSName &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **db) override;
   void setFresh(uint32_t domain_id) override;
   void getUnfreshSlaveInfos(vector<DomainInfo> *domains) override;
   void getUpdatedMasters(vector<DomainInfo> *updatedDomains) override;
   bool getDomainInfo(const DNSName &domain, DomainInfo &di, bool getSerial=true) override;
   void setNotified(uint32_t domain_id, uint32_t serial) override;
-  bool setMaster(const DNSName &domain, const string &ip) override;
+  bool setMasters(const DNSName &domain, const vector<ComboAddress> &masters) override;
   bool setKind(const DNSName &domain, const DomainInfo::DomainKind kind) override;
   bool setAccount(const DNSName &domain, const string &account) override;
 
@@ -239,7 +241,6 @@ public:
   bool searchComments(const string &pattern, int maxResults, vector<Comment>& result) override;
 
 protected:
-  bool createDomain(const DNSName &domain, const string &type, const string &masters, const string &account);
   string pattern2SQLPattern(const string& pattern);
   void extractRecord(SSqlStatement::row_t& row, DNSResourceRecord& rr);
   void extractComment(SSqlStatement::row_t& row, Comment& c);
@@ -263,11 +264,12 @@ protected:
     return d_inTransaction;
   }
 
-private:
   string d_query_name;
   DNSName d_qname;
   SSqlStatement::result_t d_result;
+  unique_ptr<SSqlStatement>* d_query_stmt;
 
+private:
   string d_NoIdQuery;
   string d_IdQuery;
   string d_ANYNoIdQuery;
@@ -283,6 +285,7 @@ private:
   string d_SuperMasterInfoQuery;
   string d_GetSuperMasterName;
   string d_GetSuperMasterIPs;
+  string d_AddSuperMaster;
 
   string d_InsertZoneQuery;
   string d_InsertRecordQuery;
@@ -342,7 +345,6 @@ private:
   string d_SearchRecordsQuery;
   string d_SearchCommentsQuery;
 
-  unique_ptr<SSqlStatement>* d_query_stmt;
 
   unique_ptr<SSqlStatement> d_NoIdQuery_stmt;
   unique_ptr<SSqlStatement> d_IdQuery_stmt;
@@ -355,6 +357,7 @@ private:
   unique_ptr<SSqlStatement> d_InfoOfAllSlaveDomainsQuery_stmt;
   unique_ptr<SSqlStatement> d_SuperMasterInfoQuery_stmt;
   unique_ptr<SSqlStatement> d_GetSuperMasterIPs_stmt;
+  unique_ptr<SSqlStatement> d_AddSuperMaster_stmt;
   unique_ptr<SSqlStatement> d_InsertZoneQuery_stmt;
   unique_ptr<SSqlStatement> d_InsertRecordQuery_stmt;
   unique_ptr<SSqlStatement> d_InsertEmptyNonTerminalOrderQuery_stmt;
index 18e5ea147494a65d3ec505ff993aab50e1276d58..5b2ff3d6fcdf64e637a3d8ba51b263d2bce4160a 100644 (file)
 // this function can clean any cache that has a getTTD() method on its entries, a preRemoval() method and a 'sequence' index as its second index
 // the ritual is that the oldest entries are in *front* of the sequence collection, so on a hit, move an item to the end
 // on a miss, move it to the beginning
-template <typename S, typename C, typename T> void pruneCollection(C& container, T& collection, unsigned int maxCached, unsigned int scanFraction=1000)
+template <typename S, typename C, typename T> void pruneCollection(C& container, T& collection, size_t maxCached, size_t scanFraction = 1000)
 {
-  time_t now=time(0);
-  unsigned int toTrim=0;
-  
-  unsigned int cacheSize=collection.size();
+  const time_t now = time(0);
+  size_t toTrim = 0;
+  const size_t cacheSize = collection.size();
 
-  if(cacheSize > maxCached) {
+  if (cacheSize > maxCached) {
     toTrim = cacheSize - maxCached;
   }
 
-//  cout<<"Need to trim "<<toTrim<<" from cache to meet target!\n";
-
-  typedef typename T::template index<S>::type sequence_t;
-  sequence_t& sidx=collection.template get<S>();
-
-  unsigned int tried=0, lookAt, erased=0;
+  auto& sidx = collection.template get<S>();
 
   // two modes - if toTrim is 0, just look through 1/scanFraction of all records 
   // and nuke everything that is expired
   // otherwise, scan first 5*toTrim records, and stop once we've nuked enough
-  if(toTrim)
-    lookAt=5*toTrim;
-  else
-    lookAt=cacheSize/scanFraction;
+  const size_t lookAt = toTrim ? 5 * toTrim : cacheSize / scanFraction;
+  size_t tried = 0, erased = 0;
 
-  typename sequence_t::iterator iter=sidx.begin(), eiter;
-  for(; iter != sidx.end() && tried < lookAt ; ++tried) {
-    if(iter->getTTD() < now) {
+  for (auto iter = sidx.begin(); iter != sidx.end() && tried < lookAt ; ++tried) {
+    if (iter->getTTD() < now) {
       container.preRemoval(*iter);
       iter = sidx.erase(iter);
       erased++;
     }
-    else
+    else {
       ++iter;
+    }
 
-    if(toTrim && erased >= toTrim)
+    if (toTrim && erased >= toTrim) {
       break;
+    }
   }
 
-  //cout<<"erased "<<erased<<" records based on ttd\n";
-  
-  if(erased >= toTrim) // done
+  if (erased >= toTrim) { // done
     return;
+  }
 
   toTrim -= erased;
 
-  //if(toTrim)
-    // cout<<"Still have "<<toTrim - erased<<" entries left to erase to meet target\n"; 
-
-  eiter=iter=sidx.begin();
-  std::advance(eiter, toTrim);
   // just lob it off from the beginning
-  for (auto i = iter; ; ) {
-    if (i == eiter) {
-      break;
-    }
-
-    container.preRemoval(*i);
-    sidx.erase(i++);
+  auto iter = sidx.begin();
+  for (size_t i = 0; i < toTrim && iter != sidx.end(); i++) {
+    container.preRemoval(*iter);
+    iter = sidx.erase(iter);
   }
 }
 
@@ -190,7 +174,7 @@ template <typename S, typename C, typename T> uint64_t pruneMutexCollectionsVect
 
   toTrim -= totErased;
 
-  while (toTrim > 0) {
+    while (true) {
     size_t pershard = toTrim / maps_size + 1;
     for (auto& mc : maps) {
       const typename C::lock l(mc);
@@ -204,11 +188,12 @@ template <typename S, typename C, typename T> uint64_t pruneMutexCollectionsVect
         totErased++;
         toTrim--;
         if (toTrim == 0) {
-          break;
+          return totErased;
         }
       }
     }
   }
+  // Not reached
   return totErased;
 }
 
index 18553ed5082b68da79ece245d61eec04a35eecba..fe4994cc1392e12ad86babd5a76d95682f7e0ba1 100644 (file)
@@ -31,6 +31,7 @@
 #include "dnsseckeeper.hh"
 #include "threadname.hh"
 #include "misc.hh"
+#include "query-local-address.hh"
 
 #include <thread>
 
@@ -70,7 +71,16 @@ void declareArguments()
 {
   ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=SYSCONFDIR;
   ::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
-  ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns when unset and not chrooted" )="";
+  ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns when unset and not chrooted"
+#ifdef HAVE_SYSTEMD
+      + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")="";
+   auto runtimeDir = getenv("RUNTIME_DIRECTORY");
+   if (runtimeDir != nullptr) {
+     ::arg().set("socket-dir") = runtimeDir;
+   }
+#else
+      )="";
+#endif
   ::arg().set("module-dir","Default directory for modules")=PKGLIBDIR;
   ::arg().set("chroot","If set, chroot to this directory for more security")="";
   ::arg().set("logging-facility","Log under a specific facility")="";
@@ -91,8 +101,8 @@ void declareArguments()
   ::arg().setSwitch("local-address-nonexist-fail","Fail to start if one or more of the local-address's do not exist on this server")="yes";
   ::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options")="no";
   ::arg().setSwitch("reuseport","Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket")="no";
-  ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
-  ::arg().set("query-local-address6","Source IPv6 address for sending queries")="::";
+  ::arg().set("query-local-address","Source IP addresses for sending queries")="0.0.0.0 ::";
+  ::arg().set("query-local-address6","DEPRECATED: Use query-local-address. Source IPv6 address for sending queries")="";
   ::arg().set("overload-queue-length","Maximum queuelength moving to packetcache only")="0";
   ::arg().set("max-queue-length","Maximum queuelength before considering situation lost")="5000";
 
@@ -323,9 +333,9 @@ void declareStats(void)
   S.declare("tcp6-queries","Number of IPv6 TCP queries received");
   S.declare("tcp6-answers","Number of IPv6 answers sent out over TCP");
 
-  S.declare("open-tcp-connections","Number of currently open TCP connections", getTCPConnectionCount);;
+  S.declare("open-tcp-connections","Number of currently open TCP connections", getTCPConnectionCount, StatType::gauge);
 
-  S.declare("qsize-q","Number of questions waiting for database attention", getQCount);
+  S.declare("qsize-q","Number of questions waiting for database attention", getQCount, StatType::gauge);
 
   S.declare("dnsupdate-queries", "DNS update packets received.");
   S.declare("dnsupdate-answers", "DNS update packets successfully answered.");
@@ -334,33 +344,33 @@ void declareStats(void)
 
   S.declare("incoming-notifications", "NOTIFY packets received.");
 
-  S.declare("uptime", "Uptime of process in seconds", uptimeOfProcess);
-  S.declare("real-memory-usage", "Actual unique use of memory in bytes (approx)", getRealMemoryUsage);
-  S.declare("special-memory-usage", "Actual unique use of memory in bytes (approx)", getSpecialMemoryUsage);
-  S.declare("fd-usage", "Number of open filedescriptors", getOpenFileDescriptors);
+  S.declare("uptime", "Uptime of process in seconds", uptimeOfProcess, StatType::counter);
+  S.declare("real-memory-usage", "Actual unique use of memory in bytes (approx)", getRealMemoryUsage, StatType::gauge);
+  S.declare("special-memory-usage", "Actual unique use of memory in bytes (approx)", getSpecialMemoryUsage, StatType::gauge);
+  S.declare("fd-usage", "Number of open filedescriptors", getOpenFileDescriptors, StatType::gauge);
 #ifdef __linux__
-  S.declare("udp-recvbuf-errors", "UDP 'recvbuf' errors", udpErrorStats);
-  S.declare("udp-sndbuf-errors", "UDP 'sndbuf' errors", udpErrorStats);
-  S.declare("udp-noport-errors", "UDP 'noport' errors", udpErrorStats);
-  S.declare("udp-in-errors", "UDP 'in' errors", udpErrorStats);
+  S.declare("udp-recvbuf-errors", "UDP 'recvbuf' errors", udpErrorStats, StatType::counter);
+  S.declare("udp-sndbuf-errors", "UDP 'sndbuf' errors", udpErrorStats, StatType::counter);
+  S.declare("udp-noport-errors", "UDP 'noport' errors", udpErrorStats, StatType::counter);
+  S.declare("udp-in-errors", "UDP 'in' errors", udpErrorStats, StatType::counter);
 #endif
 
-  S.declare("sys-msec", "Number of msec spent in system time", getSysUserTimeMsec);
-  S.declare("user-msec", "Number of msec spent in user time", getSysUserTimeMsec);
+  S.declare("sys-msec", "Number of msec spent in system time", getSysUserTimeMsec, StatType::counter);
+  S.declare("user-msec", "Number of msec spent in user time", getSysUserTimeMsec, StatType::counter);
 
 #ifdef __linux__
-  S.declare("cpu-iowait", "Time spent waiting for I/O to complete by the whole system, in units of USER_HZ", getCPUIOWait);
-  S.declare("cpu-steal", "Stolen time, which is the time spent by the whole system in other operating systems when running in a virtualized environment, in units of USER_HZ", getCPUSteal);
+  S.declare("cpu-iowait", "Time spent waiting for I/O to complete by the whole system, in units of USER_HZ", getCPUIOWait, StatType::counter);
+  S.declare("cpu-steal", "Stolen time, which is the time spent by the whole system in other operating systems when running in a virtualized environment, in units of USER_HZ", getCPUSteal, StatType::counter);
 #endif
 
-  S.declare("meta-cache-size", "Number of entries in the metadata cache", DNSSECKeeper::dbdnssecCacheSizes);
-  S.declare("key-cache-size", "Number of entries in the key cache", DNSSECKeeper::dbdnssecCacheSizes);
-  S.declare("signature-cache-size", "Number of entries in the signature cache", signatureCacheSize);
+  S.declare("meta-cache-size", "Number of entries in the metadata cache", DNSSECKeeper::dbdnssecCacheSizes, StatType::gauge);
+  S.declare("key-cache-size", "Number of entries in the key cache", DNSSECKeeper::dbdnssecCacheSizes, StatType::gauge);
+  S.declare("signature-cache-size", "Number of entries in the signature cache", signatureCacheSize, StatType::gauge);
 
   S.declare("servfail-packets","Number of times a server-failed packet was sent out");
-  S.declare("latency","Average number of microseconds needed to answer a question", getLatency);
+  S.declare("latency","Average number of microseconds needed to answer a question", getLatency, StatType::gauge);
   S.declare("timedout-packets","Number of packets which weren't answered within timeout set");
-  S.declare("security-status", "Security status based on regular polling");
+  S.declare("security-status", "Security status based on regular polling", StatType::gauge);
   S.declareDNSNameQTypeRing("queries","UDP Queries Received");
   S.declareDNSNameQTypeRing("nxdomain-queries","Queries for non-existent records within existent domains");
   S.declareDNSNameQTypeRing("noerror-queries","Queries for existing records, but for type we don't have");
@@ -624,6 +634,12 @@ void mainthread()
     }
   }
 
+  pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
+  if (!::arg()["query-local-address6"].empty()) {
+    g_log<<Logger::Warning<<"query-local-address6 is deprecated and will be removed in a future version. Please use query-local-address for IPv6 addresses as well"<<endl;
+    pdns::parseQueryLocalAddress(::arg()["query-local-address6"]);
+  }
+
   // NOW SAFE TO CREATE THREADS!
   dl->go();
 
index d5ff10eb653c148859a8736417ba521b34781e2a..fd1f168f0bfd66e75991ee03755324bf302ccdaa 100644 (file)
@@ -33,7 +33,6 @@
 #include "dnsbackend.hh"
 #include "ueberbackend.hh"
 #include "packethandler.hh"
-#include "resolver.hh"
 #include "logger.hh"
 #include "dns.hh"
 #include "arguments.hh"
@@ -55,7 +54,7 @@ void CommunicatorClass::retrievalLoopThread(void)
       sr=d_suckdomains.front();
       d_suckdomains.pop_front();
     }
-    suck(sr.domain, sr.master);
+    suck(sr.domain, sr.master, sr.force);
   }
 }
 
index 643145c34923767d1f1ed46020d29a43820fde4e..c0ac4afd698c92fc605a08d592889a30dc767682 100644 (file)
@@ -45,6 +45,7 @@ struct SuckRequest
 {
   DNSName domain;
   ComboAddress master;
+  bool force;
   bool operator<(const SuckRequest& b) const
   {
     return tie(domain, master) < tie(b.domain, b.master);
@@ -161,7 +162,7 @@ public:
   
   void drillHole(const DNSName &domain, const string &ip);
   bool justNotified(const DNSName &domain, const string &ip);
-  void addSuckRequest(const DNSName &domain, const ComboAddress& master);
+  void addSuckRequest(const DNSName &domain, const ComboAddress& master, bool force=false);
   void addSlaveCheckRequest(const DomainInfo& di, const ComboAddress& remote);
   void addTrySuperMasterRequest(const DNSPacket& p);
   void notify(const DNSName &domain, const string &ip);
@@ -176,7 +177,7 @@ private:
   int d_nsock4, d_nsock6;
   map<pair<DNSName,string>,time_t>d_holes;
   std::mutex d_holelock;
-  void suck(const DNSName &domain, const ComboAddress& remote);
+  void suck(const DNSName &domain, const ComboAddress& remote, bool force=false);
   void ixfrSuck(const DNSName &domain, const TSIGTriplet& tt, const ComboAddress& laddr, const ComboAddress& remote, std::unique_ptr<AuthLua4>& pdl,
                 ZoneStatus& zs, vector<DNSRecord>* axfr);
 
index 65394273bd6e3115b4e1157fb881c6218d9d7cb8..f2c6881561e5ead57754d53631a879c9b808c14b 100644 (file)
@@ -126,9 +126,7 @@ void DNSSECKeeper::clearCaches(const DNSName& name)
     s_keycache.erase(name); 
   }
   WriteLock l(&s_metacachelock);
-  pair<metacache_t::iterator, metacache_t::iterator> range = s_metacache.equal_range(tie(name));
-  while(range.first != range.second)
-    s_metacache.erase(range.first++);
+  s_metacache.erase(name);
 }
 
 
@@ -212,43 +210,51 @@ void DNSSECKeeper::getFromMetaOrDefault(const DNSName& zname, const std::string&
 bool DNSSECKeeper::getFromMeta(const DNSName& zname, const std::string& key, std::string& value)
 {
   static int ttl = ::arg().asNum("domain-metadata-cache-ttl");
-  bool isset = false;
-  value.clear();
-  unsigned int now = time(0);
 
   if(!((++s_ops) % 100000)) {
     cleanup();
   }
 
-  if (ttl > 0) {
-    ReadLock l(&s_metacachelock);
+  value.clear();
+  time_t now = time(nullptr);
+
+  bool ret = false;
+  bool fromCache = false;
+  METAValues meta;
 
-    metacache_t::const_iterator iter = s_metacache.find(tie(zname, key));
+  if (ttl) {
+    ReadLock l(&s_metacachelock);
+    auto iter = s_metacache.find(zname);
     if(iter != s_metacache.end() && iter->d_ttd > now) {
-      value = iter->d_value;
-      return iter->d_isset;
+      meta = iter->d_value;
+      fromCache = true;
     }
   }
-  vector<string> meta;
-  d_keymetadb->getDomainMetadata(zname, key, meta);
-  if(!meta.empty()) {
-    value=std::move(*meta.begin());
-    isset = true;
+
+  if (!fromCache) {
+    d_keymetadb->getAllDomainMetadata(zname, meta);
   }
 
-  if (ttl > 0) {
+  auto iter = meta.find(key);
+  if (iter != meta.end()) {
+    if (!iter->second.empty()) {
+      value = *iter->second.begin();
+    }
+    ret = true;
+  }
+
+  if (ttl && !fromCache) {
     METACacheEntry nce;
     nce.d_domain=zname;
     nce.d_ttd = now + ttl;
-    nce.d_key= key;
-    nce.d_value = value;
-    nce.d_isset = isset;
+    nce.d_value = std::move(meta);
     {
       WriteLock l(&s_metacachelock);
       lruReplacingInsert<SequencedTag>(s_metacache, nce);
     }
   }
-  return isset;
+
+  return ret;
 }
 
 void DNSSECKeeper::getSoaEdit(const DNSName& zname, std::string& value)
index 5869df3be1ba45cb28a570e5b4c8de8df64646e2..5c8d2950291d9f901277ab3d0c282d62b8aaee4b 100644 (file)
@@ -85,6 +85,15 @@ void BackendMakerClass::report(BackendFactory *bf)
   d_repository[bf->getName()]=bf;
 }
 
+void BackendMakerClass::clear()
+{
+  d_instances.clear();
+  for (auto& repo : d_repository) {
+    delete repo.second;
+    repo.second = nullptr;
+  }
+  d_repository.clear();
+}
 
 vector<string> BackendMakerClass::getModules()
 {
@@ -164,41 +173,50 @@ void BackendMakerClass::launch(const string &instr)
   }
 }
 
-int BackendMakerClass::numLauncheable()
+size_t BackendMakerClass::numLauncheable() const
 {
   return d_instances.size();
 }
 
-vector<DNSBackend *>BackendMakerClass::all(bool metadataOnly)
+vector<DNSBackend *> BackendMakerClass::all(bool metadataOnly)
 {
-  vector<DNSBackend *>ret;
+  vector<DNSBackend *> ret;
   if(d_instances.empty())
     throw PDNSException("No database backends configured for launch, unable to function");
 
+  ret.reserve(d_instances.size());
+
   try {
-    for(vector<pair<string,string> >::const_iterator i=d_instances.begin();i!=d_instances.end();++i) {
-      DNSBackend *made;
-      if(metadataOnly)
-        made = d_repository[i->first]->makeMetadataOnly(i->second);
-      else
-        made = d_repository[i->first]->make(i->second);
-      if(!made)
-        throw PDNSException("Unable to launch backend '"+i->first+"'");
+    for (const auto& instance : d_instances) {
+      DNSBackend *made = nullptr;
+
+      if (metadataOnly) {
+        made = d_repository[instance.first]->makeMetadataOnly(instance.second);
+      }
+      else {
+        made = d_repository[instance.first]->make(instance.second);
+      }
+
+      if (!made) {
+        throw PDNSException("Unable to launch backend '" + instance.first + "'");
+      }
 
       ret.push_back(made);
     }
   }
-  catch(PDNSException &ae) {
+  catch(const PDNSException &ae) {
     g_log<<Logger::Error<<"Caught an exception instantiating a backend: "<<ae.reason<<endl;
     g_log<<Logger::Error<<"Cleaning up"<<endl;
-    for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i)
-      delete *i;
+    for (auto i : ret) {
+      delete i;
+    }
     throw;
   } catch(...) {
     // and cleanup
     g_log<<Logger::Error<<"Caught an exception instantiating a backend, cleaning up"<<endl;
-    for(vector<DNSBackend *>::const_iterator i=ret.begin();i!=ret.end();++i)
-      delete *i;
+    for (auto i : ret) {
+      delete i;
+    }
     throw;
   }
 
index c45b07e0096a90d10d3af232a7b627dfedfa3f4b..c086e2ddf76cfac47ec28562e5c1dab02796351d 100644 (file)
@@ -315,8 +315,8 @@ public:
   {
   }
 
-  //! Called when the Master of a domain should be changed
-  virtual bool setMaster(const DNSName &domain, const string &ip)
+  //! Called when the Master list of a domain should be changed
+  virtual bool setMasters(const DNSName &domain, const vector<ComboAddress> &masters)
   {
     return false;
   }
@@ -336,6 +336,12 @@ public:
   //! Can be called to seed the getArg() function with a prefix
   void setArgPrefix(const string &prefix);
 
+  //! Add an entry for a super master
+  virtual bool superMasterAdd(const string &ip, const string &nameserver, const string &account) 
+  {
+    return false; 
+  }
+
   //! determine if ip is a supermaster or a domain
   virtual bool superMasterBackend(const string &ip, const DNSName &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **db)
   {
@@ -343,7 +349,7 @@ public:
   }
 
   //! called by PowerDNS to create a new domain
-  virtual bool createDomain(const DNSName &domain)
+  virtual bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account)
   {
     return false;
   }
@@ -412,10 +418,11 @@ class BackendMakerClass
 public:
   void report(BackendFactory *bf);
   void launch(const string &instr);
-  vector<DNSBackend *>all(bool skipBIND=false);
+  vector<DNSBackend *> all(bool skipBIND=false);
   void load(const string &module);
-  int numLauncheable();
+  size_t numLauncheable() const;
   vector<string> getModules();
+  void clear();
 
 private:
   void load_all();
index c97c6744532203e03383895ca67536a9d629987e..c31a0a554c05411033d0f88be06bb2b521d4a9c2 100644 (file)
@@ -447,12 +447,12 @@ uint64_t DNSDistPacketCache::getEntriesCount()
 
 uint64_t DNSDistPacketCache::dump(int fd)
 {
-  FILE * fp = fdopen(dup(fd), "w");
+  auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(dup(fd), "w"), fclose);
   if (fp == nullptr) {
     return 0;
   }
 
-  fprintf(fp, "; dnsdist's packet cache dump follows\n;\n");
+  fprintf(fp.get(), "; dnsdist's packet cache dump follows\n;\n");
 
   uint64_t count = 0;
   time_t now = time(nullptr);
@@ -465,14 +465,13 @@ uint64_t DNSDistPacketCache::dump(int fd)
       count++;
 
       try {
-        fprintf(fp, "%s %" PRId64 " %s ; key %" PRIu32 ", length %" PRIu16 ", tcp %d, added %" PRId64 "\n", value.qname.toString().c_str(), static_cast<int64_t>(value.validity - now), QType(value.qtype).getName().c_str(), entry.first, value.len, value.tcp, static_cast<int64_t>(value.added));
+        fprintf(fp.get(), "%s %" PRId64 " %s ; key %" PRIu32 ", length %" PRIu16 ", tcp %d, added %" PRId64 "\n", value.qname.toString().c_str(), static_cast<int64_t>(value.validity - now), QType(value.qtype).getName().c_str(), entry.first, value.len, value.tcp, static_cast<int64_t>(value.added));
       }
       catch(...) {
-        fprintf(fp, "; error printing '%s'\n", value.qname.empty() ? "EMPTY" : value.qname.toString().c_str());
+        fprintf(fp.get(), "; error printing '%s'\n", value.qname.empty() ? "EMPTY" : value.qname.toString().c_str());
       }
     }
   }
 
-  fclose(fp);
   return count;
 }
index 26702dcb65db8e6320e31fc157dec8740f515920..53475dfa9e60823920bdc088113fd1becd457443 100644 (file)
@@ -58,14 +58,13 @@ try
       const auto& server = conf.server;
       const std::string& namespace_name = conf.namespace_name;
       std::string hostname = conf.ourname;
-      if(hostname.empty()) {
-        char tmp[80];
-        memset(tmp, 0, sizeof(tmp));
-        gethostname(tmp, sizeof(tmp));
-        char *p = strchr(tmp, '.');
-        if(p) *p=0;
-        hostname=tmp;
-        boost::replace_all(hostname, ".", "_");
+      if (hostname.empty()) {
+        try {
+          hostname = getCarbonHostName();
+        }
+        catch(const std::exception& e) {
+          throw std::runtime_error(std::string("The 'ourname' setting in 'carbonServer()' has not been set and we are unable to determine the system's hostname: ") + e.what());
+        }
       }
       const std::string& instance_name = conf.instance_name;
 
@@ -87,7 +86,7 @@ try
         }
         auto states = g_dstates.getLocal();
         for(const auto& state : *states) {
-          string serverName = state->getName().empty() ? (state->remote.toString() + ":" + std::to_string(state->remote.getPort())) : state->getName();
+          string serverName = state->getName().empty() ? state->remote.toStringWithPort() : state->getName();
           boost::replace_all(serverName, ".", "_");
           const string base = namespace_name + "." + hostname + "." + instance_name + ".servers." + serverName + ".";
           str<<base<<"queries" << ' ' << state->queries.load() << " " << now << "\r\n";
@@ -111,7 +110,7 @@ try
           if (front->udpFD == -1 && front->tcpFD == -1)
             continue;
 
-          string frontName = front->local.toString() + ":" + std::to_string(front->local.getPort()) +  (front->udpFD >= 0 ? "_udp" : "_tcp");
+          string frontName = front->local.toStringWithPort() + (front->udpFD >= 0 ? "_udp" : "_tcp");
           boost::replace_all(frontName, ".", "_");
           auto dupPair = frontendDuplicates.insert({frontName, 1});
           if (!dupPair.second) {
index bb9d6998ba95b99b9bc3f86d41191466c33d02b7..d68eb6b58ffd81d901151c76c99d6fd6a7ae8f10 100644 (file)
@@ -25,6 +25,8 @@
 #include <thread>
 
 #if defined (__OpenBSD__) || defined(__NetBSD__)
+// If this is not undeffed, __attribute__ wil be redefined by /usr/include/readline/rlstdc.h
+#undef __STRICT_ANSI__
 #include <readline/readline.h>
 #include <readline/history.h>
 #else
@@ -419,7 +421,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "inClientStartup", true, "", "returns true during console client parsing of configuration" },
   { "includeDirectory", true, "path", "include configuration files from `path`" },
   { "KeyValueLookupKeyQName", true, "[wireFormat]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the qname of the query, either in wire format (default) or in plain text if 'wireFormat' is false" },
-  { "KeyValueLookupKeySourceIP", true, "", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the source IP of the client in network byte-order." },
+  { "KeyValueLookupKeySourceIP", true, "[v4Mask [, v6Mask]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the (possibly bitmasked) source IP of the client in network byte-order." },
   { "KeyValueLookupKeySuffix", true, "[minLabels [,wireFormat]]", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return a vector of keys based on the labels of the qname in DNS wire format or plain text" },
   { "KeyValueLookupKeyTag", true, "tag", "Return a new KeyValueLookupKey object that, when passed to KeyValueStoreLookupAction or KeyValueStoreLookupRule, will return the value of the corresponding tag for this query, if it exists" },
   { "KeyValueStoreLookupAction", true, "kvs, lookupKey, destinationTag", "does a lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey', and storing the result if any into the tag named 'destinationTag'" },
@@ -462,7 +464,7 @@ const std::vector<ConsoleKeyword> g_consoleKeywords{
   { "newQPSLimiter", true, "rate, burst", "configure a QPS limiter with that rate and that burst capacity" },
   { "newRemoteLogger", true, "address:port [, timeout=2, maxQueuedEntries=100, reconnectWaitTime=1]", "create a Remote Logger object, to use with `RemoteLogAction()` and `RemoteLogResponseAction()`" },
   { "newRuleAction", true, "DNS rule, DNS action [, {uuid=\"UUID\"}]", "return a pair of DNS Rule and DNS Action, to be used with `setRules()`" },
-  { "newServer", true, "{address=\"ip:port\", qps=1000, order=1, weight=10, pool=\"abuse\", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName=\"a.root-servers.net.\", checkType=\"A\", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source=\"address|interface name|address@interface\", sockets=1}", "instantiate a server" },
+  { "newServer", true, "{address=\"ip:port\", qps=1000, order=1, weight=10, pool=\"abuse\", retries=5, tcpConnectTimeout=5, tcpSendTimeout=30, tcpRecvTimeout=30, checkName=\"a.root-servers.net.\", checkType=\"A\", maxCheckFailures=1, mustResolve=false, useClientSubnet=true, source=\"address|interface name|address@interface\", sockets=1, reconnectOnUp=false}", "instantiate a server" },
   { "newServerPolicy", true, "name, function", "create a policy object from a Lua function" },
   { "newSuffixMatchNode", true, "", "returns a new SuffixMatchNode" },
   { "NoneAction", true, "", "Does nothing. Subsequent rules are processed after this action" },
index 7cda963d4a1e27d56480f9512e0a1e70d8bd4eec..6dfdbbc8f258c8d9985ee0b168dace964aa06205 100644 (file)
@@ -30,7 +30,9 @@
 /* when we add EDNS to a query, we don't want to advertise
    a large buffer size */
 size_t g_EdnsUDPPayloadSize = 512;
-uint16_t g_PayloadSizeSelfGenAnswers{s_udpIncomingBufferSize};
+static const uint16_t defaultPayloadSizeSelfGenAnswers = 1232;
+static_assert(defaultPayloadSizeSelfGenAnswers < s_udpIncomingBufferSize, "The UDP responder's payload size should be smaller or equal to our incoming buffer size");
+uint16_t g_PayloadSizeSelfGenAnswers{defaultPayloadSizeSelfGenAnswers};
 
 /* draft-ietf-dnsop-edns-client-subnet-04 "11.1.  Privacy" */
 uint16_t g_ECSSourcePrefixV4 = 24;
index ec2cad6c6029d4577274da8ad9782a15c6e62cd7..7ad5f25cff1f94e0236373aca0cd100199176f05 100644 (file)
@@ -64,7 +64,7 @@ void setPoolPolicy(pools_t& pools, const string& poolName, std::shared_ptr<Serve
 void addServerToPool(pools_t& pools, const string& poolName, std::shared_ptr<DownstreamState> server);
 void removeServerFromPool(pools_t& pools, const string& poolName, std::shared_ptr<DownstreamState> server);
 
-ServerPolicy::NumberedServerVector getDownstreamCandidates(const map<std::string,std::shared_ptr<ServerPool>>& pools, const std::string& poolName);
+const std::shared_ptr<ServerPolicy::NumberedServerVector> getDownstreamCandidates(const map<std::string,std::shared_ptr<ServerPool>>& pools, const std::string& poolName);
 
 std::shared_ptr<DownstreamState> firstAvailable(const ServerPolicy::NumberedServerVector& servers, const DNSQuestion* dq);
 
index bc66083f1b226f41cb3b0bf86400f5d67a622143..ba4cf9eac4b2c959d7a40706fbd5a2902d63f797 100644 (file)
@@ -102,6 +102,7 @@ void setupLuaBindings(bool client)
       s->pools.erase(pool);
     });
   g_lua.registerFunction<uint64_t(DownstreamState::*)()>("getOutstanding", [](const DownstreamState& s) { return s.outstanding.load(); });
+  g_lua.registerFunction<double(DownstreamState::*)()>("getLatency", [](const DownstreamState& s) { return s.latencyUsec; });
   g_lua.registerFunction("isUp", &DownstreamState::isUp);
   g_lua.registerFunction("setDown", &DownstreamState::setDown);
   g_lua.registerFunction("setUp", &DownstreamState::setUp);
index bdde09e1f8be3166182d49ac8818732bf8f8df1b..b46a04ca46a19c77091d7d5a07ede0b1feeed364 100644 (file)
@@ -41,6 +41,7 @@
 #endif /* LUAJIT_VERSION */
 #include "dnsdist-rings.hh"
 #include "dnsdist-secpoll.hh"
+#include "dnsdist-web.hh"
 
 #include "base64.hh"
 #include "dnswriter.hh"
@@ -464,6 +465,10 @@ static void setupLuaConfig(bool client, bool configCheck)
         ret->minRiseSuccesses=std::stoi(boost::get<string>(vars["rise"]));
       }
 
+      if(vars.count("reconnectOnUp")) {
+        ret->reconnectOnUp=boost::get<bool>(vars["reconnectOnUp"]);
+      }
+
       if(vars.count("cpus")) {
         for (const auto& cpu : boost::get<vector<pair<int,string>>>(vars["cpus"])) {
           cpus.insert(std::stoi(cpu.second));
@@ -553,6 +558,7 @@ static void setupLuaConfig(bool client, bool configCheck)
                         g_pools.setState(localPools);
                         states.erase(remove(states.begin(), states.end(), server), states.end());
                         g_dstates.setState(states);
+                        server->stop();
                       } );
 
   g_lua.writeFunction("truncateTC", [](bool tc) { setLuaSideEffect(); g_truncateTC=tc; });
@@ -757,7 +763,8 @@ static void setupLuaConfig(bool client, bool configCheck)
     });
 
   g_lua.writeFunction("getPoolServers", [](string pool) {
-      return getDownstreamCandidates(g_pools.getCopy(), pool);
+      const auto poolServers = getDownstreamCandidates(g_pools.getCopy(), pool);
+      return *poolServers;
     });
 
   g_lua.writeFunction("getServer", [client](boost::variant<unsigned int, std::string> i) {
@@ -796,7 +803,7 @@ static void setupLuaConfig(bool client, bool configCheck)
       g_carbon.setState(ours);
   });
 
-  g_lua.writeFunction("webserver", [client,configCheck](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey, const boost::optional<std::map<std::string, std::string> > customHeaders) {
+  g_lua.writeFunction("webserver", [client,configCheck](const std::string& address, const std::string& password, const boost::optional<std::string> apiKey, const boost::optional<std::map<std::string, std::string> > customHeaders, const boost::optional<std::string> acl) {
       setLuaSideEffect();
       ComboAddress local;
       try {
@@ -815,10 +822,13 @@ static void setupLuaConfig(bool client, bool configCheck)
        SSetsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 1);
        SBind(sock, local);
        SListen(sock, 5);
-       auto launch=[sock, local, password, apiKey, customHeaders]() {
+       auto launch=[sock, local, password, apiKey, customHeaders, acl]() {
           setWebserverPassword(password);
           setWebserverAPIKey(apiKey);
           setWebserverCustomHeaders(customHeaders);
+          if (acl) {
+            setWebserverACL(*acl);
+          }
           thread t(dnsdistWebserverThread, sock, local);
          t.detach();
        };
@@ -852,6 +862,11 @@ static void setupLuaConfig(bool client, bool configCheck)
 
         setWebserverAPIKey(apiKey);
       }
+      if (vars->count("acl")) {
+        const std::string acl = boost::get<std::string>(vars->at("acl"));
+
+        setWebserverACL(acl);
+      }
       if(vars->count("customHeaders")) {
         const boost::optional<std::map<std::string, std::string> > headers = boost::get<std::map<std::string, std::string> >(vars->at("customHeaders"));
 
@@ -1428,7 +1443,8 @@ static void setupLuaConfig(bool client, bool configCheck)
           }
           string servers;
 
-          for (const auto& server: pool->getServers()) {
+          const auto poolServers = pool->getServers();
+          for (const auto& server: *poolServers) {
             if (!servers.empty()) {
               servers += ", ";
             }
@@ -1936,6 +1952,10 @@ static void setupLuaConfig(bool client, bool configCheck)
         frontend->d_trustForwardedForHeader = boost::get<bool>((*vars)["trustForwardedForHeader"]);
       }
 
+      if (vars->count("internalPipeBufferSize")) {
+        frontend->d_internalPipeBufferSize = boost::get<int>((*vars)["internalPipeBufferSize"]);
+      }
+
       parseTLSConfig(frontend->d_tlsConfig, "addDOHLocal", vars);
     }
     g_dohlocals.push_back(frontend);
index 7347e448e507c27fc36ba9f1b51a729127b2d6bd..0c0389848d85f1a5c4089f556b00661e1740f39c 100644 (file)
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
-#include "dnsdist.hh"
-#include "dnsdist-healthchecks.hh"
-#include "dnsdist-prometheus.hh"
 
-#include "sstuff.hh"
-#include "ext/json11/json11.hpp"
-#include "ext/incbin/incbin.h"
-#include "dolog.hh"
-#include <thread>
-#include "threadname.hh"
+#include <boost/format.hpp>
 #include <sstream>
-#include <yahttp/yahttp.hpp>
-#include "namespaces.hh"
 #include <sys/time.h>
 #include <sys/resource.h>
+#include <thread>
+
 #include "ext/incbin/incbin.h"
-#include "htmlfiles.h"
+#include "ext/json11/json11.hpp"
+#include <yahttp/yahttp.hpp>
+
 #include "base64.hh"
+#include "dnsdist.hh"
+#include "dnsdist-healthchecks.hh"
+#include "dnsdist-prometheus.hh"
+#include "dnsdist-web.hh"
+#include "dolog.hh"
 #include "gettime.hh"
-#include  <boost/format.hpp>
+#include "htmlfiles.h"
+#include "threadname.hh"
+#include "sstuff.hh"
 
 bool g_apiReadWrite{false};
 WebserverConfig g_webserverConfig;
@@ -88,6 +89,8 @@ const std::map<std::string, MetricDefinition> MetricDefinitionStorage::metrics{
   { "dyn-blocked",            MetricDefinition(PrometheusMetricType::counter, "Number of queries dropped because of a dynamic block")},
   { "dyn-block-nmg-size",     MetricDefinition(PrometheusMetricType::gauge,   "Number of dynamic blocks entries") },
   { "security-status",        MetricDefinition(PrometheusMetricType::gauge,   "Security status of this software. 0=unknown, 1=OK, 2=upgrade recommended, 3=upgrade mandatory") },
+  { "doh-query-pipe-full",    MetricDefinition(PrometheusMetricType::counter, "Number of DoH queries dropped because the internal pipe used to distribute queries was full") },
+  { "doh-response-pipe-full", MetricDefinition(PrometheusMetricType::counter, "Number of DoH responses dropped because the internal pipe used to distribute responses was full") },
   { "udp-in-errors",          MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp InErrors") },
   { "udp-noport-errors",      MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp NoPorts") },
   { "udp-recvbuf-errors",     MetricDefinition(PrometheusMetricType::counter, "From /proc/net/snmp RcvbufErrors") },
@@ -221,6 +224,12 @@ static bool isMethodAllowed(const YaHTTP::Request& req)
   return false;
 }
 
+static bool isClientAllowedByACL(const ComboAddress& remote)
+{
+  std::lock_guard<std::mutex> lock(g_webserverConfig.lock);
+  return g_webserverConfig.acl.match(remote);
+}
+
 static void handleCORS(const YaHTTP::Request& req, YaHTTP::Response& resp)
 {
   const auto origin = req.headers.find("Origin");
@@ -500,7 +509,7 @@ static void connectionThread(int sock, ComboAddress remote)
         }
 
         // Latency histogram buckets
-        output << "# HELP dnsdist_latency Histogram of responses by latency (in miliseconds)\n";
+        output << "# HELP dnsdist_latency Histogram of responses by latency (in milliseconds)\n";
         output << "# TYPE dnsdist_latency histogram\n";
         uint64_t latency_amounts = g_stats.latency0_1;
         output << "dnsdist_latency_bucket{le=\"1\"} " << latency_amounts << "\n";
@@ -627,7 +636,7 @@ static void connectionThread(int sock, ComboAddress remote)
           if (front->udpFD == -1 && front->tcpFD == -1)
             continue;
 
-          const string frontName = front->local.toString() + ":" + std::to_string(front->local.getPort());
+          const string frontName = front->local.toStringWithPort();
           const string proto = front->getType();
           const string fullName = frontName + "_" + proto;
           uint64_t threadNumber = 0;
@@ -795,6 +804,10 @@ static void connectionThread(int sock, ComboAddress remote)
           }
         }
 
+        output << "# HELP dnsdist_info " << "Info from dnsdist, value is always 1" << "\n";
+        output << "# TYPE dnsdist_info " << "gauge" << "\n";
+        output << "dnsdist_info{version=\"" << VERSION << "\"} " << "1" << "\n";
+
         resp.body = output.str();
         resp.headers["Content-Type"] = "text/plain";
     }
@@ -1226,6 +1239,17 @@ void setWebserverPassword(const std::string& password)
   g_webserverConfig.password = password;
 }
 
+void setWebserverACL(const std::string& acl)
+{
+  NetmaskGroup newACL;
+  newACL.toMasks(acl);
+
+  {
+    std::lock_guard<std::mutex> lock(g_webserverConfig.lock);
+    g_webserverConfig.acl = std::move(newACL);
+  }
+}
+
 void setWebserverCustomHeaders(const boost::optional<std::map<std::string, std::string> > customHeaders)
 {
   std::lock_guard<std::mutex> lock(g_webserverConfig.lock);
@@ -1237,15 +1261,21 @@ void dnsdistWebserverThread(int sock, const ComboAddress& local)
 {
   setThreadName("dnsdist/webserv");
   warnlog("Webserver launched on %s", local.toStringWithPort());
+
   for(;;) {
     try {
       ComboAddress remote(local);
       int fd = SAccept(sock, remote);
-      vinfolog("Got connection from %s", remote.toStringWithPort());
+      if (!isClientAllowedByACL(remote)) {
+        vinfolog("Connection to webserver from client %s is not allowed, closing", remote.toStringWithPort());
+        close(fd);
+        continue;
+      }
+      vinfolog("Got a connection to the webserver from %s", remote.toStringWithPort());
       std::thread t(connectionThread, fd, remote);
       t.detach();
     }
-    catch(std::exception& e) {
+    catch (const std::exception& e) {
       errlog("Had an error accepting new webserver connection: %s", e.what());
     }
   }
index 610387ca7ae58db57249f8d0667c8d41ec87474c..ecf659952b5fd9e8606963dca9d8be08e46a3e12 100644 (file)
@@ -32,6 +32,8 @@
 #include <unistd.h>
 
 #if defined (__OpenBSD__) || defined(__NetBSD__)
+// If this is not undeffed, __attribute__ wil be redefined by /usr/include/readline/rlstdc.h
+#undef __STRICT_ANSI__
 #include <readline/readline.h>
 #else
 #include <editline/readline.h>
@@ -550,7 +552,7 @@ try {
   std::vector<int> sockets;
   sockets.reserve(dss->sockets.size());
 
-  for(;;) {
+  for(; !dss->isStopped(); ) {
     dnsheader* dh = reinterpret_cast<struct dnsheader*>(packet);
     try {
       pickBackendSocketsReadyForReceiving(dss, sockets);
@@ -559,8 +561,13 @@ try {
         char * response = packet;
         size_t responseSize = sizeof(packet);
 
-        if (got < 0 || static_cast<size_t>(got) < sizeof(dnsheader))
+        if (got == 0 && dss->isStopped()) {
+          break;
+        }
+
+        if (got < 0 || static_cast<size_t>(got) < sizeof(dnsheader)) {
           continue;
+        }
 
         uint16_t responseLen = static_cast<uint16_t>(got);
         queryId = dh->id;
@@ -638,7 +645,17 @@ try {
 #ifdef HAVE_DNS_OVER_HTTPS
             // DoH query
             du->response = std::string(response, responseLen);
-            if (send(du->rsock, &du, sizeof(du), 0) != sizeof(du)) {
+            static_assert(sizeof(du) <= PIPE_BUF, "Writes up to PIPE_BUF are guaranteed not to be interleaved and to either fully succeed or fail");
+            ssize_t sent = write(du->rsock, &du, sizeof(du));
+            if (sent != sizeof(du)) {
+              if (errno == EAGAIN || errno == EWOULDBLOCK) {
+                ++g_stats.dohResponsePipeFull;
+                vinfolog("Unable to pass a DoH response to the DoH worker thread because the pipe is full");
+              }
+              else {
+                vinfolog("Unable to pass a DoH response to the DoH worker thread because we couldn't write to the pipe: %s", stringerror());
+              }
+
               /* at this point we have the only remaining pointer on this
                  DOHUnit object since we did set ids->du to nullptr earlier,
                  except if we got the response before the pointer could be
@@ -1181,8 +1198,8 @@ ProcessQueryResult processQuery(DNSQuestion& dq, ClientState& cs, LocalHolders&
     if (serverPool->policy != nullptr) {
       policy = *(serverPool->policy);
     }
-    auto servers = serverPool->getServers();
-    selectedBackend = getSelectedBackendFromPolicy(policy, servers, dq);
+    const auto servers = serverPool->getServers();
+    selectedBackend = getSelectedBackendFromPolicy(policy, *servers, dq);
 
     uint16_t cachedResponseSize = dq.size;
     uint32_t allowExpired = selectedBackend ? 0 : g_staleCacheEntriesTTL;
index 949e5a08c74086bbd5db85b483859dfaed3400da..9c6dcd16c077eff94e07c1738ba6ca58f0e16fd8 100644 (file)
@@ -263,6 +263,8 @@ struct DNSDistStats
   stat_t cacheMisses{0};
   stat_t latency0_1{0}, latency1_10{0}, latency10_50{0}, latency50_100{0}, latency100_1000{0}, latencySlow{0}, latencySum{0};
   stat_t securityStatus{0};
+  stat_t dohQueryPipeFull{0};
+  stat_t dohResponsePipeFull{0};
 
   double latencyAvg100{0}, latencyAvg1000{0}, latencyAvg10000{0}, latencyAvg1000000{0};
   typedef std::function<uint64_t(const std::string&)> statfunction_t;
@@ -315,6 +317,8 @@ struct DNSDistStats
     {"dyn-blocked", &dynBlocked},
     {"dyn-block-nmg-size", [](const std::string&) { return g_dynblockNMG.getLocal()->size(); }},
     {"security-status", &securityStatus},
+    {"doh-query-pipe-full", &dohQueryPipeFull},
+    {"doh-response-pipe-full", &dohResponsePipeFull},
     // Latency histogram
     {"latency-sum", &latencySum},
     {"latency-count", getLatencyCount},
@@ -761,15 +765,8 @@ struct DownstreamState
 
   DownstreamState(const ComboAddress& remote_, const ComboAddress& sourceAddr_, unsigned int sourceItf, const std::string& sourceItfName, size_t numberOfSockets, bool connect);
   DownstreamState(const ComboAddress& remote_): DownstreamState(remote_, ComboAddress(), 0, std::string(), 1, true) {}
-  ~DownstreamState()
-  {
-    for (auto& fd : sockets) {
-      if (fd >= 0) {
-        close(fd);
-        fd = -1;
-      }
-    }
-  }
+  ~DownstreamState();
+
   boost::uuids::uuid id;
   std::vector<unsigned int> hashes;
   mutable ReadWriteLock d_lock;
@@ -839,6 +836,7 @@ struct DownstreamState
   std::atomic_flag threadStarted;
   bool tcpFastOpen{false};
   bool ipBindAddrNoPort{true};
+  bool reconnectOnUp{false};
 
   bool isUp() const
   {
@@ -878,6 +876,11 @@ struct DownstreamState
   void hash();
   void setId(const boost::uuids::uuid& newId);
   void setWeight(int newWeight);
+  void stop();
+  bool isStopped() const
+  {
+    return d_stopped;
+  }
 
   void updateTCPMetrics(size_t nbQueries, uint64_t durationMs)
   {
@@ -887,6 +890,7 @@ struct DownstreamState
 private:
   std::string name;
   std::string nameWithAddr;
+  bool d_stopped{false};
 };
 using servers_t =vector<std::shared_ptr<DownstreamState>>;
 
@@ -908,9 +912,10 @@ public:
 
 struct ServerPool
 {
-  ServerPool()
+  ServerPool(): d_servers(std::make_shared<ServerPolicy::NumberedServerVector>())
   {
   }
+
   ~ServerPool()
   {
   }
@@ -934,7 +939,7 @@ struct ServerPool
   {
     size_t count = 0;
     ReadLock rl(&d_lock);
-    for (const auto& server : d_servers) {
+    for (const auto& server : *d_servers) {
       if (!upOnly || std::get<1>(server)->isUp() ) {
         count++;
       }
@@ -942,9 +947,9 @@ struct ServerPool
     return count;
   }
 
-  ServerPolicy::NumberedServerVector getServers()
+  const std::shared_ptr<ServerPolicy::NumberedServerVector> getServers()
   {
-    ServerPolicy::NumberedServerVector result;
+    std::shared_ptr<ServerPolicy::NumberedServerVector> result;
     {
       ReadLock rl(&d_lock);
       result = d_servers;
@@ -955,25 +960,32 @@ struct ServerPool
   void addServer(shared_ptr<DownstreamState>& server)
   {
     WriteLock wl(&d_lock);
-    unsigned int count = (unsigned int) d_servers.size();
-    d_servers.push_back(make_pair(++count, server));
+    /* we can't update the content of the shared pointer directly even when holding the lock,
+       as other threads might hold a copy. We can however update the pointer as long as we hold the lock. */
+    unsigned int count = static_cast<unsigned int>(d_servers->size());
+    auto newServers = std::make_shared<ServerPolicy::NumberedServerVector>(*d_servers);
+    newServers->push_back(make_pair(++count, server));
     /* we need to reorder based on the server 'order' */
-    std::stable_sort(d_servers.begin(), d_servers.end(), [](const std::pair<unsigned int,std::shared_ptr<DownstreamState> >& a, const std::pair<unsigned int,std::shared_ptr<DownstreamState> >& b) {
+    std::stable_sort(newServers->begin(), newServers->end(), [](const std::pair<unsigned int,std::shared_ptr<DownstreamState> >& a, const std::pair<unsigned int,std::shared_ptr<DownstreamState> >& b) {
       return a.second->order < b.second->order;
     });
     /* and now we need to renumber for Lua (custom policies) */
     size_t idx = 1;
-    for (auto& serv : d_servers) {
+    for (auto& serv : *newServers) {
       serv.first = idx++;
     }
+    d_servers = newServers;
   }
 
   void removeServer(shared_ptr<DownstreamState>& server)
   {
     WriteLock wl(&d_lock);
+    /* we can't update the content of the shared pointer directly even when holding the lock,
+       as other threads might hold a copy. We can however update the pointer as long as we hold the lock. */
+    auto newServers = std::make_shared<ServerPolicy::NumberedServerVector>(*d_servers);
     size_t idx = 1;
     bool found = false;
-    for (auto it = d_servers.begin(); it != d_servers.end();) {
+    for (auto it = newServers->begin(); it != newServers->end();) {
       if (found) {
         /* we need to renumber the servers placed
            after the removed one, for Lua (custom policies) */
@@ -981,17 +993,18 @@ struct ServerPool
         it++;
       }
       else if (it->second == server) {
-        it = d_servers.erase(it);
+        it = newServers->erase(it);
         found = true;
       } else {
         idx++;
         it++;
       }
     }
+    d_servers = newServers;
   }
 
 private:
-  ServerPolicy::NumberedServerVector d_servers;
+  std::shared_ptr<ServerPolicy::NumberedServerVector> d_servers;
   ReadWriteLock d_lock;
   bool d_useECS{false};
 };
@@ -1094,19 +1107,6 @@ struct dnsheader;
 
 vector<std::function<void(void)>> setupLua(bool client, const std::string& config);
 
-struct WebserverConfig
-{
-  std::string password;
-  std::string apiKey;
-  boost::optional<std::map<std::string, std::string> > customHeaders;
-  std::mutex lock;
-};
-
-void setWebserverAPIKey(const boost::optional<std::string> apiKey);
-void setWebserverPassword(const std::string& password);
-void setWebserverCustomHeaders(const boost::optional<std::map<std::string, std::string> > customHeaders);
-
-void dnsdistWebserverThread(int sock, const ComboAddress& local);
 void tcpAcceptorThread(void* p);
 #ifdef HAVE_DNS_OVER_HTTPS
 void dohThread(ClientState* cs);
index 810de15926b057d14e4fc475a31dff6a98cc6ecf..2b218d5a993487b1cfb5e132892242539a495282 100644 (file)
+-- == Generic Configuration ==
+
+-- only accept queries (Do53, DNSCrypt,  DoT or DoH) from a few subnets
+-- see https://dnsdist.org/advanced/acl.html for more details
+-- please be careful when dnsdist is deployed in front of a server
+-- server granting access based on the source IP, as all queries will
+-- seem to originate from dnsdist, which might be especially relevant for
+-- AXFR, IXFR, NOTIFY and UPDATE
+-- https://dnsdist.org/advanced/axfr.html
+-- setACL({'192.0.2.0/28', '2001:DB8:1::/56'})
+
 -- listen for console connection with the given secret key
--- controlSocket("0.0.0.0")
--- setKey(please generate a fresh private key with makeKey())
+-- https://dnsdist.org/guides/console.html
+-- controlSocket("127.0.0.1:5900")
+-- setKey("please generate a fresh private key with makeKey()")
 
 -- start the web server on port 8083, using password 'set a random password here'
--- webserver("0.0.0.0:8083", "set a random password here")
-
--- accept DNS queries on UDP/5200 and TCP/5200
-addLocal("0.0.0.0:5200")
-
--- send statistics to PowerDNS metronome server
--- carbonServer("2001:888:2000:1d::2")
-
--- fix up possibly badly truncated answers from pdns 2.9.22
-truncateTC(true)
-
-warnlog(string.format("Script starting %s", "up!"))
-
--- define the good servers
-newServer("8.8.8.8", 2)  -- 2 qps
-newServer("8.8.4.4", 2)
-newServer("208.67.222.222", 1)
-newServer("208.67.220.220", 1)
-newServer("2001:4860:4860::8888", 1)
-newServer("2001:4860:4860::8844",1)
-newServer("2620:0:ccc::2", 10)
-newServer("2620:0:ccd::2", 10)
-newServer({address="192.168.1.2", qps=1000, order=2})
-newServer({address="192.168.1.79:5300", order=2})
-newServer({address="127.0.0.1:5300", order=3})
-newServer({address="192.168.1.30:5300", pool="abuse"})
-
--- switch the server balancing policy to round robin,
--- the default being least outstanding queries
--- setServerPolicy(roundrobin)
-
--- send the queries for selected domain suffixes to the server
+-- https://dnsdist.org/guides/webserver.html
+-- webserver("127.0.0.1:8083", "set a random password here")
+
+-- send statistics to PowerDNS metronome server https://metronome1.powerdns.com/
+-- https://dnsdist.org/guides/carbon.html
+-- carbonServer("37.252.122.50", 'unique-name')
+
+-- accept plain DNS (Do53) queries on UDP/5200 and TCP/5200
+-- addLocal("127.0.0.1:5200")
+
+-- accept DNSCrypt queries on UDP/8443 and TCP/8443
+-- https://dnsdist.org/guides/dnscrypt.html
+-- addDNSCryptBind("127.0.0.1:8443", "2.provider.name", "DNSCryptResolver.cert", "DNSCryptResolver.key")
+
+-- accept DNS over TLS (DoT) queries on TCP/9443
+-- https://dnsdist.org/guides/dns-over-tls.html
+-- addTLSLocal("127.0.0.1:9443", {"server.crt"}, {"server.key"}, { provider="openssl" })
+
+-- accept DNS over HTTPS (DoH) queries on TCP/443
+-- https://dnsdist.org/guides/dns-over-https.html
+-- addDOHLocal("127.0.0.1:443", {"server.crt"}, {"server.key"})
+
+-- define downstream servers, aka backends
+-- https://dnsdist.org/guides/downstreams.html
+-- https://dnsdist.org/guides/serverpools.html
+-- https://dnsdist.org/guides/serverselection.html
+-- newServer("192.0.2.1")
+-- newServer({address="192.0.2.1:5300", pool="abuse"})
+
+-- == Tuning ==
+
+-- Increase the in-memory rings size (the default, 10000, is only one second at 10k qps) used by
+-- live-traffic inspection features like grepq, and use 100 shards to improve performance
+-- setRingBuffersSize(1000000, 100)
+
+-- increase the number of TCP workers, each one being capable of handling a large number
+-- of TCP connections since 1.4.0
+-- setMaxTCPClientThreads(20)
+
+-- == Sample Actions ==
+
+-- https://dnsdist.org/rules-actions.html
+
+-- send the queries for selected domain suffixes to the servers
 -- in the 'abuse' pool
-addAction({"ezdns.it.", "xxx."}, PoolAction("abuse"))
+-- addAction({"abuse.example.org.", "xxx."}, PoolAction("abuse"))
+
+-- drop queries for this exact qname
+-- addAction(QNameRule("drop-me.example.org."), DropAction())
 
 -- send the queries from a selected subnet to the
 -- abuse pool
-addAction("192.168.1.0/24", PoolAction("abuse"))
-
--- send the queries for the "com" suffix to the "abuse"
--- pool, but only up to 100 qps
-addAction("com.", QPSPoolAction(100, "abuse"))
-
--- declare a Lua action function, routing NAPTR queries
--- to the abuse pool
-function luarule(dq)
-       if(dq.qtype==DNSQType.NAPTR)
-       then
-               return DNSAction.Pool, "abuse" -- send to abuse pool
-       else
-               return DNSAction.None, ""      -- no action
-       end
-end
--- send only queries from the selected subnet to
--- the luarule function
-addAction("192.168.1.0/24", LuaAction(luarule))
-
--- drop queries exceeding 5 qps, grouped by /24 for IPv4
--- and /64 for IPv6
-addAction(MaxQPSIPRule(5, 24, 64), DropAction())
-
--- move the last rule to the first position
-topRule()
-
--- drop queries for the following suffixes:
-addAction("powerdns.org.", DropAction())
-addAction("spectre.", DropAction())
-
--- called before we distribute a question
-block=newDNSName("powerdns.org.")
-truncateNMG = newNMG()
-truncateNMG:addMask("213.244.0.0/16")
-truncateNMG:addMask("2001:503:ba3e::2:30")
-truncateNMG:addMask("fe80::/16")
-
-print(string.format("Have %d entries in truncate NMG", truncateNMG:size()))
-
--- called to pick a downstream server, ignores 'up' status
-counter=0
-function luaroundrobin(servers, dq)
-        counter=counter+1;
-        return servers[1+(counter % #servers)]
-end
--- setServerPolicyLua("luaroundrobin", luaroundrobin)
-
-newServer({address="2001:888:2000:1d::2", pool={"auth", "dnssec"}})
-newServer({address="2a01:4f8:110:4389::2", pool={"auth", "dnssec"}})
---addAction(DNSSECRule(), PoolAction("dnssec"))
---topRule()
-
--- split queries between the 'auth' pool and the regular one,
--- based on the RD flag
-function splitSetup(servers, dq)
-        if(dq.dh:getRD() == false)
-        then
-               return firstAvailable.policy(getPoolServers("auth"), dq)
-        else
-               return firstAvailable.policy(servers, dq)
-        end
-end
--- setServerPolicyLua("splitSetup", splitSetup)
-
--- the 'maintenance' function is called every second
-function maintenance()
-        -- block all hosts that exceeded 20 qps over the past 10s,
-        -- for 60s
-        addDynBlocks(exceedQRate(20, 10), "Exceeded query rate", 60)
-end
-
--- allow queries for the domain powerdns.com., drop everything else
--- addAction(makeRule("powerdns.com."), AllowAction())
--- addAction(AllRule(), DropAction())
-
--- clear the RD flag in queries for powerdns.com.
--- addAction("powerdns.com.", NoRecurseAction())
-
--- set the CD flag in queries for powerdns.com.
--- addAction("powerdns.com.", DisableValidationAction())
-
--- delay all responses for 1000ms
--- addAction(AllRule(), DelayAction(1000))
-
--- truncate ANY queries over UDP only
--- addAction(AndRule{QTypeRule(DNSQType.ANY), TCPRule(false)}, TCAction())
-
--- truncate ANY queries over TCP only
--- addAction(AndRule({QTypeRule(DNSQType.ANY), TCPRule(true)}), TCAction())
--- can also be written as:
--- addAction(AndRule({QTypeRule("ANY"), TCPRule(true)}), TCAction())
-
--- return 'not implemented' for qtype != A over UDP
--- addAction(AndRule({NotRule(QTypeRule("A")), TCPRule(false)}), RCodeAction(DNSRCode.NOTIMP))
-
--- return 'not implemented' for qtype == A OR received over UDP
--- addAction(OrRule({QTypeRule("A"), TCPRule(false)}), RCodeAction(DNSRCode.NOTIMP))
-
--- log all queries to a 'dndist.log' file, in text-mode (not binary) appending and unbuffered
--- addAction(AllRule(), LogAction("dnsdist.log", false, true, false))
-
--- drop all queries with the DO flag set
--- addAction(DNSSECRule(), DropAction())
-
--- drop all queries for the CHAOS class
--- addAction(QClassRule(3), DropAction())
--- addAction(QClassRule(DNSClass.CHAOS), DropAction())
-
--- drop all queries with the UPDATE opcode
--- addAction(OpcodeRule(DNSOpcode.Update), DropAction())
-
--- refuse all queries not having exactly one question
--- addAction(NotRule(RecordsCountRule(DNSSection.Question, 1, 1)), RCodeAction(DNSRCode.REFUSED))
-
--- return 'refused' for domains matching the regex evil[0-9]{4,}.powerdns.com$
--- addAction(RegexRule("evil[0-9]{4,}\\.powerdns\\.com$"), RCodeAction(DNSRCode.REFUSED))
-
--- spoof responses for A, AAAA and ANY for spoof.powerdns.com.
--- A queries will get 192.0.2.1, AAAA 2001:DB8::1 and ANY both
--- addAction("spoof.powerdns.com.", SpoofAction({"192.0.2.1", "2001:DB8::1"}))
-
--- spoof responses will multiple records
--- A will get 192.0.2.1 and 192.0.2.2, AAAA 20B8::1 and 2001:DB8::2
--- ANY all of that
--- addAction("spoof.powerdns.com", SpoofAction({"192.0.2.1", "192.0.2.2", "20B8::1", "2001:DB8::2"}))
-
--- spoof responses with a CNAME
--- addAction("cnamespoof.powerdns.com.", SpoofCNAMEAction("cname.powerdns.com."))
-
--- spoof responses in Lua
---[[
-    function spoof1rule(dq)
-        if(dq.qtype==1) -- A
-        then
-                return DNSAction.Spoof, "192.0.2.1"
-        elseif(dq.qtype == 28) -- AAAA
-        then
-                return DNSAction.Spoof, "2001:DB8::1"
-        else
-                return DNSAction.None, ""
-        end
-    end
-    function spoof2rule(dq)
-        return DNSAction.Spoof, "spoofed.powerdns.com."
-    end
-    addAction("luaspoof1.powerdns.com.", LuaAction(spoof1rule))
-    addAction("luaspoof2.powerdns.com.", LuaAction(spoof2rule))
-
---]]
-
--- alter a protobuf response for anonymization purposes
---[[
-function alterProtobuf(dq, protobuf)
-    requestor = newCA(dq.remoteaddr:toString())
-    if requestor:isIPv4() then
-        requestor:truncate(24)
-    else
-        requestor:truncate(56)
-    end
-    protobuf:setRequestor(requestor)
-end
-
-rl = newRemoteLogger("127.0.0.1:4242")
-addAction(AllRule(), RemoteLogAction(rl, alterProtobuf))
---]]
+-- addAction("192.0.2.0/24", PoolAction("abuse"))
+
+-- Refuse incoming AXFR, IXFR, NOTIFY and UPDATE
+-- Add trusted sources (slaves, masters) explicitely in front of this rule
+-- addAction(OrRule({OpcodeRule(DNSOpcode.Notify), OpcodeRule(DNSOpcode.Update), QTypeRule(DNSQType.AXFR), QTypeRule(DNSQType.IXFR)}), RCodeAction(DNSRCode.REFUSED))
+
+-- == Dynamic Blocks ==
+
+-- define a dynamic block rules group object, set a few limits and apply it
+-- see https://dnsdist.org/guides/dynblocks.html for more details
+
+-- local dbr = dynBlockRulesGroup()
+-- dbr:setQueryRate(30, 10, "Exceeded query rate", 60)
+-- dbr:setRCodeRate(dnsdist.NXDOMAIN, 20, 10, "Exceeded NXD rate", 60)
+-- dbr:setRCodeRate(dnsdist.SERVFAIL, 20, 10, "Exceeded ServFail rate", 60)
+-- dbr:setQTypeRate(dnsdist.ANY, 5, 10, "Exceeded ANY rate", 60)
+-- dbr:setResponseByteRate(10000, 10, "Exceeded resp BW rate", 60)
+-- function maintenance()
+--  dbr:apply()
+-- end
+
+-- == Logging ==
+
+-- connect to a remote protobuf logger and export queries and responses
+-- https://dnsdist.org/reference/protobuf.html
+-- rl = newRemoteLogger('127.0.0.1:4242')
+-- addAction(AllRule(), RemoteLogAction(rl))
+-- addResponseAction(AllRule(), RemoteLogResponseAction(rl))
+
+-- DNSTAP is also supported
+-- https://dnsdist.org/reference/dnstap.html
+-- fstr = newFrameStreamUnixLogger(/path/to/unix/socket)
+-- or
+-- fstr = newFrameStreamTcpLogger('192.0.2.1:4242')
+-- addAction(AllRule(), DnstapLogAction(fstr))
+-- addResponseAction(AllRule(), DnstapLogResponseAction(fstr))
+
+-- == Caching ==
+
+-- https://dnsdist.org/guides/cache.html
+-- create a packet cache of at most 100k entries,
+-- and apply it to the default pool
+-- pc = newPacketCache(100000)
+-- getPool(""):setCache(pc)
index e8175a567570f0a7f28edaad577c7af31d8b9d7a..b0ca67ad4245007a82776c7e56936db21831415e 100644 (file)
@@ -159,7 +159,7 @@ dnsdist_SOURCES = \
        dnsdist-snmp.cc dnsdist-snmp.hh \
        dnsdist-systemd.cc dnsdist-systemd.hh \
        dnsdist-tcp.cc \
-       dnsdist-web.cc \
+       dnsdist-web.cc dnsdist-web.hh \
        dnsdist-xpf.cc dnsdist-xpf.hh \
        dnslabeltext.cc \
        dnsname.cc dnsname.hh \
@@ -468,7 +468,9 @@ if !HAVE_SYSTEMD_SYSTEM_CALL_FILTER
 endif
 
 dnsdist@.service: dnsdist.service
-       $(AM_V_GEN)sed -e 's!/dnsdist !&--config $(sysconfdir)/dnsdist-%i.conf !' < $< >$@
+       $(AM_V_GEN)sed -e 's!/dnsdist !&--config $(sysconfdir)/dnsdist-%i.conf !' \
+         -e 's!RuntimeDirectory=.*!&-%i!' \
+         < $< >$@
 
 systemdsystemunitdir = $(SYSTEMD_DIR)
 
index 9828ff64d7aa7f485c72158e0cb6d4fabdf1d0c9..b09a7e02b15b400db7a480a937220c22193cdc5f 100644 (file)
@@ -18,7 +18,7 @@ LT_PREREQ([2.2.2])
 LT_INIT([disable-static])
 
 CFLAGS="-g -O3 -Wall -Wextra -Wshadow -Wno-unused-parameter $CFLAGS"
-CXXFLAGS="-g -O3 -Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls $CXXFLAGS"
+CXXFLAGS="-std=c++11 -g -O3 -Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls $CXXFLAGS"
 
 PDNS_WITH_LIBSODIUM
 PDNS_CHECK_DNSTAP([auto])
@@ -101,7 +101,7 @@ AS_IF([test "x$enable_dns_over_https" != "xno"], [
 PDNS_CHECK_CDB
 PDNS_CHECK_LMDB
 
-AX_CXX_COMPILE_STDCXX_11([ext], [mandatory])
+AX_CXX_COMPILE_STDCXX_11([noext], [mandatory])
 
 AC_MSG_CHECKING([whether we will enable compiler security checks])
 AC_ARG_ENABLE([hardening],
index 2f7a7dbff822b531cbfd43495f434561d75a0c64..38398e28eb11406b3128e5d2d57481d994300943 100644 (file)
@@ -98,6 +98,21 @@ bool DownstreamState::reconnect()
 
   return connected;
 }
+
+void DownstreamState::stop()
+{
+  std::unique_lock<std::mutex> tl(connectLock);
+  std::lock_guard<std::mutex> slock(socketsLock);
+  d_stopped = true;
+
+  for (auto& fd : sockets) {
+    if (fd != -1) {
+      /* shutdown() is needed to wake up recv() in the responderThread */
+      shutdown(fd, SHUT_RDWR);
+    }
+  }
+}
+
 void DownstreamState::hash()
 {
   vinfolog("Computing hashes for id=%s and weight=%d", id, weight);
@@ -152,5 +167,20 @@ DownstreamState::DownstreamState(const ComboAddress& remote_, const ComboAddress
     idStates.resize(g_maxOutstanding);
     sw.start();
   }
+}
+
+DownstreamState::~DownstreamState()
+{
+  for (auto& fd : sockets) {
+    if (fd >= 0) {
+      close(fd);
+      fd = -1;
+    }
+  }
 
+  // we need to either detach or join the thread before it
+  // is destroyed
+  if (threadStarted.test_and_set()) {
+    tid.detach();
+  }
 }
index 05ea1517173bc1ab6101396d8bb89a1bcc1380e3..6b78c3d3c4d53159528aa2f66d43c073202082ba 100644 (file)
@@ -60,7 +60,7 @@ void updateHealthCheckResult(const std::shared_ptr<DownstreamState>& dss, bool n
   if(newState != dss->upStatus) {
     warnlog("Marking downstream %s as '%s'", dss->getNameWithAddr(), newState ? "up" : "down");
 
-    if (newState && !dss->connected) {
+    if (newState && (!dss->connected || dss->reconnectOnUp)) {
       newState = dss->reconnect();
 
       if (dss->connected && !dss->threadStarted.test_and_set()) {
index 090a17c2e2e3e95745dee345f210d9ddb1ba21bf..3b60dfc2acd0bda2636c4acaaf46b895c16a5588 100644 (file)
 std::vector<std::string> KeyValueLookupKeySourceIP::getKeys(const ComboAddress& addr)
 {
   std::vector<std::string> result;
+  ComboAddress truncated(addr);
 
-  if (addr.sin4.sin_family == AF_INET) {
-    result.emplace_back(reinterpret_cast<const char*>(&addr.sin4.sin_addr.s_addr), sizeof(addr.sin4.sin_addr.s_addr));
+  if (truncated.isIPv4()) {
+    truncated.truncate(d_v4Mask);
+    result.emplace_back(reinterpret_cast<const char*>(&truncated.sin4.sin_addr.s_addr), sizeof(truncated.sin4.sin_addr.s_addr));
   }
-  else if (addr.sin4.sin_family == AF_INET6) {
-    result.emplace_back(reinterpret_cast<const char*>(&addr.sin6.sin6_addr.s6_addr), sizeof(addr.sin6.sin6_addr.s6_addr));
+  else if (truncated.isIPv6()) {
+    truncated.truncate(d_v6Mask);
+    result.emplace_back(reinterpret_cast<const char*>(&truncated.sin6.sin6_addr.s6_addr), sizeof(truncated.sin6.sin6_addr.s6_addr));
   }
 
   return result;
index 997de05a2b086e15358da49c4fba01f9fd29af9f..f0968b08ab14b971e69d0acaf9097785f9be6fe7 100644 (file)
@@ -36,6 +36,10 @@ public:
 class KeyValueLookupKeySourceIP: public KeyValueLookupKey
 {
 public:
+  KeyValueLookupKeySourceIP(uint8_t v4Mask, uint8_t v6Mask): d_v4Mask(v4Mask), d_v6Mask(v6Mask)
+  {
+  }
+
   std::vector<std::string> getKeys(const ComboAddress& addr);
 
   std::vector<std::string> getKeys(const DNSQuestion& dq) override
@@ -45,8 +49,11 @@ public:
 
   std::string toString() const override
   {
-    return "source IP";
+    return "source IP (masked to " + std::to_string(d_v4Mask) + " (v4) / " + std::to_string(d_v6Mask) + " (v6) bits)";
   }
+private:
+  uint8_t d_v4Mask;
+  uint8_t d_v6Mask;
 };
 
 class KeyValueLookupKeyQName: public KeyValueLookupKey
index a36cc16afd783c3d556becd68e465e8563857921..0dfc7e0c562f66cdefa001d259a1398a817b2976 100644 (file)
@@ -218,7 +218,7 @@ shared_ptr<DownstreamState> roundrobin(const ServerPolicy::NumberedServerVector&
   return (*res)[(counter++) % res->size()].second;
 }
 
-ServerPolicy::NumberedServerVector getDownstreamCandidates(const pools_t& pools, const std::string& poolName)
+const std::shared_ptr<ServerPolicy::NumberedServerVector> getDownstreamCandidates(const pools_t& pools, const std::string& poolName)
 {
   std::shared_ptr<ServerPool> pool = getPool(pools, poolName);
   return pool->getServers();
index a026cfb11b4d30a318c9f880423e6a1342ee1c1b..3bc185056ed0809afc5fb6210c9c2168b50361ca 100644 (file)
@@ -26,8 +26,8 @@
 void setupLuaBindingsKVS(bool client)
 {
   /* Key Value Store objects */
-  g_lua.writeFunction("KeyValueLookupKeySourceIP", []() {
-    return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeySourceIP());
+  g_lua.writeFunction("KeyValueLookupKeySourceIP", [](boost::optional<uint8_t> v4Mask, boost::optional<uint8_t> v6Mask) {
+    return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeySourceIP(v4Mask.get_value_or(32), v6Mask.get_value_or(128)));
   });
   g_lua.writeFunction("KeyValueLookupKeyQName", [](boost::optional<bool> wireFormat) {
     return std::shared_ptr<KeyValueLookupKey>(new KeyValueLookupKeyQName(wireFormat ? *wireFormat : true));
@@ -65,7 +65,7 @@ void setupLuaBindingsKVS(bool client)
 
     if (keyVar.type() == typeid(ComboAddress)) {
       const auto ca = boost::get<ComboAddress>(&keyVar);
-      KeyValueLookupKeySourceIP lookup;
+      KeyValueLookupKeySourceIP lookup(32, 128);
       for (const auto& key : lookup.getKeys(*ca)) {
         if (kvs->getValue(key, result)) {
           return result;
index b534651ac12627ad508026e9ce0568603b5b28cf..27c20b41b0b773ce27e88c5d52050184031c6abe 100644 (file)
@@ -92,12 +92,32 @@ void setupLuaBindingsPacketCache()
 
       return res;
     });
-  g_lua.registerFunction("toString", &DNSDistPacketCache::toString);
-  g_lua.registerFunction("isFull", &DNSDistPacketCache::isFull);
-  g_lua.registerFunction("purgeExpired", &DNSDistPacketCache::purgeExpired);
-  g_lua.registerFunction("expunge", &DNSDistPacketCache::expunge);
+  g_lua.registerFunction<std::string(std::shared_ptr<DNSDistPacketCache>::*)()>("toString", [](const std::shared_ptr<DNSDistPacketCache>& cache) {
+      if (cache) {
+        return cache->toString();
+      }
+      return std::string();
+    });
+  g_lua.registerFunction<bool(std::shared_ptr<DNSDistPacketCache>::*)()>("isFull", [](const std::shared_ptr<DNSDistPacketCache>& cache) {
+      if (cache) {
+        return cache->isFull();
+      }
+      return false;
+    });
+  g_lua.registerFunction<size_t(std::shared_ptr<DNSDistPacketCache>::*)(size_t)>("purgeExpired", [](std::shared_ptr<DNSDistPacketCache>& cache, size_t upTo) {
+      if (cache) {
+        return cache->purgeExpired(upTo);
+      }
+      return static_cast<size_t>(0);
+    });
+  g_lua.registerFunction<size_t(std::shared_ptr<DNSDistPacketCache>::*)(size_t)>("expunge", [](std::shared_ptr<DNSDistPacketCache>& cache, size_t upTo) {
+      if (cache) {
+        return cache->expunge(upTo);
+      }
+      return static_cast<size_t>(0);
+    });
   g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const DNSName& dname, boost::optional<uint16_t> qtype, boost::optional<bool> suffixMatch)>("expungeByName", [](
-              std::shared_ptr<DNSDistPacketCache> cache,
+              std::shared_ptr<DNSDistPacketCache>& cache,
               const DNSName& dname,
               boost::optional<uint16_t> qtype,
               boost::optional<bool> suffixMatch) {
@@ -105,7 +125,7 @@ void setupLuaBindingsPacketCache()
                   g_outputBuffer="Expunged " + std::to_string(cache->expungeByName(dname, qtype ? *qtype : QType(QType::ANY).getCode(), suffixMatch ? *suffixMatch : false)) + " records\n";
                 }
     });
-  g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)()>("printStats", [](const std::shared_ptr<DNSDistPacketCache> cache) {
+  g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)()>("printStats", [](const std::shared_ptr<DNSDistPacketCache>& cache) {
       if (cache) {
         g_outputBuffer="Entries: " + std::to_string(cache->getEntriesCount()) + "/" + std::to_string(cache->getMaxEntries()) + "\n";
         g_outputBuffer+="Hits: " + std::to_string(cache->getHits()) + "\n";
@@ -117,7 +137,7 @@ void setupLuaBindingsPacketCache()
         g_outputBuffer+="TTL Too Shorts: " + std::to_string(cache->getTTLTooShorts()) + "\n";
       }
     });
-  g_lua.registerFunction<std::unordered_map<std::string, uint64_t>(std::shared_ptr<DNSDistPacketCache>::*)()>("getStats", [](const std::shared_ptr<DNSDistPacketCache> cache) {
+  g_lua.registerFunction<std::unordered_map<std::string, uint64_t>(std::shared_ptr<DNSDistPacketCache>::*)()>("getStats", [](const std::shared_ptr<DNSDistPacketCache>& cache) {
       std::unordered_map<std::string, uint64_t> stats;
       if (cache) {
         stats["entries"] = cache->getEntriesCount();
@@ -132,7 +152,7 @@ void setupLuaBindingsPacketCache()
       }
       return stats;
     });
-  g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const std::string& fname)>("dump", [](const std::shared_ptr<DNSDistPacketCache> cache, const std::string& fname) {
+  g_lua.registerFunction<void(std::shared_ptr<DNSDistPacketCache>::*)(const std::string& fname)>("dump", [](const std::shared_ptr<DNSDistPacketCache>& cache, const std::string& fname) {
       if (cache) {
 
         int fd = open(fname.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0660);
index d56b73510ad7a0864645417ef7a2adf44fa2e400..efa1c40dd0a1ef7e2f7561eef182b20b0b552798 100644 (file)
@@ -112,3 +112,4 @@ const char* dnsdist_ffi_server_get_name(const dnsdist_ffi_server_t* server) __at
 const char* dnsdist_ffi_server_get_name_with_addr(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
 int dnsdist_ffi_server_get_weight(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
 int dnsdist_ffi_server_get_order(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
+double dnsdist_ffi_server_get_latency(const dnsdist_ffi_server_t* server) __attribute__ ((visibility ("default")));
index f709feaa5d101adb248c10c6f89ca93d19503c25..a40a4ab196ad5dd1fd9b842b55c34cfa2c63ec08 100644 (file)
@@ -468,6 +468,11 @@ int dnsdist_ffi_server_get_order(const dnsdist_ffi_server_t* server)
   return server->server->order;
 }
 
+double dnsdist_ffi_server_get_latency(const dnsdist_ffi_server_t* server)
+{
+  return server->server->latencyUsec;
+}
+
 bool dnsdist_ffi_server_is_up(const dnsdist_ffi_server_t* server)
 {
   return server->server->isUp();
index e689902fdd2f891e5909496cc4d8015b61c93f0c..083b0d345af3cddc890182f152815f6fd0b6460b 100644 (file)
@@ -41,6 +41,10 @@ bool addProxyProtocol(std::vector<uint8_t>& buffer, bool tcp, const ComboAddress
   auto payload = makeProxyHeader(tcp, source, destination, values);
 
   auto previousSize = buffer.size();
+  if (payload.size() > (std::numeric_limits<size_t>::max() - previousSize)) {
+    return false;
+  }
+
   buffer.resize(previousSize + payload.size());
   std::copy_backward(buffer.begin(), buffer.begin() + previousSize, buffer.end());
   std::copy(payload.begin(), payload.end(), buffer.begin());
index 179779f15d3cdeeac279f7c8e3f55448acb91879..358f943b18bae084f47ead8461577d6565829b53 100644 (file)
@@ -234,7 +234,7 @@ void doSecPoll(const std::string& suffix)
   }
 
   if (releaseVersion) {
-    warnlog("Could not retrieve security status update for '%s' on %s", pkgv, queriedName);
+    warnlog("Failed to retrieve security status update for '%s' on %s", pkgv, queriedName);
   }
   else if (!g_secPollDone) {
     infolog("Not validating response for security status update, this is a non-release version.");
diff --git a/pdns/dnsdistdist/dnsdist-web.hh b/pdns/dnsdistdist/dnsdist-web.hh
new file mode 100644 (file)
index 0000000..1b5d001
--- /dev/null
@@ -0,0 +1,22 @@
+#pragma once
+
+struct WebserverConfig
+{
+  WebserverConfig()
+  {
+    acl.toMasks("127.0.0.1, ::1");
+  }
+
+  NetmaskGroup acl;
+  std::string password;
+  std::string apiKey;
+  boost::optional<std::map<std::string, std::string> > customHeaders;
+  std::mutex lock;
+};
+
+void setWebserverAPIKey(const boost::optional<std::string> apiKey);
+void setWebserverPassword(const std::string& password);
+void setWebserverACL(const std::string& acl);
+void setWebserverCustomHeaders(const boost::optional<std::map<std::string, std::string> > customHeaders);
+
+void dnsdistWebserverThread(int sock, const ComboAddress& local);
index 8187e6d0519c4359ab656a49a50f5a0b3a327809..7dc7a90e9241367e1dc92d70d146248771dd1cb0 100644 (file)
@@ -1,6 +1,196 @@
 Changelog
 =========
 
+.. changelog::
+  :version: 1.5.0
+  :released: 30th of July 2020
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9231
+
+    Use explicit flag for the specific version of c++ we are targeting.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9320
+
+    Prevent a possible overflow via large Proxy Protocol values. (Valentei Sergey)
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9348
+    :tickets: 9279
+
+    Avoid name clashes on Solaris derived systems.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9343
+
+    Resize hostname to final size in getCarbonHostname(). (Aki Tuomi)
+
+  .. change::
+    :tags: Bug Fixes, DNS over HTTPS
+    :pullreq: 9344
+
+    Fix compilation with h2o_socket_get_ssl_server_name().
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9346
+
+    Fix compilation on OpenBSD/amd64.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9356
+
+    Handle calling PacketCache methods on a nil object.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9360
+
+    Prevent a copy of a pool's backends when selecting a server.
+
+.. changelog::
+  :version: 1.5.0-rc4
+  :released: 7th of July 2020
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9278
+
+    Prevent a race between the DoH handling threads
+
+.. changelog::
+  :version: 1.5.0-rc3
+  :released: 18th of June 2020
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9100
+
+    Less negatives in secpoll error messages improves readability.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9127
+    :tickets: 9125
+
+    Fix compilation on systems that do not define HOST_NAME_MAX
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9207
+
+    Use std::string_view when available (Rosen Penev)
+
+  .. change::
+    :tags: Bug Fixes, DNS over HTTPS
+    :pullreq: 9211
+    :tickets: 9206
+
+    Use non-blocking pipes to pass DoH queries/responses around
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9213
+
+    Do not use `using namespace std;`
+
+  .. change::
+    :tags: New Features
+    :pullreq: 9229
+
+    Implement an ACL in the internal web server
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9238
+    :tickets: 8038
+
+    Clean up dnsdistconf.lua as a default configuration file
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9244
+
+    Add optional masks to KeyValueLookupKeySourceIP
+
+.. changelog::
+  :version: 1.5.0-rc2
+  :released: 13th of May 2020
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9031
+    :tickets: 9025
+
+    Fix compilation of the ports event multiplexer
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9042
+
+    Avoid copies in for loops
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9054
+
+    Build with -Wmissing-declarations -Wredundant-decls
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9016
+    :tickets: 9004
+
+    Use std::shuffle instead of std::random_shuffle
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9053
+
+    Get rid of a naked pointer in the /dev/poll event multiplexer
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9059
+
+    A few warnings fixed, reported by clang on OpenBSD
+
+  .. change::
+    :tags: Bug Fixes, DNS over HTTPS
+    :pullreq: 9068
+
+    Fix duplicated HTTP/1 counter in 'showDOHFrontends()'
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9057
+
+    Gracefully handle a failure to remove FD on (re)-connection
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9067
+
+    Wrap pthread objects
+
+  .. change::
+    :tags: Improvements, Metrics
+    :pullreq: 9084
+
+    Add the unit to the help for latency buckets
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9078
+
+    NetmaskTree: do not test node for null, the loop guarantees node is not null.
+
 .. changelog::
   :version: 1.5.0-rc1
   :released: 16th of April 2020
@@ -382,7 +572,7 @@ Changelog
     :tags: Improvements
     :pullreq: 8440
 
-    Fix -WShadow warnings (Aki Tuomi)
+    Fix -Wshadow warnings (Aki Tuomi)
 
   .. change::
     :tags: Improvements
@@ -686,7 +876,7 @@ Changelog
     :tags: Bug Fixes
     :pullreq: 7886
 
-     SuffixMatchTree: fix root removal, partial match of non-leaf nodes
+    SuffixMatchTree: fix root removal, partial match of non-leaf nodes
 
   .. change::
     :tags: Improvements
@@ -1201,7 +1391,7 @@ Changelog
     :pullreq: 7585
     :tickets: 7534
 
-     Prevent 0-ttl cache hits
+    Prevent 0-ttl cache hits
 
   .. change::
     :tags: Improvements
@@ -1379,7 +1569,7 @@ Changelog
     :pullreq: 7015
     :tickets: 7004, 6990
 
-     Add support for exporting a server id in protobuf
+    Add support for exporting a server id in protobuf
 
   .. change::
     :tags: Improvements
@@ -1404,7 +1594,7 @@ Changelog
     :pullreq: 7064
     :tickets: 7060
 
-     Wrap GnuTLS and OpenSSL pointers in smart pointers
+    Wrap GnuTLS and OpenSSL pointers in smart pointers
 
   .. change::
     :tags: New Features
@@ -1562,7 +1752,7 @@ Changelog
     :pullreq: 6523
     :tickets: 6430
 
-     Tests: avoid failure on not-so-optimal distribution
+    Tests: avoid failure on not-so-optimal distribution
 
   .. change::
     :tags: New Features
@@ -1624,7 +1814,7 @@ Changelog
     :tags: Bug Fixes
     :pullreq: 6672
 
-     Fix reconnection handling
+    Fix reconnection handling
 
   .. change::
     :tags: Improvements
index 339e3563bae6b5aafc97ff04e51a310139f88f8c..ab0c23fdacd294f0c0c85f0b9dc95073d175bfdc 100644 (file)
@@ -17,13 +17,10 @@ In order to support multiple certificates and keys, for example an ECDSA and an
 
 The certificate chain presented by the server to an incoming client will then be selected based on the algorithms this client advertised support for.
 
-A fourth parameter may be added to specify the URL path(s) used by
-DoH. If you want your DoH server to handle
-``https://example.com/dns-query``, you have to add ``"/dns-query"`` to
-the call to :func:`addDOHLocal`. It is optional and defaults to ``/``, the root of your HTTP site.
+A fourth parameter may be added to specify the URL path(s) used by DoH. If you want your DoH server to handle ``https://example.com/dns-query-endpoint``, you have to add ``"/dns-query-endpoint"`` to
+the call to :func:`addDOHLocal`. It is optional and defaults to ``/`` in 1.4.0, and ``/dns-query`` since 1.5.0.
 
-The fifth parameter, if present, indicates various options. For
-instance, you use it to indicate custom HTTP headers. An example is::
+The fifth parameter, if present, indicates various options. For instance, you use it to indicate custom HTTP headers. An example is::
 
   addDOHLocal('2001:db8:1:f00::1', '/etc/ssl/certs/example.com.pem', '/etc/ssl/private/example.com.key', "/dns", {customResponseHeaders={["x-foo"]="bar"}}
 
@@ -31,6 +28,13 @@ A more complicated (and more realistic) example is when you want to indicate met
 
   addDOHLocal('2001:db8:1:f00::1', '/etc/ssl/certs/example.com.pem', '/etc/ssl/private/example.com.key', "/", {customResponseHeaders={["link"]="<https://example.com/policy.html> rel=\\"service-meta\\"; type=\\"text/html\\""}})
 
+It is also possible to set HTTP response rules to intercept HTTP queries early, before the DNS payload, if any, has been processed, to send custom responses including error pages, redirects or even serve static content. First a rule needs to be defined using :func:`newDOHResponseMapEntry`, then a set of rules can be applied to a DoH frontend via :meth:`DOHFrontend.setResponsesMap`.
+For example, to send an HTTP redirect to queries asking for ``/rfc``, the following configuration can be used:
+
+  map = { newDOHResponseMapEntry("^/rfc$", 307, "https://www.rfc-editor.org/info/rfc8484") }
+  dohFE = getDOHFrontend(0)
+  dohFE:setResponsesMap(map)
+
 In case you want to run DNS-over-HTTPS behind a reverse proxy you probably don't want to encrypt your traffic between reverse proxy and dnsdist.
 To let dnsdist listen for DoH queries over HTTP on localhost at port 8053 add one of the following to your config::
 
index f8ee873de652cae025ddc7f4770741629e5c72fb..01e6992e9dbd64c219352967a2fd159f97556c4c 100755 (executable)
@@ -9,6 +9,13 @@ To visually interact with dnsdist, try add :func:`webserver` to the configuratio
 
 Now point your browser at http://127.0.0.1:8083 and log in with any username, and that password. Enjoy!
 
+Since 1.5.0, only connections from 127.0.0.1 and ::1 are allowed by default. To allow connections from 192.0.2.0/24 but not from 192.0.2.1, instead:
+
+.. code-block:: lua
+
+  webserver("127.0.0.1:8083", "supersecretpassword", "supersecretAPIkey", {}, "192.0.2.0/24, !192.0.2.1")
+
+
 Security of the Webserver
 -------------------------
 
@@ -27,7 +34,7 @@ For example, to remove the X-Frame-Options header and add a X-Custom one:
 
 .. code-block:: lua
 
-  webserver("127.0.0.1:8080", "supersecret", "apikey", {["X-Frame-Options"]= "", ["X-Custom"]="custom"}
+  webserver("127.0.0.1:8080", "supersecret", "apikey", {["X-Frame-Options"]= "", ["X-Custom"]="custom"})
 
 Credentials can be changed over time using the :func:`setWebserverConfig` function.
 
index a5db904ecad84c04af9c46b04ded4cfc0945a968..49826341ce66b595e937e8401efbcb59f9f5f0ff 100644 (file)
@@ -113,7 +113,7 @@ Listen Sockets
   .. versionadded:: 1.4.0
 
   .. versionchanged:: 1.5.0
-    ``sendCacheControlHeaders``, ``sessionTimeout``, ``trustForwardedForHeader`` options added.
+    ``internalPipeBufferSize``, ``sendCacheControlHeaders``, ``sessionTimeout``, ``trustForwardedForHeader`` options added.
     ``url`` now defaults to ``/dns-query`` instead of ``/``. Added ``tcpListenQueueSize`` parameter.
 
   Listen on the specified address and TCP port for incoming DNS over HTTPS connections, presenting the specified X.509 certificate.
@@ -123,7 +123,7 @@ Listen Sockets
                       The default port is 443.
   :param str certFile(s): The path to a X.509 certificate file in PEM format, or a list of paths to such files.
   :param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones.
-  :param str-or-list urls: A base URL, or a list of base URLs, to accept queries on. Any query with a path under one of these will be treated as a DoH query. The default is /dns-query.
+  :param str-or-list urls: The path part of a URL, or a list of paths, to accept queries on. Any query with a path under one of these will be treated as a DoH query. The default is /dns-query.
   :param table options: A table with key: value pairs with listen options.
 
   Options:
@@ -150,6 +150,7 @@ Listen Sockets
   * ``sendCacheControlHeaders``: bool - Whether to parse the response to find the lowest TTL and set a HTTP Cache-Control header accordingly. Default is true.
   * ``trustForwardedForHeader``: bool - Whether to parse any existing X-Forwarded-For header in the HTTP query and use the right-most value as the client source address and port, for ACL checks, rules, logging and so on. Default is false.
   * ``tcpListenQueueSize=SOMAXCONN``: int - Set the size of the listen queue. Default is ``SOMAXCONN``.
+  * ``internalPipeBufferSize=0``: int - Set the size in bytes of the internal buffer of the pipes used internally to pass queries and responses between threads. Requires support for ``F_SETPIPE_SZ`` which is present in Linux since 2.6.35. The actual size might be rounded up to a multiple of a page size. 0 means that the OS default size is used.
 
 .. function:: addTLSLocal(address, certFile(s), keyFile(s) [, options])
 
@@ -296,7 +297,10 @@ Control Socket, Console and Webserver
 Webserver configuration
 ~~~~~~~~~~~~~~~~~~~~~~~
 
-.. function:: webserver(listen_address, password[, apikey[, custom_headers]])
+.. function:: webserver(listen_address, password[, apikey[, custom_headers[, acl]]])
+
+  .. versionchanged:: 1.5.0
+    ``acl`` optional parameter added.
 
   Launch the :doc:`../guides/webserver` with statistics and the API.
 
@@ -304,6 +308,7 @@ Webserver configuration
   :param str password: The password required to access the webserver
   :param str apikey: The key required to access the API
   :param {[str]=str,...} custom_headers: Allows setting custom headers and removing the defaults
+  :param str acl: List of netmasks, as a string, that are allowed to open a connection to the web server. Defaults to "127.0.0.1, ::1". It accepts the same syntax that :func:`NetmaskGroup:addMask` does
 
 .. function:: setAPIWritable(allow [,dir])
 
@@ -318,6 +323,9 @@ Webserver configuration
 
   .. versionadded:: 1.3.3
 
+  .. versionchanged:: 1.5.0
+    ``acl`` optional parameter added.
+
   Setup webserver configuration. See :func:`webserver`.
 
   :param table options: A table with key: value pairs with webserver options.
@@ -327,7 +335,8 @@ Webserver configuration
   * ``password=newPassword``: string - Changes the API password
   * ``apiKey=newKey``: string - Changes the API Key (set to an empty string do disable it)
   * ``custom_headers={[str]=str,...}``: map of string - Allows setting custom headers and removing the defaults.
-                 
+  * ``acl=newACL``: string - List of IP addresses, as a string, that are allowed to open a connection to the web server. Defaults to "127.0.0.1, ::1".
+
 Access Control Lists
 ~~~~~~~~~~~~~~~~~~~~
 
@@ -460,7 +469,8 @@ Servers
       sockets=NUM,           -- Number of sockets (and thus source ports) used toward the backend server, defaults to a single one
       disableZeroScope=BOOL, -- Disable the EDNS Client Subnet 'zero scope' feature, which does a cache lookup for an answer valid for all subnets (ECS scope of 0) before adding ECS information to the query and doing the regular lookup. This requires the ``parseECS`` option of the corresponding cache to be set to true
       rise=NUM,              -- Require NUM consecutive successful checks before declaring the backend up, default: 1
-      useProxyProtocol=BOOL  -- Add a proxy protocol header to the query, passing along the client's IP address and port along with the original destination address and port. Default is disabled.
+      useProxyProtocol=BOOL, -- Add a proxy protocol header to the query, passing along the client's IP address and port along with the original destination address and port. Default is disabled.
+      reconnectOnUp=BOOL     -- Close and reopen the sockets when a server transits from Down to Up. This helps when an interface is missing when dnsdist is started. Default is disabled.
     })
 
   :param str server_string: A simple IP:PORT string.
@@ -506,6 +516,14 @@ A server object returned by :func:`getServer` can be manipulated with these func
 
     :param str pool: The pool to add the server to
 
+  .. method:: Server:getLatency() -> double
+
+    .. versionadded:: 1.6.0
+
+    Return the average latency of this server over the last 128 UDP queries, in microseconds.
+
+    :returns: The number of outstanding queries
+
   .. method:: Server:getName() -> string
 
     Get the name of this server.
@@ -720,7 +738,7 @@ See :doc:`../guides/cache` for a how to.
 
     :param DNSName name: The name to expunge
     :param int qtype: The type to expunge, can be a pre-defined :ref:`DNSQType`
-    :param bool suffixMatch: When set to true, remove al entries under ``name``
+    :param bool suffixMatch: When set to true, remove all entries under ``name``
 
   .. method:: PacketCache:getStats()
 
@@ -1363,7 +1381,7 @@ DOHFrontend
 
   .. versionadded:: 1.4.0
 
-  Return a DOHResponseMapEntry that can be used with :meth:`DOHFrontend.setResponsesMap`. Every query whose path matches the regular expression supplied in ``regex`` will be immediately answered with a HTTP response.
+  Return a DOHResponseMapEntry that can be used with :meth:`DOHFrontend.setResponsesMap`. Every query whose path is listed in the ``urls`` parameter to :func:`addDOHLocal` and matches the regular expression supplied in ``regex`` will be immediately answered with a HTTP response.
   The status of the HTTP response will be the one supplied by ``status``, and the content set to the one supplied by ``content``, except if the status is a redirection (3xx) in which case the content is expected to be the URL to redirect to.
 
   :param str regex: A regular expression to match the path against.
@@ -1416,8 +1434,8 @@ record if the received request had one, which is the case by default and can be
 :func:`setAddEDNSToSelfGeneratedResponses`.
 
 We must, however, provide a responder's maximum payload size in this record, and we can't easily know the
-maximum payload size of the actual backend so we need to provide one. The default value is 1500 and can be
-overridden using :func:`setPayloadSizeOnSelfGeneratedAnswers`.
+maximum payload size of the actual backend so we need to provide one. The default value is 1232 since 1.6.0,
+and can be overridden using :func:`setPayloadSizeOnSelfGeneratedAnswers`.
 
 .. function:: setAddEDNSToSelfGeneratedResponses(add)
 
@@ -1431,10 +1449,13 @@ overridden using :func:`setPayloadSizeOnSelfGeneratedAnswers`.
 
   .. versionadded:: 1.3.3
 
+  .. versionchanged:: 1.6.0
+    Default value changed from 1500 to 1232.
+
   Set the UDP payload size advertised via EDNS on self-generated responses. In accordance with
   :rfc:`RFC 6891 <6891#section-6.2.5>`, values lower than 512 will be treated as equal to 512.
 
-  :param int payloadSize: The responder's maximum UDP payload size, in bytes. Default is 1500.
+  :param int payloadSize: The responder's maximum UDP payload size, in bytes. Default is 1232 since 1.6.0, it was 1500 before.
 
 Security Polling
 ~~~~~~~~~~~~~~~~
index 47e69945824f3b23c9d09f66d3cad84d678ea7ad..8519c8828501eb2094c153be8dc0c8ec805e8c2c 100644 (file)
@@ -3,8 +3,6 @@ Key Value Store functions and objects
 
 These are all the functions, objects and methods related to the CDB and LMDB key value stores.
 
-As of 1.4.0, the CDB and LMDB code is considered experimental.
-
 A lookup into a key value store can be done via the :func:`KeyValueStoreLookupRule` rule or
 the :func:`KeyValueStoreLookupAction` action, using the usual selectors to match the incoming
 queries for which the lookup should be done.
@@ -81,12 +79,18 @@ If the value found in the LMDB database for the key '\\8powerdns\\3com\\0' was '
 
   :param bool wireFormat: Whether to do the lookup in wire format (default) or in plain text
 
-.. function:: KeyValueLookupKeySourceIP() -> KeyValueLookupKey
+.. function:: KeyValueLookupKeySourceIP([v4mask [, v6mask]]) -> KeyValueLookupKey
 
   .. versionadded:: 1.4.0
 
+  .. versionchanged:: 1.5.0
+    Optional parameters ``v4mask`` and ``v6mask`` added.
+
   Return a new KeyValueLookupKey object that, when passed to :func:`KeyValueStoreLookupAction` or :func:`KeyValueStoreLookupRule`, will return the source IP of the client in network byte-order.
 
+  :param int v4mask: Mask applied to IPv4 addresses. Default is 32 (the whole address)
+  :param int v6mask: Mask applied to IPv6 addresses. Default is 128 (the whole address)
+
 .. function:: KeyValueLookupKeySuffix([minLabels [, wireFormat]]) -> KeyValueLookupKey
 
   .. versionadded:: 1.4.0
index 840651a7e2041080f5e34e119bcd6f2fad3bbd4b..f42dd290aa8a5a603d55127d7a14dfc837cacac6 100644 (file)
@@ -10,7 +10,7 @@ NetmaskGroup
    Represents a group of netmasks that can be used to match :class:`ComboAddress`\ es against.
 
   .. method:: NetmaskGroup:addMask(mask)
-              NetmaskGroup:addMask(masks)
+              NetmaskGroup:addMasks(masks)
 
     Add one or more masks to the NMG.
 
index b1ff0bbf27c6827c8ddb29374710bdd55738740d..b13df7968f5ca779f777f2483446eda7cc458ae2 100644 (file)
@@ -606,8 +606,6 @@ These ``DNSRule``\ s be one of the following items:
 
   .. versionadded:: 1.4.0
 
-  As of 1.4.0, this code is considered experimental.
-
   Return true if the key returned by 'lookupKey' exists in the key value store referenced by 'kvs'.
   The store can be a CDB (:func:`newCDBKVStore`) or a LMDB database (:func:`newLMDBKVStore`).
   The key can be based on the qname (:func:`KeyValueLookupKeyQName` and :func:`KeyValueLookupKeySuffix`),
@@ -1025,8 +1023,6 @@ The following actions exist.
 
   .. versionadded:: 1.4.0
 
-  As of 1.4.0, this code is considered experimental.
-
   Does a lookup into the key value store referenced by 'kvs' using the key returned by 'lookupKey',
   and storing the result if any into the tag named 'destinationTag'.
   The store can be a CDB (:func:`newCDBKVStore`) or a LMDB database (:func:`newLMDBKVStore`).
index 21cc34f779fedde27e82d166be4ef78c378ca621..0acfcddbffcc94fab1feadd4ac88d04e46949e66 100644 (file)
@@ -4,8 +4,15 @@ Upgrade Guide
 1.4.0 to 1.5.x
 --------------
 
-DOH endpoints specified in the fourth parameter of :func:`addDOHLocal` are now specified as exact URLs instead of path prefixes. The default endpoint also switched from ``/`` to ``/dns-query``.
+DOH endpoints specified in the fourth parameter of :func:`addDOHLocal` are now specified as exact paths instead of path prefixes. The default endpoint also switched from ``/`` to ``/dns-query``.
 For example, ``addDOHLocal('2001:db8:1:f00::1', '/etc/ssl/certs/example.com.pem', '/etc/ssl/private/example.com.key', { "/dns-query" })`` will now only accept queries for ``/dns-query`` and no longer for ``/dns-query/foo/bar``.
+This change also impacts the HTTP response rules set via :meth:`DOHFrontend.setResponsesMap`, since queries whose paths are not allowed will be discarded before the rules are evaluated.
+If you want to accept DoH queries on ``/dns-query`` and redirect ``/rfc`` to the DoH RFC, you need to list ``/rfc`` in the list of paths:
+
+  addDOHLocal('2001:db8:1:f00::1', '/etc/ssl/certs/example.com.pem', '/etc/ssl/private/example.com.key', { '/dns-query', '/rfc'})
+  map = { newDOHResponseMapEntry("^/rfc$", 307, "https://www.rfc-editor.org/info/rfc8484") }
+  dohFE = getDOHFrontend(0)
+  dohFE:setResponsesMap(map)
 
 The systemd service-file that is installed no longer uses the ``root`` user to start. It uses the user and group set with the ``--with-service-user`` and ``--with-service-group`` switches during
 configuration, "dnsdist" by default.
@@ -18,6 +25,8 @@ Packages provided on `the PowerDNS Repository <https://repo.powerdns.com>`__ wil
 This might not be sufficient if the dnsdist configuration refers to files outside of the /etc/dnsdist directory, like DoT or DoH certificates and private keys.
 Many ACME clients used to get and renew certificates, like CertBot, set permissions assuming that services are started as root. For that particular case, making a copy of the necessary files in the /etc/dnsdist directory is advised, using for example CertBot's ``--deploy-hook`` feature to copy the files with the right permissions after a renewal.
 
+The :func:`webserver` configuration now has an optional ACL parameter, that defaults to "127.0.0.1, ::1".
+
 1.3.x to 1.4.0
 --------------
 
index 16132d9874a666bdcc1dd3cbcd0bbe3e70ab3a93..24e17b000c345feb45853a45c1991e2edb8535ab 100644 (file)
@@ -31,8 +31,6 @@
 #include "threadname.hh"
 #include "views.hh"
 
-using namespace std;
-
 /* So, how does this work. We use h2o for our http2 and TLS needs.
    If the operator has configured multiple IP addresses to listen on,
    we launch multiple h2o listener threads. We can hook in to multiple
@@ -43,11 +41,11 @@ using namespace std;
    dnsdist worker thread which we also launched.
 
    This dnsdist worker thread injects the query into the normal dnsdist flow
-   (as a datagram over a socketpair). The response also goes back over a
-   (different) socketpair, where we pick it up and deliver it back to h2o.
+   (over a pipe). The response also goes back over a (different) pipe,
+   where we pick it up and deliver it back to h2o.
 
    For coordination, we use the h2o socket multiplexer, which is sensitive to our
-   socketpair too.
+   pipe too.
 */
 
 /* h2o notes.
@@ -175,18 +173,36 @@ private:
 // through the bowels of h2o
 struct DOHServerConfig
 {
-  DOHServerConfig(uint32_t idleTimeout): accept_ctx(new DOHAcceptContext)
+  DOHServerConfig(uint32_t idleTimeout, uint32_t internalPipeBufferSize): accept_ctx(new DOHAcceptContext)
   {
-    if(socketpair(AF_LOCAL, SOCK_DGRAM, 0, dohquerypair) < 0) {
-      unixDie("Creating a socket pair for DNS over HTTPS");
+    int fd[2];
+    if (pipe(fd) < 0) {
+      unixDie("Creating a pipe for DNS over HTTPS");
     }
+    dohquerypair[0] = fd[1];
+    dohquerypair[1] = fd[0];
 
-    if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, dohresponsepair) < 0) {
+    if (pipe(fd) < 0) {
       close(dohquerypair[0]);
       close(dohquerypair[1]);
-      unixDie("Creating a socket pair for DNS over HTTPS");
+      unixDie("Creating a pipe for DNS over HTTPS");
+    }
+
+    dohresponsepair[0] = fd[1];
+    dohresponsepair[1] = fd[0];
+
+    setNonBlocking(dohquerypair[0]);
+    if (internalPipeBufferSize > 0) {
+      setPipeBufferSize(dohquerypair[0], internalPipeBufferSize);
+    }
+
+    setNonBlocking(dohresponsepair[0]);
+    if (internalPipeBufferSize > 0) {
+      setPipeBufferSize(dohresponsepair[0], internalPipeBufferSize);
     }
 
+    setNonBlocking(dohresponsepair[1]);
+
     h2o_config_init(&h2o_config);
     h2o_config.http2.idle_timeout = idleTimeout * 1000;
   }
@@ -211,6 +227,8 @@ struct DOHServerConfig
   int dohresponsepair[2]{-1,-1};
 };
 
+/* This function is called from other threads than the main DoH one,
+   instructing it to send a 502 error to the client */
 void handleDOHTimeout(DOHUnit* oldDU)
 {
   if (oldDU == nullptr) {
@@ -222,9 +240,21 @@ void handleDOHTimeout(DOHUnit* oldDU)
 
   /* increase the ref counter before sending the pointer */
   oldDU->get();
-  if (send(oldDU->rsock, &oldDU, sizeof(oldDU), 0) != sizeof(oldDU)) {
+
+  static_assert(sizeof(oldDU) <= PIPE_BUF, "Writes up to PIPE_BUF are guaranteed not to be interleaved and to either fully succeed or fail");
+  ssize_t sent = write(oldDU->rsock, &oldDU, sizeof(oldDU));
+  if (sent != sizeof(oldDU)) {
+    if (errno == EAGAIN || errno == EWOULDBLOCK) {
+      ++g_stats.dohResponsePipeFull;
+      vinfolog("Unable to pass a DoH timeout to the DoH worker thread because the pipe is full");
+    }
+    else {
+      vinfolog("Unable to pass a DoH timeout to the DoH worker thread because we couldn't write to the pipe: %s", stringerror());
+    }
+
     oldDU->release();
   }
+
   oldDU->release();
   oldDU = nullptr;
 }
@@ -287,6 +317,7 @@ static const std::string& getReasonFromStatusCode(uint16_t statusCode)
   }
 }
 
+/* Always called from the main DoH thread */
 static void handleResponse(DOHFrontend& df, st_h2o_req_t* req, uint16_t statusCode, const std::string& response, const std::vector<std::pair<std::string, std::string>>& customResponseHeaders, const std::string& contentType, bool addContentType)
 {
   constexpr int overwrite_if_exists = 1;
@@ -360,6 +391,7 @@ static void handleResponse(DOHFrontend& df, st_h2o_req_t* req, uint16_t statusCo
 /*
    this function calls 'return -1' to drop a query without sending it
    caller should make sure HTTPS thread hears of that
+   We are not in the main DoH thread but in the DoH 'client' thread.
 */
 static int processDOHQuery(DOHUnit* du)
 {
@@ -367,12 +399,14 @@ static int processDOHQuery(DOHUnit* du)
   ComboAddress remote;
   bool duRefCountIncremented = false;
   try {
-    if(!du->req) {
+    if (!du->req) {
       // we got closed meanwhile. XXX small race condition here
+      // but we should be fine as long as we don't touch du->req
+      // outside of the main DoH thread
       return -1;
     }
     remote = du->remote;
-    DOHServerConfig* dsc = reinterpret_cast<DOHServerConfig*>(du->req->conn->ctx->storage.entries[0].data);
+    DOHServerConfig* dsc = du->dsc;
     auto& holders = dsc->holders;
     ClientState& cs = *dsc->cs;
 
@@ -410,13 +444,7 @@ static int processDOHQuery(DOHUnit* du)
     dq.ednsAdded = du->ednsAdded;
     dq.du = du;
     queryId = ntohs(dh->id);
-#ifdef HAVE_H2O_SOCKET_GET_SSL_SERVER_NAME
-    h2o_socket_t* sock = du->req->conn->callbacks->get_socket(du->req->conn);
-    const char * sni = h2o_socket_get_ssl_server_name(sock);
-    if (sni != nullptr) {
-      dq.sni = sni;
-    }
-#endif /* HAVE_H2O_SOCKET_GET_SSL_SERVER_NAME */
+    dq.sni = std::move(du->sni);
 
     std::shared_ptr<DownstreamState> ss{nullptr};
     auto result = processQuery(dq, cs, holders, ss);
@@ -432,7 +460,18 @@ static int processDOHQuery(DOHUnit* du)
       }
       /* increase the ref counter before sending the pointer */
       du->get();
-      if (send(du->rsock, &du, sizeof(du), 0) != sizeof(du)) {
+
+      static_assert(sizeof(du) <= PIPE_BUF, "Writes up to PIPE_BUF are guaranteed not to be interleaved and to either fully succeed or fail");
+      ssize_t sent = write(du->rsock, &du, sizeof(du));
+      if (sent != sizeof(du)) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+          ++g_stats.dohResponsePipeFull;
+          vinfolog("Unable to pass a DoH self-answered response to the DoH worker thread because the pipe is full");
+        }
+        else {
+          vinfolog("Unable to pass a DoH self-answered to the DoH worker thread because we couldn't write to the pipe: %s", stringerror());
+        }
+
         du->release();
       }
       return 0;
@@ -547,7 +586,7 @@ static int processDOHQuery(DOHUnit* du)
   return 0;
 }
 
-/* called when a HTTP response is about to be sent */
+/* called when a HTTP response is about to be sent, from the main DoH thread */
 static void on_response_ready_cb(struct st_h2o_filter_t *self, h2o_req_t *req, h2o_ostream_t **slot)
 {
   if (req == nullptr) {
@@ -590,56 +629,71 @@ static void on_response_ready_cb(struct st_h2o_filter_t *self, h2o_req_t *req, h
   h2o_setup_next_ostream(req, slot);
 }
 
-static h2o_pathconf_t *register_handler(h2o_hostconf_t *hostconf, const char *path, int (*on_req)(h2o_handler_t *, h2o_req_t *))
-{
-  h2o_pathconf_t *pathconf = h2o_config_register_path(hostconf, path, 0);
-  if (pathconf == nullptr) {
-    return pathconf;
-  }
-  h2o_filter_t *filter = h2o_create_filter(pathconf, sizeof(*filter));
-  if (filter) {
-    filter->on_setup_ostream = on_response_ready_cb;
-  }
-
-  h2o_handler_t *handler = h2o_create_handler(pathconf, sizeof(*handler));
-  if (handler != nullptr) {
-    handler->on_req = on_req;
-  }
-
-  return pathconf;
-}
-
 /* this is called by h2o when our request dies.
    We use this to signal to the 'du' that this req is no longer alive */
 static void on_generator_dispose(void *_self)
 {
-  DOHUnit** du = (DOHUnit**)_self;
-  if(*du) { // if 0, on_dnsdist cleaned up du already
-//    cout << "du "<<(void*)*du<<" containing req "<<(*du)->req<<" got killed"<<endl;
+  DOHUnit** du = reinterpret_cast<DOHUnit**>(_self);
+  if (*du) { // if 0, on_dnsdist cleaned up du already
+    (*du)->self = nullptr;
     (*du)->req = nullptr;
   }
 }
 
-/* We allocate a DOHUnit and send it to dnsdistclient() function in the doh client thread
+/* This executes in the main DoH thread.
+   We allocate a DOHUnit and send it to dnsdistclient() function in the doh client thread
    via a pipe */
-static void doh_dispatch_query(DOHServerConfig* dsc, h2o_handler_t* self, h2o_req_t* req, std::string&& query, const ComboAddress& local, const ComboAddress& remote)
+static void doh_dispatch_query(DOHServerConfig* dsc, h2o_handler_t* self, h2o_req_t* req, std::string&& query, const ComboAddress& local, const ComboAddress& remote, std::string&& path)
 {
   try {
+    /* we only parse it there as a sanity check, we will parse it again later */
     uint16_t qtype;
     DNSName qname(query.c_str(), query.size(), sizeof(dnsheader), false, &qtype);
 
     auto du = std::unique_ptr<DOHUnit>(new DOHUnit);
+    du->dsc = dsc;
     du->req = req;
     du->dest = local;
     du->remote = remote;
     du->rsock = dsc->dohresponsepair[0];
     du->query = std::move(query);
-    du->qtype = qtype;
+    du->path = std::move(path);
+    /* we are doing quite some copies here, sorry about that,
+       but we can't keep accessing the req object once we are in a different thread
+       because the request might get killed by h2o at pretty much any time */
+    if (req->scheme != nullptr) {
+      du->scheme = std::string(req->scheme->name.base, req->scheme->name.len);
+    }
+    du->host = std::string(req->authority.base, req->authority.len);
+    du->query_at = req->query_at;
+    du->headers.reserve(req->headers.size);
+    for (size_t i = 0; i < req->headers.size; ++i) {
+      du->headers.push_back(std::make_pair(std::string(req->headers.entries[i].name->base, req->headers.entries[i].name->len),
+                                           std::string(req->headers.entries[i].value.base, req->headers.entries[i].value.len)));
+    }
+
+#ifdef HAVE_H2O_SOCKET_GET_SSL_SERVER_NAME
+    h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn);
+    const char * sni = h2o_socket_get_ssl_server_name(sock);
+    if (sni != nullptr) {
+      du->sni = sni;
+    }
+#endif /* HAVE_H2O_SOCKET_GET_SSL_SERVER_NAME */
+
     du->self = reinterpret_cast<DOHUnit**>(h2o_mem_alloc_shared(&req->pool, sizeof(*self), on_generator_dispose));
     auto ptr = du.release();
     *(ptr->self) = ptr;
     try  {
-      if(send(dsc->dohquerypair[0], &ptr, sizeof(ptr), 0) != sizeof(ptr)) {
+      static_assert(sizeof(ptr) <= PIPE_BUF, "Writes up to PIPE_BUF are guaranteed not to be interleaved and to either fully succeed or fail");
+      ssize_t sent = write(dsc->dohquerypair[0], &ptr, sizeof(ptr));
+      if (sent != sizeof(ptr)) {
+        if (errno == EAGAIN || errno == EWOULDBLOCK) {
+          ++g_stats.dohQueryPipeFull;
+          vinfolog("Unable to pass a DoH query to the DoH worker thread because the pipe is full");
+        }
+        else {
+          vinfolog("Unable to pass a DoH query to the DoH worker thread because we couldn't write to the pipe: %s", stringerror());
+        }
         ptr->release();
         ptr = nullptr;
         h2o_send_error_500(req, "Internal Server Error", "Internal Server Error", 0);
@@ -655,6 +709,7 @@ static void doh_dispatch_query(DOHServerConfig* dsc, h2o_handler_t* self, h2o_re
   }
 }
 
+/* can only be called from the main DoH thread */
 static bool getHTTPHeaderValue(const h2o_req_t* req, const std::string& headerName, string_view& value)
 {
   bool found = false;
@@ -672,6 +727,7 @@ static bool getHTTPHeaderValue(const h2o_req_t* req, const std::string& headerNa
   return found;
 }
 
+/* can only be called from the main DoH thread */
 static void processForwardedForHeader(const h2o_req_t* req, ComboAddress& remote)
 {
   static const std::string headerName = "x-forwarded-for";
@@ -703,7 +759,7 @@ static void processForwardedForHeader(const h2o_req_t* req, ComboAddress& remote
 }
 
 /*
-  A query has been parsed by h2o.
+  A query has been parsed by h2o, this executes in the main DoH thread.
   For GET, the base64url-encoded payload is in the 'dns' parameter, which might be the first parameter, or not.
   For POST, the payload is the payload.
  */
@@ -717,7 +773,14 @@ try
   h2o_socket_t* sock = req->conn->callbacks->get_socket(req->conn);
   ComboAddress remote;
   ComboAddress local;
-  h2o_socket_getpeername(sock, reinterpret_cast<struct sockaddr*>(&remote));
+
+  if (h2o_socket_getpeername(sock, reinterpret_cast<struct sockaddr*>(&remote)) == 0) {
+    /* getpeername failed, likely because the connection has already been closed,
+       but anyway that means we can't get the remote address, which could allow an ACL bypass */
+    h2o_send_error_500(req, getReasonFromStatusCode(500).c_str(), "Internal Server Error - Unable to get remote address", 0);
+    return 0;
+  }
+
   h2o_socket_getsockname(sock, reinterpret_cast<struct sockaddr*>(&local));
   DOHServerConfig* dsc = reinterpret_cast<DOHServerConfig*>(req->conn->ctx->storage.entries[0].data);
 
@@ -781,7 +844,7 @@ try
        at least s_maxPacketCacheEntrySize bytes to be able to fill the answer from the packet cache */
     query.reserve(std::max(req->entity.len + 512, s_maxPacketCacheEntrySize));
     query.assign(req->entity.base, req->entity.len);
-    doh_dispatch_query(dsc, self, req, std::move(query), local, remote);
+    doh_dispatch_query(dsc, self, req, std::move(query), local, remote, std::move(path));
   }
   else if(req->query_at != SIZE_MAX && (req->path.len - req->query_at > 5)) {
     auto pos = path.find("?dns=");
@@ -820,7 +883,7 @@ try
         else
           ++dsc->df->d_http1Stats.d_nbQueries;
 
-        doh_dispatch_query(dsc, self, req, std::move(decoded), local, remote);
+        doh_dispatch_query(dsc, self, req, std::move(decoded), local, remote, std::move(path));
       }
     }
     else
@@ -837,7 +900,7 @@ try
   }
   return 0;
 }
-catch(const exception& e)
+ catch(const std::exception& e)
 {
   errlog("DOH Handler function failed with error %s", e.what());
   return 0;
@@ -854,10 +917,9 @@ bool HTTPHeaderRule::matches(const DNSQuestion* dq) const
     return false;
   }
 
-  for (size_t i = 0; i < dq->du->req->headers.size; ++i) {
-    if(std::string(dq->du->req->headers.entries[i].name->base, dq->du->req->headers.entries[i].name->len) == d_header &&
-       d_regex.match(std::string(dq->du->req->headers.entries[i].value.base, dq->du->req->headers.entries[i].value.len))) {
-      return true;
+  for (const auto& header : dq->du->headers) {
+    if (header.first == d_header) {
+      return d_regex.match(header.second);
     }
   }
   return false;
@@ -876,15 +938,15 @@ HTTPPathRule::HTTPPathRule(const std::string& path)
 
 bool HTTPPathRule::matches(const DNSQuestion* dq) const
 {
-  if(!dq->du) {
+  if (!dq->du) {
     return false;
   }
 
-  if(dq->du->req->query_at == SIZE_MAX) {
-    return dq->du->req->path.base == d_path;
+  if (dq->du->query_at == SIZE_MAX) {
+    return dq->du->path == d_path;
   }
   else {
-    return d_path.compare(0, d_path.size(), dq->du->req->path.base, dq->du->req->query_at) == 0;
+    return d_path.compare(0, d_path.size(), dq->du->path, 0, dq->du->query_at) == 0;
   }
 }
 
@@ -914,11 +976,10 @@ string HTTPPathRegexRule::toString() const
 std::unordered_map<std::string, std::string> DOHUnit::getHTTPHeaders() const
 {
   std::unordered_map<std::string, std::string> results;
-  results.reserve(req->headers.size);
+  results.reserve(headers.size());
 
-  for (size_t i = 0; i < req->headers.size; ++i) {
-    results.insert({std::string(req->headers.entries[i].name->base, req->headers.entries[i].name->len),
-                    std::string(req->headers.entries[i].value.base, req->headers.entries[i].value.len)});
+  for (const auto& header : headers) {
+    results.insert(header);
   }
 
   return results;
@@ -926,35 +987,31 @@ std::unordered_map<std::string, std::string> DOHUnit::getHTTPHeaders() const
 
 std::string DOHUnit::getHTTPPath() const
 {
-  if (req->query_at == SIZE_MAX) {
-    return std::string(req->path.base, req->path.len);
+  if (query_at == SIZE_MAX) {
+    return path;
   }
   else {
-    return std::string(req->path.base, req->query_at);
+    return std::string(path, 0, query_at);
   }
 }
 
 std::string DOHUnit::getHTTPHost() const
 {
-  return std::string(req->authority.base, req->authority.len);
+  return host;
 }
 
 std::string DOHUnit::getHTTPScheme() const
 {
-  if (req->scheme == nullptr) {
-    return std::string();
-  }
-
-  return std::string(req->scheme->name.base, req->scheme->name.len);
+  return scheme;
 }
 
 std::string DOHUnit::getHTTPQueryString() const
 {
-  if (req->query_at == SIZE_MAX) {
+  if (query_at == SIZE_MAX) {
     return std::string();
   }
   else {
-    return std::string(req->path.base + req->query_at, req->path.len - req->query_at);
+    return path.substr(query_at);
   }
 }
 
@@ -968,14 +1025,14 @@ void DOHUnit::setHTTPResponse(uint16_t statusCode, const std::string& body_, con
 /* query has been parsed by h2o, which called doh_handler() in the main DoH thread.
    In order not to blockfor long, doh_handler() called doh_dispatch_query() which allocated
    a DOHUnit object and passed it to us */
-static void dnsdistclient(int qsock, int rsock)
+static void dnsdistclient(int qsock)
 {
   setThreadName("dnsdist/doh-cli");
 
   for(;;) {
     try {
       DOHUnit* du = nullptr;
-      ssize_t got = recv(qsock, &du, sizeof(du), 0);
+      ssize_t got = read(qsock, &du, sizeof(du));
       if (got < 0) {
         warnlog("Error receiving internal DoH query: %s", strerror(errno));
         continue;
@@ -984,11 +1041,22 @@ static void dnsdistclient(int qsock, int rsock)
         continue;
       }
 
+      /* we are not in the main DoH thread anymore, so there is a real risk of
+         a race condition where h2o kills the query while we are processing it,
+         so we can't touch the content of du->req until we are back into the
+         main DoH thread */
+      if (!du->req) {
+        // it got killed in flight already
+        du->self = nullptr;
+        du->release();
+        continue;
+      }
+
       // if there was no EDNS, we add it with a large buffer size
       // so we can use UDP to talk to the backend.
       auto dh = const_cast<struct dnsheader*>(reinterpret_cast<const struct dnsheader*>(du->query.c_str()));
 
-      if(!dh->arcount) {
+      if (!dh->arcount) {
         std::string res;
         generateOptRR(std::string(), res, 4096, 0, false);
 
@@ -1001,12 +1069,24 @@ static void dnsdistclient(int qsock, int rsock)
         // we leave existing EDNS in place
       }
 
-      if(processDOHQuery(du) < 0) {
+      if (processDOHQuery(du) < 0) {
         du->status_code = 500;
         /* increase the ref count before sending the pointer */
         du->get();
-        if(send(du->rsock, &du, sizeof(du), 0) != sizeof(du)) {
-          du->release();     // XXX but now what - will h2o time this out for us?
+
+        static_assert(sizeof(du) <= PIPE_BUF, "Writes up to PIPE_BUF are guaranteed not to be interleaved and to either fully succeed or fail");
+        ssize_t sent = write(du->rsock, &du, sizeof(du));
+        if (sent != sizeof(du)) {
+          if (errno == EAGAIN || errno == EWOULDBLOCK) {
+            ++g_stats.dohResponsePipeFull;
+            vinfolog("Unable to pass a DoH internal error to the DoH worker thread because the pipe is full");
+          }
+          else {
+            vinfolog("Unable to pass a DoH internal error to the DoH worker thread because we couldn't write to the pipe: %s", stringerror());
+          }
+
+          // XXX but now what - will h2o time this out for us?
+          du->release();
         }
       }
       du->release();
@@ -1020,7 +1100,7 @@ static void dnsdistclient(int qsock, int rsock)
   }
 }
 
-/* called if h2o finds that dnsdist gave us an answer by writing into
+/* Called in the main DoH thread if h2o finds that dnsdist gave us an answer by writing into
    the dohresponsepair[0] side of the pipe so from:
    - handleDOHTimeout() when we did not get a response fast enough (called
      either from the health check thread (active) or from the frontend ones (reused))
@@ -1031,7 +1111,7 @@ static void on_dnsdist(h2o_socket_t *listener, const char *err)
 {
   DOHUnit *du = nullptr;
   DOHServerConfig* dsc = reinterpret_cast<DOHServerConfig*>(listener->data);
-  ssize_t got = recv(dsc->dohresponsepair[1], &du, sizeof(du), 0);
+  ssize_t got = read(dsc->dohresponsepair[1], &du, sizeof(du));
 
   if (got < 0) {
     warnlog("Error reading a DOH internal response: %s", strerror(errno));
@@ -1041,13 +1121,18 @@ static void on_dnsdist(h2o_socket_t *listener, const char *err)
     return;
   }
 
-  if(!du->req) { // it got killed in flight
-//    cout << "du "<<(void*)du<<" came back from dnsdist, but it was killed"<<endl;
+  if (!du->req) { // it got killed in flight
+    du->self = nullptr;
     du->release();
     return;
   }
 
-  *du->self = nullptr; // so we don't clean up again in on_generator_dispose
+  if (du->self) {
+    // we are back in the h2o main thread now, so we don't risk
+    // a race (h2o killing the query) when accessing du->req anymore
+    *du->self = nullptr; // so we don't clean up again in on_generator_dispose
+    du->self = nullptr;
+  }
 
   handleResponse(*dsc->df, du->req, du->status_code, du->response, dsc->df->d_customResponseHeaders, du->contentType, true);
 
@@ -1235,7 +1320,7 @@ void DOHFrontend::setup()
 {
   registerOpenSSLUser();
 
-  d_dsc = std::make_shared<DOHServerConfig>(d_idleTimeout);
+  d_dsc = std::make_shared<DOHServerConfig>(d_idleTimeout, d_internalPipeBufferSize);
 
   if  (!d_tlsConfig.d_certKeyPairs.empty()) {
     try {
@@ -1249,6 +1334,25 @@ void DOHFrontend::setup()
   }
 }
 
+static h2o_pathconf_t *register_handler(h2o_hostconf_t *hostconf, const char *path, int (*on_req)(h2o_handler_t *, h2o_req_t *))
+{
+  h2o_pathconf_t *pathconf = h2o_config_register_path(hostconf, path, 0);
+  if (pathconf == nullptr) {
+    return pathconf;
+  }
+  h2o_filter_t *filter = h2o_create_filter(pathconf, sizeof(*filter));
+  if (filter) {
+    filter->on_setup_ostream = on_response_ready_cb;
+  }
+
+  h2o_handler_t *handler = h2o_create_handler(pathconf, sizeof(*handler));
+  if (handler != nullptr) {
+    handler->on_req = on_req;
+  }
+
+  return pathconf;
+}
+
 // this is the entrypoint from dnsdist.cc
 void dohThread(ClientState* cs)
 try
@@ -1260,7 +1364,7 @@ try
   dsc->h2o_config.server_name = h2o_iovec_init(df->d_serverTokens.c_str(), df->d_serverTokens.size());
 
 
-  std::thread dnsdistThread(dnsdistclient, dsc->dohquerypair[1], dsc->dohresponsepair[0]);
+  std::thread dnsdistThread(dnsdistclient, dsc->dohquerypair[1]);
   dnsdistThread.detach(); // gets us better error reporting
 
   setThreadName("dnsdist/doh");
index 41516d756af7b8d761d04ed1afd84be2bc661d1f..9322f61dad69a0a312fcb3bb32463b001fd90614 100644 (file)
@@ -7,11 +7,14 @@
 #include "dnsdist-kvs.hh"
 
 #if defined(HAVE_LMDB) || defined(HAVE_CDB)
+static const ComboAddress v4ToMask("203.0.113.255");
+static const ComboAddress v6ToMask("2001:db8:ff:ff:ff:ff:ff:ff");
+
 static void doKVSChecks(std::unique_ptr<KeyValueStore>& kvs, const ComboAddress& lc, const ComboAddress& rem, const DNSQuestion& dq, const DNSName& plaintextDomain)
 {
   /* source IP */
   {
-    auto lookupKey = make_unique<KeyValueLookupKeySourceIP>();
+    auto lookupKey = make_unique<KeyValueLookupKeySourceIP>(32, 128);
     std::string value;
     /* local address is not in the db, remote is */
     BOOST_CHECK_EQUAL(kvs->getValue(std::string(reinterpret_cast<const char*>(&lc.sin4.sin_addr.s_addr), sizeof(lc.sin4.sin_addr.s_addr)), value), false);
@@ -27,6 +30,27 @@ static void doKVSChecks(std::unique_ptr<KeyValueStore>& kvs, const ComboAddress&
     }
   }
 
+  /* masked source IP */
+  {
+    auto lookupKey = make_unique<KeyValueLookupKeySourceIP>(25, 65);
+
+    auto keys = lookupKey->getKeys(v4ToMask);
+    BOOST_CHECK_EQUAL(keys.size(), 1U);
+    for (const auto& key : keys) {
+      std::string value;
+      BOOST_CHECK_EQUAL(kvs->getValue(key, value), true);
+      BOOST_CHECK_EQUAL(value, "this is the value for the masked v4 addr");
+    }
+
+    keys = lookupKey->getKeys(v6ToMask);
+    BOOST_CHECK_EQUAL(keys.size(), 1U);
+    for (const auto& key : keys) {
+      std::string value;
+      BOOST_CHECK_EQUAL(kvs->getValue(key, value), true);
+      BOOST_CHECK_EQUAL(value, "this is the value for the masked v6 addr");
+    }
+  }
+
   const DNSName subdomain = DNSName("sub") + *dq.qname;
   const DNSName notPDNS("not-powerdns.com.");
 
@@ -221,6 +245,10 @@ BOOST_AUTO_TEST_CASE(test_LMDB) {
   gettime(&expiredTime);
 
   DNSQuestion dq(&qname, qtype, qclass, qname.wirelength(), &lc, &rem, &dh, bufferSize, queryLen, isTcp, &queryRealTime);
+  ComboAddress v4Masked(v4ToMask);
+  ComboAddress v6Masked(v6ToMask);
+  v4Masked.truncate(25);
+  v6Masked.truncate(65);
 
   const string dbPath("/tmp/test_lmdb.XXXXXX");
   {
@@ -228,6 +256,8 @@ BOOST_AUTO_TEST_CASE(test_LMDB) {
     auto transaction = env.getRWTransaction();
     auto dbi = transaction->openDB("db-name", MDB_CREATE);
     transaction->put(dbi, MDBInVal(std::string(reinterpret_cast<const char*>(&rem.sin4.sin_addr.s_addr), sizeof(rem.sin4.sin_addr.s_addr))), MDBInVal("this is the value for the remote addr"));
+    transaction->put(dbi, MDBInVal(std::string(reinterpret_cast<const char*>(&v4Masked.sin4.sin_addr.s_addr), sizeof(v4Masked.sin4.sin_addr.s_addr))), MDBInVal("this is the value for the masked v4 addr"));
+    transaction->put(dbi, MDBInVal(std::string(reinterpret_cast<const char*>(&v6Masked.sin6.sin6_addr.s6_addr), sizeof(v6Masked.sin6.sin6_addr.s6_addr))), MDBInVal("this is the value for the masked v6 addr"));
     transaction->put(dbi, MDBInVal(qname.toDNSStringLC()), MDBInVal("this is the value for the qname"));
     transaction->put(dbi, MDBInVal(plaintextDomain.toStringRootDot()), MDBInVal("this is the value for the plaintext domain"));
     transaction->commit();
@@ -273,6 +303,10 @@ BOOST_AUTO_TEST_CASE(test_CDB) {
   gettime(&expiredTime);
 
   DNSQuestion dq(&qname, qtype, qclass, qname.wirelength(), &lc, &rem, &dh, bufferSize, queryLen, isTcp, &queryRealTime);
+  ComboAddress v4Masked(v4ToMask);
+  ComboAddress v6Masked(v6ToMask);
+  v4Masked.truncate(25);
+  v6Masked.truncate(65);
 
   char db[] = "/tmp/test_cdb.XXXXXX";
   {
@@ -280,6 +314,8 @@ BOOST_AUTO_TEST_CASE(test_CDB) {
     BOOST_REQUIRE(fd >= 0);
     CDBWriter writer(fd);
     BOOST_REQUIRE(writer.addEntry(std::string(reinterpret_cast<const char*>(&rem.sin4.sin_addr.s_addr), sizeof(rem.sin4.sin_addr.s_addr)), "this is the value for the remote addr"));
+    BOOST_REQUIRE(writer.addEntry(std::string(reinterpret_cast<const char*>(&v4Masked.sin4.sin_addr.s_addr), sizeof(v4Masked.sin4.sin_addr.s_addr)), "this is the value for the masked v4 addr"));
+    BOOST_REQUIRE(writer.addEntry(std::string(reinterpret_cast<const char*>(&v6Masked.sin6.sin6_addr.s6_addr), sizeof(v6Masked.sin6.sin6_addr.s6_addr)), "this is the value for the masked v6 addr"));
     BOOST_REQUIRE(writer.addEntry(qname.toDNSStringLC(), "this is the value for the qname"));
     BOOST_REQUIRE(writer.addEntry(plaintextDomain.toStringRootDot(), "this is the value for the plaintext domain"));
     writer.close();
index 1d3dfb4758d18d8fa505cbb586dde9923f99dd14..7e56c1590daa566f48f5f6f398417f6cfd060188 100644 (file)
@@ -22,8 +22,9 @@
 
 #pragma once
 
-// apple compiler somehow has string_view even in c++11!
-#if __cplusplus < 201703L && !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
+#ifdef __cpp_lib_string_view
+using std::string_view;
+#else
 #include <boost/version.hpp>
 #if BOOST_VERSION >= 106100
 #include <boost/utility/string_view.hpp>
@@ -32,6 +33,4 @@ using boost::string_view;
 #include <boost/utility/string_ref.hpp>
 using string_view = boost::string_ref;
 #endif
-#else // C++17
-using std::string_view;
 #endif
index d23be23d908946cba41bbed2027713c4c478622c..222edbaf3810e0f00eb7db35e603f5288f7fdf94 100644 (file)
@@ -85,7 +85,7 @@ public:
   explicit DNSName(const std::string& str) : DNSName(str.c_str(), str.length()) {}; //!< Constructs from a human formatted, escaped presentation
   DNSName(const char* p, int len, int offset, bool uncompress, uint16_t* qtype=nullptr, uint16_t* qclass=nullptr, unsigned int* consumed=nullptr, uint16_t minOffset=0); //!< Construct from a DNS Packet, taking the first question if offset=12. If supplied, consumed is set to the number of bytes consumed from the packet, which will not be equal to the wire length of the resulting name in case of compression.
   
-  bool isPartOf(const DNSName& rhs) const;   //!< Are we part of the rhs name?
+  bool isPartOf(const DNSName& rhs) const;   //!< Are we part of the rhs name? Note that name.isPartOf(name).
   inline bool operator==(const DNSName& rhs) const; //!< DNS-native comparison (case insensitive) - empty compares to empty
   bool operator!=(const DNSName& other) const { return !(*this == other); }
 
index 3924a698d17a05007215ac8d4137dd765dedf65a..bf3ef84690e2d12d5e809b790ba882094b46b9ab 100644 (file)
 #include "namespaces.hh"
 PcapPacketReader::PcapPacketReader(const string& fname) : d_fname(fname)
 {
-  d_fp=fopen(fname.c_str(),"r");
-  if(!d_fp)
+  d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname.c_str(), "r"), fclose);
+  if (!d_fp) {
     unixDie("Unable to open file " + fname);
-  
-  int flags=fcntl(fileno(d_fp),F_GETFL,0);
-  fcntl(fileno(d_fp), F_SETFL,flags&(~O_NONBLOCK)); // bsd needs this in stdin (??)
-  
+  }
+
+  int flags = fcntl(fileno(d_fp.get()), F_GETFL, 0);
+  fcntl(fileno(d_fp.get()), F_SETFL, flags & (~O_NONBLOCK)); // bsd needs this in stdin (??)
+
   checkedFread(&d_pfh);
-  
-  if(d_pfh.magic != 2712847316UL)
+
+  if (d_pfh.magic != 2712847316UL) {
     throw runtime_error((format("PCAP file %s has bad magic %x, should be %x") % fname % d_pfh.magic % 2712847316UL).str());
-  
+  }
+
   if( d_pfh.linktype==1) {
     d_skipMediaHeader=sizeof(struct ether_header);
   }
+  else if( d_pfh.linktype==12) { // LOOP
+    d_skipMediaHeader=4;
+  }
   else if(d_pfh.linktype==101) {
     d_skipMediaHeader=0;
   }
@@ -52,22 +57,17 @@ PcapPacketReader::PcapPacketReader(const string& fname) : d_fname(fname)
     d_skipMediaHeader=16;
   }
   else throw runtime_error((format("Unsupported link type %d") % d_pfh.linktype).str());
-  
-  d_runts = d_oversized = d_correctpackets = d_nonetheripudp = 0;
-}
 
-PcapPacketReader::~PcapPacketReader()
-{
-  fclose(d_fp);
+  d_runts = d_oversized = d_correctpackets = d_nonetheripudp = 0;
 }
 
-
 void PcapPacketReader::checkedFreadSize(void* ptr, size_t size) 
 {
-  int ret=fread(ptr, 1, size, d_fp);
-  if(ret < 0)
+  int ret = fread(ptr, 1, size, d_fp.get());
+  if (ret < 0) {
     unixDie( (format("Error reading %d bytes from %s") % size % d_fname).str());
-  
+  }
+
   if(!ret)
     throw EofException();
   
@@ -114,6 +114,16 @@ try
       d_ether=reinterpret_cast<struct ether_header*>(d_buffer);
       contentCode=ntohs(d_ether->ether_type);
     }
+    else if(d_pfh.linktype == 12) { // LOOP
+      if (d_pheader.caplen < (d_skipMediaHeader + sizeof(*d_ip))) {
+        d_runts++;
+        continue;
+      }
+      if(d_ip->ip_v == 4)
+       contentCode = 0x0800;
+      else
+       contentCode = 0x86dd;
+    }
     else if(d_pfh.linktype==101) {
       if (d_pheader.caplen < (d_skipMediaHeader + sizeof(*d_ip))) {
         d_runts++;
@@ -218,12 +228,14 @@ PcapPacketWriter::PcapPacketWriter(const string& fname, const PcapPacketReader&
 
 PcapPacketWriter::PcapPacketWriter(const string& fname) : d_fname(fname)
 {
-  d_fp=fopen(fname.c_str(),"w");
-  if(!d_fp)
+  d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname.c_str(),"w"), fclose);
+
+  if (!d_fp) {
     unixDie("Unable to open file");
-  
-  int flags=fcntl(fileno(d_fp),F_GETFL,0);
-  fcntl(fileno(d_fp), F_SETFL,flags&(~O_NONBLOCK)); // bsd needs this in stdin (??)
+  }
+
+  int flags = fcntl(fileno(d_fp.get()), F_GETFL, 0);
+  fcntl(fileno(d_fp.get()), F_SETFL,flags & (~O_NONBLOCK)); // bsd needs this in stdin (??)
 }
 
 void PcapPacketWriter::write()
@@ -233,14 +245,9 @@ void PcapPacketWriter::write()
   }
 
   if(d_first) {
-    fwrite(&d_ppr->d_pfh, 1, sizeof(d_ppr->d_pfh), d_fp);
+    fwrite(&d_ppr->d_pfh, 1, sizeof(d_ppr->d_pfh), d_fp.get());
     d_first=false;
   }
-  fwrite(&d_ppr->d_pheader, 1, sizeof(d_ppr->d_pheader), d_fp);
-  fwrite(d_ppr->d_buffer, 1, d_ppr->d_pheader.caplen, d_fp);
-}
-
-PcapPacketWriter::~PcapPacketWriter()
-{
-  fclose(d_fp);
+  fwrite(&d_ppr->d_pheader, 1, sizeof(d_ppr->d_pheader), d_fp.get());
+  fwrite(d_ppr->d_buffer, 1, d_ppr->d_pheader.caplen, d_fp.get());
 }
index a8a6e9a1201cc2dc3d58e46625a600faf4e9c5bd..5cb73a325c3c6d921f845be23b0cf8ea537a68c3 100644 (file)
@@ -90,8 +90,6 @@ public:
 
   PcapPacketReader(const string& fname); 
 
-  ~PcapPacketReader();
-
   template<typename T>
   void checkedFread(T* ptr)
   {
@@ -119,7 +117,7 @@ public:
   unsigned int d_runts, d_oversized, d_correctpackets, d_nonetheripudp;
   char d_buffer[32768];
 private:
-  FILE* d_fp;
+  std::unique_ptr<FILE, int(*)(FILE*)> d_fp{nullptr, fclose};
   string d_fname;
   unsigned int d_skipMediaHeader;
 };
@@ -132,12 +130,11 @@ public:
   
   void write();
   void setPPR(const PcapPacketReader& ppr) { d_ppr = &ppr; }
-  ~PcapPacketWriter();
 
 private:
   string d_fname;
   const PcapPacketReader* d_ppr{nullptr};
 
-  FILE *d_fp;
+  std::unique_ptr<FILE, int(*)(FILE*)> d_fp{nullptr, fclose};
   bool d_first{true};
 }; 
index 22482737b06ee2764e1f15b2ff4255f7d1307a32..664ba89e5ecbc3eb615b1ed4c599678597a14fc6 100644 (file)
@@ -64,7 +64,7 @@ try {
 
   PcapPacketReader pr(argv[1]);
 
-  FILE* fp = fopen(argv[2], "w");
+  auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(argv[2], "w"), fclose);
   if (!fp) {
     cerr<<"Error opening output file "<<argv[2]<<": "<<stringerror()<<endl;
     exit(EXIT_FAILURE);
@@ -149,17 +149,14 @@ try {
       message.serialize(str);
 
       uint16_t mlen = htons(str.length());
-      fwrite(&mlen, 1, sizeof(mlen), fp);
-      fwrite(str.c_str(), 1, str.length(), fp);
+      fwrite(&mlen, 1, sizeof(mlen), fp.get());
+      fwrite(str.c_str(), 1, str.length(), fp.get());
     }
   }
   catch (const std::exception& e) {
     cerr<<"Error while parsing the PCAP file: "<<e.what()<<endl;
-    fclose(fp);
     exit(EXIT_FAILURE);
   }
-
-  fclose(fp);
 }
 catch(const std::exception& e) {
   cerr<<"Error opening PCAP file: "<<e.what()<<endl;
index 2f2268bf544c342e8d2a102cd316cfa94a7666cb..5d85ed1d03cbe49604b13a1815bad7f897cdbd62 100644 (file)
@@ -632,6 +632,7 @@ void reportOtherTypes()
    RRSIGRecordContent::report();
    DSRecordContent::report();
    CDSRecordContent::report();
+   IPSECKEYRecordContent::report();
    SSHFPRecordContent::report();
    CERTRecordContent::report();
    NSECRecordContent::report();
index 0daa22cdd80e1d6ce5d125891c314f4a8416b2a4..8cc1851b150e08b8420c1102dad9d9e33b68d7ec 100644 (file)
@@ -541,55 +541,6 @@ DNSKEYRecordContent DNSSECPrivateKey::getDNSKEY() const
   return makeDNSKEYFromDNSCryptoKeyEngine(getKey(), d_algorithm, d_flags);
 }
 
-class DEREater
-{
-public:
-  DEREater(const std::string& str) : d_str(str), d_pos(0)
-  {}
-  
-  struct eof{};
-  
-  uint8_t getByte()
-  {
-    if(d_pos >= d_str.length()) {
-      throw eof();
-    }
-    return (uint8_t) d_str[d_pos++];
-  }
-  
-  uint32_t getLength()
-  {
-    uint8_t first = getByte();
-    if(first < 0x80) {
-      return first;
-    }
-    first &= ~0x80;
-    
-    uint32_t len=0;
-    for(int n=0; n < first; ++n) {
-      len *= 0x100;
-      len += getByte();
-    }
-    return len;
-  }
-  
-  std::string getBytes(unsigned int len)
-  {
-    std::string ret;
-    for(unsigned int n=0; n < len; ++n)
-      ret.append(1, (char)getByte());
-    return ret;
-  }
-  
-  std::string::size_type getOffset() 
-  {
-    return d_pos;
-  }
-private:
-  const std::string& d_str;
-  std::string::size_type d_pos;
-};
-
 static string calculateHMAC(const std::string& key, const std::string& text, TSIGHashEnum hasher) {
 
   const EVP_MD* md_type;
index 5ec297b0ffb439f07a6540ca30977f38e9b977ce..cf44be5dd6c6d30d64493f187608a6051ebc5951 100644 (file)
@@ -239,6 +239,7 @@ public:
 
   static void setMaxEntries(size_t maxEntries);
 
+  typedef std::map<std::string, std::vector<std::string> > METAValues;
 private:
 
 
@@ -258,16 +259,14 @@ private:
   
   struct METACacheEntry
   {
-    uint32_t getTTD() const
+    time_t getTTD() const
     {
       return d_ttd;
     }
-  
+
     DNSName d_domain;
-    mutable std::string d_key, d_value;
-    mutable bool d_isset;
-    unsigned int d_ttd;
-  
+    mutable METAValues d_value;
+    time_t d_ttd;
   };
   
   struct KeyCacheTag{};
@@ -285,12 +284,7 @@ private:
   typedef multi_index_container<
     METACacheEntry,
     indexed_by<
-      ordered_unique<tag<CompositeTag>,
-        composite_key< 
-          METACacheEntry, 
-          member<METACacheEntry, DNSName, &METACacheEntry::d_domain> ,
-          member<METACacheEntry, std::string, &METACacheEntry::d_key>
-        >, composite_key_compare<std::less<DNSName>, CIStringCompare> >,
+      ordered_unique<member<METACacheEntry, DNSName, &METACacheEntry::d_domain> >,
       sequenced<tag<SequencedTag>>
     >
   > metacache_t;
index 4f36ff2e995442c33ea0cc8b3a46a7738230136b..68cfddb67d741d4539ba2154b10403d39ce91689 100644 (file)
@@ -255,23 +255,25 @@ try
   std::vector<std::thread> workers;
   workers.reserve(numworkers);
 
-  FILE* fp;
-  if(!g_vm.count("file"))
-    fp=fdopen(0, "r");
+  std::unique_ptr<FILE, int(*)(FILE*)> fp{nullptr, fclose};
+  if (!g_vm.count("file")) {
+    fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(0, "r"), fclose);
+  }
   else {
-    fp=fopen(g_vm["file"].as<string>().c_str(), "r");
-    if(!fp)
+    fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(g_vm["file"].as<string>().c_str(), "r"), fclose);
+    if (!fp) {
       unixDie("Unable to open "+g_vm["file"].as<string>()+" for input");
+    }
   }
   pair<string, string> q;
   string line;
-  while(stringfgets(fp, line)) {
+  while(stringfgets(fp.get(), line)) {
     trim_right(line);
     q=splitField(line, ' ');
     g_queries.push_back(BenchQuery(q.first, DNSRecordContent::TypeToNumber(q.second)));
   }
-  fclose(fp);
-    
+  fp.reset();
+
   for (unsigned int n = 0; n < numworkers; ++n) {
     workers.push_back(std::thread(worker));
   }
index 9e51c2e065359951176d3f26343d416cf11fd7c7..36d720cd427df8304ea1d223c20c0a1d847706be 100644 (file)
@@ -97,6 +97,7 @@ struct DOHFrontend
 
   HTTPVersionStats d_http1Stats;
   HTTPVersionStats d_http2Stats;
+  uint32_t d_internalPipeBufferSize{0};
   bool d_sendCacheControlHeaders{true};
   bool d_trustForwardedForHeader{false};
 
@@ -175,20 +176,30 @@ struct DOHUnit
   void release()
   {
     if (--d_refcnt == 0) {
+      if (self) {
+        *self = nullptr;
+      }
+
       delete this;
     }
   }
 
+  std::vector<std::pair<std::string, std::string>> headers;
   std::string query;
   std::string response;
+  std::string sni;
+  std::string path;
+  std::string scheme;
+  std::string host;
   ComboAddress remote;
   ComboAddress dest;
   st_h2o_req_t* req{nullptr};
   DOHUnit** self{nullptr};
+  DOHServerConfig* dsc{nullptr};
   std::string contentType;
   std::atomic<uint64_t> d_refcnt{1};
+  size_t query_at{0};
   int rsock;
-  uint16_t qtype;
   /* the status_code is set from
      processDOHQuery() (which is executed in
      the DOH client thread) so that the correct
index 4aef4a2f32fae33575989240a62b494720fa5abc..5614f9c212a95af75a4891684fecec5941b42b8b 100644 (file)
@@ -133,6 +133,7 @@ string DLPurgeHandler(const vector<string>&parts, Utility::pid_t ppid)
 
   if(parts.size()>1) {
     for (vector<string>::const_iterator i=++parts.begin();i<parts.end();++i) {
+      g_log<<Logger::Warning<<"Cache clear request for '"<<*i<<"' received from operator"<<endl;
       ret+=purgeAuthCaches(*i);
       if(!boost::ends_with(*i, "$"))
         DNSSECKeeper::clearCaches(DNSName(*i));
@@ -141,6 +142,7 @@ string DLPurgeHandler(const vector<string>&parts, Utility::pid_t ppid)
     }
   }
   else {
+    g_log<<Logger::Warning<<"Cache clear request received from operator"<<endl;
     ret = purgeAuthCaches();
     DNSSECKeeper::clearAllCaches();
   }
@@ -220,6 +222,7 @@ string DLSettingsHandler(const vector<string>&parts, Utility::pid_t ppid)
       break;
   if(*p) {
     ::arg().set(parts[1])=parts[2];
+    g_log<<Logger::Warning<<"Configuration change for setting '"<<parts[1]<<"' to value '"<<parts[2]<<"' received from operator"<<endl;
     return "done";
   }
   else
@@ -236,8 +239,8 @@ string DLNotifyRetrieveHandler(const vector<string>&parts, Utility::pid_t ppid)
 {
   extern CommunicatorClass Communicator;
   ostringstream os;
-  if(parts.size()!=2)
-    return "syntax: retrieve domain";
+  if(parts.size()!=2 && parts.size()!=3)
+    return "syntax: retrieve domain [ip]";
 
   DNSName domain;
   try {
@@ -246,17 +249,36 @@ string DLNotifyRetrieveHandler(const vector<string>&parts, Utility::pid_t ppid)
     return "Failed to parse domain as valid DNS name";
   }
 
+  ComboAddress master_ip;
+  bool override_master = false;
+  if (parts.size() == 3) {
+    try {
+      master_ip = ComboAddress{parts[2], 53};
+    } catch (...) {
+      return "Invalid master address";
+    }
+    override_master = true;
+  }
+
   DomainInfo di;
   UeberBackend B;
-  if(!B.getDomainInfo(domain, di))
-    return "Domain '"+domain.toString()+"' unknown";
-  
-  if(di.kind != DomainInfo::Slave || di.masters.empty())
+  if(!B.getDomainInfo(domain, di)) {
+    return " Domain '"+domain.toString()+"' unknown";
+  }
+
+  if (override_master) {
+    di.masters.clear();
+    di.masters.push_back(master_ip);
+  }
+
+  if(!override_master && (di.kind != DomainInfo::Slave || di.masters.empty()))
     return "Domain '"+domain.toString()+"' is not a slave domain (or has no master defined)";
 
   shuffle(di.masters.begin(), di.masters.end(), pdns::dns_random_engine());
-  Communicator.addSuckRequest(domain, di.masters.front()); 
-  return "Added retrieval request for '"+domain.toString()+"' from master "+di.masters.front().toLogString();
+  const auto& master = di.masters.front();
+  Communicator.addSuckRequest(domain, master, override_master);
+  g_log<<Logger::Warning<<"Retrieval request for domain '"<<domain<<"' from master '"<<master<<"' received from operator"<<endl;
+  return "Added retrieval request for '"+domain.toLogString()+"' from master "+master.toLogString();
 }
 
 string DLNotifyHostHandler(const vector<string>&parts, Utility::pid_t ppid)
index 54bdda9c6ee549333ef02c8ef939856b5e90dcee..fc3287b474fa3259d413de27dc1f83c38d00b7d7 100644 (file)
@@ -80,6 +80,7 @@ public:
     std::unordered_set<std::string> d_tags;
     std::string d_name;
     Priority d_priority{maximumPriority};
+    bool d_policyOverridesGettag{true};
   };
 
   struct Policy
@@ -139,6 +140,13 @@ public:
       return notSet;
     }
 
+    bool policyOverridesGettag() const {
+      if (d_zoneData) {
+        return d_zoneData->d_policyOverridesGettag;
+      }
+      return true;
+    }
+
     std::vector<DNSRecord> getCustomRecords(const DNSName& qname, uint16_t qtype) const;
     std::vector<DNSRecord> getRecords(const DNSName& qname) const;
 
@@ -191,6 +199,10 @@ public:
     {
       d_zoneData->d_tags = std::move(tags);
     }
+    void setPolicyOverridesGettag(bool flag)
+    {
+      d_zoneData->d_policyOverridesGettag = flag;
+    }
     const std::string& getName() const
     {
       return d_zoneData->d_name;
@@ -262,6 +274,7 @@ public:
     void setPriority(Priority p) {
       d_zoneData->d_priority = p;
     }
+    
   private:
     static DNSName maskToRPZ(const Netmask& nm);
     static bool findExactNamedPolicy(const std::unordered_map<DNSName, DNSFilterEngine::Policy>& polmap, const DNSName& qname, DNSFilterEngine::Policy& pol);
index 968864100abab752fb406af488c02206d5838b51..88d383a1dce00f89a2f556b88c0ea589dfe8ff6b 100644 (file)
@@ -339,9 +339,9 @@ union ComboAddress {
         index = 32 + index;
       }
 
-      uint32_t s_addr = ntohl(sin4.sin_addr.s_addr);
+      uint32_t ls_addr = ntohl(sin4.sin_addr.s_addr);
 
-      return ((s_addr & (1<<index)) != 0x00000000);
+      return ((ls_addr & (1<<index)) != 0x00000000);
     }
     if(isIPv6()) {
       if (index >= 128)
@@ -352,11 +352,11 @@ union ComboAddress {
         index = 128 + index;
       }
 
-      uint8_t *s_addr = (uint8_t*)sin6.sin6_addr.s6_addr;
+      uint8_t *ls_addr = (uint8_t*)sin6.sin6_addr.s6_addr;
       uint8_t byte_idx = index / 8;
       uint8_t bit_idx = index % 8;
 
-      return ((s_addr[15-byte_idx] & (1 << bit_idx)) != 0x00);
+      return ((ls_addr[15-byte_idx] & (1 << bit_idx)) != 0x00);
     }
     return false;
   }
index 0ad22589efb472d2a0ceee7a8a343604fb2c1aeb..bb48caad71aed3395d314f4d002bba3247c8baae 100644 (file)
@@ -36,7 +36,7 @@
 #include <condition_variable>
 #include "ixfr.hh"
 #include "ixfrutils.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
 #include "dns_random.hh"
 #include "sstuff.hh"
 #include "mplexer.hh"
index feec7c33927eb86f217b07502a184c46f3bbb3a5..9d94c821508de0e384c14beff90d75a194fae5fa 100644 (file)
@@ -124,30 +124,31 @@ void writeZoneToDisk(const records_t& records, const DNSName& zone, const std::s
   DNSRecord soa;
   auto serial = getSerialFromRecords(records, soa);
   string fname=directory +"/"+std::to_string(serial);
-  FILE* fp=fopen((fname+".partial").c_str(), "w");
-  if(!fp)
+  auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen((fname+".partial").c_str(), "w"), fclose);
+  if (!fp) {
     throw runtime_error("Unable to open file '"+fname+".partial' for writing: "+stringerror());
+  }
 
   records_t soarecord;
   soarecord.insert(soa);
-  if(fprintf(fp, "$ORIGIN %s\n", zone.toString().c_str()) < 0) {
+  if (fprintf(fp.get(), "$ORIGIN %s\n", zone.toString().c_str()) < 0) {
     string error = "Error writing to zone file for " + zone.toLogString() + " in file " + fname + ".partial" + ": " + stringerror();
-    fclose(fp);
+    fp.reset();
     unlink((fname+".partial").c_str());
     throw std::runtime_error(error);
   }
 
   try {
-    writeRecords(fp, soarecord);
-    writeRecords(fp, records);
-    writeRecords(fp, soarecord);
+    writeRecords(fp.get(), soarecord);
+    writeRecords(fp.get(), records);
+    writeRecords(fp.get(), soarecord);
   } catch (runtime_error &e) {
-    fclose(fp);
+    fp.reset();
     unlink((fname+".partial").c_str());
     throw runtime_error("Error closing zone file for " + zone.toLogString() + " in file " + fname + ".partial" + ": " + e.what());
   }
 
-  if(fclose(fp) != 0) {
+  if (fclose(fp.release()) != 0) {
     string error = "Error closing zone file for " + zone.toLogString() + " in file " + fname + ".partial" + ": " + stringerror();
     unlink((fname+".partial").c_str());
     throw std::runtime_error(error);
index b68e884a8697c7a4e2c875432b5e9351696e7638..3b48bee69e76cbc04e9fb44aae48807d8a3e6783 100644 (file)
@@ -34,7 +34,7 @@
 #include "dns_random.hh"
 #include "gss_context.hh"
 #include <boost/multi_index_container.hpp>
-#include "resolver.hh"
+#include "axfr-retriever.hh"
 #include <fstream>
 #include "ixfr.hh"
 #include "ixfrutils.hh"
index ede5d1b038e5b55ab9c4cbe5132c35879e6b3d0e..90c527626f0892c10c59a58e2a80e4bf7576b467 100644 (file)
@@ -117,21 +117,27 @@ bool AuthLua4::axfrfilter(const ComboAddress& remote, const DNSName& zone, const
 
   const auto& rows = std::get<1>(ret);
 
-  for(const auto& row: rows) {
-    DNSResourceRecord rec;
-    for(const auto& col: row.second) {
-      if (col.first == "qtype")
-        rec.qtype = QType(boost::get<unsigned int>(col.second));
-      else if (col.first == "qname")
-        rec.qname = DNSName(boost::get<std::string>(col.second)).makeLowerCase();
-      else if (col.first == "ttl")
-        rec.ttl = boost::get<unsigned int>(col.second);
-      else if (col.first == "content")
-        rec.setContent(boost::get<std::string>(col.second));
-      else
-        throw PDNSException("Cannot understand "+col.first+" in axfr filter response on row "+std::to_string(row.first));
+  try {
+    for(const auto& row: rows) {
+      DNSResourceRecord rec;
+
+      const auto& map = row.second;
+      rec.qtype = QType(boost::get<unsigned int>(map.at("qtype")));
+      rec.qname = DNSName(boost::get<std::string>(map.at("qname")));
+      rec.qname.makeUsLowerCase();
+      if (map.count("ttl")) {
+        rec.ttl = boost::get<unsigned int>(map.at("ttl"));
+      }
+      rec.setContent(boost::get<std::string>(map.at("content")));
+
+      out.push_back(rec);
     }
-    out.push_back(rec);
+  }
+  catch (const std::exception& e) {
+    throw PDNSException("Cannot understand axfr filter response: " + std::string(e.what()));
+  }
+  catch (const PDNSException& e) {
+    throw PDNSException("Cannot understand axfr filter response: " + e.reason);
   }
 
   return true;
index 163f4687a0504aed2270e106a1d0cc3c2b7afc5f..bce88d14182ce608192dead4f7ef7504bfbab642 100644 (file)
@@ -174,7 +174,7 @@ void BaseLua4::prepareContext() {
   d_lw->registerFunction("match", (bool (NetmaskGroup::*)(const ComboAddress&) const)&NetmaskGroup::match);
 
   // DNSRecord
-  d_lw->writeFunction("newDR", [](const DNSName &name, const std::string &type, unsigned int ttl, const std::string &content, int place){ QType qtype; qtype = type; auto dr = DNSRecord(); dr.d_name = name; dr.d_type = qtype.getCode(); dr.d_ttl = ttl; dr.d_content = shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(dr.d_type, 1, content)); dr.d_place = static_cast<DNSResourceRecord::Place>(place); return dr; });
+  d_lw->writeFunction("newDR", [](const DNSName &name, const std::string &type, unsigned int ttl, const std::string &content, int place){ QType qtype; qtype = type; auto dr = DNSRecord(); dr.d_name = name; dr.d_type = qtype.getCode(); dr.d_ttl = ttl; dr.d_content = shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(dr.d_type, QClass::IN, content)); dr.d_place = static_cast<DNSResourceRecord::Place>(place); return dr; });
   d_lw->registerMember("name", &DNSRecord::d_name);
   d_lw->registerMember("type", &DNSRecord::d_type);
   d_lw->registerMember("ttl", &DNSRecord::d_ttl);
index 50bbf48e7422c2fb9a5c2bdf5a185c7005d7e175..9c7e5dcd86f79326a6dd78616564ea369b91338f 100644 (file)
@@ -606,21 +606,47 @@ static void setupLuaRecords()
       return std::string("error");
     });
   lua.writeFunction("createForward", []() {
+      static string allZerosIP("0.0.0.0");
       DNSName rel=s_lua_record_ctx->qname.makeRelative(s_lua_record_ctx->zone);
+      // parts is something like ["1", "2", "3", "4", "static"] or
+      // ["1", "2", "3", "4"] or ["ip40414243", "ip-addresses", ...]
       auto parts = rel.getRawLabels();
-      if(parts.size()==4)
-        return parts[0]+"."+parts[1]+"."+parts[2]+"."+parts[3];
-      if(parts.size()==1) {
+      // Yes, this still breaks if an 1-2-3-4.XXXX is nested too deeply...
+      if(parts.size()>=4) {
+        try {
+          ComboAddress ca(parts[0]+"."+parts[1]+"."+parts[2]+"."+parts[3]);
+          return ca.toString();
+        } catch (const PDNSException &e) {
+          return allZerosIP;
+        }
+      } else if (parts.size() >= 1) {
         // either hex string, or 12-13-14-15
-        //        cout<<parts[0]<<endl;
+        vector<string> ip_parts;
+        stringtok(ip_parts, parts[0], "-");
         unsigned int x1, x2, x3, x4;
-        if(sscanf(parts[0].c_str()+2, "%02x%02x%02x%02x", &x1, &x2, &x3, &x4)==4) {
+        if (ip_parts.size() >= 4) {
+          // 1-2-3-4 with any prefix (e.g. ip-foo-bar-1-2-3-4)
+          string ret;
+          for (size_t n=4; n > 0; n--) {
+            auto octet = ip_parts[ip_parts.size() - n];
+            try {
+              auto octetVal = std::stol(octet);
+              if (octetVal >= 0 && octetVal <= 255) {
+                ret += ip_parts.at(ip_parts.size() - n) + ".";
+              } else {
+                return allZerosIP;
+              }
+            } catch (const std::exception &e) {
+              return allZerosIP;
+            }
+          }
+          ret.resize(ret.size() - 1); // remove trailing dot after last octet
+          return ret;
+        } else if(parts[0].length() == 10 && sscanf(parts[0].c_str()+2, "%02x%02x%02x%02x", &x1, &x2, &x3, &x4)==4) {
           return std::to_string(x1)+"."+std::to_string(x2)+"."+std::to_string(x3)+"."+std::to_string(x4);
         }
-
-
       }
-      return std::string("0.0.0.0");
+      return allZerosIP;
     });
 
   lua.writeFunction("createForward6", []() {
@@ -660,12 +686,12 @@ static void setupLuaRecords()
         for(int i=0; i<8; ++i) {
           if(i)
             together+=":";
-          string quad;
+          string lquad;
           for(int j=0; j <4; ++j) {
-            quad.append(1, labels[31-i*4-j][0]);
+            lquad.append(1, labels[31-i*4-j][0]);
             together += labels[31-i*4-j][0];
           }
-          quads.push_back(quad);
+          quads.push_back(lquad);
         }
         ComboAddress ip6(together,0);
 
@@ -685,8 +711,8 @@ static void setupLuaRecords()
           fmt % labels[i];
         fmt % dashed;
 
-        for(const auto& quad : quads)
-          fmt % quad;
+        for(const auto& lquad : quads)
+          fmt % lquad;
 
         return fmt.str();
       }
index da82ace25efce4a53ec62141ee5c3ef225378aef..79cc6d48fc56dfdaba1612d6a890250656f98de7 100644 (file)
@@ -133,7 +133,7 @@ void RecursorLua4::DNSQuestion::addRecord(uint16_t type, const std::string& cont
   dr.d_ttl=ttl.get_value_or(3600);
   dr.d_type = type;
   dr.d_place = place;
-  dr.d_content = DNSRecordContent::mastermake(type, 1, content);
+  dr.d_content = DNSRecordContent::mastermake(type, QClass::IN, content);
   records.push_back(dr);
 }
 
@@ -263,7 +263,7 @@ void RecursorLua4::postPrepareContext()
   d_lw->registerFunction<const ProxyProtocolValue, std::string()>("getContent", [](const ProxyProtocolValue& value) { return value.content; });
   d_lw->registerFunction<const ProxyProtocolValue, uint8_t()>("getType", [](const ProxyProtocolValue& value) { return value.type; });
 
-  d_lw->registerFunction<void(DNSRecord::*)(const std::string&)>("changeContent", [](DNSRecord& dr, const std::string& newContent) { dr.d_content = DNSRecordContent::mastermake(dr.d_type, 1, newContent); });
+  d_lw->registerFunction<void(DNSRecord::*)(const std::string&)>("changeContent", [](DNSRecord& dr, const std::string& newContent) { dr.d_content = DNSRecordContent::mastermake(dr.d_type, QClass::IN, newContent); });
   d_lw->registerFunction("addAnswer", &DNSQuestion::addAnswer);
   d_lw->registerFunction("addRecord", &DNSQuestion::addRecord);
   d_lw->registerFunction("getRecords", &DNSQuestion::getRecords);
@@ -335,10 +335,10 @@ void RecursorLua4::postPrepareContext()
     d_pd.push_back({n.first, n.second});
 
   d_pd.push_back({"validationstates", in_t{
-        {"Indeterminate", Indeterminate },
-        {"Bogus", Bogus },
-        {"Insecure", Insecure },
-        {"Secure", Secure },
+        {"Indeterminate", static_cast<unsigned int>(vState::Indeterminate) },
+        {"Bogus", static_cast<unsigned int>(vState::Bogus) },
+        {"Insecure", static_cast<unsigned int>(vState::Insecure) },
+        {"Secure", static_cast<unsigned int>(vState::Secure) },
   }});
 
   d_pd.push_back({"now", &g_now});
@@ -487,9 +487,9 @@ unsigned int RecursorLua4::gettag(const ComboAddress& remote, const Netmask& edn
       deviceName = *deviceNameret;
     }
 
-    const auto routingTagret = std::get<6>(ret);
-    if (routingTagret) {
-      routingTag = *routingTagret;
+    const auto routingTarget = std::get<6>(ret);
+    if (routingTarget) {
+      routingTag = *routingTarget;
     }
 
     return std::get<0>(ret);
index 570d3fd3e45476411ecf7373244acb386213d94d..08bfefe0f6246127bce92e4e4dc473821f01b71b 100644 (file)
@@ -81,7 +81,7 @@ public:
     std::string requestorId;
     std::string deviceId;
     std::string deviceName;
-    vState validationState{Indeterminate};
+    vState validationState{vState::Indeterminate};
     bool& variable;
     bool& wantsRPZ;
     bool& logResponse;
index 2fb111fde1a878a16f858e184c80cb53f06089fd..cc40ae9b66ada023af6681ff14f11f7359864bf8 100644 (file)
@@ -47,6 +47,7 @@
 #include <boost/algorithm/string.hpp>
 #include "validate-recursor.hh"
 #include "ednssubnet.hh"
+#include "query-local-address.hh"
 
 #ifdef HAVE_PROTOBUF
 
@@ -55,6 +56,8 @@
 #ifdef HAVE_FSTRM
 #include "rec-dnstap.hh"
 #include "fstrm_logger.hh"
+
+
 bool g_syslog;
 
 static bool isEnabledForQueries(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
@@ -315,7 +318,7 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d
       Socket s(ip.sin4.sin_family, SOCK_STREAM);
 
       s.setNonBlocking();
-      ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0);
+      ComboAddress local = pdns::getQueryLocalAddress(ip.sin4.sin_family, 0);
 
       s.bind(local);
         
index 7693cec963710379955e84c3ffa791527c24272f..9e0f9eb561e676b2f078347182ab638dfcc7d405 100644 (file)
@@ -40,6 +40,7 @@
 #include "packetcache.hh"
 #include "base64.hh"
 #include "namespaces.hh"
+#include "query-local-address.hh"
 
 
 void CommunicatorClass::queueNotifyDomain(const DomainInfo& di, UeberBackend* B)
@@ -214,7 +215,7 @@ time_t CommunicatorClass::doNotifications(PacketHandler *P)
         ComboAddress remote(ip, 53); // default to 53
         if((d_nsock6 < 0 && remote.sin4.sin_family == AF_INET6) ||
            (d_nsock4 < 0 && remote.sin4.sin_family == AF_INET)) {
-             g_log<<Logger::Warning<<"Unable to notify "<<remote.toStringWithPort()<<" for domain '"<<domain<<"', address family is disabled. Is query-local-address"<<(remote.sin4.sin_family == AF_INET ? "" : "6")<<" unset?"<<endl;
+             g_log<<Logger::Warning<<"Unable to notify "<<remote.toStringWithPort()<<" for domain '"<<domain<<"', address family is disabled. Is an IPv"<<(remote.sin4.sin_family == AF_INET ? "4" : "6")<<" address set in query-local-address?"<<endl;
              d_nq.removeIf(remote.toStringWithPort(), id, domain); // Remove, we'll never be able to notify
              continue; // don't try to notify what we can't!
         }
@@ -299,13 +300,13 @@ bool CommunicatorClass::justNotified(const DNSName &domain, const string &ip)
 
 void CommunicatorClass::makeNotifySockets()
 {
-  if(!::arg()["query-local-address"].empty()) {
-    d_nsock4 = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind"));
+  if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+    d_nsock4 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind"));
   } else {
     d_nsock4 = -1;
   }
-  if(!::arg()["query-local-address6"].empty()) {
-    d_nsock6 = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind"));
+  if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
+    d_nsock6 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind"));
   } else {
     d_nsock6 = -1;
   }
index fb253e5f1c40d7e5563e36b827c2f201818c664b..ef743a8e75172c25d6671655334870726aab9774 100644 (file)
@@ -57,6 +57,7 @@
 #include <sys/types.h>
 #include <pwd.h>
 #include <grp.h>
+#include <limits.h>
 #ifdef __FreeBSD__
 #  include <pthread_np.h>
 #endif
@@ -1560,3 +1561,35 @@ DNSName reverseNameFromIP(const ComboAddress& ip)
 
   throw std::runtime_error("Calling reverseNameFromIP() for an address which is neither an IPv4 nor an IPv6");
 }
+
+static size_t getMaxHostNameSize()
+{
+#if defined(HOST_NAME_MAX)
+  return HOST_NAME_MAX;
+#endif
+
+#if defined(_SC_HOST_NAME_MAX)
+  auto tmp = sysconf(_SC_HOST_NAME_MAX);
+  if (tmp != -1) {
+    return tmp;
+  }
+#endif
+
+  /* _POSIX_HOST_NAME_MAX */
+  return 255;
+}
+
+std::string getCarbonHostName()
+{
+  std::string hostname;
+  hostname.resize(getMaxHostNameSize() + 1, 0);
+
+  if (gethostname(const_cast<char*>(hostname.c_str()), hostname.size()) != 0) {
+    throw std::runtime_error(stringerror());
+  }
+
+  boost::replace_all(hostname, ".", "_");
+  hostname.resize(strlen(hostname.c_str()));
+
+  return hostname;
+}
index 1db0f5ce31c081005a4cc51ca15ad3b912a3af9c..a1bc98ba29ec5b506ad4e6d3db2a4220c3527087 100644 (file)
@@ -605,3 +605,5 @@ int mapThreadToCPUList(pthread_t tid, const std::set<int>& cpus);
 std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath);
 
 DNSName reverseNameFromIP(const ComboAddress& ip);
+
+std::string getCarbonHostName();
index 5fc8110a439a76d6d5bf863153414917a860a8f5..983f39fb6b3c995c73da4c2a3e911cadd650cfa2 100644 (file)
@@ -46,7 +46,7 @@
 static std::vector<std::mutex> openssllocks;
 
 extern "C" {
-void openssl_pthreads_locking_callback(int mode, int type, const char *file, int line)
+static void openssl_pthreads_locking_callback(int mode, int type, const char *file, int line)
 {
   if (mode & CRYPTO_LOCK) {
     openssllocks.at(type).lock();
@@ -56,7 +56,7 @@ void openssl_pthreads_locking_callback(int mode, int type, const char *file, int
   }
 }
 
-unsigned long openssl_pthreads_id_callback()
+static unsigned long openssl_pthreads_id_callback(void)
 {
   return (unsigned long)pthread_self();
 }
index 7fd616d9668ac58dfe753d41f3759222634d72f1..2eec1d1b6268b64dd0d606882243b86ab792f469 100644 (file)
@@ -444,7 +444,7 @@ int PacketHandler::doAdditionalProcessingAndDropAA(DNSPacket& p, std::unique_ptr
 {
   DNSZoneRecord rr;
   SOAData sd;
-  sd.db=0;
+  sd.db = nullptr;
 
   if(p.qtype.getCode()!=QType::AXFR) { // this packet needs additional processing
     // we now have a copy, push_back on packet might reallocate!
@@ -853,7 +853,7 @@ int PacketHandler::trySuperMasterSynchronous(const DNSPacket& p, const DNSName&
     return RCode::Refused;
   }
   try {
-    db->createSlaveDomain(p.getRemote().toString(), p.qdomain, nameserver, account);
+    db->createSlaveDomain(remote.toString(), p.qdomain, nameserver, account);
     if (tsigkeyname.empty() == false) {
       vector<string> meta;
       meta.push_back(tsigkeyname.toStringNoDot());
index 1ce670228ab1eb346d927c62480e7c5d04e0ccc1..76f08f91d30bcbc68ca72787608955f131cca72d 100644 (file)
@@ -7,6 +7,7 @@ After=network-online.target mysqld.service postgresql.service slapd.service mari
 
 [Service]
 ExecStart=@sbindir@/pdns_server --guardian=no --daemon=no --disable-syslog --log-timestamp=no --write-pid=no
+SyslogIdentifier=pdns_server
 User=@service_user@
 Group=@service_group@
 Type=notify
index 431ae994cef426ffd238680f8f5525f10e2da1f4..7b461701942c904e91f60965e07b77e9dca75622 100644 (file)
@@ -94,6 +94,7 @@
 #ifdef NOD_ENABLED
 #include "nod.hh"
 #endif /* NOD_ENABLED */
+#include "query-local-address.hh"
 
 #include "rec-protobuf.hh"
 #include "rec-snmp.hh"
@@ -172,6 +173,7 @@ struct RecThreadInfo
   std::thread thread;
   MT_t* mt{nullptr};
   uint64_t numberOfDistributedQueries{0};
+  int exitCode{0};
   /* handle the web server, carbon, statistics and the control channel */
   bool isHandler{false};
   /* accept incoming queries (and distributes them to the workers if pdns-distributes-queries is set) */
@@ -191,10 +193,8 @@ static deferredAdd_t g_deferredAdds;
 typedef vector<int> tcpListenSockets_t;
 typedef map<int, ComboAddress> listenSocketsAddresses_t; // is shared across all threads right now
 
-static const ComboAddress g_local4("0.0.0.0"), g_local6("::");
 static listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now
 static set<int> g_fromtosockets; // listen sockets that use 'sendfromto()' mechanism
-static vector<ComboAddress> g_localQueryAddresses4, g_localQueryAddresses6;
 static AtomicCounter counter;
 static std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup
 static std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this
@@ -463,7 +463,7 @@ string GenUDPQueryResponse(const ComboAddress& dest, const string& query)
 {
   Socket s(dest.sin4.sin_family, SOCK_DGRAM);
   s.setNonBlocking();
-  ComboAddress local = getQueryLocalAddress(dest.sin4.sin_family, 0);
+  ComboAddress local = pdns::getQueryLocalAddress(dest.sin4.sin_family, 0);
   
   s.bind(local);
   s.connect(dest);
@@ -489,28 +489,6 @@ string GenUDPQueryResponse(const ComboAddress& dest, const string& query)
   return data;
 }
 
-//! pick a random query local address
-ComboAddress getQueryLocalAddress(int family, uint16_t port)
-{
-  ComboAddress ret;
-  if(family==AF_INET) {
-    if(g_localQueryAddresses4.empty())
-      ret = g_local4;
-    else
-      ret = g_localQueryAddresses4[dns_random(g_localQueryAddresses4.size())];
-    ret.sin4.sin_port = htons(port);
-  }
-  else {
-    if(g_localQueryAddresses6.empty())
-      ret = g_local6;
-    else
-      ret = g_localQueryAddresses6[dns_random(g_localQueryAddresses6.size())];
-
-    ret.sin6.sin6_port = htons(port);
-  }
-  return ret;
-}
-
 static void handleUDPServerResponse(int fd, FDMultiplexer::funcparam_t&);
 
 static void setSocketBuffer(int fd, int optname, uint32_t size)
@@ -626,7 +604,7 @@ private:
         while (s_avoidUdpSourcePorts.count(port));
       }
 
-      sin=getQueryLocalAddress(family, port); // does htons for us
+      sin=pdns::getQueryLocalAddress(family, port); // does htons for us
 
       if (::bind(ret, (struct sockaddr *)&sin, sin.getSocklen()) >= 0)
         break;
@@ -1061,8 +1039,6 @@ static bool checkFrameStreamExport(LocalStateHolder<LuaConfigItems>& luaconfsLoc
 #ifdef NOD_ENABLED
 static bool nodCheckNewDomain(const DNSName& dname)
 {
-  static const QType qt(QType::A);
-  static const uint16_t qc(QClass::IN);
   bool ret = false;
   // First check the (sub)domain isn't whitelisted for NOD purposes
   if (!g_nodDomainWL.check(dname)) {
@@ -1070,14 +1046,7 @@ static bool nodCheckNewDomain(const DNSName& dname)
     if (t_nodDBp && t_nodDBp->isNewDomain(dname)) {
       if (g_nodLog) {
         // This should probably log to a dedicated log file
-        g_log<<Logger::Notice<<"Newly observed domain nod="<<dname.toLogString()<<endl;
-      }
-      if (!(g_nodLookupDomain.isRoot())) {
-        // Send a DNS A query to <domain>.g_nodLookupDomain
-        DNSName qname = dname;
-        vector<DNSRecord> dummy;
-        qname += g_nodLookupDomain;
-        directResolve(qname, qt, qc, dummy);
+        g_log<<Logger::Notice<<"Newly observed domain nod="<<dname<<endl;
       }
       ret = true;
     }
@@ -1085,6 +1054,18 @@ static bool nodCheckNewDomain(const DNSName& dname)
   return ret;
 }
 
+static void sendNODLookup(const DNSName& dname)
+{
+  if (!(g_nodLookupDomain.isRoot())) {
+    // Send a DNS A query to <domain>.g_nodLookupDomain
+    static const QType qt(QType::A);
+    static const uint16_t qc(QClass::IN);
+    DNSName qname = dname + g_nodLookupDomain;
+    vector<DNSRecord> dummy;
+    directResolve(qname, qt, qc, dummy);
+  }
+}
+
 static bool udrCheckUniqueDNSRecord(const DNSName& dname, uint16_t qtype, const DNSRecord& record)
 {
   bool ret = false;
@@ -1096,7 +1077,7 @@ static bool udrCheckUniqueDNSRecord(const DNSName& dname, uint16_t qtype, const
     if (t_udrDBp && t_udrDBp->isUniqueResponse(ss.str())) {
       if (g_udrLog) {  
         // This should also probably log to a dedicated file. 
-        g_log<<Logger::Notice<<"Unique response observed: qname="<<dname.toLogString()<<" qtype="<<QType(qtype).getName()<< " rrtype=" << QType(record.d_type).getName() << " rrname=" << record.d_name.toLogString() << " rrcontent=" << record.d_content->getZoneRepresentation() << endl;
+        g_log<<Logger::Notice<<"Unique response observed: qname="<<dname<<" qtype="<<QType(qtype).getName()<< " rrtype=" << QType(record.d_type).getName() << " rrname=" << record.d_name << " rrcontent=" << record.d_content->getZoneRepresentation() << endl;
       }
       ret = true;
     }
@@ -1257,7 +1238,23 @@ static PolicyResult handlePolicyHit(const DNSFilterEngine::Policy& appliedPolicy
       auto spoofed = appliedPolicy.getCustomRecords(dc->d_mdp.d_qname, dc->d_mdp.d_qtype);
       for (auto& dr : spoofed) {
         ret.push_back(dr);
-        handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret);
+        try {
+          handleRPZCustom(dr, QType(dc->d_mdp.d_qtype), sr, res, ret);
+        }
+        catch (const ImmediateServFailException& e) {
+          if (g_logCommonErrors) {
+            g_log << Logger::Notice << "Sending SERVFAIL to " << dc->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '"<<dc->d_mdp.d_qname<<"' because: "<<e.reason<<endl;
+          }
+          res = RCode::ServFail;
+          break;
+        }
+        catch (const PolicyHitException& e) {
+          if (g_logCommonErrors) {
+            g_log << Logger::Notice << "Sending SERVFAIL to " << dc->getRemote() << " during resolve of the custom filter policy '" << appliedPolicy.getName() << "' while resolving '"<<dc->d_mdp.d_qname<<"' because another RPZ policy was hit"<<endl;
+          }
+          res = RCode::ServFail;
+          break;
+        }
       }
     }
     return PolicyResult::HaveAnswer;
@@ -1451,16 +1448,6 @@ static void startDoResolve(void *p)
       sr.setCacheOnly();
     }
 
-    if (dc->d_rcode != boost::none) {
-      /* we have a response ready to go, most likely from gettag_ffi */
-      ret = std::move(dc->d_records);
-      res = *dc->d_rcode;
-      if (res == RCode::NoError && dc->d_followCNAMERecords) {
-        res = followCNAMERecords(ret, QType(dc->d_mdp.d_qtype));
-      }
-      goto haveAnswer;
-    }
-
     if (t_pdl) {
       t_pdl->prerpz(dq, res);
     }
@@ -1472,6 +1459,21 @@ static void startDoResolve(void *p)
       }
     }
 
+    // If we are doing RPZ and a policy was matched, it normally takes precedence over an answer from gettag.
+    // So process the gettag_ffi answer only if no RPZ action was matched or the policy indicates gettag should
+    // have precedence.
+    if (!wantsRPZ || !appliedPolicy.policyOverridesGettag() || appliedPolicy.d_type == DNSFilterEngine::PolicyType::None) {
+      if (dc->d_rcode != boost::none) {
+        /* we have a response ready to go, most likely from gettag_ffi */
+        ret = std::move(dc->d_records);
+        res = *dc->d_rcode;
+        if (res == RCode::NoError && dc->d_followCNAMERecords) {
+          res = followCNAMERecords(ret, QType(dc->d_mdp.d_qtype));
+        }
+        goto haveAnswer;
+      }
+    }
+
     // if there is a RecursorLua active, and it 'took' the query in preResolve, we don't launch beginResolve
     if (!t_pdl || !t_pdl->preresolve(dq, res)) {
 
@@ -1536,7 +1538,7 @@ static void startDoResolve(void *p)
         }
       }
 
-      if (t_pdl || (g_dns64Prefix && dq.qtype == QType::AAAA && dq.validationState != Bogus)) {
+      if (t_pdl || (g_dns64Prefix && dq.qtype == QType::AAAA && dq.validationState != vState::Bogus)) {
         if (res == RCode::NoError) {
           auto i = ret.cbegin();
           for(; i!= ret.cend(); ++i) {
@@ -1550,7 +1552,7 @@ static void startDoResolve(void *p)
             if (t_pdl && t_pdl->nodata(dq, res)) {
               shouldNotValidate = true;
             }
-            else if (g_dns64Prefix && dq.qtype == QType::AAAA && dq.validationState != Bogus) {
+            else if (g_dns64Prefix && dq.qtype == QType::AAAA && dq.validationState != vState::Bogus) {
               res = getFakeAAAARecords(dq.qname, *g_dns64Prefix, ret);
               shouldNotValidate = true;
             }
@@ -1612,7 +1614,7 @@ static void startDoResolve(void *p)
 
           auto state = sr.getValidationState();
 
-          if(state == Secure) {
+          if(state == vState::Secure) {
             if(sr.doLog()) {
               g_log<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->getRemote()<<" validates correctly"<<endl;
             }
@@ -1621,14 +1623,14 @@ static void startDoResolve(void *p)
             if (dc->d_mdp.d_header.ad || DNSSECOK)
               pw.getHeader()->ad=1;
           }
-          else if(state == Insecure) {
+          else if(state == vState::Insecure) {
             if(sr.doLog()) {
               g_log<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->getRemote()<<" validates as Insecure"<<endl;
             }
             
             pw.getHeader()->ad=0;
           }
-          else if(state == Bogus) {
+          else if(state == vState::Bogus) {
             if(t_bogusremotes)
               t_bogusremotes->push_back(dc->d_source);
             if(t_bogusqueryring)
@@ -1742,8 +1744,9 @@ static void startDoResolve(void *p)
 #ifdef NOD_ENABLED
     bool nod = false;
     if (g_nodEnabled) {
-      if (nodCheckNewDomain(dc->d_mdp.d_qname))
+      if (nodCheckNewDomain(dc->d_mdp.d_qname)) {
         nod = true;
+      }
     }
 #endif /* NOD_ENABLED */
 #ifdef HAVE_PROTOBUF
@@ -1835,16 +1838,16 @@ static void startDoResolve(void *p)
       int wret=Utility::writev(dc->d_socket, iov, 2);
       bool hadError=true;
 
-      if(wret == 0)
-        g_log<<Logger::Error<<"EOF writing TCP answer to "<<dc->getRemote()<<endl;
-      else if(wret < 0 ) {
+      if (wret == 0) {
+        g_log<<Logger::Warning<<"EOF writing TCP answer to "<<dc->getRemote()<<endl;
+      } else if (wret < 0 ) {
         int err = errno;
-        g_log << Logger::Error << "Error writing TCP answer to " << dc->getRemote() << ": " << strerror(err) << endl;
-      } else if((unsigned int)wret != 2 + packet.size())
-        g_log<<Logger::Error<<"Oops, partial answer sent to "<<dc->getRemote()<<" for "<<dc->d_mdp.d_qname<<" (size="<< (2 + packet.size()) <<", sent "<<wret<<")"<<endl;
-      else
+        g_log << Logger::Warning << "Error writing TCP answer to " << dc->getRemote() << ": " << strerror(err) << endl;
+      } else if ((unsigned int)wret != 2 + packet.size()) {
+        g_log<<Logger::Warning<<"Oops, partial answer sent to "<<dc->getRemote()<<" for "<<dc->d_mdp.d_qname<<" (size="<< (2 + packet.size()) <<", sent "<<wret<<")"<<endl;
+      } else {
         hadError=false;
-
+      }
       // update tcp connection status, closing if needed and doing the fd multiplexer accounting
       if  (dc->d_tcpConnection->d_requestsInFlight > 0) {
         dc->d_tcpConnection->d_requestsInFlight--;
@@ -1893,19 +1896,18 @@ static void startDoResolve(void *p)
         }
       }
     }
+
     float spent=makeFloat(sr.getNow()-dc->d_now);
-    if(!g_quiet) {
+    if (!g_quiet) {
       g_log<<Logger::Error<<t_id<<" ["<<MT->getTid()<<"/"<<MT->numProcesses()<<"] answer to "<<(dc->d_mdp.d_header.rd?"":"non-rd ")<<"question '"<<dc->d_mdp.d_qname<<"|"<<DNSRecordContent::NumberToType(dc->d_mdp.d_qtype);
       g_log<<"': "<<ntohs(pw.getHeader()->ancount)<<" answers, "<<ntohs(pw.getHeader()->arcount)<<" additional, took "<<sr.d_outqueries<<" packets, "<<
        sr.d_totUsec/1000.0<<" netw ms, "<< spent*1000.0<<" tot ms, "<<
        sr.d_throttledqueries<<" throttled, "<<sr.d_timeouts<<" timeouts, "<<sr.d_tcpoutqueries<<" tcp connections, rcode="<< res;
 
       if(!shouldNotValidate && sr.isDNSSECValidationRequested()) {
-       g_log<< ", dnssec="<<vStates[sr.getValidationState()];
+       g_log<< ", dnssec="<<sr.getValidationState();
       }
-       
       g_log<<endl;
-
     }
 
     if (sr.d_outqueries || sr.d_authzonequeries) {
@@ -1952,6 +1954,13 @@ static void startDoResolve(void *p)
       newLat=ourtime*1000; // usec
       g_stats.avgLatencyOursUsec=(1-1.0/g_latencyStatSize)*g_stats.avgLatencyOursUsec + (float)newLat/g_latencyStatSize;
     }
+
+#ifdef NOD_ENABLED
+    if (nod) {
+      sendNODLookup(dc->d_mdp.d_qname);
+    }
+#endif /* NOD_ENABLED */
+
     //    cout<<dc->d_mdp.d_qname<<"\t"<<MT->getUsec()<<"\t"<<sr.d_outqueries<<endl;
   }
   catch(PDNSException &ae) {
@@ -2584,7 +2593,7 @@ static string* doProcessUDPQuestion(const std::string& question, const ComboAddr
     }
 
     if (cacheHit) {
-      if(valState == Bogus) {
+      if(valState == vState::Bogus) {
         if(t_bogusremotes)
           t_bogusremotes->push_back(source);
         if(t_bogusqueryring)
@@ -3191,11 +3200,29 @@ static void houseKeeping(void *)
         last_RC_prune = now.tv_sec;
       }
       // XXX !!! global
-      if(now.tv_sec - last_rootupdate > 7200) {
-        int res = SyncRes::getRootNS(g_now, nullptr);
+      if (now.tv_sec - last_rootupdate > 7200) {
+        int res = SyncRes::getRootNS(g_now, nullptr, 0);
         if (!res) {
           last_rootupdate=now.tv_sec;
-          primeRootNSZones(g_dnssecmode != DNSSECMode::Off);
+          try {
+            primeRootNSZones(g_dnssecmode != DNSSECMode::Off, 0);
+          }
+          catch (const std::exception& e) {
+            g_log<<Logger::Error<<"Exception while priming the root NS zones: "<<e.what()<<endl;
+          }
+          catch (const PDNSException& e) {
+            g_log<<Logger::Error<<"Exception while priming the root NS zones: "<<e.reason<<endl;
+          }
+          catch (const ImmediateServFailException& e) {
+            g_log<<Logger::Error<<"Exception while priming the root NS zones: "<<e.reason<<endl;
+          }
+          catch (const PolicyHitException& e) {
+            g_log<<Logger::Error<<"Policy hit while priming the root NS zones"<<endl;
+          }
+          catch (...)
+          {
+            g_log<<Logger::Error<<"Exception while priming the root NS zones"<<endl;
+          }
         }
       }
 
@@ -3203,22 +3230,22 @@ static void houseKeeping(void *)
        try {
          doSecPoll(&last_secpoll);
        }
-       catch(const std::exception& e)
+       catch (const std::exception& e)
         {
           g_log<<Logger::Error<<"Exception while performing security poll: "<<e.what()<<endl;
         }
-        catch(const PDNSException& e)
+        catch (const PDNSException& e)
         {
           g_log<<Logger::Error<<"Exception while performing security poll: "<<e.reason<<endl;
         }
-        catch(const ImmediateServFailException &e)
+        catch (const ImmediateServFailException &e)
         {
           g_log<<Logger::Error<<"Exception while performing security poll: "<<e.reason<<endl;
         }
-        catch(const PolicyHitException& e) {
+        catch (const PolicyHitException& e) {
           g_log<<Logger::Error<<"Policy hit while performing security poll"<<endl;
         }
-        catch(...)
+        catch (...)
         {
           g_log<<Logger::Error<<"Exception while performing security poll"<<endl;
         }
@@ -3240,14 +3267,20 @@ static void houseKeeping(void *)
         }
       }
     }
-    s_running=false;
+    s_running = false;
+  }
+  catch (const PDNSException& ae)
+  {
+    s_running = false;
+    g_log<<Logger::Error<<"Fatal error in housekeeping thread: "<<ae.reason<<endl;
+    throw;
+  }
+  catch (...)
+  {
+    s_running = false;
+    g_log<<Logger::Error<<"Uncaught exception in housekeeping thread"<<endl;
+    throw;
   }
-  catch(PDNSException& ae)
-    {
-      s_running=false;
-      g_log<<Logger::Error<<"Fatal error in housekeeping thread: "<<ae.reason<<endl;
-      throw;
-    }
 }
 
 static void makeThreadPipes()
@@ -4096,6 +4129,8 @@ static void checkSocketDir(void)
 
 static int serviceMain(int argc, char*argv[])
 {
+  int ret = EXIT_SUCCESS;
+
   g_log.setName(s_programname);
   g_log.disableSyslog(::arg().mustDo("disable-syslog"));
   g_log.setTimestamps(::arg().mustDo("log-timestamp"));
@@ -4114,23 +4149,11 @@ static int serviceMain(int argc, char*argv[])
 
   checkLinuxIPv6Limits();
   try {
-    vector<string> addrs;
-    if(!::arg()["query-local-address6"].empty()) {
-      SyncRes::s_doIPv6=true;
-      g_log<<Logger::Warning<<"Enabling IPv6 transport for outgoing queries"<<endl;
-
-      stringtok(addrs, ::arg()["query-local-address6"], ", ;");
-      for(const string& addr : addrs) {
-        g_localQueryAddresses6.push_back(ComboAddress(addr));
-      }
-    }
-    else {
-      g_log<<Logger::Warning<<"NOT using IPv6 for outgoing queries - set 'query-local-address6=::' to enable"<<endl;
-    }
-    addrs.clear();
-    stringtok(addrs, ::arg()["query-local-address"], ", ;");
-    for(const string& addr : addrs) {
-      g_localQueryAddresses4.push_back(ComboAddress(addr));
+    pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
+    if (!::arg()["query-local-address6"].empty()) {
+      // TODO remove in 4.5.0
+      g_log<<Logger::Warning<<"query-local-address6 is deprecated and will be removed in a future version. Please use query-local-address for IPv6 addresses as well"<<endl;
+      pdns::parseQueryLocalAddress(::arg()["query-local-address6"]);
     }
   }
   catch(std::exception& e) {
@@ -4138,6 +4161,28 @@ static int serviceMain(int argc, char*argv[])
     exit(99);
   }
 
+  if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+    SyncRes::s_doIPv4=true;
+    g_log<<Logger::Warning<<"Enabling IPv4 transport for outgoing queries"<<endl;
+  }
+  else {
+    g_log<<Logger::Warning<<"NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable"<<endl;
+  }
+
+
+  if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
+    SyncRes::s_doIPv6=true;
+    g_log<<Logger::Warning<<"Enabling IPv6 transport for outgoing queries"<<endl;
+  }
+  else {
+    g_log<<Logger::Warning<<"NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable"<<endl;
+  }
+
+  if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) {
+    g_log<<Logger::Error<<"No outgoing addresses configured! Can not continue"<<endl;
+    exit(99);
+  }
+
   // keep this ABOVE loadRecursorLuaConfig!
   if(::arg()["dnssec"]=="off")
     g_dnssecmode=DNSSECMode::Off;
@@ -4236,6 +4281,7 @@ static int serviceMain(int argc, char*argv[])
   SyncRes::s_serverdownthrottletime=::arg().asNum("server-down-throttle-time");
   SyncRes::s_serverID=::arg()["server-id"];
   SyncRes::s_maxqperq=::arg().asNum("max-qperq");
+  SyncRes::s_maxnsaddressqperq=::arg().asNum("max-ns-address-qperq");
   SyncRes::s_maxtotusec=1000*::arg().asNum("max-total-msec");
   SyncRes::s_maxdepth=::arg().asNum("max-recursion-depth");
   SyncRes::s_rootNXTrust = ::arg().mustDo( "root-nx-trust");
@@ -4274,26 +4320,25 @@ static int serviceMain(int argc, char*argv[])
     SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128));
   }
   else {
-    bool found = false;
-    for (const auto& addr : g_localQueryAddresses4) {
-      if (!IsAnyAddress(addr)) {
-        SyncRes::setECSScopeZeroAddress(Netmask(addr, 32));
-        found = true;
-        break;
-      }
+    Netmask nm;
+    bool done = false;
+
+    auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
+    if (addr.sin4.sin_family != 0) {
+      nm = Netmask(addr, 32);
+      done = true;
     }
-    if (!found) {
-      for (const auto& addr : g_localQueryAddresses6) {
-        if (!IsAnyAddress(addr)) {
-          SyncRes::setECSScopeZeroAddress(Netmask(addr, 128));
-          found = true;
-          break;
-        }
-      }
-      if (!found) {
-        SyncRes::setECSScopeZeroAddress(Netmask("127.0.0.1/32"));
+    if (!done) {
+      addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
+      if (addr.sin4.sin_family != 0) {
+        nm = Netmask(addr, 128);
+        done = true;
       }
     }
+    if (!done) {
+      nm = Netmask(ComboAddress("127.0.0.1"), 32);
+    }
+    SyncRes::setECSScopeZeroAddress(nm);
   }
 
   SyncRes::parseEDNSSubnetWhitelist(::arg()["edns-subnet-whitelist"]);
@@ -4607,6 +4652,9 @@ static int serviceMain(int argc, char*argv[])
     recursorThread(currentThreadId++, "worker");
     
     handlerInfos.thread.join();
+    if (handlerInfos.exitCode != 0) {
+      ret = handlerInfos.exitCode;
+    }
   }
   else {
 
@@ -4651,13 +4699,16 @@ static int serviceMain(int argc, char*argv[])
 
     for (auto & ti : s_threadInfos) {
       ti.thread.join();
+      if (ti.exitCode != 0) {
+        ret = ti.exitCode;
+      }
     }
   }
 
 #ifdef HAVE_PROTOBUF
   google::protobuf::ShutdownProtobufLibrary();
 #endif /* HAVE_PROTOBUF */
-  return 0;
+  return ret;
 }
 
 static void* recursorThread(unsigned int n, const string& threadName)
@@ -4674,11 +4725,19 @@ try
   t_allowFrom = g_initialAllowFrom;
   t_udpclientsocks = std::unique_ptr<UDPClientSocks>(new UDPClientSocks());
   t_tcpClientCounts = std::unique_ptr<tcpClientCounts_t>(new tcpClientCounts_t());
-  primeHints();
+
+  if (threadInfo.isHandler) {
+    if (!primeHints()) {
+      threadInfo.exitCode = EXIT_FAILURE;
+      RecursorControlChannel::stop = 1;
+      g_log<<Logger::Critical<<"Priming cache failed, stopping"<<endl;
+      return nullptr;
+    }
+    g_log<<Logger::Warning<<"Done priming cache with root hints"<<endl;
+  }
 
   t_packetCache = std::unique_ptr<RecursorPacketCache>(new RecursorPacketCache());
 
-  g_log<<Logger::Warning<<"Done priming cache with root hints"<<endl;
 
 #ifdef NOD_ENABLED
   if (threadInfo.isWorker)
@@ -4796,7 +4855,9 @@ try
   while (!RecursorControlChannel::stop) {
     while(MT->schedule(&g_now)); // MTasker letting the mthreads do their thing
 
-    if(!(counter%500)) {
+    // Use primes, it avoid not being scheduled in cases where the counter has a regular pattern.
+    // We want to call handler thread often, it gets scheduled about 2 times per second
+    if ((threadInfo.isHandler && counter % 11 == 0) || counter % 499 == 0) {
       MT->makeThread(houseKeeping, 0);
     }
 
@@ -4947,10 +5008,19 @@ int main(int argc, char **argv)
     ::arg().set("socket-group","Group of socket")="";
     ::arg().set("socket-mode", "Permissions for socket")="";
 
-    ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns-recursor when unset and not chrooted" )="";
+    ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns-recursor when unset and not chrooted"
+#ifdef HAVE_SYSTEMD
+      + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")="";
+   auto runtimeDir = getenv("RUNTIME_DIRECTORY");
+   if (runtimeDir != nullptr) {
+     ::arg().set("socket-dir") = runtimeDir;
+   }
+#else
+      )="";
+#endif
     ::arg().set("delegation-only","Which domains we only accept delegations from")="";
     ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
-    ::arg().set("query-local-address6","Source IPv6 address for sending queries. IF UNSET, IPv6 WILL NOT BE USED FOR OUTGOING QUERIES")="";
+    ::arg().set("query-local-address6","DEPRECATED: Use query-local-address for IPv6 as well. Source IPv6 address for sending queries. IF UNSET, IPv6 WILL NOT BE USED FOR OUTGOING QUERIES")="";
     ::arg().set("client-tcp-timeout","Timeout in seconds when talking to TCP clients")="2";
     ::arg().set("max-mthreads", "Maximum number of simultaneous Mtasker threads")="2048";
     ::arg().set("max-tcp-clients","Maximum number of simultaneous TCP clients")="128";
@@ -5012,6 +5082,7 @@ int main(int argc, char **argv)
     ::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size")="1232";
     ::arg().set("minimum-ttl-override", "Set under adverse conditions, a minimum TTL")="0";
     ::arg().set("max-qperq", "Maximum outgoing queries per query")="60";
+    ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query")="10";
     ::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited")="7000";
     ::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited")="40";
     ::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing")="10000";
@@ -5063,7 +5134,7 @@ int main(int argc, char **argv)
     ::arg().setSwitch("qname-minimization", "Use Query Name Minimization")="yes";
     ::arg().setSwitch("nothing-below-nxdomain", "When an NXDOMAIN exists in cache for a name with fewer labels than the qname, send NXDOMAIN without doing a lookup (see RFC 8020)")="dnssec";
     ::arg().set("max-generate-steps", "Maximum number of $GENERATE steps when loading a zone from a file")="0";
-    ::arg().set("cache-shards", "Number of shards in the record cache")="1024";
+    ::arg().set("record-cache-shards", "Number of shards in the record cache")="1024";
 
 #ifdef NOD_ENABLED
     ::arg().set("new-domain-tracking", "Track newly observed domains (i.e. never seen before).")="no";
@@ -5162,7 +5233,7 @@ int main(int argc, char **argv)
       exit(0);
     }
 
-    s_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache(::arg().asNum("cache-shards")));
+    s_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache(::arg().asNum("record-cache-shards")));
 
     Logger::Urgency logUrgency = (Logger::Urgency)::arg().asNum("loglevel");
 
@@ -5174,7 +5245,7 @@ int main(int argc, char **argv)
     g_log.setLoglevel(logUrgency);
     g_log.toConsole(logUrgency);
 
-    serviceMain(argc, argv);
+    ret = serviceMain(argc, argv);
   }
   catch(PDNSException &ae) {
     g_log<<Logger::Error<<"Exception: "<<ae.reason<<endl;
index fd5bcccfe91e13065d3e3e6284bf17e3b7bd0be4..56e180f696296ab28081522445e7dd326f7ff54a 100644 (file)
@@ -2,6 +2,9 @@
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
+
+#include <fcntl.h>
+
 #include "dnsseckeeper.hh"
 #include "dnssecinfra.hh"
 #include "statbag.hh"
@@ -51,6 +54,14 @@ ArgvMap &arg()
   return arg;
 }
 
+static std::string comboAddressVecToString(const std::vector<ComboAddress>& vec) {
+  vector<string> strs;
+  for (const auto& ca : vec) {
+    strs.push_back(ca.toStringWithPortExcept(53));
+  }
+  return boost::join(strs, ",");
+}
+
 static void loadMainConfig(const std::string& configdir)
 {
   ::arg().set("config-dir","Location of configuration directory (pdns.conf)")=configdir;
@@ -355,7 +366,7 @@ static int checkZone(DNSSECKeeper &dk, UeberBackend &B, const DNSName& zone, con
       rr.content = "\""+rr.content+"\"";
 
     try {
-      shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), 1, rr.content));
+      shared_ptr<DNSRecordContent> drc(DNSRecordContent::mastermake(rr.qtype.getCode(), QClass::IN, rr.content));
       string tmp=drc->serialize(rr.qname);
       tmp = drc->getZoneRepresentation(true);
       if (rr.qtype.getCode() != QType::AAAA) {
@@ -1085,7 +1096,7 @@ static int loadZone(DNSName zone, const string& fname) {
   }
   else {
     cerr<<"Creating '"<<zone<<"'"<<endl;
-    B.createDomain(zone);
+    B.createDomain(zone, DomainInfo::Native, vector<ComboAddress>(), "");
 
     if(!B.getDomainInfo(zone, di)) {
       cerr<<"Domain '"<<zone<<"' was not created - perhaps backend ("<<::arg()["launch"]<<") does not support storing new zones."<<endl;
@@ -1128,7 +1139,7 @@ static int createZone(const DNSName &zone, const DNSName& nsname) {
     return EXIT_FAILURE;
   }
   cerr<<"Creating empty zone '"<<zone<<"'"<<endl;
-  B.createDomain(zone);
+  B.createDomain(zone, DomainInfo::Native, vector<ComboAddress>(), "");
   if(!B.getDomainInfo(zone, di)) {
     cerr<<"Domain '"<<zone<<"' was not created!"<<endl;
     return EXIT_FAILURE;
@@ -1170,19 +1181,16 @@ static int createSlaveZone(const vector<string>& cmds) {
     cerr<<"Domain '"<<zone<<"' exists already"<<endl;
     return EXIT_FAILURE;
   }
-  vector<string> masters;
+  vector<ComboAddress> masters;
   for (unsigned i=2; i < cmds.size(); i++) {
-    ComboAddress master(cmds[i], 53);
-    masters.push_back(master.toStringWithPort());
+    masters.emplace_back(cmds[i], 53);
   }
-  cerr<<"Creating slave zone '"<<zone<<"', with master(s) '"<<boost::join(masters, ",")<<"'"<<endl;
-  B.createDomain(zone);
+  cerr<<"Creating slave zone '"<<zone<<"', with master(s) '"<<comboAddressVecToString(masters)<<"'"<<endl;
+  B.createDomain(zone, DomainInfo::Slave, masters, "");
   if(!B.getDomainInfo(zone, di)) {
     cerr<<"Domain '"<<zone<<"' was not created!"<<endl;
     return EXIT_FAILURE;
   }
-  di.backend->setKind(zone, DomainInfo::Slave);
-  di.backend->setMaster(zone, boost::join(masters, ","));
   return EXIT_SUCCESS;
 }
 
@@ -1194,14 +1202,13 @@ static int changeSlaveZoneMaster(const vector<string>& cmds) {
     cerr<<"Domain '"<<zone<<"' doesn't exist"<<endl;
     return EXIT_FAILURE;
   }
-  vector<string> masters;
+  vector<ComboAddress> masters;
   for (unsigned i=2; i < cmds.size(); i++) {
-    ComboAddress master(cmds[i], 53);
-    masters.push_back(master.toStringWithPort());
+    masters.emplace_back(cmds[i], 53);
   }
-  cerr<<"Updating slave zone '"<<zone<<"', master(s) to '"<<boost::join(masters, ",")<<"'"<<endl;
+  cerr<<"Updating slave zone '"<<zone<<"', master(s) to '"<<comboAddressVecToString(masters)<<"'"<<endl;
   try {
-    di.backend->setMaster(zone, boost::join(masters, ","));
+    di.backend->setMasters(zone, masters);
     return EXIT_SUCCESS;
   }
   catch (PDNSException& e) {
@@ -1301,6 +1308,17 @@ static int addOrReplaceRecord(bool addOrReplace, const vector<string>& cmds) {
   return EXIT_SUCCESS;
 }
 
+// addSuperMaster add anew super master
+int addSuperMaster(const std::string &IP, const std::string &nameserver, const std::string &account)
+{
+  UeberBackend B("default");
+
+  if ( B.superMasterAdd(IP, nameserver, account) ){ 
+    return EXIT_SUCCESS; 
+  }
+  return EXIT_FAILURE;
+}
+
 // delete-rrset zone name type
 static int deleteRRSet(const std::string& zone_, const std::string& name_, const std::string& type_)
 {
@@ -1682,7 +1700,7 @@ static bool showZone(DNSSECKeeper& dk, const DNSName& zone, bool exportDS = fals
         cout<<"ID = "<<value.second.id<<" ("<<DNSSECKeeper::keyTypeToString(value.second.keyType)<<")";
       }
       if (value.first.getKey()->getBits() < 1) {
-        cerr<<" <key missing or defunct>" <<endl;
+        cout<<" <key missing or defunct, perhaps you should run pdnsutil hsm create-key>" <<endl;
         continue;
       }
       if (!exportDS) {
@@ -1810,7 +1828,7 @@ static bool secureZone(DNSSECKeeper& dk, const DNSName& zone)
   return true;
 }
 
-static void testSchema(DNSSECKeeper& dk, const DNSName& zone)
+static int testSchema(DNSSECKeeper& dk, const DNSName& zone)
 {
   cout<<"Note: test-schema will try to create the zone, but it will not remove it."<<endl;
   cout<<"Please clean up after this."<<endl;
@@ -1827,7 +1845,7 @@ static void testSchema(DNSSECKeeper& dk, const DNSName& zone)
   DomainInfo di;
   if(!B.getDomainInfo(zone, di) || !di.backend) { // di.backend and B are mostly identical
     cout<<"Can't find domain we just created, aborting"<<endl;
-    return;
+    return EXIT_FAILURE;
   }
   db=di.backend;
   DNSResourceRecord rr, rrget;
@@ -1857,13 +1875,13 @@ static void testSchema(DNSSECKeeper& dk, const DNSName& zone)
     if(db->get(rrthrowaway)) // should not touch rr but don't assume anything
     {
       cout<<"Expected one record, got multiple, aborting"<<endl;
-      exit(EXIT_FAILURE);
+      return EXIT_FAILURE;
     }
     int size=rrget.content.size();
     if(size != 302)
     {
       cout<<"Expected 302 bytes, got "<<size<<", aborting"<<endl;
-      exit(EXIT_FAILURE);
+      return EXIT_FAILURE;
     }
   }
   cout<<"[+] content field is over 255 bytes"<<endl;
@@ -1900,12 +1918,12 @@ static void testSchema(DNSSECKeeper& dk, const DNSName& zone)
   if(before != DNSName("_underscore")+zone)
   {
     cout<<"before is wrong, got '"<<before.toString()<<"', expected '_underscore."<<zone.toString()<<"', aborting"<<endl;
-    exit(EXIT_FAILURE);
+    return EXIT_FAILURE;
   }
   if(after != zone)
   {
     cout<<"after is wrong, got '"<<after.toString()<<"', expected '"<<zone.toString()<<"', aborting"<<endl;
-    exit(EXIT_FAILURE);
+    return EXIT_FAILURE;
   }
   cout<<"[+] ordername sorting is correct for names starting with _"<<endl;
   cout<<"Setting low notified serial"<<endl;
@@ -1913,7 +1931,7 @@ static void testSchema(DNSSECKeeper& dk, const DNSName& zone)
   db->getDomainInfo(zone, di);
   if(di.notified_serial != 500) {
     cout<<"[-] Set serial 500, got back "<<di.notified_serial<<", aborting"<<endl;
-    exit(EXIT_FAILURE);
+    return EXIT_FAILURE;
   }
   cout<<"Setting serial that needs 32 bits"<<endl;
   try {
@@ -1921,17 +1939,19 @@ static void testSchema(DNSSECKeeper& dk, const DNSName& zone)
   } catch(const PDNSException &pe) {
     cout<<"While setting serial, got error: "<<pe.reason<<endl;
     cout<<"aborting"<<endl;
-    exit(EXIT_FAILURE);
+    return EXIT_FAILURE;
   }
   db->getDomainInfo(zone, di);
   if(di.notified_serial != 2147484148) {
     cout<<"[-] Set serial 2147484148, got back "<<di.notified_serial<<", aborting"<<endl;
-    exit(EXIT_FAILURE);
+    return EXIT_FAILURE;
   } else {
     cout<<"[+] Big serials work correctly"<<endl;
   }
   cout<<endl;
   cout<<"End of tests, please remove "<<zone<<" from domains+records"<<endl;
+
+  return EXIT_SUCCESS;
 }
 
 static int addOrSetMeta(const DNSName& zone, const string& kind, const vector<string>& values, bool clobber) {
@@ -1998,6 +2018,8 @@ try
     cout<<"activate-zone-key ZONE KEY-ID      Activate the key with key id KEY-ID in ZONE"<<endl;
     cout<<"add-record ZONE NAME TYPE [ttl] content"<<endl;
     cout<<"             [content..]           Add one or more records to ZONE"<<endl;
+    cout<<"add-supermaster IP NAMESERVER [account]"<<endl;
+    cout<<"                                   Add a new super-master "<<endl;
     cout<<"add-zone-key ZONE {zsk|ksk} [BITS] [active|inactive] [published|unpublished]"<<endl;
     cout<<"             [rsasha1|rsasha1-nsec3-sha1|rsasha256|rsasha512|ecdsa256|ecdsa384";
 #if defined(HAVE_LIBSODIUM) || defined(HAVE_LIBDECAF) || defined(HAVE_LIBCRYPTO_ED25519)
@@ -2176,8 +2198,7 @@ try
       cerr << "Syntax: pdnsutil test-schema ZONE"<<endl;
       return 0;
     }
-    testSchema(dk, DNSName(cmds[1]));
-    return 0;
+    return testSchema(dk, DNSName(cmds[1]));
   }
   if(cmds[0] == "rectify-zone") {
     if(cmds.size() < 2) {
@@ -2202,14 +2223,14 @@ try
       return 0;
     }
     UeberBackend B("default");
-    exit(checkZone(dk, B, DNSName(cmds[1])));
+    return checkZone(dk, B, DNSName(cmds[1]));
   }
   else if(cmds[0] == "bench-db") {
     dbBench(cmds.size() > 1 ? cmds[1] : "");
   }
   else if (cmds[0] == "check-all-zones") {
     bool exitOnError = ((cmds.size() >= 2 ? cmds[1] : "") == "exit-on-error");
-    exit(checkAllZones(dk, exitOnError));
+    return checkAllZones(dk, exitOnError);
   }
   else if (cmds[0] == "list-all-zones") {
     if (cmds.size() > 2) {
@@ -2398,13 +2419,13 @@ try
         bits = pdns_stou(cmds[n]);
       } else {
         cerr<<"Unknown algorithm, key flag or size '"<<cmds[n]<<"'"<<endl;
-        exit(EXIT_FAILURE);;
+        return EXIT_FAILURE;
       }
     }
     int64_t id;
     if (!dk.addKey(zone, keyOrZone, algorithm, id, bits, active, published)) {
       cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
-      exit(1);
+      return 1;
     } else {
       cerr<<"Added a " << (keyOrZone ? "KSK" : "ZSK")<<" with algorithm = "<<algorithm<<", active="<<active<<endl;
       if (bits)
@@ -2436,49 +2457,56 @@ try
       cerr<<"Syntax: pdnsutil delete-zone ZONE"<<endl;
       return 0;
     }
-    exit(deleteZone(DNSName(cmds[1])));
+    return deleteZone(DNSName(cmds[1]));
   }
   else if(cmds[0] == "create-zone") {
     if(cmds.size() != 2 && cmds.size()!=3 ) {
       cerr<<"Syntax: pdnsutil create-zone ZONE [nsname]"<<endl;
       return 0;
     }
-    exit(createZone(DNSName(cmds[1]), cmds.size() > 2 ? DNSName(cmds[2]): DNSName()));
+    return createZone(DNSName(cmds[1]), cmds.size() > 2 ? DNSName(cmds[2]): DNSName());
   }
   else if(cmds[0] == "create-slave-zone") {
     if(cmds.size() < 3 ) {
       cerr<<"Syntax: pdnsutil create-slave-zone ZONE master-ip [master-ip..]"<<endl;
       return 0;
     }
-    exit(createSlaveZone(cmds));
+    return createSlaveZone(cmds);
   }
   else if(cmds[0] == "change-slave-zone-master") {
     if(cmds.size() < 3 ) {
       cerr<<"Syntax: pdnsutil change-slave-zone-master ZONE master-ip [master-ip..]"<<endl;
       return 0;
     }
-    exit(changeSlaveZoneMaster(cmds));
+    return changeSlaveZoneMaster(cmds);
   }
   else if(cmds[0] == "add-record") {
     if(cmds.size() < 5) {
       cerr<<"Syntax: pdnsutil add-record ZONE name type [ttl] \"content\" [\"content\"...]"<<endl;
       return 0;
     }
-    exit(addOrReplaceRecord(true, cmds));
+    return addOrReplaceRecord(true, cmds);
+  }
+  else if(cmds[0] == "add-supermaster") {
+    if(cmds.size() < 3) {
+      cerr<<"Syntax: pdnsutil add-supermaster IP NAMESERVER [account]"<<endl;
+      return 0;
+    }
+    exit(addSuperMaster(cmds[1], cmds[2], cmds.size() > 3 ? cmds[3] : "" ));
   }
   else if(cmds[0] == "replace-rrset") {
     if(cmds.size() < 5) {
       cerr<<"Syntax: pdnsutil replace-rrset ZONE name type [ttl] \"content\" [\"content\"...]"<<endl;
       return 0;
     }
-    exit(addOrReplaceRecord(false , cmds));
+    return addOrReplaceRecord(false , cmds);
   }
   else if(cmds[0] == "delete-rrset") {
     if(cmds.size() != 4) {
       cerr<<"Syntax: pdnsutil delete-rrset ZONE name type"<<endl;
       return 0;
     }
-    exit(deleteRRSet(cmds[1], cmds[2], cmds[3]));
+    return deleteRRSet(cmds[1], cmds[2], cmds[3]);
   }
   else if(cmds[0] == "list-zone") {
     if(cmds.size() != 2) {
@@ -2488,7 +2516,7 @@ try
     if(cmds[1]==".")
       cmds[1].clear();
 
-    exit(listZone(DNSName(cmds[1])));
+    return listZone(DNSName(cmds[1]));
   }
   else if(cmds[0] == "edit-zone") {
     if(cmds.size() != 2) {
@@ -2498,7 +2526,7 @@ try
     if(cmds[1]==".")
       cmds[1].clear();
 
-    exit(editZone(DNSName(cmds[1])));
+    return editZone(DNSName(cmds[1]));
   }
   else if(cmds[0] == "clear-zone") {
     if(cmds.size() != 2) {
@@ -2508,7 +2536,7 @@ try
     if(cmds[1]==".")
       cmds[1].clear();
 
-    exit(clearZone(dk, DNSName(cmds[1])));
+    return clearZone(dk, DNSName(cmds[1]));
   }
   else if(cmds[0] == "list-keys") {
     if(cmds.size() > 2) {
@@ -2516,7 +2544,7 @@ try
       return 0;
     }
     string zname = (cmds.size() == 2) ? cmds[1] : "all";
-    exit(listKeys(zname, dk));
+    return listKeys(zname, dk);
   }
   else if(cmds[0] == "load-zone") {
     if(cmds.size() < 3) {
@@ -2597,7 +2625,7 @@ try
     }
     DNSName zone(cmds[1]);
     auto kind=DomainInfo::stringToKind(cmds[2]);
-    exit(setZoneKind(zone, kind));
+    return setZoneKind(zone, kind);
   }
   else if(cmds[0]=="set-account") {
     if(cmds.size() != 3) {
@@ -2605,7 +2633,7 @@ try
       return 0;
     }
     DNSName zone(cmds[1]);
-    exit(setZoneAccount(zone, cmds[2]));
+    return setZoneAccount(zone, cmds[2]);
   }
   else if(cmds[0]=="set-nsec3") {
     if(cmds.size() < 2) {
@@ -2746,7 +2774,7 @@ try
   else if(cmds[0]=="export-zone-key") {
     if(cmds.size() < 3) {
       cerr<<"Syntax: pdnsutil export-zone-key ZONE KEY-ID"<<endl;
-      return 0;
+      return 1;
     }
 
     string zone=cmds[1];
@@ -2757,14 +2785,14 @@ try
   else if(cmds[0]=="increase-serial") {
     if (cmds.size() < 2) {
       cerr<<"Syntax: pdnsutil increase-serial ZONE"<<endl;
-      return 0;
+      return 1;
     }
     return increaseSerial(DNSName(cmds[1]), dk);
   }
   else if(cmds[0]=="import-zone-key-pem") {
     if(cmds.size() < 4) {
       cerr<<"Syntax: pdnsutil import-zone-key-pem ZONE FILE ALGORITHM {ksk|zsk}"<<endl;
-      exit(1);
+      return 1;
     }
     string zone=cmds[1];
     string fname=cmds[2];
@@ -2797,7 +2825,7 @@ try
         dpk.d_flags = 257;
       else {
         cerr<<"Unknown key flag '"<<cmds[4]<<"'"<<endl;
-        exit(1);
+        return 1;
       }
     }
     else
@@ -2806,7 +2834,7 @@ try
     int64_t id;
     if (!dk.addKey(DNSName(zone), dpk, id)) {
       cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
-      exit(1);
+      return 1;
     }
     if (id == -1) {
       cerr<<std::to_string(id)<<"Key was added, but backend does not support returning of key id"<<endl;
@@ -2820,7 +2848,7 @@ try
   else if(cmds[0]=="import-zone-key") {
     if(cmds.size() < 3) {
       cerr<<"Syntax: pdnsutil import-zone-key ZONE FILE [ksk|zsk] [active|inactive]"<<endl;
-      exit(1);
+      return 1;
     }
     string zone=cmds[1];
     string fname=cmds[2];
@@ -2852,13 +2880,13 @@ try
         published = 0;
       else {
         cerr<<"Unknown key flag '"<<cmds[n]<<"'"<<endl;
-        exit(1);
+        return 1;
       }
     }
     int64_t id;
     if (!dk.addKey(DNSName(zone), dpk, id, active, published)) {
       cerr<<"Adding key failed, perhaps DNSSEC not enabled in configuration?"<<endl;
-      exit(1);
+      return 1;
     }
     if (id == -1) {
       cerr<<std::to_string(id)<<"Key was added, but backend does not support returning of key id"<<endl;
@@ -2871,7 +2899,7 @@ try
   else if(cmds[0]=="export-zone-dnskey") {
     if(cmds.size() < 3) {
       cerr<<"Syntax: pdnsutil export-zone-dnskey ZONE KEY-ID"<<endl;
-      exit(1);
+      return 1;
     }
 
     DNSName zone(cmds[1]);
@@ -3264,7 +3292,7 @@ try
          return 1;
       }
 
-      cerr << "Key of size " << bits << " created" << std::endl;
+      cerr << "Key of size " << dke->getBits() << " created" << std::endl;
       return 0;
     }
 #else
@@ -3309,19 +3337,8 @@ try
       DNSResourceRecord rr;
       cout<<"Processing '"<<di.zone<<"'"<<endl;
       // create zone
-      if (!tgt->createDomain(di.zone)) throw PDNSException("Failed to create zone");
+      if (!tgt->createDomain(di.zone, di.kind, di.masters, di.account)) throw PDNSException("Failed to create zone");
       if (!tgt->getDomainInfo(di.zone, di_new)) throw PDNSException("Failed to create zone");
-      tgt->setKind(di_new.zone, di.kind);
-      tgt->setAccount(di_new.zone,di.account);
-      string masters="";
-      bool first = true;
-      for(const auto& master: di.masters) {
-        if (!first)
-          masters += ", ";
-        first = false;
-        masters += master.toStringWithPortExcept(53);
-      }
-      tgt->setMaster(di_new.zone, masters);
       // move records
       if (!src->list(di.zone, di.id, true)) throw PDNSException("Failed to list records");
       nr=0;
index ddb1506492f606845111878a724df3929885b070..d6073b865b182ad7ad18042fb8fb197a72c49993 100644 (file)
@@ -45,6 +45,9 @@ in it. you need to use softhsm tools to manage this all.
 static CK_FUNCTION_LIST** p11_modules;
 #endif
 
+#define ECDSA256_PARAMS "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07"
+#define ECDSA384_PARAMS "\x06\x05\x2b\x81\x04\x00\x22"
+
 // map for signing algorithms
 static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2smech = boost::assign::map_list_of
 (5, CKM_SHA1_RSA_PKCS)
@@ -63,6 +66,14 @@ static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2hmech = boost::assign::ma
 (13, CKM_SHA256)
 (14, CKM_SHA384);
 
+static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2cmech = boost::assign::map_list_of
+(5, CKM_RSA_PKCS_KEY_PAIR_GEN)
+(7, CKM_RSA_PKCS_KEY_PAIR_GEN)
+(8, CKM_RSA_PKCS_KEY_PAIR_GEN)
+(10, CKM_RSA_PKCS_KEY_PAIR_GEN)
+(13, CKM_ECDSA_KEY_PAIR_GEN)
+(14, CKM_ECDSA_KEY_PAIR_GEN);
+
 typedef enum { Attribute_Byte, Attribute_Long, Attribute_String } CkaValueType;
 
 // Attribute handling
@@ -212,7 +223,7 @@ class Pkcs11Slot {
 
     void logError(const std::string& operation) const {
       if (d_err) {
-        std::string msg = boost::str( boost::format("PKCS#11 operation %s failed: %s (0x%X)") % operation % p11_kit_strerror(d_err) % d_err );
+        std::string msg = boost::str( boost::format("PKCS#11 operation %s failed: %s (0x%X) (%s)") % operation % p11_kit_strerror(d_err) % d_err % p11_kit_message() );
         g_log<<Logger::Error<< msg << endl;
       }
     }
@@ -290,7 +301,7 @@ class Pkcs11Token {
 
     void logError(const std::string& operation) const {
       if (d_err) {
-        std::string msg = boost::str( boost::format("PKCS#11 operation %s failed: %s (0x%X)") % operation % p11_kit_strerror(d_err) % d_err );
+        std::string msg = boost::str( boost::format("PKCS#11 operation %s failed: %s (0x%X) (%s)") % operation % p11_kit_strerror(d_err) % d_err % p11_kit_message());
         g_log<<Logger::Error<< msg << endl;
       }
     }
@@ -316,8 +327,8 @@ class Pkcs11Token {
 
       return bits;
 #else
-      if (d_ecdsa_params == "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07") return 256;
-      else if (d_ecdsa_params == "\x06\x05\x2b\x81\x04\x00\x22") return 384;
+      if (d_ecdsa_params == ECDSA256_PARAMS) return 256;
+      else if (d_ecdsa_params == ECDSA384_PARAMS) return 384;
       else throw PDNSException("Unsupported EC key");
 #endif
     }
@@ -793,32 +804,65 @@ void PKCS11DNSCryptoKeyEngine::create(unsigned int bits) {
 
   std::string pubExp("\000\001\000\001", 4); // 65537
 
-  pubAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PUBLIC_KEY));
-  pubAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_RSA));
-  pubAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
-  pubAttr.push_back(P11KitAttribute(CKA_ENCRYPT, (char)CK_TRUE));
-  pubAttr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
-  pubAttr.push_back(P11KitAttribute(CKA_WRAP, (char)CK_TRUE));
-  pubAttr.push_back(P11KitAttribute(CKA_MODULUS_BITS, (unsigned long)bits));
-  pubAttr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, pubExp));
-  pubAttr.push_back(P11KitAttribute(CKA_LABEL, d_pub_label));
-
-  privAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PRIVATE_KEY));
-  privAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_RSA));
-  privAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
-  privAttr.push_back(P11KitAttribute(CKA_PRIVATE, (char)CK_TRUE));
-//  privAttr.push_back(P11KitAttribute(CKA_SUBJECT, "CN=keygen"));
-  privAttr.push_back(P11KitAttribute(CKA_ID, "\x01\x02\x03\x04")); // this is mandatory if you want to export anything
-  privAttr.push_back(P11KitAttribute(CKA_SENSITIVE, (char)CK_TRUE));
-  privAttr.push_back(P11KitAttribute(CKA_DECRYPT, (char)CK_TRUE));
-  privAttr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE));
-  privAttr.push_back(P11KitAttribute(CKA_UNWRAP, (char)CK_TRUE));
-  privAttr.push_back(P11KitAttribute(CKA_LABEL, d_label));
-
-  mech.mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
+  try {
+    mech.mechanism = dnssec2cmech.at(d_algorithm);
+  } catch (std::out_of_range& e) {
+    throw PDNSException("pkcs11: unsupported algorithm "+std::to_string(d_algorithm)+ " for key pair generation");
+  }
+
   mech.pParameter = NULL;
   mech.ulParameterLen = 0;
 
+  if (mech.mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN) {
+    pubAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PUBLIC_KEY));
+    pubAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_RSA));
+    pubAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
+    pubAttr.push_back(P11KitAttribute(CKA_ENCRYPT, (char)CK_TRUE));
+    pubAttr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
+    pubAttr.push_back(P11KitAttribute(CKA_WRAP, (char)CK_TRUE));
+    pubAttr.push_back(P11KitAttribute(CKA_MODULUS_BITS, (unsigned long)bits));
+    pubAttr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, pubExp));
+    pubAttr.push_back(P11KitAttribute(CKA_LABEL, d_pub_label));
+
+    privAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PRIVATE_KEY));
+    privAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_RSA));
+    privAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_PRIVATE, (char)CK_TRUE));
+  //  privAttr.push_back(P11KitAttribute(CKA_SUBJECT, "CN=keygen"));
+    privAttr.push_back(P11KitAttribute(CKA_ID, "\x01\x02\x03\x04")); // this is mandatory if you want to export anything
+    privAttr.push_back(P11KitAttribute(CKA_SENSITIVE, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_DECRYPT, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_UNWRAP, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_LABEL, d_label));
+  } else if (mech.mechanism == CKM_ECDSA_KEY_PAIR_GEN) {
+    pubAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PUBLIC_KEY));
+    pubAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_ECDSA));
+    pubAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
+    pubAttr.push_back(P11KitAttribute(CKA_ENCRYPT, (char)CK_TRUE));
+    pubAttr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
+    pubAttr.push_back(P11KitAttribute(CKA_WRAP, (char)CK_TRUE));
+    pubAttr.push_back(P11KitAttribute(CKA_LABEL, d_pub_label));
+    if (d_algorithm == 13) pubAttr.push_back(P11KitAttribute(CKA_ECDSA_PARAMS, ECDSA256_PARAMS));
+    else if (d_algorithm == 14) pubAttr.push_back(P11KitAttribute(CKA_ECDSA_PARAMS, ECDSA384_PARAMS));
+    else throw PDNSException("pkcs11: unknown algorithm "+std::to_string(d_algorithm)+" for ECDSA key pair generation");
+
+    privAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PRIVATE_KEY));
+    privAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_ECDSA));
+    privAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_PRIVATE, (char)CK_TRUE));
+  //  privAttr.push_back(P11KitAttribute(CKA_SUBJECT, "CN=keygen"));
+    privAttr.push_back(P11KitAttribute(CKA_ID, "\x01\x02\x03\x04")); // this is mandatory if you want to export anything
+    privAttr.push_back(P11KitAttribute(CKA_SENSITIVE, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_DECRYPT, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_UNWRAP, (char)CK_TRUE));
+    privAttr.push_back(P11KitAttribute(CKA_LABEL, d_label));
+  } else {
+    throw PDNSException("pkcs11: don't know how make key for algorithm "+std::to_string(d_algorithm));
+  }
+
+
   if (d_slot->GenerateKeyPair(&mech, pubAttr, privAttr, &pubKey, &privKey)) {
     throw PDNSException("Keypair generation failed");
   }
index be635c4241c34a9dd87655370cac6387ec078050..5e62e9ea0114f9d4de5fdf70ccccca62d72fc721 100644 (file)
@@ -69,6 +69,9 @@ std::string makeProxyHeader(bool tcp, const ComboAddress& source, const ComboAdd
       throw std::runtime_error("The size of proxy protocol values is limited to " + std::to_string(std::numeric_limits<uint16_t>::max()) + ", trying to add a value of size " + std::to_string(value.content.size()));
     }
     valuesSize += sizeof(uint8_t) + sizeof(uint8_t) * 2 + value.content.size();
+    if (valuesSize > std::numeric_limits<uint16_t>::max()) {
+      throw std::runtime_error("The total size of proxy protocol values is limited to " + std::to_string(std::numeric_limits<uint16_t>::max()));
+    }
   }
 
   size_t total = (addrSize * 2) + sizeof(sourcePort) + sizeof(destinationPort) + valuesSize;
diff --git a/pdns/query-local-address.cc b/pdns/query-local-address.cc
new file mode 100644 (file)
index 0000000..2acccab
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "query-local-address.hh"
+#include "iputils.hh"
+#include "dns_random.hh"
+
+namespace pdns {
+  static const ComboAddress local4("0.0.0.0");
+  static const ComboAddress local6("::");
+
+  static vector<ComboAddress> g_localQueryAddresses4;
+  static vector<ComboAddress> g_localQueryAddresses6;
+
+  ComboAddress getQueryLocalAddress(const sa_family_t family, const in_port_t port) {
+    ComboAddress ret;
+    if (family==AF_INET) {
+      if (g_localQueryAddresses4.empty()) {
+        ret = local4;
+      } else if (g_localQueryAddresses4.size() == 1) {
+        ret = g_localQueryAddresses4.at(0);
+      } else {
+        ret = g_localQueryAddresses4[dns_random(g_localQueryAddresses4.size())];
+      }
+      ret.sin4.sin_port = htons(port);
+    }
+    else {
+      if (g_localQueryAddresses6.empty()) {
+        ret = local6;
+      } else if (g_localQueryAddresses6.size() == 1) {
+        ret = g_localQueryAddresses6.at(0);
+      } else {
+        ret = g_localQueryAddresses6[dns_random(g_localQueryAddresses6.size())];
+      }
+      ret.sin6.sin6_port = htons(port);
+    }
+    return ret;
+  }
+
+  ComboAddress getNonAnyQueryLocalAddress(const sa_family_t family) {
+    if (family == AF_INET) {
+      for (const auto& addr : pdns::g_localQueryAddresses4) {
+        if (!IsAnyAddress(addr)) {
+          return addr;
+        }
+      }
+    }
+    if (family == AF_INET6) {
+      for (const auto& addr : pdns::g_localQueryAddresses6) {
+        if (!IsAnyAddress(addr)) {
+          return addr;
+        }
+      }
+    }
+    ComboAddress ret("0.0.0.0");
+    ret.reset(); // Ensure all is zero, even the addr family
+    return ret;
+  }
+
+  void parseQueryLocalAddress(const std::string &qla) {
+    vector<string> addrs;
+    stringtok(addrs, qla, ", ;");
+    for(const string& addr : addrs) {
+      ComboAddress tmp(addr);
+      if (tmp.isIPv4()) {
+        g_localQueryAddresses4.push_back(tmp);
+        continue;
+      }
+      g_localQueryAddresses6.push_back(tmp);
+    }
+  }
+
+  bool isQueryLocalAddressFamilyEnabled(const sa_family_t family) {
+    if (family == AF_INET) {
+      return !g_localQueryAddresses4.empty();
+    }
+    if (family == AF_INET6) {
+      return !g_localQueryAddresses6.empty();
+    }
+    return false;
+  }
+} // namespace pdns
diff --git a/pdns/query-local-address.hh b/pdns/query-local-address.hh
new file mode 100644 (file)
index 0000000..9515f4a
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "iputils.hh"
+
+namespace pdns {
+  /*! pick a random query local address for family
+   *
+   * Will always return a ComboAddress.
+   *
+   * @param family Address Family, only AF_INET and AF_INET6 are supported
+   * @param port   Port to set in the returned ComboAddress
+   */
+  ComboAddress getQueryLocalAddress(const sa_family_t family, const in_port_t port);
+
+  /*! Returns a non-Any address QLA, or an empty QLA when the QLA is any
+   *
+   * @param family  Address Family
+   */
+  ComboAddress getNonAnyQueryLocalAddress(const sa_family_t family);
+
+  /*! Populate the query local address vectors
+   *
+   * Will throw when an address can't be parsed
+   *
+   * @param qla  A string of one or more ip addresses, separated by
+   *             spaces, semi-colons or commas
+   */
+  void parseQueryLocalAddress(const std::string &qla);
+
+  /*! Is the address family explicitly enabled
+   *
+   * i.e. was there an address parsed by parseQueryLocalAddress belonging
+   * to this family
+   *
+   * @param family  Address Family, only AF_INET and AF_INET6 are supported
+   */
+  bool isQueryLocalAddressFamilyEnabled(const sa_family_t family);
+} // namespace pdns
index 3eba50bd8c5bb9d48cedce4c5e3bf6fce9687ba7..c03bae308214a1f64a6734782ccc7c2d1a6eaa6e 100644 (file)
@@ -32,15 +32,13 @@ try
   if(namespace_name.empty()) {
     namespace_name="pdns";
   }
-  if(hostname.empty()) {
-    char tmp[80];
-    memset(tmp, 0, sizeof(tmp));
-    gethostname(tmp, sizeof(tmp));
-    char *p = strchr(tmp, '.');
-    if(p) *p=0;
-
-    hostname=tmp;
-    boost::replace_all(hostname, ".", "_");    
+  if (hostname.empty()) {
+    try {
+      hostname = getCarbonHostName();
+    }
+    catch(const std::exception& e) {
+      throw std::runtime_error(std::string("The 'carbon-ourname' setting has not been set and we are unable to determine the system's hostname: ") + e.what());
+    }
   }
   if(instance_name.empty()) {
     instance_name="recursor";
index 73bffa823e5d3019963fcf006e8aef81f5ff8546..0481c8a0cc06b11d8ef02b8af125a0c4c852660c 100644 (file)
@@ -53,7 +53,7 @@ typename C::value_type::second_type constGet(const C& c, const std::string& name
 
 typedef std::unordered_map<std::string, boost::variant<bool, uint32_t, std::string, std::vector<std::pair<int, std::string>> > > rpzOptions_t;
 
-static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::optional<DNSFilterEngine::Policy>& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL, size_t& zoneSizeHint, std::unordered_set<std::string>& tags)
+static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::optional<DNSFilterEngine::Policy>& defpol, bool& defpolOverrideLocal, uint32_t& maxTTL, size_t& zoneSizeHint, std::unordered_set<std::string>& tags, bool& overridesGettag)
 {
   if(have.count("policyName")) {
     polName = boost::get<std::string>(have["policyName"]);
@@ -88,6 +88,9 @@ static void parseRPZParameters(rpzOptions_t& have, std::string& polName, boost::
       tags.insert(tag.second);
     }
   }
+  if (have.count("overridesGettag")) {
+    overridesGettag = boost::get<bool>(have["overridesGettag"]);
+  }
 }
 
 #if HAVE_PROTOBUF
@@ -241,11 +244,12 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
         std::string polName("rpzFile");
         std::shared_ptr<DNSFilterEngine::Zone> zone = std::make_shared<DNSFilterEngine::Zone>();
         uint32_t maxTTL = std::numeric_limits<uint32_t>::max();
+        bool overridesGettag = true;
         if(options) {
           auto& have = *options;
           size_t zoneSizeHint = 0;
           std::unordered_set<std::string> tags;
-          parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags);
+          parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags, overridesGettag);
           if (zoneSizeHint > 0) {
             zone->reserve(zoneSizeHint);
           }
@@ -253,6 +257,7 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
         }
         g_log<<Logger::Warning<<"Loading RPZ from file '"<<filename<<"'"<<endl;
         zone->setName(polName);
+        zone->setPolicyOverridesGettag(overridesGettag);
         loadRPZFromFile(filename, zone, defpol, defpolOverrideLocal, maxTTL);
         lci.dfe.addZone(zone);
         g_log<<Logger::Warning<<"Done loading RPZ from file '"<<filename<<"'"<<endl;
@@ -295,11 +300,13 @@ void loadRecursorLuaConfig(const std::string& fname, luaConfigDelayedThreads& de
           auto& have = *options;
           size_t zoneSizeHint = 0;
           std::unordered_set<std::string> tags;
-          parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags);
+          bool overridesGettag = true;
+          parseRPZParameters(have, polName, defpol, defpolOverrideLocal, maxTTL, zoneSizeHint, tags, overridesGettag);
           if (zoneSizeHint > 0) {
             zone->reserve(zoneSizeHint);
           }
           zone->setTags(std::move(tags));
+          zone->setPolicyOverridesGettag(overridesGettag);
 
           if(have.count("tsigname")) {
             tt.name=DNSName(toLower(boost::get<string>(have["tsigname"])));
index a3bc85bb4d9bc60660a4c04cc647846905cf2a97..b692e076457f301b990d5f94bd3af92229139fce 100644 (file)
@@ -1157,11 +1157,11 @@ void registerAllStats()
 #endif
 
   addGetStat("dnssec-validations", &g_stats.dnssecValidations);
-  addGetStat("dnssec-result-insecure", &g_stats.dnssecResults[Insecure]);
-  addGetStat("dnssec-result-secure", &g_stats.dnssecResults[Secure]);
-  addGetStat("dnssec-result-bogus", &g_stats.dnssecResults[Bogus]);
-  addGetStat("dnssec-result-indeterminate", &g_stats.dnssecResults[Indeterminate]);
-  addGetStat("dnssec-result-nta", &g_stats.dnssecResults[NTA]);
+  addGetStat("dnssec-result-insecure", &g_stats.dnssecResults[vState::Insecure]);
+  addGetStat("dnssec-result-secure", &g_stats.dnssecResults[vState::Secure]);
+  addGetStat("dnssec-result-bogus", &g_stats.dnssecResults[vState::Bogus]);
+  addGetStat("dnssec-result-indeterminate", &g_stats.dnssecResults[vState::Indeterminate]);
+  addGetStat("dnssec-result-nta", &g_stats.dnssecResults[vState::NTA]);
 
   addGetStat("policy-result-noaction", &g_stats.policyResults[DNSFilterEngine::PolicyKind::NoAction]);
   addGetStat("policy-result-drop", &g_stats.policyResults[DNSFilterEngine::PolicyKind::Drop]);
index da65e8bf2591a9e3a5aaac613ec280a751b8c8a1..3be7c8c1e0c75855cb56278f688cefef1e819de6 100644 (file)
@@ -586,8 +586,8 @@ int main(int argc, char **argv)
     DynListener::registerFunc("RESPSIZES", &DLRSizesHandler, "get histogram of response sizes");
     DynListener::registerFunc("REMOTES", &DLRemotesHandler, "get top remotes");
     DynListener::registerFunc("SET",&DLSettingsHandler, "set config variables", "<var> <value>");
-    DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler, "retrieve slave domain", "<domain>");
-    DynListener::registerFunc("CURRENT-CONFIG",&DLCurrentConfigHandler, "retrieve the current configuration", "[diff|default]");
+    DynListener::registerFunc("RETRIEVE",&DLNotifyRetrieveHandler, "retrieve slave domain", "<domain> [<ip>]");
+    DynListener::registerFunc("CURRENT-CONFIG",&DLCurrentConfigHandler, "retrieve the current configuration", "[diff]");
     DynListener::registerFunc("LIST-ZONES",&DLListZones, "show list of zones", "[master|slave|native]");
     DynListener::registerFunc("TOKEN-LOGIN", &DLTokenLogin, "Login to a PKCS#11 token", "<module> <slot> <pin>");
 
index 1b952ba19670044ea9ea5a9d8617cb40ef3abcd2..f4d145fffcb66e60f08511f25c19a3e418062b88 100644 (file)
@@ -212,7 +212,7 @@ uint64_t RecursorPacketCache::bytes()
   return sum;
 }
 
-void RecursorPacketCache::doPruneTo(unsigned int maxCached)
+void RecursorPacketCache::doPruneTo(size_t maxCached)
 {
   pruneCollection<SequencedTag>(*this, d_packetCache, maxCached);
 }
index 790d714fb879af7fb9d2e7815f76af956cd6c41f..75adce7d2aaffd84e6aad769af9bb33fc21b3989 100644 (file)
@@ -56,7 +56,7 @@ public:
   bool getResponsePacket(unsigned int tag, const std::string& queryPacket, const DNSName& qname, uint16_t qtype, uint16_t qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage);
   bool getResponsePacket(unsigned int tag, const std::string& queryPacket, DNSName& qname, uint16_t* qtype, uint16_t* qclass, time_t now, std::string* responsePacket, uint32_t* age, vState* valState, uint32_t* qhash, uint16_t* ecsBegin, uint16_t* ecsEnd, RecProtoBufMessage* protobufMessage);
   void insertResponsePacket(unsigned int tag, uint32_t qhash, std::string&& query, const DNSName& qname, uint16_t qtype, uint16_t qclass, std::string&& responsePacket, time_t now, uint32_t ttl, const vState& valState, uint16_t ecsBegin, uint16_t ecsEnd, boost::optional<RecProtoBufMessage>&& protobufMessage);
-  void doPruneTo(unsigned int maxSize=250000);
+  void doPruneTo(size_t maxSize=250000);
   uint64_t doDump(int fd);
   int doWipePacketCache(const DNSName& name, uint16_t qtype=0xffff, bool subtree=false);
   
index 28735b97b126e2b7fef096346616a5383a396fbd..953562123f6821b505976643a8f829cb8c08588f 100644 (file)
@@ -554,7 +554,7 @@ uint64_t MemRecursorCache::doDump(int fd)
       for (const auto& j : i.d_records) {
         count++;
         try {
-          fprintf(fp.get(), "%s %" PRId64 " IN %s %s ; (%s) auth=%i %s %s\n", i.d_qname.toString().c_str(), static_cast<int64_t>(i.d_ttd - now), DNSRecordContent::NumberToType(i.d_qtype).c_str(), j->getZoneRepresentation().c_str(), vStates[i.d_state], i.d_auth, i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str(), !i.d_rtag ? "" : i.d_rtag.get().c_str());
+          fprintf(fp.get(), "%s %" PRId64 " IN %s %s ; (%s) auth=%i %s %s\n", i.d_qname.toString().c_str(), static_cast<int64_t>(i.d_ttd - now), DNSRecordContent::NumberToType(i.d_qtype).c_str(), j->getZoneRepresentation().c_str(), vStateToString(i.d_state).c_str(), i.d_auth, i.d_netmask.empty() ? "" : i.d_netmask.toString().c_str(), !i.d_rtag ? "" : i.d_rtag.get().c_str());
         }
         catch(...) {
           fprintf(fp.get(), "; error printing '%s'\n", i.d_qname.empty() ? "EMPTY" : i.d_qname.toString().c_str());
index 2adae2f802c1ece2b553bc036049e0abf68b63c5..d2f66cef5897ab3da0c8f294c0bcb3f0de302a28 100644 (file)
@@ -59,7 +59,7 @@ public:
 
   int32_t get(time_t, const DNSName &qname, const QType& qt, bool requireAuth, vector<DNSRecord>* res, const ComboAddress& who, const OptTag& routingTag = boost::none, vector<std::shared_ptr<RRSIGRecordContent>>* signatures=nullptr, std::vector<std::shared_ptr<DNSRecord>>* authorityRecs=nullptr, bool* variable=nullptr, vState* state=nullptr, bool* wasAuth=nullptr);
 
-  void replace(time_t, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask=boost::none, const OptTag& routingTag = boost::none, vState state=Indeterminate);
+  void replace(time_t, const DNSName &qname, const QType& qt,  const vector<DNSRecord>& content, const vector<shared_ptr<RRSIGRecordContent>>& signatures, const std::vector<std::shared_ptr<DNSRecord>>& authorityRecs, bool auth, boost::optional<Netmask> ednsmask=boost::none, const OptTag& routingTag = boost::none, vState state=vState::Indeterminate);
 
   void doPrune(size_t keep);
   uint64_t doDump(int fd);
@@ -75,7 +75,7 @@ private:
   struct CacheEntry
   {
     CacheEntry(const boost::tuple<DNSName, uint16_t, OptTag, Netmask>& key, bool auth):
-      d_qname(key.get<0>()), d_netmask(key.get<3>().getNormalized()), d_rtag(key.get<2>()), d_state(Indeterminate), d_ttd(0), d_qtype(key.get<1>()), d_auth(auth)
+      d_qname(key.get<0>()), d_netmask(key.get<3>().getNormalized()), d_rtag(key.get<2>()), d_state(vState::Indeterminate), d_ttd(0), d_qtype(key.get<1>()), d_auth(auth)
     {
     }
 
index 6842f7d2487ca8001dff456d2fa82cebf1fdc9aa..573047c75eec4a66bbcf773835a3b7c42553b4e4 100644 (file)
@@ -148,6 +148,7 @@ pdns_recursor_SOURCES = \
        pubsuffix.hh pubsuffix.cc \
        pubsuffixloader.cc \
        qtype.hh qtype.cc \
+       query-local-address.hh query-local-address.cc \
        rcpgenerator.cc rcpgenerator.hh \
        rec-carbon.cc \
        rec-lua-conf.hh rec-lua-conf.cc \
@@ -160,6 +161,7 @@ pdns_recursor_SOURCES = \
        reczones.cc \
        remote_logger.cc remote_logger.hh \
        resolver.hh resolver.cc \
+       axfr-retriever.hh axfr-retriever.cc \
        resolve-context.hh \
        responsestats.hh responsestats.cc \
        root-addresses.hh \
@@ -222,6 +224,7 @@ endif
 
 testrunner_SOURCES = \
        arguments.cc \
+       axfr-retriever.hh axfr-retriever.cc \
        base32.cc \
        base64.cc base64.hh \
        circular_buffer.hh \
@@ -253,6 +256,7 @@ testrunner_SOURCES = \
        pollmplexer.cc \
        protobuf.cc protobuf.hh \
        qtype.cc qtype.hh \
+       query-local-address.hh query-local-address.cc \
        rcpgenerator.cc \
        rec-protobuf.cc rec-protobuf.hh \
        recpacketcache.cc recpacketcache.hh \
@@ -302,6 +306,7 @@ testrunner_SOURCES = \
        test-syncres_cc7.cc \
        test-syncres_cc8.cc \
        test-syncres_cc9.cc \
+       test-syncres_cc10.cc \
        test-tsig.cc \
        test-xpf_cc.cc \
        testrunner.cc \
@@ -549,6 +554,7 @@ endif
 pdns-recursor@.service: pdns-recursor.service
        $(AM_V_GEN)sed -e 's!/pdns_recursor!& --config-name=%i!' \
          -e 's!Recursor!& %i!' \
+         -e 's!RuntimeDirectory=.*!&-%i!' \
          < $< > $@
 
 systemdsystemunitdir = $(SYSTEMD_DIR)
diff --git a/pdns/recursordist/axfr-retriever.cc b/pdns/recursordist/axfr-retriever.cc
new file mode 120000 (symlink)
index 0000000..e3e1df8
--- /dev/null
@@ -0,0 +1 @@
+../axfr-retriever.cc
\ No newline at end of file
diff --git a/pdns/recursordist/axfr-retriever.hh b/pdns/recursordist/axfr-retriever.hh
new file mode 120000 (symlink)
index 0000000..7e99348
--- /dev/null
@@ -0,0 +1 @@
+../axfr-retriever.hh
\ No newline at end of file
index 9a9581f01a4b854f609eda8ad0f64c39d5df7be9..0b756510ae58af44ff2e17564d14162a74016346 100644 (file)
@@ -12,7 +12,7 @@ AC_CONFIG_HEADERS([config.h])
 AC_CANONICAL_HOST
 # Add some default CFLAGS and CXXFLAGS, can be appended to using the environment variables
 CFLAGS="-Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls -g -O2 $CFLAGS"
-CXXFLAGS="-Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls -g -O2 $CXXFLAGS"
+CXXFLAGS="-std=c++11 -Wall -Wextra -Wshadow -Wno-unused-parameter -Wmissing-declarations -Wredundant-decls -g -O2 $CXXFLAGS"
 
 AC_SUBST([pdns_configure_args],["$ac_configure_args"])
 AC_DEFINE_UNQUOTED([PDNS_CONFIG_ARGS],
@@ -33,7 +33,7 @@ AC_DEFINE([RECURSOR], [1],
 # Warn when pkg.m4 is missing
 m4_pattern_forbid([^_?PKG_[A-Z_]+$], [*** pkg.m4 missing, please install pkg-config])
 
-AX_CXX_COMPILE_STDCXX_11([ext], [mandatory])
+AX_CXX_COMPILE_STDCXX_11([noext], [mandatory])
 AC_PROG_LIBTOOL
 
 PDNS_CHECK_OS
index 6d2a505e2ac43639e21344c008a4f4237e3b75d4..57975560b94aa18a8acef17cdbaf0d51e527e910 100644 (file)
@@ -1,6 +1,12 @@
 End of life statements
 ======================
 
+We aim to have a release every six months.
+The latest and previous release receive correctness, stability and security updates.
+The release before that gets critical security updates only.
+Older releases are marked end of life and receive no updates at all.
+Pre-releases do not receive immediate security updates.
+
 The currently supported release train of the PowerDNS Recursor is 4.3.
 
 PowerDNS Recursor 4.2 will only receive correctness, stability and
@@ -16,3 +22,28 @@ PowerDNS Recursor 4.0, 3.x, and 2.x are end of life.
 Note: Users with a commercial agreement with PowerDNS.COM BV or Open-Xchange
 can receive extended support for releases which are End Of Life. If you are
 such a user, these EOL statements do not apply to you.
+
+.. list-table:: PowerDNS Recursor Release Life Cycle
+   :header-rows: 1
+
+   * - Version
+     - Release date
+     - Security-Only updates
+     - End of Life
+   * - 4.3
+     - March 3 2020
+     - ~ March 2021
+     - ~ September 2021
+   * - 4.2
+     - July 16 2019
+     - ~ September 2020
+     - ~ March 2021
+   * - 4.1
+     - December 4 2017
+     - March 3 2020
+     - ~ September 2020
+   * - 4.0 and older
+     - EOL
+     - EOL
+     - EOL
+
index 46812a96714a1b8868e98d79475ec9f5fc7d3b62..67a25ac7a64cc4119676952d173084a81ca1c6a2 100644 (file)
@@ -2,7 +2,54 @@ Changelogs for 4.1.x
 ====================
 
 .. changelog::
-  :version: 4.1.15
+  :version: 4.1.17
+  :released: 1st of July 2020
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9283
+
+    Backport of CVE-2020-14196: Enforce webserver ACL.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9129
+    :tickets: 9127, 8640
+
+    Fix compilation on systems that do not define HOST_NAME_MAX.
+
+
+.. changelog::
+  :version: 4.1.16
+  :released: 19th of May 2020
+
+  .. change::
+     :tags: Bug Fixes
+     :pullreq: 9117
+
+     Backport of security fixes for CVE-2020-10995, CVE-2020-12244 and
+     CVE-2020-10030, plus avoid a crash when loading an invalid RPZ.
+
+  .. change::
+     :tags: Internals
+     :pullreq: 8809
+
+     Update python dependencies for docs generation.
+
+  .. change::
+     :tags: Improvements
+     :pullreq:  8868
+
+     Only log qname parsing errors when 'log-common-errors' is set.
+
+  .. change::
+     :tags: Internals
+     :pullreq: 8753
+
+     Update boost.m4.
+
+.. changelog::
+   :version: 4.1.15
   :released: 6th of December 2019
 
   .. change::
index 7eaf6809454adcc13f8f4d98d1b0f0418f8edd63..1da98268fb2a4a96893f6eb0ef5d34fb118ef18b 100644 (file)
@@ -1,5 +1,140 @@
 Changelogs for 4.2.x
 ====================
+.. changelog::
+  :version: 4.2.4
+  :released: 17th of July 2020
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9334
+    :tickets: 9309
+
+    Validate cached DNSKEYs against the DSs, not the RRSIGs only.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9333
+    :tickets: 9297
+
+    Ignore cache-only for DNSKEYS/DS retrieval.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9332
+    :tickets: 9292
+
+    A ServFail while retrieving DS/DNSKEY records is just that.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9331
+    :tickets: 9188
+
+    Refuse DS records received from child zones.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9306
+    :tickets: 9268
+
+    Better exception handling in housekeeping/handlePolicyHit.
+
+.. changelog::
+  :version: 4.2.3
+  :released: 1st of July 2020
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9284
+
+    Backport of CVE-2020-14196: Enforce webserver ACL.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9261
+    :tickets: 9251
+
+    Copy the negative cache entry before validating it.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9133
+    :tickets: 9127
+
+    Fix compilation on systems that do not define HOST_NAME_MAX.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9123
+    :tickets: 8640
+
+    Fix build with gcc-10
+
+.. changelog::
+  :version: 4.2.2
+  :released: 19th of May 2020
+
+  .. change::
+     :tags: Bug Fixes
+     :pullreq: 9116
+
+     Backport of security fixes for CVE-2020-10995, CVE-2020-12244 and
+     CVE-2020-10030, plus avoid a crash when loading an invalid RPZ.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9081
+
+    Add ubuntu focal target.
+
+  .. change::
+    :tags: Internals
+    :pullreq: 8988
+
+    Update gen-version to use latest tag for version number.
+
+  .. change::
+    :tags: Internals
+    :pullreq: 8964, 8752
+    :tickets: 8875
+
+    Update boost.m4.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 8869
+
+    Only log qname parsing errors when 'log-common-errors' is set.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 8832
+
+    Refuse NSEC records with a bitmap length > 32.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 8802
+
+    Avoid startup race by setting the state of a tread before starting it.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 8696
+
+    Better detection of Bogus zone cuts for DNSSEC validation.
+
+  .. change::
+    :tags: Bug Fixes.
+    :pullreq: 8674
+
+    Debian postinst / do not fail on user creation if it already exists.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 8686
+
+    Fix parsing `dont-throttle-names` and `dont-throttle-netmasks` as comma separated lists.
 
 .. changelog::
   :version: 4.2.1
index dd189c8f6fe2c123295c0920b5e1cb909d8d0692..acb4ae32b02f4d0ab11f3837823faac9f8bbe274 100644 (file)
@@ -1,5 +1,161 @@
 Changelogs for 4.3.x
 ====================
+.. changelog::
+  :version: 4.3.3
+  :released: 17th of July 2020
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9330
+    :tickets: 9309
+
+    Validate cached DNSKEYs against the DSs, not the RRSIGs only.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9329
+    :tickets: 9297
+
+    Ignore cache-only for DNSKEYs and DS retrieval.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9328
+    :tickets: 9292
+
+    A ServFail while retrieving DS/DNSKEY records is just that.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9327
+    :tickets: 9188
+
+    Refuse DS records received from child zones.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9305
+    :tickets: 9268
+
+    Better exception handling in houseKeeping/handlePolicyHit.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9304
+    :tickets: 9299, 9301
+
+    Take initial refresh time from loaded zone.
+
+.. changelog::
+  :version: 4.3.2
+  :released: 1st of July 2020
+
+   .. change::
+     :tags: Bug Fixes
+     :pullreq: 9285
+
+     Backport of CVE-2020-14196: Enforce webserver ACL.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9262
+    :tickets: 9251
+
+    Copy the negative cache entry before validating it.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9242
+    :tickets: 9031
+
+    Fix compilation of the ports event multiplexer.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9243
+    :tickets: 9142
+
+    Defer the NOD lookup until after the response has been sent.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9245
+    :tickets: 9151
+
+    Fix the handling of DS queries for the root.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9246
+    :tickets: 9172
+
+    Fix RPZ removals when an update has several deltas.
+
+  .. change::
+    :tags: Bug Fixes.
+    :pullreq: 9247
+    :tickets: 9192, 9184
+
+    Correct depth increments.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9248
+    :tickets: 9194, 9202, 9216
+
+    CNAME loop detection.
+
+  .. change::
+    :tags: Bug Fixes.
+    :pullreq: 9249
+    :tickets: 9205
+
+    Limit the TTL of RRSIG records as well
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9128
+    :tickets: 9127
+
+    Fix compilation on systems that do not define HOST_NAME_MAX.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9122
+    :tickets: 8640
+
+    Fix build with gcc-10.
+
+.. changelog::
+  :version: 4.3.1
+  :released: 19th of May 2020
+
+  .. change::
+     :tags: Bug Fixes
+     :pullreq: 9115
+
+     Backport of security fixes for CVE-2020-10995, CVE-2020-12244 and
+     CVE-2020-10030, plus avoid a crash when loading an invalid RPZ.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9082
+
+    Add ubuntu focal target.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9048
+    :tickets: 8778
+
+    RPZ dumpFile/seedFile: store/get SOA refresh on dump/load.
+
+  .. change::
+    :tags: Internals
+    :pullreq: 8963
+    :tickets: 8875
+
+    Update boost.m4.
 
 .. changelog::
   :version: 4.3.0
@@ -170,7 +326,7 @@ Changelogs for 4.3.x
     :tags: Improvements
     :pullreq: 8440
 
-    Fix -WShadow warnings (Aki Tuomi)
+    Fix -Wshadow warnings (Aki Tuomi)
 
   .. change::
     :tags: Improvements
index b639496b2ca0511fdf55bcb443f804f4934bb05d..67ab2d20c034da028121ccc515ea6c6042498451 100644 (file)
@@ -1,6 +1,225 @@
 Changelogs for 4.4.x
 ====================
 
+.. changelog::
+  :version: 4.4.0-alpha2
+  :released: 20th of July 2020
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9320
+
+    Update proxy-protocol.cc (ihsinme).
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9308
+
+    Check that DNSKEYs have the zone flag set.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9314
+
+    Remove redundant toLogString() calls (Chris Hofstaedtler).
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9312
+
+    Stop cluttering the global namespace with validation states.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9231
+
+    Use explicit flag for the specific version of c++ we're targeting.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9303
+
+    Use new operator to print states.
+
+  .. change::
+    :tags: Internals, Bug Fixes
+    :pullreq: 9302
+
+    Kill an signed vs unsigned warning on OpenBSD.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9290
+
+    Refuse QType 0 right away, based on rfc6895 section 3.1.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9295
+
+    Specify a storage type for validation states.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9289
+
+    Common TCP write problems should only be logged if wanted.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9288
+
+    Dump the authority records of a negative cache entry as well.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9237
+
+    Don't validate a NXD with a NSEC proving that the name is an ENT.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9272
+    :tickets: 9266
+
+    Alternative way to do "skip cname check" for DS and DNSKEY records
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9267
+
+    Control stack depth when priming.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9252
+
+    Add version 'statistic' to prometheus.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9236
+
+    Cleanup cache cleaner pruneCollection function.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9226
+
+    Fix three shared cache issues.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9203
+
+    RPZ policy should override gettag_ffi answer by default.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9216
+
+    Don't copy the records when scanning for CNAME loops.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9213
+
+    Do not use `using namespace std;` .
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9202
+    :tickets: 9153, 9194
+
+    More sophisticated CNAME loop detection.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9205
+    :tickets: 9193
+
+    Limit the TTL of RRSIG records as well.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9207
+
+    Use std::string_view when available (Rosen Penev).
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9152
+
+    Make sure we can install unsigned packages.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9162
+
+    Clarify docs (Josh Soref).
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9073
+
+    Ensure runtime dirs for virtual services differ.
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9085
+    :tickets: 8094
+
+    Builder: improve shipped config files (Chris Hofstaedtler).
+
+  .. change::
+    :tags: Improvements
+    :pullreq: 9100
+
+    Less negatives in error messages improves readability.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9070
+
+    Boost 1.73 moved boost::bind placeholders to the placeholders namespace.
+
+  .. change::
+    :tags: Bug Fixes
+    :pullreq: 9079
+
+    Avoid throwing an exception in Logger::log().
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9076
+
+    Fix useless copies in loop reported by clang++ 10.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9078
+
+    NetmaskTree: do not test node for null, the loop guarantees node is not null.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9067
+
+    Wrap pthread objects
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9053
+
+    Get rid of a naked pointer in the /dev/poll event multiplexer.
+
+  .. change::
+    :tags: Internals, Improvements
+    :pullreq: 9016
+    :tickets: 9004
+
+    Random engine.
+
 .. changelog::
   :version: 4.4.0-alpha1
   :released: 22th of April 2020
index f7b57e354f001cfab6aa029826878f0e39499eef..a27342fbc3d75008de90adcd6ab776d39ba5e7a6 100644 (file)
@@ -92,12 +92,12 @@ The PowerDNS Recursor ships with the DNSSEC Root key built-in.
 Configuring DNSSEC key material must be done in the :ref:`setting-lua-config-file`, using :func:`addTA`.
 This function takes 2 arguments: the node in the DNS-tree and the data of the corresponding DS record.
 
-To e.g. add a trust anchor for the root and powerdns.com, use the following config in the Lua file:
+To e.g. add a trust anchor for the root and example.com, use the following config in the Lua file:
 
 .. code:: Lua
 
     addTA('.', "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a") -- This is not an ICANN root
-    addTA('powerdns.com', "44030 8 2 D4C3D5552B8679FAEEBC317E5F048B614B2E5F607DC57F1553182D49 AB2179F7")
+    addTA('example.com', "44030 8 2 D4C3D5552B8679FAEEBC317E5F048B614B2E5F607DC57F1553182D49 AB2179F7")
 
 For PowerDNS Recursor 4.1.x and below, use the :func:`addDS` function instead.
 
index 7c3adb6d6752cdf10e74a4837830c926aceda596..5e00612c7c50f5e4d9cf4ac99501e17e75bf7ed7 100644 (file)
@@ -34,7 +34,7 @@ Configuring the Recursor
 The configuration file is called ``recursor.conf`` and is located in the ``SYSCONFDIR`` defined at compile-time.
 This is usually ``/etc/powerdns``, ``/etc/pdns``, ``/etc/pdns-recursor``, ``/usr/local/etc`` or similar.
 
-Run ``pdns_recursor --no-config --config | grep config-dir`` to find this location on you installation.
+Run ``pdns_recursor --config=default | grep config-dir`` to find this location on you installation.
 
 The PowerDNS Recursor listens on the local loopback interface by default, this can be changed with the :ref:`setting-local-address` setting.
 
index 5ebfc86066e021e19309b90b82a5429d201102de..eed0c533a00d7f3b8deb6a917d7d341c3e4e4b2a 100644 (file)
@@ -64,7 +64,7 @@ If possible, supply the actual name of your domain and the IP address of your se
 I found a bug!
 ^^^^^^^^^^^^^^
 As much as we'd like to think we are perfect, bugs happen.
-If you have found a bug, please file a bug report on `GitHub <https://github.com/PowerDNS/pdns/issues/new?template=bug_report.md>`_.
+If you have found a bug, please file a bug report on `GitHub bug report <https://github.com/PowerDNS/pdns/issues/new?template=bug_report.md>`_.
 Please fill in the template and we'll try our best to help you.
 
 I found a security issue!
@@ -74,4 +74,4 @@ Please report this in private, see the :ref:`securitypolicy`.
 I have a good idea for a feature!
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 We like to work on new things!
-You can file a feature request on `GitHub <https://github.com/PowerDNS/pdns/issues/new?template=feature_request.md>`_.
+You can file a feature request on `GitHub feature request <https://github.com/PowerDNS/pdns/issues/new?template=feature_request.md>`_.
index 8cd75debf51a75a82bb49554b807c4398dcf9089..48836bb144b6b93d83ad6a894175e08407ede0d0 100644 (file)
@@ -112,6 +112,14 @@ tags
 
 List of tags as string, that will be added to the policy tags exported over protobuf when a policy of this zone matches.
 
+overridesGettag
+^^^^^^^^^^^^^^^
+.. versionadded:: 4.4.0
+
+`gettag_ffi` can set an answer to a query.
+By default an RPZ hit overrides this answer, unless this option is set to `false`.
+The default is `true`.
+
 zoneSizeHint
 ^^^^^^^^^^^^
 An indication of the number of expected entries in the zone, speeding up the loading of huge zones by reserving space in advance.
@@ -146,7 +154,7 @@ The default value of 0 means no restriction.
 localAddress
 ^^^^^^^^^^^^
 The source IP address to use when transferring the RPZ.
-When unset, :ref:`setting-query-local-address` and :ref:`setting-query-local-address6` are used.
+When unset, :ref:`setting-query-local-address` is used.
 
 axfrTimeout
 ^^^^^^^^^^^
index c91671a730d2ad68f3d42987e89a87d02ec212f9..0014afe8ff8269439ef904b25d4bfc2bbcb10edd 100644 (file)
@@ -50,8 +50,10 @@ A small example of the functionality of a :class:`DNSName` is shown below:
   .. method:: DNSName:equal(name) -> bool
 
     Returns true when both names are equal for the DNS, i.e case insensitive.
+    
+    To compare two ``DNSName`` objects, use ``==``.
 
-    :param DNSName name: The name to compare against.
+    :param DNSName string: The name to compare against.
 
   .. method:: DNSName:isPartOf(name) -> bool
 
index 1e74044b468c40235e72e33492acfd88a681a194..ddd9c6664a75999837889784bac05c85d9a40b86 100644 (file)
@@ -6,6 +6,7 @@ Please find it `here <https://github.com/PowerDNS/pdns/blob/master/pdns/recursor
 Queries can be intercepted in many places:
 
 -  before any packet parsing begins (:func:`ipfilter`)
+-  before the packet cache has been looked up (:func:`gettag` and its FFI counterpart, :func:`gettag_ffi`)
 -  before any filtering policy have been applied (:func:`prerpz`)
 -  before the resolving logic starts to work (:func:`preresolve`)
 -  after the resolving process failed to find a correct answer for a domain (:func:`nodata`, :func:`nxdomain`)
@@ -100,6 +101,18 @@ Interception Functions
 
     :return: ``tag`` [``, policyTags`` [``, data`` [``, reqId`` [``, deviceId`` [``, deviceName`` [``, routingTag`` ]]]]]]
 
+.. function:: gettag_ffi(param) -> optional Lua object
+
+    .. versionadded:: 4.1.2
+
+    .. versionchanged:: 4.3.0
+
+      The ability to craft answers was added.
+
+    This function is the FFI counterpart of the :func:`gettag` function, and offers the same functionality.
+    It accepts a single, scalable parameter which can be accessed using FFI accessors.
+    Like the non-FFI version, it has the ability to set a tag for the packetcache, policy tags, a routing tag, the :attr:`DNSQuestion.requestorId` and :attr:`DNSQuestion.deviceId`values and to fill the :attr:`DNSQuestion.data` table. It also offers ways to mark the answer as variable so it's not inserted into the packetcache, to set a cap on the TTL of the returned records, and to generate a response by adding records and setting the RCode. It can also instruct the recursor to do a proper resolution in order to follow any `CNAME` records added in this step.
+
 .. function:: prerpz(dq)
 
   This hook is called before any filtering policy have been applied,  making it possible to completely disable filtering by setting  :attr:`dq.wantsRPZ <DNSQuestion.wantsRPZ>` to false.
index be117f450129d398bfe74cbad1ae92f3e3fab73e..505224e6576d3faca5c7bb141de5b76cb5134627 100644 (file)
@@ -76,6 +76,8 @@ at `<https://doc.powerdns.com/>`
     Load root hints from this *filename*
 --local-address=<address>
     Listen on *address*, separated by spaces or commas.
+    Addresses specified can include port numbers; any which do not
+    include port numbers will listen on *--local-port*.
 --local-port=<port>
     Listen on *port*.
 --log-common-errors
@@ -88,12 +90,8 @@ at `<https://doc.powerdns.com/>`
     Maximum number of simultaneous TCP clients.
 --max-tcp-per-client=<num>
     If set, maximum number of TCP sessions per client (IP address).
---query-local-address=<address>
+--query-local-address=<address[,address...]>
     Use *address* as Source IP address when sending queries.
---query-local-address6=<address>
-    Send out local IPv6 queries from *address*. Disabled by default,
-    which also disables outgoing IPv6 support. A useful setting is
-    '::0'.
 --quiet
     Suppress logging of questions and answers.
 --server-id=<text>
index a9935ee5cb8e00720d2045df64f894581095ef48..54bcd2b8a5c26981ede544cd5a8f982c87ac0852 100644 (file)
@@ -41,7 +41,7 @@ Options
 --socket-pid=<pid>    When running in SMP mode, pid of **pdns_recursor** to
                       control.
 --timeout=<num>       Number of seconds to wait for the remote PowerDNS
-                      Recursor to respond. Set to 0 for infinite.
+                      Recursor to respond.
 
 Commands
 --------
index 8ee47de8f8b2aca4a32f495775892719cf83038a..aed4878d5b23724fa146a24028770f981af1ebaa 100644 (file)
@@ -59,7 +59,10 @@ Should Carbon not be the preferred way of receiving metric, several other techni
 
 Using the Webserver
 ^^^^^^^^^^^^^^^^^^^
-The :doc:`API <http-api/index>` exposes a statistics endpoint at :http:get:`/api/v1/servers/:server_id/statistics`.
+The :doc:`API <http-api/index>` exposes a statistics endpoint at
+
+.. http:get:: /api/v1/servers/:server_id/statistics
+              
 This endpoint exports all statistics in a single JSON document.
 
 Using ``rec_control``
@@ -252,7 +255,8 @@ number of outgoing queries dropped because of   :ref:`setting-dont-query` settin
 qname-min-fallback-success
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 .. versionadded:: 4.3.0
-number of successful queries due to fallback mechanism within :ref:`qname-minimization` setting.
+
+number of successful queries due to fallback mechanism within :ref:`setting-qname-minimization` setting.
 
 ecs-queries
 ^^^^^^^^^^^
diff --git a/pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-01.rst b/pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-01.rst
new file mode 100644 (file)
index 0000000..050436f
--- /dev/null
@@ -0,0 +1,33 @@
+PowerDNS Security Advisory 2020-01: Denial of Service
+=====================================================
+
+-  CVE: CVE-2020-10995
+-  Date: May 19th 2020
+-  Affects: PowerDNS Recursor from 4.1.0 up to and including 4.3.0
+-  Not affected: 4.1.16, 4.2.2, 4.3.1
+-  Severity: Medium
+-  Impact: Degraded Service
+-  Exploit: This problem can be triggered via a crafted reply
+-  Risk of system compromise: No
+-  Solution: Upgrade to a non-affected version
+-  Workaround: None
+
+An issue in the DNS protocol has been found that allow malicious
+parties to use recursive DNS services to attack third party
+authoritative name servers. The attack uses a crafted reply by an
+authoritative name server to amplify the resulting traffic between the
+recursive and other authoritative name servers.  Both types of service
+can suffer degraded performance as an effect.
+
+This issue has been assigned CVE-2020-10995.
+
+PowerDNS Recursor from 4.1.0 up to and including 4.3.0 is
+affected. PowerDNS Recursor 4.1.16, 4.2.2 and 4.3.1 contain a
+mitigation to limit the impact of this DNS protocol issue.
+
+Please note that at the time of writing, PowerDNS Recursor 4.0 and
+below are no longer supported, as described in
+https://doc.powerdns.com/recursor/appendices/EOL.html.
+
+We would like to thank Lior Shafir, Yehuda Afek and Anat Bremler-Barr
+for finding and subsequently reporting this issue!
diff --git a/pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-02.rst b/pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-02.rst
new file mode 100644 (file)
index 0000000..4c11151
--- /dev/null
@@ -0,0 +1,32 @@
+PowerDNS Security Advisory 2020-02: Insufficient validation of DNSSEC signatures
+================================================================================
+
+-  CVE: CVE-2020-12244
+-  Date: May 19th 2020
+-  Affects: PowerDNS Recursor from 4.1.0 up to and including 4.3.0
+-  Not affected: 4.3.1, 4.2.2, 4.1.16
+-  Severity: Medium
+-  Impact: Denial of existence spoofing
+-  Exploit: This problem can be triggered by an attacker in position
+   of man-in-the-middle
+-  Risk of system compromise: No
+-  Solution: Upgrade to a non-affected version
+-  Workaround: None
+
+An issue has been found in PowerDNS Recursor 4.1.0 through 4.3.0 where
+records in the answer section of a NXDOMAIN response lacking an SOA
+were not properly validated in SyncRes::processAnswer. This would
+allow an attacker in position of man-in-the-middle to send a NXDOMAIN
+answer for a name that does exist, bypassing DNSSEC validation.
+
+This issue has been assigned CVE-2020-12244.
+
+PowerDNS Recursor from 4.1.0 up to and including 4.3.0 is affected.
+
+Please note that at the time of writing, PowerDNS Authoritative 4.0 and
+below are no longer supported, as described in
+https://doc.powerdns.com/authoritative/appendices/EOL.html.
+
+We would like to thank Matt Nordhoff for finding and subsequently
+reporting this issue!
+
diff --git a/pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-03.rst b/pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-03.rst
new file mode 100644 (file)
index 0000000..db7e4e5
--- /dev/null
@@ -0,0 +1,38 @@
+PowerDNS Security Advisory 2020-03: Information disclosure
+==========================================================
+
+-  CVE: CVE-2020-10030
+-  Date: May 19th 2020
+-  Affects: PowerDNS Recursor from 4.1.0 up to and including 4.3.0
+-  Not affected: 4.3.1, 4.2.2, 4.1.16
+-  Severity: Low
+-  Impact: Information Disclosure, Denial of Service
+-  Exploit: This problem can be triggered via a crafted hostname
+-  Risk of system compromise: No
+-  Solution: Upgrade to a non-affected version
+-  Workaround: None
+
+An issue has been found in PowerDNS Recursor allowing an
+attacker with enough privileges to change the system's hostname to
+cause disclosure of uninitialized memory content via a stack-based
+out-of-bounds read.
+It only occurs on systems where gethostname() does not null-terminate
+the returned string if the hostname is larger than the supplied buffer.
+Linux systems are not affected because the buffer is always large enough.
+OpenBSD systems are not affected because the returned hostname is always
+null-terminated.
+Under some conditions this issue can lead to the writing of one null-byte
+out-of-bounds on the stack, causing a denial of service or possibly
+arbitrary code execution.
+
+This issue has been assigned CVE-2020-10030.
+
+PowerDNS Recursor from 4.1.0 up to and including 4.3.0 is affected.
+
+Please note that at the time of writing, PowerDNS Recursor 4.0 and
+below are no longer supported, as described in
+https://doc.powerdns.com/recursor/appendices/EOL.html.
+
+We would like to thank Valentei Sergey for finding and subsequently
+reporting this issue!
+
diff --git a/pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-04.rst b/pdns/recursordist/docs/security-advisories/powerdns-advisory-2020-04.rst
new file mode 100644 (file)
index 0000000..5f99c01
--- /dev/null
@@ -0,0 +1,27 @@
+PowerDNS Security Advisory 2020-04: Access restriction bypass
+=============================================================
+
+-  CVE: CVE-2020-14196
+-  Date: July 1st 2020
+-  Affects: PowerDNS Recursor up to and including 4.3.1, 4.2.2 and 4.1.16
+-  Not affected: 4.3.2, 4.2.3, 4.1.17
+-  Severity: Low
+-  Impact: Access restriction bypass
+-  Exploit: This problem can be triggered by sending HTTP queries
+-  Risk of system compromise: No
+-  Solution: Upgrade to a non-affected version 
+-  Workaround: Disable the webserver, set a password or an API key.
+   Additionally, restrict the binding address using the
+   `webserver-address` setting to local addresses only and/or use a
+   firewall to disallow web requests from untrusted sources reaching the
+   webserver listening address.
+
+An issue has been found in PowerDNS Recursor where the ACL applied to
+the internal web server via `webserver-allow-from` is not properly
+enforced, allowing a remote attacker to send HTTP queries to the
+internal web server, bypassing the restriction.
+In the default configuration the API webserver is not enabled. Only
+installations using a non-default value for `webserver` and
+`webserver-address` are affected.
+
index bec850bd0b4a58657e50b5cdee0e2ea25378f181..d82c616988a284d07b14a0f22e0b7fbf6dfa6a7d 100644 (file)
@@ -16,8 +16,8 @@ As an example:
 
 ``allow-from``
 --------------
--  IP ranges, separated by commas
--  Default: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
+-  IP addresses or netmasks, separated by commas
+-  Default: 127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10
 
 Netmasks (both IPv4 and IPv6) that are allowed to use the server.
 The default allows access only from :rfc:`1918` private IP addresses.
@@ -26,6 +26,8 @@ Questions from IP addresses not listed here are ignored and do not get an answer
 
 When the Proxy Protocol is enabled (see `proxy-protocol-from`_), the recursor will check the address of the client IP advertised in the Proxy Protocol header instead of the one of the proxy.
 
+Note that specifying an IP address without a netmask uses an implicit netmask of /32 or /128.
+
 .. _setting-allow-from-file:
 
 ``allow-from-file``
@@ -121,7 +123,7 @@ Not recommended unless you have to tick an 'RFC 2181 compliant' box.
 --------------
 -  Comma separated list of 'zonename=filename' pairs
 
-Zones read from these files (in BIND format) are served authoritatively.
+Zones read from these files (in BIND format) are served authoritatively (but without the AA bit set in responses).
 DNSSEC is not supported. Example:
 
 .. code-block:: none
@@ -543,7 +545,7 @@ The IP address sent via EDNS Client Subnet to authoritative servers listed in
 `edns-subnet-whitelist`_ when `use-incoming-edns-subnet`_ is set and the query has
 an ECS source prefix-length set to 0.
 The default is to look for the first usable (not an ``any`` one) address in
-`query-local-address`_ then `query-local-address6`_. If no suitable address is
+`query-local-address`_ (starting with IPv4). If no suitable address is
 found, the recursor fallbacks to sending 127.0.0.1.
 
 .. _setting-edns-outgoing-bufsize:
@@ -715,14 +717,22 @@ Indication of how many queries will be averaged to get the average latency repor
 
 ``local-address``
 -----------------
--  IP addresses, comma separated
--  Default: 127.0.0.1
+-  IPv4/IPv6 Addresses, with optional port numbers, separated by commas or whitespace
+-  Default: ``0.0.0.0, ::``
 
-Local IPv4 or IPv6 addresses to bind to.
-Addresses can also contain port numbers, for IPv4 specify like this: ``192.0.2.4:5300``, for IPv6: ``[::1]:5300``.
+Local IP addresses to which we bind. Each address specified can
+include a port number; if no port is included then the
+:ref:`setting-local-port` port will be used for that address. If a
+port number is specified, it must be separated from the address with a
+':'; for an IPv6 address the address must be enclosed in square
+brackets.
 
-**Warning**: When binding to wildcard addresses, UNIX semantics mean that answers may not be sent from the address a query was received on.
-It is highly recommended to bind to explicit addresses.
+Examples::
+
+  local-address=127.0.0.1 ::1
+  local-address=0.0.0.0:5353
+  local-address=[::]:8053
+  local-address=127.0.0.1:53, [::1]:5353
 
 .. _setting-local-port:
 
@@ -877,7 +887,7 @@ Maximum number of seconds to cache an item in the DNS cache, no matter what the
 
     The minimum value of this setting is 15. i.e. setting this to lower than 15 will make this value 15.
 
-.. _setting max-concurrent-requests-per-tcp-connection:
+.. _setting-max-concurrent-requests-per-tcp-connection:
 
 ``max-concurrent-requests-per-tcp-connection``
 ----------------------------------------------
@@ -937,6 +947,26 @@ This is used to limit endlessly chasing CNAME redirections.
 If qname-minimization is enabled, the number will be forced to be 100
 at a minimum to allow for the extra queries qname-minimization generates when the cache is empty.
 
+.. _setting-max-ns-address-qperq:
+
+``max-ns-address-qperq``
+------------------------
+.. versionadded:: 4.1.16
+.. versionadded:: 4.2.2
+.. versionadded:: 4.3.1
+
+-  Integer
+-  Default: 10
+
+The maximum number of outgoing queries with empty replies for
+resolving nameserver names to addresses we allow during the resolution
+of a single client query. If IPv6 is enabled, an A and a AAAA query
+for a name counts as 1. If a zone publishes more than this number of
+NS records, the limit is further reduced for that zone by lowering
+it by the number of NS records found above the
+`max-ns-address-qperq`_ value. The limit wil not be reduced to a
+number lower than 5.
+
 .. _setting-max-negative-ttl:
 
 ``max-negative-ttl``
@@ -1226,10 +1256,10 @@ Maximum number of seconds to cache a 'server failure' answer in the packet cache
 -  Boolean
 -  Default: yes
 
-If set, PowerDNS will have only 1 thread listening on client sockets, and distribute work by itself over threads by using a hash of the query,
-maximizing the cache hit ratio. Starting with version 4.2.0, more than one distributing thread can be started using the `distributor-threads`_
-setting.
-Improves performance on Linux.
+If set, PowerDNS will use distinct threads to listen to client sockets and distribute that work to worker-threads using a hash of the query.
+This feature should maximize the cache hit ratio.
+To use more than one thread set `distributor-threads` in version 4.2.0 or newer.
+Enabling should improve performance for medium sized resolvers.
 
 .. _setting-protobuf-use-kernel-timestamp:
 
@@ -1248,7 +1278,7 @@ Whether to compute the latency of responses in protobuf messages using the times
 -----------------------
 .. versionadded:: 4.4.0
 
--  IP ranges, separated by commas
+-  IP addresses or netmasks, separated by commas
 -  Default: empty
 
 Ranges that are required to send a Proxy Protocol version 2 header in front of UDP and TCP queries, to pass the original source and destination addresses and ports to the recursor, as well as custom values.
@@ -1294,15 +1324,24 @@ described in :rfc:`7816`.
 
 ``query-local-address``
 -----------------------
--  IPv4 Address, comma separated
+.. versionchanged:: 4.4.0
+  IPv6 addresses can be set with this option as well.
+
+-  IP addresses, comma separated
 -  Default: 0.0.0.0
 
-Send out local queries from this address, or addresses, by adding multiple addresses, increased spoofing resilience is achieved.
+Send out local queries from this address, or addresses. By adding multiple
+addresses, increased spoofing resilience is achieved. When no address of a certain
+address family is configured, there are *no* queries sent with that address family.
+In the default configuration this means that IPv6 is not used for outgoing queries.
 
 .. _setting-query-local-address6:
 
 ``query-local-address6``
 ------------------------
+.. deprecated:: 4.4.0
+  Use :ref:`setting-query-local-address` for IPv4 and IPv6.
+
 -  IPv6 addresses, comma separated
 -  Default: unset
 
@@ -1318,6 +1357,19 @@ Disabled by default, which also disables outgoing IPv6 support.
 
 Don't log queries.
 
+.. _setting-record-cache-shards
+
+``record-cache-shards``
+------------------------
+.. versionadded:: 4.4.0
+-  Integer
+-  Default: 1024
+
+Sets the number of shards in the record cache. If you have high
+contention as reported by
+``record-cache-contented/record-cache-acquired``, you can try to
+enlarge this value or run with fewer threads.
+
 .. _setting-reuseport:
 
 ``reuseport``
@@ -1325,9 +1377,9 @@ Don't log queries.
 -  Boolean
 -  Default: no
 
-If ``SO_REUSEPORT`` support is available, allows multiple processes to open a listening socket on the same port.
+If ``SO_REUSEPORT`` support is available, allows multiple threads and processes to open listening sockets for the same port.
 
-Since 4.1.0, when ``pdns-distributes-queries`` is set to false and ``reuseport`` is enabled, every thread will open a separate listening socket to let the kernel distribute the incoming queries, avoiding any thundering herd issue as well as the distributor thread being a bottleneck, thus leading to much higher performance on multi-core boxes.
+Since 4.1.0, when ``pdns-distributes-queries`` is set to false and ``reuseport`` is enabled, every worker-thread will open a separate listening socket to let the kernel distribute the incoming queries instead of running a distributor thread (which could otherwise be a bottleneck) and avoiding thundering herd issues, thus leading to much higher performance on multi-core boxes.
 
 .. _setting-rng:
 
@@ -1801,14 +1853,16 @@ IP address for the webserver to listen on.
 
 ``webserver-allow-from``
 ------------------------
--  IP addresses, comma separated
+-  IP addresses or netmasks, comma separated
 -  Default: 127.0.0.1,::1
 
 .. versionchanged:: 4.1.0
 
-    Default is now 127.0.0.1,::1, was 0.0.0.0,::/0 before.
+    Default is now 127.0.0.1,::1, was 0.0.0.0/0,::/0 before.
 
-These subnets are allowed to access the webserver.
+These IPs and subnets are allowed to access the webserver. Note that
+specifying an IP address without a netmask uses an implicit netmask
+of /32 or /128.
 
 .. _setting-webserver-loglevel:
 
@@ -1849,7 +1903,7 @@ When set to "detailed", all information about the request and response are logge
 The value between the hooks is a UUID that is generated for each request. This can be used to find all lines related to a single request.
 
 .. note::
-  The webserver logs these line on the NOTICE level. The :ref:`settings-loglevel` seting must be 5 or higher for these lines to end up in the log.
+  The webserver logs these line on the NOTICE level. The :ref:`setting-loglevel` seting must be 5 or higher for these lines to end up in the log.
 
 .. _setting-webserver-password:
 
@@ -1884,7 +1938,7 @@ If a PID file should be written to `socket-dir`_
 ------------------
 .. versionadded:: 4.2.0
 
--  IP ranges, separated by commas
+-  IP addresses or netmasks, separated by commas
 -  Default: empty
 
 .. note::
index ffe2f7a77535c6c61eba6b4b270d698ec9531fe1..7ea9588fe79b3b5331c1327890f9ce90e6295693 100644 (file)
@@ -37,11 +37,11 @@ Packages provided on `the PowerDNS Repository <https://repo.powerdns.com>`__ wil
 
 New settings
 ^^^^^^^^^^^^
-- The :ref:`allow-trust-anchor-query` setting has been added. This setting controls if negative trust anchors can be queried. The default is `no`.
-- The :ref:`max-concurrent-requests-per-tcp-connection` has been added. This setting controls how many requests are handled concurrently per incoming TCP connection. The default is 10.
-- The :ref:`max-generate-steps` setting has been added. This sets the maximum number of steps that will be performed when loading a BIND zone with the ``$GENERATE`` directive. The default is 0, which is unlimited.
-- The :ref:`nothing-below-nxdomain` setting has been added. This setting controls the way cached NXDOMAIN replies imply non-existence of a whole subtree. The default is `dnssec` which means that only DNSSEC validated NXDOMAINS results are used.
-- The :ref:`qname-minimization` setting has been added. This options controls if QName Minimization is used. The default is `yes`.
+- The :ref:`setting-allow-trust-anchor-query` setting has been added. This setting controls if negative trust anchors can be queried. The default is `no`.
+- The :ref:`setting-max-concurrent-requests-per-tcp-connection` has been added. This setting controls how many requests are handled concurrently per incoming TCP connection. The default is 10.
+- The :ref:`setting-max-generate-steps` setting has been added. This sets the maximum number of steps that will be performed when loading a BIND zone with the ``$GENERATE`` directive. The default is 0, which is unlimited.
+- The :ref:`setting-nothing-below-nxdomain` setting has been added. This setting controls the way cached NXDOMAIN replies imply non-existence of a whole subtree. The default is `dnssec` which means that only DNSSEC validated NXDOMAINS results are used.
+- The :ref:`setting-qname-minimization` setting has been added. This options controls if QName Minimization is used. The default is `yes`.
   
 4.1.x to 4.2.0
 --------------
index a48107e761c633f0f8cb057635897ed6636c6b6e..e512579dcad32967d86037e77c9e9ca55da04e90 100644 (file)
@@ -35,7 +35,7 @@
  * \param ne       A NegCacheEntry that is filled when there is a cache entry
  * \return         true if ne was filled out, false otherwise
  */
-bool NegCache::getRootNXTrust(const DNSName& qname, const struct timeval& now, const NegCacheEntry** ne)
+bool NegCache::getRootNXTrust(const DNSName& qname, const struct timeval& now, NegCacheEntry& ne)
 {
   // Never deny the root.
   if (qname.isRoot())
@@ -49,7 +49,7 @@ bool NegCache::getRootNXTrust(const DNSName& qname, const struct timeval& now, c
   while (ni != d_negcache.end() && ni->d_name == lastLabel && ni->d_auth.isRoot() && ni->d_qtype == qtnull) {
     // We have something
     if ((uint32_t)now.tv_sec < ni->d_ttd) {
-      *ne = &(*ni);
+      ne = *ni;
       moveCacheItemToBack<SequenceTag>(d_negcache, ni);
       return true;
     }
@@ -68,7 +68,7 @@ bool NegCache::getRootNXTrust(const DNSName& qname, const struct timeval& now, c
  * \param ne       A NegCacheEntry that is filled when there is a cache entry
  * \return         true if ne was filled out, false otherwise
  */
-bool NegCache::get(const DNSName& qname, const QType& qtype, const struct timeval& now, const NegCacheEntry** ne, bool typeMustMatch)
+bool NegCache::get(const DNSName& qname, const QType& qtype, const struct timeval& now, NegCacheEntry& ne, bool typeMustMatch)
 {
   const auto& idx = d_negcache.get<2>();
   auto range = idx.equal_range(qname);
@@ -82,7 +82,7 @@ bool NegCache::get(const DNSName& qname, const QType& qtype, const struct timeva
 
       if ((uint32_t)now.tv_sec < ni->d_ttd) {
         // Not expired
-        *ne = &(*ni);
+        ne = *ni;
         moveCacheItemToBack<SequenceTag>(d_negcache, firstIndexIterator);
         return true;
       }
@@ -183,7 +183,7 @@ void NegCache::clear()
  *
  * \param maxEntries The maximum number of entries that may exist in the cache.
  */
-void NegCache::prune(unsigned int maxEntries)
+void NegCache::prune(size_t maxEntries)
 {
   pruneCollection<SequenceTag>(*this, d_negcache, maxEntries, 200);
 }
@@ -202,12 +202,18 @@ uint64_t NegCache::dumpToFile(FILE* fp)
   negcache_sequence_t& sidx = d_negcache.get<SequenceTag>();
   for (const NegCacheEntry& ne : sidx) {
     ret++;
-    fprintf(fp, "%s %" PRId64 " IN %s VIA %s ; (%s)\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), ne.d_qtype.getName().c_str(), ne.d_auth.toString().c_str(), vStates[ne.d_validationState]);
+    fprintf(fp, "%s %" PRId64 " IN %s VIA %s ; (%s)\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), ne.d_qtype.getName().c_str(), ne.d_auth.toString().c_str(), vStateToString(ne.d_validationState).c_str());
+    for (const auto& rec : ne.authoritySOA.records) {
+      fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", rec.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.d_content->getZoneRepresentation().c_str(), vStateToString(ne.d_validationState).c_str());
+    }
+    for (const auto& sig : ne.authoritySOA.signatures) {
+      fprintf(fp, "%s %" PRId64 " IN RRSIG %s ;\n", sig.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), sig.d_content->getZoneRepresentation().c_str());
+    }
     for (const auto& rec : ne.DNSSECRecords.records) {
-      fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.d_content->getZoneRepresentation().c_str(), vStates[ne.d_validationState]);
+      fprintf(fp, "%s %" PRId64 " IN %s %s ; (%s)\n", rec.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), DNSRecordContent::NumberToType(rec.d_type).c_str(), rec.d_content->getZoneRepresentation().c_str(), vStateToString(ne.d_validationState).c_str());
     }
     for (const auto& sig : ne.DNSSECRecords.signatures) {
-      fprintf(fp, "%s %" PRId64 " IN RRSIG %s ;\n", ne.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), sig.d_content->getZoneRepresentation().c_str());
+      fprintf(fp, "%s %" PRId64 " IN RRSIG %s ;\n", sig.d_name.toString().c_str(), static_cast<int64_t>(ne.d_ttd - now.tv_sec), sig.d_content->getZoneRepresentation().c_str());
     }
   }
   return ret;
index 55200ff4a257a4d65c78efc8413d405999bb772a..adb0202008649c49b90fcf027ddf1737e26e6287 100644 (file)
@@ -55,7 +55,7 @@ public:
     mutable uint32_t d_ttd; // Timestamp when this entry should die
     recordsAndSignatures authoritySOA; // The upstream SOA record and RRSIGs
     recordsAndSignatures DNSSECRecords; // The upstream NSEC(3) and RRSIGs
-    mutable vState d_validationState{Indeterminate};
+    mutable vState d_validationState{vState::Indeterminate};
     uint32_t getTTD() const
     {
       return d_ttd;
@@ -64,11 +64,11 @@ public:
 
   void add(const NegCacheEntry& ne);
   void updateValidationStatus(const DNSName& qname, const QType& qtype, const vState newState, boost::optional<uint32_t> capTTD);
-  bool get(const DNSName& qname, const QType& qtype, const struct timeval& now, const NegCacheEntry** ne, bool typeMustMatch = false);
-  bool getRootNXTrust(const DNSName& qname, const struct timeval& now, const NegCacheEntry** ne);
+  bool get(const DNSName& qname, const QType& qtype, const struct timeval& now, NegCacheEntry& ne, bool typeMustMatch = false);
+  bool getRootNXTrust(const DNSName& qname, const struct timeval& now, NegCacheEntry& ne);
   uint64_t count(const DNSName& qname) const;
   uint64_t count(const DNSName& qname, const QType qtype) const;
-  void prune(unsigned int maxEntries);
+  void prune(size_t maxEntries);
   void clear();
   uint64_t dumpToFile(FILE* fd);
   uint64_t wipe(const DNSName& name, bool subtree = false);
diff --git a/pdns/recursordist/query-local-address.cc b/pdns/recursordist/query-local-address.cc
new file mode 120000 (symlink)
index 0000000..fd2ad3c
--- /dev/null
@@ -0,0 +1 @@
+../query-local-address.cc
\ No newline at end of file
diff --git a/pdns/recursordist/query-local-address.hh b/pdns/recursordist/query-local-address.hh
new file mode 120000 (symlink)
index 0000000..18e39c8
--- /dev/null
@@ -0,0 +1 @@
+../query-local-address.hh
\ No newline at end of file
index 5a96f2fd0ac6a536625204fe7d1f24862343749b..babb3bbd19cad92a8fb28287ca7101cd3c08e367 100644 (file)
@@ -60,13 +60,13 @@ BOOST_AUTO_TEST_CASE(test_get_entry)
 
   BOOST_CHECK_EQUAL(cache.size(), 1U);
 
-  const NegCache::NegCacheEntry* ne = nullptr;
-  bool ret = cache.get(qname, QType(1), now, &ne);
+  NegCache::NegCacheEntry ne;
+  bool ret = cache.get(qname, QType(1), now, ne);
 
   BOOST_CHECK(ret);
-  BOOST_CHECK_EQUAL(ne->d_name, qname);
-  BOOST_CHECK_EQUAL(ne->d_qtype.getName(), QType(0).getName());
-  BOOST_CHECK_EQUAL(ne->d_auth, auth);
+  BOOST_CHECK_EQUAL(ne.d_name, qname);
+  BOOST_CHECK_EQUAL(ne.d_qtype.getName(), QType(0).getName());
+  BOOST_CHECK_EQUAL(ne.d_auth, auth);
 }
 
 BOOST_AUTO_TEST_CASE(test_get_entry_exact_type)
@@ -85,11 +85,10 @@ BOOST_AUTO_TEST_CASE(test_get_entry_exact_type)
 
   BOOST_CHECK_EQUAL(cache.size(), 1U);
 
-  const NegCache::NegCacheEntry* ne = nullptr;
-  bool ret = cache.get(qname, QType(1), now, &ne, true);
+  NegCache::NegCacheEntry ne;
+  bool ret = cache.get(qname, QType(1), now, ne, true);
 
   BOOST_CHECK_EQUAL(ret, false);
-  BOOST_CHECK(ne == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(test_get_NODATA_entry)
@@ -105,18 +104,17 @@ BOOST_AUTO_TEST_CASE(test_get_NODATA_entry)
 
   BOOST_CHECK_EQUAL(cache.size(), 1U);
 
-  const NegCache::NegCacheEntry* ne = nullptr;
-  bool ret = cache.get(qname, QType(1), now, &ne);
+  NegCache::NegCacheEntry ne;
+  bool ret = cache.get(qname, QType(1), now, ne);
 
   BOOST_CHECK(ret);
-  BOOST_CHECK_EQUAL(ne->d_name, qname);
-  BOOST_CHECK_EQUAL(ne->d_qtype.getName(), QType(1).getName());
-  BOOST_CHECK_EQUAL(ne->d_auth, auth);
+  BOOST_CHECK_EQUAL(ne.d_name, qname);
+  BOOST_CHECK_EQUAL(ne.d_qtype.getName(), QType(1).getName());
+  BOOST_CHECK_EQUAL(ne.d_auth, auth);
 
-  const NegCache::NegCacheEntry* ne2 = nullptr;
-  ret = cache.get(qname, QType(16), now, &ne2);
+  NegCache::NegCacheEntry ne2;
+  ret = cache.get(qname, QType(16), now, ne2);
   BOOST_CHECK_EQUAL(ret, false);
-  BOOST_CHECK(ne2 == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(test_getRootNXTrust_entry)
@@ -132,13 +130,13 @@ BOOST_AUTO_TEST_CASE(test_getRootNXTrust_entry)
 
   BOOST_CHECK_EQUAL(cache.size(), 1U);
 
-  const NegCache::NegCacheEntry* ne = nullptr;
-  bool ret = cache.getRootNXTrust(qname, now, &ne);
+  NegCache::NegCacheEntry ne;
+  bool ret = cache.getRootNXTrust(qname, now, ne);
 
   BOOST_CHECK(ret);
-  BOOST_CHECK_EQUAL(ne->d_name, qname);
-  BOOST_CHECK_EQUAL(ne->d_qtype.getName(), QType(0).getName());
-  BOOST_CHECK_EQUAL(ne->d_auth, auth);
+  BOOST_CHECK_EQUAL(ne.d_name, qname);
+  BOOST_CHECK_EQUAL(ne.d_qtype.getName(), QType(0).getName());
+  BOOST_CHECK_EQUAL(ne.d_auth, auth);
 }
 
 BOOST_AUTO_TEST_CASE(test_add_and_get_expired_entry)
@@ -155,13 +153,12 @@ BOOST_AUTO_TEST_CASE(test_add_and_get_expired_entry)
 
   BOOST_CHECK_EQUAL(cache.size(), 1U);
 
-  const NegCache::NegCacheEntry* ne = nullptr;
+  NegCache::NegCacheEntry ne;
 
   now.tv_sec += 1000;
-  bool ret = cache.get(qname, QType(1), now, &ne);
+  bool ret = cache.get(qname, QType(1), now, ne);
 
   BOOST_CHECK_EQUAL(ret, false);
-  BOOST_CHECK(ne == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(test_getRootNXTrust_expired_entry)
@@ -178,13 +175,12 @@ BOOST_AUTO_TEST_CASE(test_getRootNXTrust_expired_entry)
 
   BOOST_CHECK_EQUAL(cache.size(), 1U);
 
-  const NegCache::NegCacheEntry* ne = nullptr;
+  NegCache::NegCacheEntry ne;
 
   now.tv_sec += 1000;
-  bool ret = cache.getRootNXTrust(qname, now, &ne);
+  bool ret = cache.getRootNXTrust(qname, now, ne);
 
   BOOST_CHECK_EQUAL(ret, false);
-  BOOST_CHECK(ne == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(test_add_updated_entry)
@@ -203,12 +199,12 @@ BOOST_AUTO_TEST_CASE(test_add_updated_entry)
 
   BOOST_CHECK_EQUAL(cache.size(), 1U);
 
-  const NegCache::NegCacheEntry* ne = nullptr;
-  bool ret = cache.get(qname, QType(1), now, &ne);
+  NegCache::NegCacheEntry ne;
+  bool ret = cache.get(qname, QType(1), now, ne);
 
   BOOST_CHECK(ret);
-  BOOST_CHECK_EQUAL(ne->d_name, qname);
-  BOOST_CHECK_EQUAL(ne->d_auth, auth2);
+  BOOST_CHECK_EQUAL(ne.d_name, qname);
+  BOOST_CHECK_EQUAL(ne.d_auth, auth2);
 }
 
 BOOST_AUTO_TEST_CASE(test_getRootNXTrust)
@@ -225,12 +221,12 @@ BOOST_AUTO_TEST_CASE(test_getRootNXTrust)
   cache.add(genNegCacheEntry(qname, auth, now));
   cache.add(genNegCacheEntry(qname2, auth2, now));
 
-  const NegCache::NegCacheEntry* ne = nullptr;
-  bool ret = cache.getRootNXTrust(qname, now, &ne);
+  NegCache::NegCacheEntry ne;
+  bool ret = cache.getRootNXTrust(qname, now, ne);
 
   BOOST_CHECK(ret);
-  BOOST_CHECK_EQUAL(ne->d_name, qname2);
-  BOOST_CHECK_EQUAL(ne->d_auth, auth2);
+  BOOST_CHECK_EQUAL(ne.d_name, qname2);
+  BOOST_CHECK_EQUAL(ne.d_auth, auth2);
 }
 
 BOOST_AUTO_TEST_CASE(test_getRootNXTrust_full_domain_only)
@@ -247,11 +243,10 @@ BOOST_AUTO_TEST_CASE(test_getRootNXTrust_full_domain_only)
   cache.add(genNegCacheEntry(qname, auth, now));
   cache.add(genNegCacheEntry(qname2, auth2, now, 1)); // Add the denial for COM|A
 
-  const NegCache::NegCacheEntry* ne = nullptr;
-  bool ret = cache.getRootNXTrust(qname, now, &ne);
+  NegCache::NegCacheEntry ne;
+  bool ret = cache.getRootNXTrust(qname, now, ne);
 
   BOOST_CHECK_EQUAL(ret, false);
-  BOOST_CHECK(ne == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(test_prune)
@@ -301,11 +296,11 @@ BOOST_AUTO_TEST_CASE(test_prune_valid_entries)
   cache.prune(1);
   BOOST_CHECK_EQUAL(cache.size(), 1U);
 
-  const NegCache::NegCacheEntry* got = nullptr;
-  bool ret = cache.get(power2, QType(1), now, &got);
+  NegCache::NegCacheEntry got;
+  bool ret = cache.get(power2, QType(1), now, got);
   BOOST_REQUIRE(ret);
-  BOOST_CHECK_EQUAL(got->d_name, power2);
-  BOOST_CHECK_EQUAL(got->d_auth, auth);
+  BOOST_CHECK_EQUAL(got.d_name, power2);
+  BOOST_CHECK_EQUAL(got.d_auth, auth);
 
   /* insert power1 back */
   ne = genNegCacheEntry(power1, auth, now);
@@ -323,11 +318,11 @@ BOOST_AUTO_TEST_CASE(test_prune_valid_entries)
   cache.prune(1);
 
   BOOST_CHECK_EQUAL(cache.size(), 1U);
-  got = nullptr;
-  ret = cache.get(power2, QType(1), now, &got);
+  got = NegCache::NegCacheEntry();
+  ret = cache.get(power2, QType(1), now, got);
   BOOST_REQUIRE(ret);
-  BOOST_CHECK_EQUAL(got->d_name, power2);
-  BOOST_CHECK_EQUAL(got->d_auth, auth);
+  BOOST_CHECK_EQUAL(got.d_name, power2);
+  BOOST_CHECK_EQUAL(got.d_auth, auth);
 }
 
 BOOST_AUTO_TEST_CASE(test_wipe_single)
@@ -354,20 +349,18 @@ BOOST_AUTO_TEST_CASE(test_wipe_single)
   cache.wipe(auth);
   BOOST_CHECK_EQUAL(cache.size(), 400U);
 
-  const NegCache::NegCacheEntry* ne2 = nullptr;
-  bool ret = cache.get(auth, QType(1), now, &ne2);
+  NegCache::NegCacheEntry ne2;
+  bool ret = cache.get(auth, QType(1), now, ne2);
 
   BOOST_CHECK_EQUAL(ret, false);
-  BOOST_CHECK(ne2 == nullptr);
 
   cache.wipe(DNSName("1.powerdns.com"));
   BOOST_CHECK_EQUAL(cache.size(), 399U);
 
-  const NegCache::NegCacheEntry* ne3 = nullptr;
-  ret = cache.get(auth, QType(1), now, &ne3);
+  NegCache::NegCacheEntry ne3;
+  ret = cache.get(auth, QType(1), now, ne3);
 
   BOOST_CHECK_EQUAL(ret, false);
-  BOOST_CHECK(ne3 == nullptr);
 }
 
 BOOST_AUTO_TEST_CASE(test_wipe_subtree)
@@ -424,11 +417,15 @@ BOOST_AUTO_TEST_CASE(test_dumpToFile)
   NegCache cache;
   vector<string> expected;
   expected.push_back("www1.powerdns.com. 600 IN TYPE0 VIA powerdns.com. ; (Indeterminate)\n");
-  expected.push_back("www1.powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n");
-  expected.push_back("www1.powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
+  expected.push_back("powerdns.com. 600 IN SOA ns1. hostmaster. 1 2 3 4 5 ; (Indeterminate)\n");
+  expected.push_back("powerdns.com. 600 IN RRSIG SOA 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
+  expected.push_back("powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n");
+  expected.push_back("powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
   expected.push_back("www2.powerdns.com. 600 IN TYPE0 VIA powerdns.com. ; (Indeterminate)\n");
-  expected.push_back("www2.powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n");
-  expected.push_back("www2.powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
+  expected.push_back("powerdns.com. 600 IN SOA ns1. hostmaster. 1 2 3 4 5 ; (Indeterminate)\n");
+  expected.push_back("powerdns.com. 600 IN RRSIG SOA 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
+  expected.push_back("powerdns.com. 600 IN NSEC deadbeef. ; (Indeterminate)\n");
+  expected.push_back("powerdns.com. 600 IN RRSIG NSEC 5 3 600 20370101000000 20370101000000 24567 dummy. data ;\n");
 
   struct timeval now;
   Utility::gettimeofday(&now, 0);
@@ -436,19 +433,19 @@ BOOST_AUTO_TEST_CASE(test_dumpToFile)
   cache.add(genNegCacheEntry(DNSName("www1.powerdns.com"), DNSName("powerdns.com"), now));
   cache.add(genNegCacheEntry(DNSName("www2.powerdns.com"), DNSName("powerdns.com"), now));
 
-  FILE* fp = tmpfile();
+  auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(tmpfile(), fclose);
   if (!fp)
     BOOST_FAIL("Temporary file could not be opened");
 
-  cache.dumpToFile(fp);
+  cache.dumpToFile(fp.get());
 
-  rewind(fp);
+  rewind(fp.get());
   char* line = nullptr;
   size_t len = 0;
   ssize_t read;
 
   for (const auto& str : expected) {
-    read = getline(&line, &len, fp);
+    read = getline(&line, &len, fp.get());
     if (read == -1)
       BOOST_FAIL("Unable to read a line from the temp file");
     BOOST_CHECK_EQUAL(line, str);
@@ -460,8 +457,6 @@ BOOST_AUTO_TEST_CASE(test_dumpToFile)
        last allocation if any. */
     free(line);
   }
-
-  fclose(fp);
 }
 
 BOOST_AUTO_TEST_CASE(test_count)
index 54fa05b1f6711689d2dda5def78dda3d1ec001a2..ab18ae9d489cef886217cbdc73baf23a11e5a4f5 100644 (file)
@@ -1087,7 +1087,7 @@ BOOST_AUTO_TEST_CASE(test_RecursorCacheTagged)
     BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), boost::none), 0);
     BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
 
-    // if no tag given and no-non-tagged entries matches nothing shoudl be returned
+    // if no tag given and no-non-tagged entries matches nothing should be returned
     BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), 0);
     BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
 
index 16fb153b5183de526a44c89eaa03103fcb9a2d93..8d4d70345361fe8a6d431b450ae185c867ea8aea 100644 (file)
 
 // Provide stubs for some symbols
 bool g_logRPZChanges{false};
-ComboAddress getQueryLocalAddress(int family, uint16_t port)
-{
-  cerr << "getQueryLocalAddress() STUBBED IN TEST!" << endl;
-  BOOST_ASSERT(false);
-  return ComboAddress();
-}
 
 BOOST_AUTO_TEST_SUITE(rpzloader_cc)
 
index b1f89dd345f6ac79f1e6b137f628d22f8cfdf95c..07660b08a8ced870f15673e404e3df4c3a2cf702 100644 (file)
@@ -20,7 +20,7 @@ static bool checkBasicMessage1(const PDNSException& ex)
 
 static bool checkBasicMessage2(const PDNSException& ex)
 {
-  BOOST_CHECK_EQUAL(ex.reason, "RCODE was not NOERROR but " + RCode::to_s(1));
+  BOOST_CHECK_EQUAL(ex.reason, "RCODE was " + RCode::to_s(1));
   return true;
 }
 
index 1eec4369cd65065f81fa54aa4e38716a094f56b9..af352b31eb18c9e0134c7ee2e58dc9def4368fd2 100644 (file)
@@ -22,7 +22,7 @@ ArgvMap& arg()
   return theArg;
 }
 
-void primeRootNSZones(bool)
+void primeRootNSZones(bool, unsigned int)
 {
 }
 
@@ -44,7 +44,7 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d
 
 #include "root-addresses.hh"
 
-void primeHints(void)
+bool primeHints(void)
 {
   vector<DNSRecord> nsset;
   if (!s_RC)
@@ -79,6 +79,7 @@ void primeHints(void)
     nsset.push_back(nsrr);
   }
   s_RC->replace(time(nullptr), g_rootdnsname, QType(QType::NS), nsset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false); // and stuff in the cache
+  return true;
 }
 
 LuaConfigItems::LuaConfigItems()
@@ -108,6 +109,7 @@ void initSR(bool debug)
   s_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
 
   SyncRes::s_maxqperq = 50;
+  SyncRes::s_maxnsaddressqperq = 10;
   SyncRes::s_maxtotusec = 1000 * 7000;
   SyncRes::s_maxdepth = 40;
   SyncRes::s_maxnegttl = 3600;
@@ -117,6 +119,7 @@ void initSR(bool debug)
   SyncRes::s_packetcacheservfailttl = 60;
   SyncRes::s_serverdownmaxfails = 64;
   SyncRes::s_serverdownthrottletime = 60;
+  SyncRes::s_doIPv4 = true;
   SyncRes::s_doIPv6 = true;
   SyncRes::s_ecsipv4limit = 24;
   SyncRes::s_ecsipv6limit = 56;
index 3bb11de1002fe9a46220292a6bf97ffb45fe5277..49672a6bcebe1a3767813ebe9cdaf43b8273c00e 100644 (file)
@@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE(test_root_primed)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::AAAA), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK(ret[0].d_type == QType::AAAA);
   BOOST_CHECK_EQUAL(ret[0].d_name, target);
@@ -1125,8 +1125,85 @@ BOOST_AUTO_TEST_CASE(test_cname_loop)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::ServFail);
-  BOOST_CHECK_GT(ret.size(), 0U);
+  BOOST_CHECK_EQUAL(ret.size(), 0U);
   BOOST_CHECK_EQUAL(count, 2U);
+
+  // Again to check cache
+  try {
+    res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+    BOOST_CHECK(false);
+  }
+  catch (const ImmediateServFailException& ex) {
+    BOOST_CHECK(true);
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_cname_long_loop)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr);
+
+  primeHints();
+
+  size_t count = 0;
+  const DNSName target1("cname1.powerdns.com.");
+  const DNSName target2("cname2.powerdns.com.");
+  const DNSName target3("cname3.powerdns.com.");
+  const DNSName target4("cname4.powerdns.com.");
+
+  sr->setAsyncCallback([target1, target2, target3, target4, &count](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    count++;
+
+    if (isRootServer(ip)) {
+
+      setLWResult(res, 0, false, false, true);
+      addRecordToLW(res, domain, QType::NS, "a.gtld-servers.net.", DNSResourceRecord::AUTHORITY, 172800);
+      addRecordToLW(res, "a.gtld-servers.net.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+      return 1;
+    }
+    else if (ip == ComboAddress("192.0.2.1:53")) {
+
+      if (domain == target1) {
+        setLWResult(res, 0, true, false, false);
+        addRecordToLW(res, domain, QType::CNAME, target2.toString());
+        return 1;
+      }
+      else if (domain == target2) {
+        setLWResult(res, 0, true, false, false);
+        addRecordToLW(res, domain, QType::CNAME, target3.toString());
+        return 1;
+      }
+      else if (domain == target3) {
+        setLWResult(res, 0, true, false, false);
+        addRecordToLW(res, domain, QType::CNAME, target4.toString());
+        return 1;
+      }
+      else if (domain == target4) {
+        setLWResult(res, 0, true, false, false);
+        addRecordToLW(res, domain, QType::CNAME, target1.toString());
+        return 1;
+      }
+
+      return 1;
+    }
+
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::ServFail);
+  BOOST_CHECK_EQUAL(ret.size(), 0U);
+  BOOST_CHECK_EQUAL(count, 8U);
+
+  // And again to check cache
+  try {
+    res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
+    BOOST_CHECK(false);
+  }
+  catch (const ImmediateServFailException& ex) {
+    BOOST_CHECK(true);
+  }
 }
 
 BOOST_AUTO_TEST_CASE(test_cname_depth)
@@ -1455,7 +1532,7 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure)
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
 
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 5U); /* DNAME + RRSIG(DNAME) + CNAME + A + RRSIG(A) */
 
   BOOST_CHECK_EQUAL(queries, 11U);
@@ -1481,7 +1558,7 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_secure)
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
 
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 5U); /* DNAME + RRSIG(DNAME) + CNAME + A + RRSIG(A) */
 
   BOOST_CHECK_EQUAL(queries, 11U);
@@ -1602,7 +1679,7 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_insecure)
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
 
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U); /* DNAME + RRSIG(DNAME) + CNAME + A */
 
   BOOST_CHECK_EQUAL(queries, 9U);
@@ -1625,7 +1702,7 @@ BOOST_AUTO_TEST_CASE(test_dname_dnssec_insecure)
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
 
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U); /* DNAME + RRSIG(DNAME) + CNAME + A */
 
   BOOST_CHECK_EQUAL(queries, 9U);
diff --git a/pdns/recursordist/test-syncres_cc10.cc b/pdns/recursordist/test-syncres_cc10.cc
new file mode 100644 (file)
index 0000000..08641de
--- /dev/null
@@ -0,0 +1,138 @@
+#define BOOST_TEST_DYN_LINK
+#include <boost/test/unit_test.hpp>
+
+#include "test-syncres_cc.hh"
+
+BOOST_AUTO_TEST_SUITE(syncres_cc10)
+BOOST_AUTO_TEST_CASE(test_outgoing_v4_only)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr);
+  SyncRes::s_doIPv6 = false;
+  primeHints();
+  bool v6Hit = false;
+  bool v4Hit = false;
+  int queries = 0;
+
+  const DNSName target("powerdns.com.");
+  sr->setAsyncCallback([target, &v4Hit, &v6Hit, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queries++;
+    if (isRootServer(ip)) {
+      setLWResult(res, 0, false, false, true);
+      v4Hit |= ip.isIPv4();
+      v6Hit |= ip.isIPv6();
+
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::AAAA, "2001:DB8:1::53", DNSResourceRecord::ADDITIONAL, 3600);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+      }
+      return 1;
+    }
+    else if (ip == ComboAddress("192.0.2.1:53")) {
+      setLWResult(res, 0, true, false, false);
+      v4Hit |= true;
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+      }
+      return 1;
+    }
+    else if (ip == ComboAddress("[2001:DB8:1::53]:53")) {
+      setLWResult(res, 0, true, false, false);
+      v6Hit |= true;
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+      }
+      return 1;
+    }
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int rcode;
+  rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_REQUIRE_EQUAL(queries, 2);
+  BOOST_REQUIRE_EQUAL(v4Hit, true);
+  BOOST_REQUIRE_EQUAL(v6Hit, false);
+  BOOST_CHECK_EQUAL(rcode, RCode::NoError);
+  BOOST_CHECK_EQUAL(ret.size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(test_outgoing_v4_only_no_A_in_delegation)
+{
+  // The name is not resolvable, as there's no A glue for an in-bailiwick NS
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr);
+  SyncRes::s_doIPv6 = false;
+  primeHints();
+  int queries = 0;
+
+  const DNSName target("powerdns.com.");
+  sr->setAsyncCallback([target, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queries++;
+    if (isRootServer(ip)) {
+      setLWResult(res, 0, false, false, true);
+
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::AAAA, "2001:DB8:1::53", DNSResourceRecord::ADDITIONAL, 3600);
+      }
+      return 1;
+    }
+    else if (ip == ComboAddress("[2001:DB8:1::53]:53")) {
+      setLWResult(res, 0, true, false, false);
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+      }
+      return 1;
+    }
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int rcode;
+  rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_REQUIRE_EQUAL(queries, 14); // We keep trying all parent nameservers, this is wrong!
+  BOOST_CHECK_EQUAL(rcode, RCode::ServFail);
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(test_outgoing_v6_only_no_AAAA_in_delegation)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr);
+  SyncRes::s_doIPv4 = false;
+  SyncRes::s_doIPv6 = true;
+  primeHints();
+  int queries = 0;
+
+  const DNSName target("powerdns.com.");
+  sr->setAsyncCallback([target, &queries](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queries++;
+    if (isRootServer(ip)) {
+      setLWResult(res, 0, false, false, true);
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+      }
+      return 1;
+    }
+    else if (ip == ComboAddress("192.0.2.1:53")) {
+      setLWResult(res, 0, true, false, false);
+      if (domain == DNSName("powerdns.com.")) {
+        addRecordToLW(res, domain, QType::A, "192.0.2.2");
+      }
+      return 1;
+    }
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int rcode;
+  rcode = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_REQUIRE_EQUAL(queries, 14); // The recursor tries all parent nameservers... this needs to be fixed
+  BOOST_CHECK_EQUAL(rcode, RCode::ServFail);
+  BOOST_CHECK_EQUAL(ret.size(), 0);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
index cc804adc592d38448443357a373db8b99019d591..28f43c21b231a26a69d4bde890e546d96811a2ec 100644 (file)
@@ -56,7 +56,7 @@ static void do_test_referral_depth(bool limited)
 
   if (limited) {
     /* Set the maximum depth low */
-    SyncRes::s_maxdepth = 10;
+    SyncRes::s_maxdepth = 4;
     try {
       vector<DNSRecord> ret;
       sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
@@ -655,7 +655,7 @@ BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath_dnssec)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_CHECK_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
   BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
@@ -663,7 +663,7 @@ BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath_dnssec)
   ret.clear();
   res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_CHECK_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
   BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
@@ -671,7 +671,7 @@ BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath_dnssec)
   ret.clear();
   res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_CHECK_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
   BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
@@ -679,7 +679,7 @@ BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath_dnssec)
   ret.clear();
   res = sr->beginResolve(target4, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_CHECK_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
   BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
@@ -691,7 +691,7 @@ BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath_dnssec)
   ret.clear();
   res = sr->beginResolve(target1, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_CHECK_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
   BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 1U);
@@ -700,7 +700,7 @@ BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath_dnssec)
   ret.clear();
   res = sr->beginResolve(target2, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_CHECK_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
   BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 2U);
@@ -708,7 +708,7 @@ BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath_dnssec)
   ret.clear();
   res = sr->beginResolve(target3, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_CHECK_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 13U);
   BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 3U);
@@ -716,7 +716,7 @@ BOOST_AUTO_TEST_CASE(test_rfc8020_nothing_underneath_dnssec)
   ret.clear();
   res = sr->beginResolve(target4, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_CHECK_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 15U);
   BOOST_CHECK_EQUAL(SyncRes::getNegCacheSize(), 4U);
@@ -1275,6 +1275,49 @@ BOOST_AUTO_TEST_CASE(test_completely_flawed_nsset)
   BOOST_CHECK_EQUAL(queriesCount, 5U);
 }
 
+BOOST_AUTO_TEST_CASE(test_completely_flawed_big_nsset)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr);
+
+  primeHints();
+
+  const DNSName target("powerdns.com.");
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([&queriesCount, target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queriesCount++;
+
+    if (isRootServer(ip) && domain == target) {
+      setLWResult(res, 0, false, false, true);
+      // 20 NS records
+      for (int i = 0; i < 20; i++) {
+        string n = string("pdns-public-ns") + std::to_string(i) + string(".powerdns.com.");
+        addRecordToLW(res, domain, QType::NS, n, DNSResourceRecord::AUTHORITY, 172800);
+      }
+      return 1;
+    }
+    else if (domain.toString().length() > 14 && domain.toString().substr(0, 14) == "pdns-public-ns") {
+      setLWResult(res, 0, true, false, true);
+      addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
+      return 1;
+    }
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  try {
+    sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+    BOOST_CHECK(0);
+  }
+  catch (const ImmediateServFailException& ex) {
+    BOOST_CHECK_EQUAL(ret.size(), 0U);
+    // one query to get NSs, then A and AAAA for each NS, 5th NS hits the limit
+    // limit is reduced to 5, because zone publishes many (20) NS
+    BOOST_CHECK_EQUAL(queriesCount, 11U);
+  }
+}
+
 BOOST_AUTO_TEST_CASE(test_cache_hit)
 {
   std::unique_ptr<SyncRes> sr;
index 2fc7e325bfdd1e9038e724003bb8a275c9ae8f66..abf4680ac240a0da520dc8398ac54a5c62ba0ac9 100644 (file)
@@ -869,7 +869,7 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_rd_dnssec)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 5U);
 
@@ -877,7 +877,7 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_rd_dnssec)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 5U);
 }
@@ -940,7 +940,7 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_rd_dnssec_bogus)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 5U);
 
@@ -948,7 +948,7 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_rd_dnssec_bogus)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 5U);
 }
@@ -1003,7 +1003,7 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_rd_dnssec_nodata_bogus)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 0U);
   /* com|NS, powerdns.com|NS, powerdns.com|A */
   BOOST_CHECK_EQUAL(queriesCount, 3U);
@@ -1012,7 +1012,7 @@ BOOST_AUTO_TEST_CASE(test_forward_zone_recurse_rd_dnssec_nodata_bogus)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 0U);
   /* we don't store empty results */
   BOOST_CHECK_EQUAL(queriesCount, 4U);
@@ -1054,7 +1054,7 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_oob)
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 0U);
   BOOST_CHECK(sr->wasOutOfBand());
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
 
   /* a second time, to check that the OOB flag is set when the query cache is used */
   ret.clear();
@@ -1064,7 +1064,7 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_oob)
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 0U);
   BOOST_CHECK(sr->wasOutOfBand());
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
 
   /* a third time, to check that the validation is disabled when the OOB flag is set */
   ret.clear();
@@ -1075,7 +1075,7 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_oob)
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 0U);
   BOOST_CHECK(sr->wasOutOfBand());
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
 }
 
 BOOST_AUTO_TEST_CASE(test_auth_zone_oob_cname)
@@ -1123,7 +1123,7 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_oob_cname)
   BOOST_CHECK(ret[1].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 0U);
   BOOST_CHECK(sr->wasOutOfBand());
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
 
   /* a second time, to check that the OOB flag is set when the query cache is used */
   ret.clear();
@@ -1134,7 +1134,7 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_oob_cname)
   BOOST_CHECK(ret[1].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 0U);
   BOOST_CHECK(sr->wasOutOfBand());
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
 
   /* a third time, to check that the validation is disabled when the OOB flag is set */
   ret.clear();
@@ -1146,7 +1146,7 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_oob_cname)
   BOOST_CHECK(ret[1].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 0U);
   BOOST_CHECK(sr->wasOutOfBand());
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
 }
 
 BOOST_AUTO_TEST_CASE(test_auth_zone)
index f879409d4ab56668f5e7d2b0b35478a718c99d37..5971b9163b57cd7e00ee0063ef453763f5e7d146 100644 (file)
@@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE(test_auth_zone_delegation)
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
 }
 
 BOOST_AUTO_TEST_CASE(test_auth_zone_delegation_point)
@@ -516,7 +516,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_csk)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
@@ -525,7 +525,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_csk)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
 }
@@ -604,7 +604,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_ksk_zsk)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
@@ -613,7 +613,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_root_validation_ksk_zsk)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
 }
@@ -670,7 +670,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_dnskey)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
@@ -679,18 +679,171 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_dnskey)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
 }
 
-BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_doesnt_match_ds)
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_without_zone_flag)
 {
   std::unique_ptr<SyncRes> sr;
   initSR(sr, true);
 
   setDNSSECValidation(sr, DNSSECMode::ValidateAll);
 
+  primeHints();
+  const DNSName target(".");
+  testkeysset_t keys;
+
+  /* Generate key material for "." */
+  auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dcke->create(dcke->getBits());
+  DNSSECPrivateKey csk;
+  csk.d_flags = 0;
+  csk.setKey(dcke);
+  DSRecordContent ds = makeDSFromDNSKey(target, csk.getDNSKEY(), DNSSECKeeper::DIGEST_SHA256);
+
+  keys[target] = std::pair<DNSSECPrivateKey, DSRecordContent>(csk, ds);
+
+  /* Set the root DS */
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  luaconfsCopy.dsAnchors[g_rootdnsname].insert(ds);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target, &queriesCount, keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queriesCount++;
+
+    if (domain == target && type == QType::NS) {
+
+      setLWResult(res, 0, true, false, true);
+      char addr[] = "a.root-servers.net.";
+      for (char idx = 'a'; idx <= 'm'; idx++) {
+        addr[0] = idx;
+        addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+      }
+
+      addRRSIG(keys, res->d_records, domain, 300);
+      addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+      addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+      return 1;
+    }
+    else if (domain == target && type == QType::DNSKEY) {
+
+      setLWResult(res, 0, true, false, true);
+
+      addDNSKEY(keys, domain, 300, res->d_records);
+      addRRSIG(keys, res->d_records, domain, 300);
+
+      return 1;
+    }
+
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14U);
+  BOOST_CHECK_EQUAL(queriesCount, 2U);
+
+  /* again, to test the cache */
+  ret.clear();
+  res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+  BOOST_REQUIRE_EQUAL(ret.size(), 14U);
+  BOOST_CHECK_EQUAL(queriesCount, 2U);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_revoked)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr, true);
+
+  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+  primeHints();
+  const DNSName target(".");
+  testkeysset_t keys;
+
+  /* Generate key material for "." */
+  auto dcke = std::shared_ptr<DNSCryptoKeyEngine>(DNSCryptoKeyEngine::make(DNSSECKeeper::ECDSA256));
+  dcke->create(dcke->getBits());
+  DNSSECPrivateKey csk;
+  csk.d_flags = 257 | 128;
+  csk.setKey(dcke);
+  DSRecordContent ds = makeDSFromDNSKey(target, csk.getDNSKEY(), DNSSECKeeper::DIGEST_SHA256);
+
+  keys[target] = std::pair<DNSSECPrivateKey, DSRecordContent>(csk, ds);
+
+  /* Set the root DS */
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  luaconfsCopy.dsAnchors[g_rootdnsname].insert(ds);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target, &queriesCount, keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queriesCount++;
+
+    if (domain == target && type == QType::NS) {
+
+      setLWResult(res, 0, true, false, true);
+      char addr[] = "a.root-servers.net.";
+      for (char idx = 'a'; idx <= 'm'; idx++) {
+        addr[0] = idx;
+        addRecordToLW(res, domain, QType::NS, std::string(addr), DNSResourceRecord::ANSWER, 3600);
+      }
+
+      addRRSIG(keys, res->d_records, domain, 300);
+      addRecordToLW(res, "a.root-servers.net.", QType::A, "198.41.0.4", DNSResourceRecord::ADDITIONAL, 3600);
+      addRecordToLW(res, "a.root-servers.net.", QType::AAAA, "2001:503:ba3e::2:30", DNSResourceRecord::ADDITIONAL, 3600);
+
+      return 1;
+    }
+    else if (domain == target && type == QType::DNSKEY) {
+
+      setLWResult(res, 0, true, false, true);
+
+      addDNSKEY(keys, domain, 300, res->d_records);
+      addRRSIG(keys, res->d_records, domain, 300);
+
+      return 1;
+    }
+
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14U);
+  BOOST_CHECK_EQUAL(queriesCount, 2U);
+
+  /* again, to test the cache */
+  ret.clear();
+  res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+  BOOST_REQUIRE_EQUAL(ret.size(), 14U);
+  BOOST_CHECK_EQUAL(queriesCount, 2U);
+}
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_doesnt_match_ds)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr, true);
+
+  setDNSSECValidation(sr, DNSSECMode::Process);
+
   primeHints();
   const DNSName target(".");
   testkeysset_t dskeys;
@@ -754,10 +907,12 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_doesnt_match_ds)
     return 0;
   });
 
+  /* === with validation enabled === */
+  sr->setDNSSECValidationRequested(true);
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
@@ -766,9 +921,42 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_dnskey_doesnt_match_ds)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
+
+  /* === first without validation, then with (just-in-time validation) === */
+  /* clear the caches */
+  s_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
+  SyncRes::clearNegCache();
+  sr->setDNSSECValidationRequested(false);
+  primeHints();
+
+  ret.clear();
+  res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
+  /* 13 NS + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 14U);
+  BOOST_CHECK_EQUAL(queriesCount, 3U);
+
+  /* now we ask for the DNSKEYs (still without validation) */
+  ret.clear();
+  res = sr->beginResolve(target, QType(QType::DNSKEY), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
+  /* 1 SOA + 1 RRSIG */
+  BOOST_REQUIRE_EQUAL(ret.size(), 2U);
+  BOOST_CHECK_EQUAL(queriesCount, 4U);
+
+  /* again, to test the cache WITH validation */
+  sr->setDNSSECValidationRequested(true);
+  ret.clear();
+  res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+  BOOST_REQUIRE_EQUAL(ret.size(), 14U);
+  BOOST_CHECK_EQUAL(queriesCount, 4U);
 }
 
 BOOST_AUTO_TEST_CASE(test_dnssec_bogus_rrsig_signed_with_unknown_dnskey)
@@ -834,7 +1022,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_rrsig_signed_with_unknown_dnskey)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
@@ -843,7 +1031,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_rrsig_signed_with_unknown_dnskey)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
 }
@@ -904,7 +1092,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_rrsig)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   /* 13 NS + 0 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 13U);
   /* no RRSIG so no query for DNSKEYs */
@@ -914,7 +1102,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_no_rrsig)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 13U);
   /* check that we capped the TTL to max-cache-bogus-ttl */
   for (const auto& record : ret) {
@@ -991,7 +1179,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_algorithm)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   /* no supported DS so no query for DNSKEYs */
@@ -1001,7 +1189,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_algorithm)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
 }
@@ -1072,7 +1260,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_digest)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   /* no supported DS so no query for DNSKEYs */
@@ -1082,7 +1270,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_unknown_ds_digest)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
 }
@@ -1141,7 +1329,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_bad_sig)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
@@ -1150,7 +1338,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_bad_sig)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
 }
@@ -1210,7 +1398,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_bad_algo)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
@@ -1219,7 +1407,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_bad_algo)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 2U);
 }
@@ -1285,7 +1473,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_unsigned_ds)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 3U);
 
@@ -1293,7 +1481,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_unsigned_ds)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 3U);
 
@@ -1301,7 +1489,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_unsigned_ds)
   ret.clear();
   res = sr->beginResolve(DNSName("com."), QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 3U);
 }
@@ -1359,7 +1547,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_unsigned_ds_direct)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(DNSName("com."), QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
 }
index 48b566d21b26e8f06814c21aefe842c0da673653..29f94e23c2e01495d8c6ea23c939c5415f9244a4 100644 (file)
@@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_various_algos)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 
@@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_various_algos)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 }
@@ -189,7 +189,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_a_then_ns)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 
@@ -197,7 +197,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_a_then_ns)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 
@@ -206,7 +206,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_a_then_ns)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
 }
@@ -290,7 +290,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_a_then_ns)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 7U);
 
@@ -298,7 +298,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_a_then_ns)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 7U);
 
@@ -307,7 +307,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_a_then_ns)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 }
@@ -399,7 +399,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_with_nta)
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
   /* Should be insecure because of the NTA */
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 5U);
 
@@ -408,7 +408,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_with_nta)
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
   /* Should be insecure because of the NTA */
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 5U);
 }
@@ -488,7 +488,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_with_nta)
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
   /* Should be insecure because of the NTA */
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
 
@@ -496,7 +496,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_with_nta)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
 }
@@ -579,7 +579,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 
@@ -587,7 +587,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 }
@@ -695,7 +695,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nxdomain_nsec)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
 
@@ -703,7 +703,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nxdomain_nsec)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
 }
@@ -804,7 +804,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
 
@@ -812,7 +812,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
 }
@@ -884,7 +884,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_nodata_nowildcard)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
 
@@ -892,7 +892,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_nodata_nowildcard)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
 }
@@ -975,7 +975,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 8U);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
 
@@ -983,7 +983,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 8U);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
 }
@@ -1068,7 +1068,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard_duplicated_n
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   /* because we pass along the duplicated NSEC3 */
   BOOST_REQUIRE_EQUAL(ret.size(), 9U);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
@@ -1077,7 +1077,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard_duplicated_n
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   /* because we pass along the duplicated NSEC3 */
   BOOST_REQUIRE_EQUAL(ret.size(), 9U);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
@@ -1162,7 +1162,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard_too_many_ite
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 8U);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
 
@@ -1170,7 +1170,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_nodata_nowildcard_too_many_ite
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 8U);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
 }
@@ -1280,7 +1280,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_wildcard)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 10U);
 
@@ -1288,7 +1288,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_wildcard)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 10U);
 }
@@ -1395,7 +1395,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_wildcard_too_many_iterations)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
 
@@ -1403,7 +1403,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec3_wildcard_too_many_iterations)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
 }
@@ -1501,7 +1501,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard_missing)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
 
@@ -1509,7 +1509,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_nsec_wildcard_missing)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
 }
@@ -1564,7 +1564,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_wildcard_expanded_onto_itself)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   /* A + RRSIG, NSEC + RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
 }
@@ -1626,7 +1626,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_wildcard_like_expanded_from_wildcard
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   /* A + RRSIG, NSEC + RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
 }
@@ -1692,14 +1692,14 @@ BOOST_AUTO_TEST_CASE(test_dnssec_incomplete_cache_zonecut_qm)
       if (domain == hero && type == QType::NS) {
         setLWResult(res, 0, false, false, true);
         addRecordToLW(res, hero, QType::NS, "dns1.p03.nsone.net.", DNSResourceRecord::AUTHORITY, 3600);
-        addDS(DNSName(hero), 300, res->d_records, keys);
-        addRRSIG(keys, res->d_records, DNSName(hero), 300);
+        addDS(hero, 300, res->d_records, keys);
+        addRRSIG(keys, res->d_records, com, 300);
       }
       else if (domain == nsone && type == QType::A) {
         setLWResult(res, 0, false, false, true);
         addRecordToLW(res, nsone, QType::NS, "dns1.p01.nsone.net.", DNSResourceRecord::AUTHORITY, 3600);
         addNSECRecordToLW(nsone, DNSName("zzz.nsone.net."), {QType::NS, QType::SOA, QType::RRSIG, QType::DNSKEY}, 600, res->d_records);
-        addRRSIG(keys, res->d_records, nsone, 300);
+        addRRSIG(keys, res->d_records, net, 300);
         addRecordToLW(res, "dns1.p01.nsone.net", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
       }
       else {
@@ -1746,16 +1746,237 @@ BOOST_AUTO_TEST_CASE(test_dnssec_incomplete_cache_zonecut_qm)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(DNSName("herokuapp.com."), QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 12U);
 
   ret.clear();
   res = sr->beginResolve(DNSName("dns1.p03.nsone.net."), QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 16U);
 }
 
+BOOST_AUTO_TEST_CASE(test_dnssec_secure_servfail_ds)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr, true);
+
+  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+  primeHints();
+  const DNSName target("powerdns.com.");
+  const ComboAddress targetAddr("192.0.2.42");
+  testkeysset_t keys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+  generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  /* make sure that the signature inception and validity times are computed
+     based on the SyncRes time, not the current one, in case the function
+     takes too long. */
+
+  const time_t fixedNow = sr->getNow().tv_sec;
+
+  sr->setAsyncCallback([target, targetAddr, &queriesCount, keys, fixedNow](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queriesCount++;
+
+    DNSName auth = domain;
+    if (domain == target) {
+      auth = DNSName("powerdns.com.");
+    }
+
+    if (type == QType::DS && domain == DNSName("powerdns.com.")) {
+      /* time out */
+      return 0;
+    }
+
+    if (type == QType::DS || type == QType::DNSKEY) {
+      return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, true, fixedNow);
+    }
+
+    if (isRootServer(ip)) {
+      setLWResult(res, 0, false, false, true);
+      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
+      addDS(DNSName("com."), 300, res->d_records, keys);
+      addRRSIG(keys, res->d_records, DNSName("."), 300, false, boost::none, boost::none, fixedNow);
+      addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+      return 1;
+    }
+
+    if (ip == ComboAddress("192.0.2.1:53")) {
+      if (domain == DNSName("com.")) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
+        addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
+        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+        addRRSIG(keys, res->d_records, domain, 300);
+      }
+      else {
+        setLWResult(res, 0, false, false, true);
+        addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
+        /* do NOT include the DS here */
+        //addDS(auth, 300, res->d_records, keys);
+        //addRRSIG(keys, res->d_records, DNSName("com."), 300, false, boost::none, boost::none, fixedNow);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+      }
+      return 1;
+    }
+
+    if (ip == ComboAddress("192.0.2.2:53")) {
+      if (type == QType::NS) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
+        addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+        addRRSIG(keys, res->d_records, auth, 300);
+      }
+      else {
+        setLWResult(res, RCode::NoError, true, false, true);
+        addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
+        addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
+      }
+      return 1;
+    }
+
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  try {
+    sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+    BOOST_CHECK(false);
+  }
+  catch (const ImmediateServFailException& e) {
+    BOOST_CHECK(e.reason.find("Server Failure while retrieving DS records for powerdns.com") != string::npos);
+  }
+
+  /* and a second time to check nothing was cached */
+  try {
+    sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+    BOOST_CHECK(false);
+  }
+  catch (const ImmediateServFailException& e) {
+    BOOST_CHECK(e.reason.find("Server Failure while retrieving DS records for powerdns.com") != string::npos);
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_secure_servfail_dnskey)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr, true);
+
+  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+  primeHints();
+  const DNSName target("powerdns.com.");
+  const ComboAddress targetAddr("192.0.2.42");
+  testkeysset_t keys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+  generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  /* make sure that the signature inception and validity times are computed
+     based on the SyncRes time, not the current one, in case the function
+     takes too long. */
+
+  const time_t fixedNow = sr->getNow().tv_sec;
+
+  sr->setAsyncCallback([target, targetAddr, &queriesCount, keys, fixedNow](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queriesCount++;
+
+    DNSName auth = domain;
+    if (domain == target) {
+      auth = DNSName("powerdns.com.");
+    }
+
+    if (type == QType::DNSKEY && domain == DNSName("powerdns.com.")) {
+      /* time out */
+      return 0;
+    }
+
+    if (type == QType::DS || type == QType::DNSKEY) {
+      return genericDSAndDNSKEYHandler(res, domain, auth, type, keys, true, fixedNow);
+    }
+
+    if (isRootServer(ip)) {
+      setLWResult(res, 0, false, false, true);
+      addRecordToLW(res, "com.", QType::NS, "a.gtld-servers.com.", DNSResourceRecord::AUTHORITY, 3600);
+      addDS(DNSName("com."), 300, res->d_records, keys);
+      addRRSIG(keys, res->d_records, DNSName("."), 300, false, boost::none, boost::none, fixedNow);
+      addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+      return 1;
+    }
+
+    if (ip == ComboAddress("192.0.2.1:53")) {
+      if (domain == DNSName("com.")) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, domain, QType::NS, "a.gtld-servers.com.");
+        addRRSIG(keys, res->d_records, domain, 300, false, boost::none, boost::none, fixedNow);
+        addRecordToLW(res, "a.gtld-servers.com.", QType::A, "192.0.2.1", DNSResourceRecord::ADDITIONAL, 3600);
+        addRRSIG(keys, res->d_records, domain, 300);
+      }
+      else {
+        setLWResult(res, 0, false, false, true);
+        addRecordToLW(res, auth, QType::NS, "ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 3600);
+        addDS(auth, 300, res->d_records, keys);
+        addRRSIG(keys, res->d_records, DNSName("com."), 300, false, boost::none, boost::none, fixedNow);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+      }
+      return 1;
+    }
+
+    if (ip == ComboAddress("192.0.2.2:53")) {
+      if (type == QType::NS) {
+        setLWResult(res, 0, true, false, true);
+        addRecordToLW(res, domain, QType::NS, "ns1.powerdns.com.");
+        addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
+        addRecordToLW(res, "ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 3600);
+        addRRSIG(keys, res->d_records, auth, 300);
+      }
+      else {
+        setLWResult(res, RCode::NoError, true, false, true);
+        addRecordToLW(res, domain, QType::A, targetAddr.toString(), DNSResourceRecord::ANSWER, 3600);
+        addRRSIG(keys, res->d_records, auth, 300, false, boost::none, boost::none, fixedNow);
+      }
+      return 1;
+    }
+
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  try {
+    sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+    BOOST_CHECK(false);
+  }
+  catch (const ImmediateServFailException& e) {
+    BOOST_CHECK(e.reason.find("Server Failure while retrieving DNSKEY records for powerdns.com") != string::npos);
+  }
+
+  /* and a second time to check nothing was cached */
+  try {
+    sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+    BOOST_CHECK(false);
+  }
+  catch (const ImmediateServFailException& e) {
+    BOOST_CHECK(e.reason.find("Server Failure while retrieving DNSKEY records for powerdns.com") != string::npos);
+  }
+}
+
 BOOST_AUTO_TEST_SUITE_END()
index ca0852f9c8720fcc66335e0cfa8f0e472d2522c8..7c93429b8dd028bd7ac4348035e3980323c126b1 100644 (file)
@@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_no_ds_on_referral_secure)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
   BOOST_CHECK_EQUAL(dsQueriesCount, 3U);
@@ -117,7 +117,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_no_ds_on_referral_secure)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
   BOOST_CHECK_EQUAL(dsQueriesCount, 3U);
@@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_ds_sign_loop)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 
@@ -233,11 +233,61 @@ BOOST_AUTO_TEST_CASE(test_dnssec_ds_sign_loop)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 }
 
+BOOST_AUTO_TEST_CASE(test_dnssec_ds_root)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr, true);
+
+  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+  primeHints();
+  const DNSName target(".");
+  testkeysset_t keys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(g_rootdnsname, DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target, &queriesCount, keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queriesCount++;
+
+    if (type == QType::DS) {
+      setLWResult(res, 0, true, false, true);
+      addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032301 10800 3600 604800 3600", DNSResourceRecord::AUTHORITY, 3600);
+      addRRSIG(keys, res->d_records, DNSName("."), 300);
+      addNSECRecordToLW(domain, DNSName("aaa."), {QType::DNSKEY, QType::SOA, QType::NS, QType::NSEC, QType::RRSIG}, 600, res->d_records);
+      addRRSIG(keys, res->d_records, DNSName("."), 300);
+      return 1;
+    }
+
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
+  BOOST_REQUIRE_EQUAL(ret.size(), 4U);
+  BOOST_CHECK_EQUAL(queriesCount, 1U);
+
+  /* again, to test the cache */
+  ret.clear();
+  res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NoError);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
+  BOOST_REQUIRE_EQUAL(ret.size(), 4U);
+  BOOST_CHECK_EQUAL(queriesCount, 1U);
+}
+
 BOOST_AUTO_TEST_CASE(test_dnssec_dnskey_signed_child)
 {
   /* check that we don't accept a signer below us */
@@ -335,7 +385,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_dnskey_signed_child)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 10U);
 
@@ -343,7 +393,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_dnskey_signed_child)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 10U);
 }
@@ -443,7 +493,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_no_ds_on_referral_insecure)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 7U);
   BOOST_CHECK_EQUAL(dsQueriesCount, 2U);
@@ -452,7 +502,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_no_ds_on_referral_insecure)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 7U);
   BOOST_CHECK_EQUAL(dsQueriesCount, 2U);
@@ -533,7 +583,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_bogus_unsigned_nsec)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_CHECK_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 
@@ -541,7 +591,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_bogus_unsigned_nsec)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 }
@@ -621,7 +671,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_bogus_no_nsec)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_CHECK_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 
@@ -629,7 +679,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_bogus_no_nsec)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 8U);
 }
@@ -728,7 +778,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK(ret[0].d_type == QType::A);
   /* 4 NS: com at ., com at com, powerdns.com at com, powerdns.com at powerdns.com
@@ -740,7 +790,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 7U);
@@ -848,7 +898,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_optout)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK(ret[0].d_type == QType::SOA);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
@@ -857,7 +907,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_optout)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK(ret[0].d_type == QType::SOA);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
@@ -954,7 +1004,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_nxd_optout)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK(ret[0].d_type == QType::SOA);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
@@ -963,7 +1013,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_nxd_optout)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 6U);
   BOOST_CHECK(ret[0].d_type == QType::SOA);
   BOOST_CHECK_EQUAL(queriesCount, 6U);
@@ -1017,7 +1067,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_direct_ds)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::DS || record.d_type == QType::RRSIG);
@@ -1028,7 +1078,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_direct_ds)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::DS || record.d_type == QType::RRSIG);
@@ -1083,7 +1133,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_direct_ds)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::SOA || record.d_type == QType::NSEC || record.d_type == QType::RRSIG);
@@ -1094,7 +1144,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_direct_ds)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::SOA || record.d_type == QType::NSEC || record.d_type == QType::RRSIG);
@@ -1210,7 +1260,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_skipped_cut)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
@@ -1219,7 +1269,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_skipped_cut)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 9U);
index 0ed32254fd867f3e8aa9d4fecea06ad1f82b574d..6fbcc2725ec07c7b9e555ddeef6bc3812a404863 100644 (file)
@@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_to_ta_skipped_cut)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 7U);
@@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_to_ta_skipped_cut)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 7U);
@@ -228,7 +228,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_nodata)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   /* 4 NS (com from root, com from com, powerdns.com from com,
      powerdns.com from powerdns.com)
@@ -241,7 +241,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_nodata)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 7U);
 }
@@ -362,7 +362,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_cname)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 
@@ -370,7 +370,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_cname)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 }
@@ -485,7 +485,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_cname_glue)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 
@@ -493,7 +493,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_insecure_cname_glue)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 }
@@ -611,7 +611,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_to_secure_cname)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 
@@ -619,7 +619,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_to_secure_cname)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 }
@@ -707,7 +707,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_to_secure_cname)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 
@@ -715,7 +715,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_to_secure_cname)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 }
@@ -803,7 +803,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_bogus_cname)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 
@@ -811,7 +811,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_bogus_cname)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 11U);
 }
@@ -899,7 +899,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_secure_cname)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 12U);
 
@@ -907,7 +907,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_secure_to_secure_cname)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 12U);
 }
@@ -1020,7 +1020,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_to_insecure_cname)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   /* no RRSIG to show */
   BOOST_CHECK_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 10U);
@@ -1029,7 +1029,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_to_insecure_cname)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_CHECK_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 10U);
 }
@@ -1114,7 +1114,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_ta)
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
   /* should be insecure but we have a TA for powerdns.com. */
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   /* We got a RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK(ret[0].d_type == QType::A);
@@ -1124,7 +1124,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_ta)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 5U);
@@ -1210,7 +1210,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_ta_norrsig)
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
   /* should be insecure but we have a TA for powerdns.com., but no RRSIG so Bogus */
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   /* No RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK(ret[0].d_type == QType::A);
@@ -1220,7 +1220,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_insecure_ta_norrsig)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK(ret[0].d_type == QType::A);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
@@ -1280,7 +1280,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_nta)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   /* 13 NS + 1 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
@@ -1289,7 +1289,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_nta)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 14U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
 }
@@ -1336,7 +1336,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_no_ta)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   /* 13 NS + 0 RRSIG */
   BOOST_REQUIRE_EQUAL(ret.size(), 13U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
@@ -1345,7 +1345,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_no_ta)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::NS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 13U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
 }
@@ -1388,7 +1388,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_nodata)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 0U);
   /* com|NS, powerdns.com|NS, powerdns.com|A */
   BOOST_CHECK_EQUAL(queriesCount, 3U);
@@ -1397,7 +1397,60 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_nodata)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+  BOOST_REQUIRE_EQUAL(ret.size(), 0U);
+  /* we don't store empty results */
+  BOOST_CHECK_EQUAL(queriesCount, 4U);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_nxdomain)
+{
+  std::unique_ptr<SyncRes> sr;
+  initSR(sr, true);
+
+  setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+  primeHints();
+  const DNSName target("powerdns.com.");
+  testkeysset_t keys;
+
+  auto luaconfsCopy = g_luaconfs.getCopy();
+  luaconfsCopy.dsAnchors.clear();
+  generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+  generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+  generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+  g_luaconfs.setState(luaconfsCopy);
+
+  size_t queriesCount = 0;
+
+  sr->setAsyncCallback([target, &queriesCount, keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+    queriesCount++;
+
+    if (type == QType::DS || type == QType::DNSKEY) {
+      return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
+    }
+    else {
+
+      setLWResult(res, RCode::NXDomain, true, false, true);
+      return 1;
+    }
+
+    return 0;
+  });
+
+  vector<DNSRecord> ret;
+  int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+  BOOST_REQUIRE_EQUAL(ret.size(), 0U);
+  /* com|NS, powerdns.com|NS, powerdns.com|A */
+  BOOST_CHECK_EQUAL(queriesCount, 3U);
+
+  /* again, to test the cache */
+  ret.clear();
+  res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+  BOOST_CHECK_EQUAL(res, RCode::NXDomain);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 0U);
   /* we don't store empty results */
   BOOST_CHECK_EQUAL(queriesCount, 4U);
index 056fb1ef88a12694441111b2ae057c654c10d0ec..ccddd4cf013e21f8754b403bef0181cdb31c336b 100644 (file)
@@ -47,11 +47,11 @@ BOOST_AUTO_TEST_CASE(test_nsec_denial_nowrap)
   denialMap[std::make_pair(DNSName("example.org."), QType::NSEC)] = pair;
 
   dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, false);
-  BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+  BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
 
   denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false, false);
   /* let's check that d.example.org. is not denied by this proof */
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_1)
@@ -83,11 +83,11 @@ BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_1)
   denialMap[std::make_pair(DNSName("z.example.org."), QType::NSEC)] = pair;
 
   dState denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A, false, false);
-  BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+  BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
 
   denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false, false);
   /* let's check that d.example.org. is not denied by this proof */
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_2)
@@ -119,11 +119,11 @@ BOOST_AUTO_TEST_CASE(test_nsec_denial_wrap_case_2)
   denialMap[std::make_pair(DNSName("y.example.org."), QType::NSEC)] = pair;
 
   dState denialState = getDenial(denialMap, DNSName("z.example.org."), QType::A, false, false);
-  BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+  BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
 
   denialState = getDenial(denialMap, DNSName("d.example.org."), QType::A, false, false);
   /* let's check that d.example.org. is not denied by this proof */
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec_denial_only_one_nsec)
@@ -155,11 +155,11 @@ BOOST_AUTO_TEST_CASE(test_nsec_denial_only_one_nsec)
   denialMap[std::make_pair(DNSName("a.example.org."), QType::NSEC)] = pair;
 
   dState denialState = getDenial(denialMap, DNSName("b.example.org."), QType::A, false, false);
-  BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+  BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
 
   denialState = getDenial(denialMap, DNSName("a.example.org."), QType::A, false, false);
   /* let's check that d.example.org. is not denied by this proof */
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec_root_nxd_denial)
@@ -204,7 +204,7 @@ BOOST_AUTO_TEST_CASE(test_nsec_root_nxd_denial)
   denialMap[std::make_pair(DNSName("."), QType::NSEC)] = pair;
 
   dState denialState = getDenial(denialMap, DNSName("b."), QType::A, false, false);
-  BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+  BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec_ancestor_nxqtype_denial)
@@ -247,14 +247,14 @@ BOOST_AUTO_TEST_CASE(test_nsec_ancestor_nxqtype_denial)
   dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, false);
   /* no data means the qname/qtype is not denied, because an ancestor
      delegation NSEC can only deny the DS */
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 
   /* it can not be used to deny any RRs below that owner name either */
   denialState = getDenial(denialMap, DNSName("sub.a."), QType::A, false, false);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 
   denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
-  BOOST_CHECK_EQUAL(denialState, NXQTYPE);
+  BOOST_CHECK_EQUAL(denialState, dState::NXQTYPE);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec_insecure_delegation_denial)
@@ -296,7 +296,7 @@ BOOST_AUTO_TEST_CASE(test_nsec_insecure_delegation_denial)
   /* Insecure because the NS is not set, so while it does
      denies the DS, it can't prove an insecure delegation */
   dState denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec_nxqtype_cname)
@@ -325,7 +325,7 @@ BOOST_AUTO_TEST_CASE(test_nsec_nxqtype_cname)
 
   /* this NSEC is not valid to deny a.powerdns.com|A since it states that a CNAME exists */
   dState denialState = getDenial(denialMap, DNSName("a.powerdns.com."), QType::A, true, true);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec3_nxqtype_cname)
@@ -354,7 +354,7 @@ BOOST_AUTO_TEST_CASE(test_nsec3_nxqtype_cname)
 
   /* this NSEC3 is not valid to deny a.powerdns.com|A since it states that a CNAME exists */
   dState denialState = getDenial(denialMap, DNSName("a.powerdns.com."), QType::A, false, true);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec_nxdomain_denial_missing_wildcard)
@@ -382,7 +382,7 @@ BOOST_AUTO_TEST_CASE(test_nsec_nxdomain_denial_missing_wildcard)
   denialMap[std::make_pair(DNSName("a.powerdns.com."), QType::NSEC)] = pair;
 
   dState denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, false, false);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec3_nxdomain_denial_missing_wildcard)
@@ -422,7 +422,7 @@ BOOST_AUTO_TEST_CASE(test_nsec3_nxdomain_denial_missing_wildcard)
   denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
 
   dState denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, false, false);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec_ent_denial)
@@ -452,16 +452,16 @@ BOOST_AUTO_TEST_CASE(test_nsec_ent_denial)
   /* this NSEC is valid to prove a NXQTYPE at c.powerdns.com because it proves that
      it is an ENT */
   dState denialState = getDenial(denialMap, DNSName("c.powerdns.com."), QType::AAAA, true, true);
-  BOOST_CHECK_EQUAL(denialState, NXQTYPE);
+  BOOST_CHECK_EQUAL(denialState, dState::NXQTYPE);
 
   /* this NSEC is not valid to prove a NXQTYPE at b.powerdns.com,
      it could prove a NXDOMAIN if it had an additional wildcard denial */
   denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::AAAA, true, true);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 
   /* this NSEC is not valid to prove a NXQTYPE for QType::A at a.c.powerdns.com either */
   denialState = getDenial(denialMap, DNSName("a.c.powerdns.com."), QType::A, true, true);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 
   /* if we add the wildcard denial proof, we should get a NXDOMAIN proof for b.powerdns.com */
   recordContents.clear();
@@ -476,7 +476,12 @@ BOOST_AUTO_TEST_CASE(test_nsec_ent_denial)
   denialMap[std::make_pair(DNSName(").powerdns.com."), QType::NSEC)] = pair;
 
   denialState = getDenial(denialMap, DNSName("b.powerdns.com."), QType::A, true, false);
-  BOOST_CHECK_EQUAL(denialState, NXDOMAIN);
+  BOOST_CHECK_EQUAL(denialState, dState::NXDOMAIN);
+
+  /* this NSEC is NOT valid to prove a NXDOMAIN at c.powerdns.com because it proves that
+     it exists and is an ENT */
+  denialState = getDenial(denialMap, DNSName("c.powerdns.com."), QType::AAAA, true, false);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec3_ancestor_nxqtype_denial)
@@ -519,10 +524,10 @@ BOOST_AUTO_TEST_CASE(test_nsec3_ancestor_nxqtype_denial)
   dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, true);
   /* no data means the qname/qtype is not denied, because an ancestor
      delegation NSEC3 can only deny the DS */
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 
   denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
-  BOOST_CHECK_EQUAL(denialState, NXQTYPE);
+  BOOST_CHECK_EQUAL(denialState, dState::NXQTYPE);
 
   /* it can not be used to deny any RRs below that owner name either */
   /* Add NSEC3 for the next closer */
@@ -552,7 +557,7 @@ BOOST_AUTO_TEST_CASE(test_nsec3_ancestor_nxqtype_denial)
   denialMap[std::make_pair(records.at(0).d_name, records.at(0).d_type)] = pair;
 
   denialState = getDenial(denialMap, DNSName("sub.a."), QType::A, false, true);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec3_denial_too_many_iterations)
@@ -582,7 +587,7 @@ BOOST_AUTO_TEST_CASE(test_nsec3_denial_too_many_iterations)
 
   dState denialState = getDenial(denialMap, DNSName("a."), QType::A, false, true);
   /* since we refuse to compute more than g_maxNSEC3Iterations iterations, it should be Insecure */
-  BOOST_CHECK_EQUAL(denialState, INSECURE);
+  BOOST_CHECK_EQUAL(denialState, dState::INSECURE);
 }
 
 BOOST_AUTO_TEST_CASE(test_nsec3_insecure_delegation_denial)
@@ -624,7 +629,7 @@ BOOST_AUTO_TEST_CASE(test_nsec3_insecure_delegation_denial)
   /* Insecure because the NS is not set, so while it does
      denies the DS, it can't prove an insecure delegation */
   dState denialState = getDenial(denialMap, DNSName("a."), QType::DS, true, true);
-  BOOST_CHECK_EQUAL(denialState, NODATA);
+  BOOST_CHECK_EQUAL(denialState, dState::NODENIAL);
 }
 
 BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_negcache_validity)
@@ -671,26 +676,26 @@ BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_negcache_validity)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
 
   /* check that the entry has not been negatively cached for longer than the RRSIG validity */
-  const NegCache::NegCacheEntry* ne = nullptr;
+  NegCache::NegCacheEntry ne;
   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
-  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
-  BOOST_CHECK_EQUAL(ne->d_ttd, fixedNow + 1);
-  BOOST_CHECK_EQUAL(ne->d_validationState, Secure);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 1U);
+  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
+  BOOST_CHECK_EQUAL(ne.d_ttd, fixedNow + 1);
+  BOOST_CHECK_EQUAL(ne.d_validationState, vState::Secure);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 1U);
 
   /* again, to test the cache */
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
 }
@@ -742,26 +747,26 @@ BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_negcache_bogus_validity)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
 
   /* check that the entry has been negatively cached but not longer than s_maxbogusttl */
-  const NegCache::NegCacheEntry* ne = nullptr;
+  NegCache::NegCacheEntry ne;
   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
-  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
-  BOOST_CHECK_EQUAL(ne->d_ttd, fixedNow + SyncRes::s_maxbogusttl);
-  BOOST_CHECK_EQUAL(ne->d_validationState, Bogus);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0U);
+  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
+  BOOST_CHECK_EQUAL(ne.d_ttd, fixedNow + SyncRes::s_maxbogusttl);
+  BOOST_CHECK_EQUAL(ne.d_validationState, vState::Bogus);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 0U);
 
   /* again, to test the cache */
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 3U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
 }
@@ -809,7 +814,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_cache_validity)
   vector<DNSRecord> ret;
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
 
@@ -826,7 +831,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_rrsig_cache_validity)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
 }
@@ -878,7 +883,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_secure)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::A || record.d_type == QType::RRSIG);
@@ -890,7 +895,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_secure)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::A || record.d_type == QType::RRSIG);
@@ -943,7 +948,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_insecure)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::A);
@@ -955,7 +960,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_insecure)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::A);
@@ -1012,7 +1017,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_bogus)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::A);
@@ -1025,7 +1030,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_bogus)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   /* check that we correctly capped the TTD for a Bogus record after
      just-in-time validation */
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
@@ -1041,7 +1046,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cache_bogus)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::A);
index 59c1ad5ceb777b7af72b1950283ff23a8d8994eb..6339951e27cc817e5de063d2298b0fdea38f5015 100644 (file)
@@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_secure)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A || record.d_type == QType::RRSIG);
@@ -73,12 +73,12 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_secure)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A || record.d_type == QType::RRSIG);
   }
-  BOOST_CHECK_EQUAL(queriesCount, 5U);
+  BOOST_CHECK_EQUAL(queriesCount, 4U);
 }
 
 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_insecure)
@@ -133,7 +133,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_insecure)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
@@ -145,7 +145,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_insecure)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
@@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_bogus)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
@@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_bogus)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   /* check that we correctly capped the TTD for a Bogus record after
      just-in-time validation */
@@ -232,7 +232,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_bogus)
     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
     BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
   }
-  BOOST_CHECK_EQUAL(queriesCount, 5U);
+  BOOST_CHECK_EQUAL(queriesCount, 4U);
 
   ret.clear();
   /* and a third time to make sure that the validation status (and TTL!)
@@ -240,13 +240,13 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_cname_cache_bogus)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::CNAME || record.d_type == QType::A);
     BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
   }
-  BOOST_CHECK_EQUAL(queriesCount, 5U);
+  BOOST_CHECK_EQUAL(queriesCount, 4U);
 }
 
 BOOST_AUTO_TEST_CASE(test_dnssec_validation_additional_without_rrsig)
@@ -310,7 +310,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_additional_without_rrsig)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_CHECK_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::RRSIG || record.d_type == QType::A);
@@ -323,7 +323,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_additional_without_rrsig)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(addTarget, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_CHECK_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK(record.d_type == QType::RRSIG || record.d_type == QType::A);
@@ -383,34 +383,34 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_secure)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
   /* check that the entry has been negatively cached */
-  const NegCache::NegCacheEntry* ne = nullptr;
+  NegCache::NegCacheEntry ne;
   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
-  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
-  BOOST_CHECK_EQUAL(ne->d_validationState, Indeterminate);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 1U);
+  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
+  BOOST_CHECK_EQUAL(ne.d_validationState, vState::Indeterminate);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 1U);
 
   ret.clear();
   /* second one _does_ require validation */
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
-  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
-  BOOST_CHECK_EQUAL(ne->d_validationState, Secure);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 1U);
+  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
+  BOOST_CHECK_EQUAL(ne.d_validationState, vState::Secure);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 1U);
 }
 
 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_secure_ds)
@@ -461,7 +461,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_secure_ds)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
 
@@ -470,7 +470,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_secure_ds)
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::DS), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Secure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Secure);
   BOOST_REQUIRE_EQUAL(ret.size(), 4U);
   BOOST_CHECK_EQUAL(queriesCount, 4U);
 }
@@ -521,33 +521,33 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_insecure)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
   /* check that the entry has not been negatively cached */
-  const NegCache::NegCacheEntry* ne = nullptr;
+  NegCache::NegCacheEntry ne;
   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
-  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
-  BOOST_CHECK_EQUAL(ne->d_validationState, Indeterminate);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 0U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0U);
+  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
+  BOOST_CHECK_EQUAL(ne.d_validationState, vState::Indeterminate);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 0U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 0U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 0U);
 
   ret.clear();
   /* second one _does_ require validation */
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Insecure);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Insecure);
   BOOST_REQUIRE_EQUAL(ret.size(), 1U);
   BOOST_CHECK_EQUAL(queriesCount, 1U);
-  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
-  BOOST_CHECK_EQUAL(ne->d_validationState, Insecure);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 0U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0U);
+  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
+  BOOST_CHECK_EQUAL(ne.d_validationState, vState::Insecure);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 0U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 0U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 0U);
 }
 
 BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_bogus)
@@ -604,7 +604,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_bogus)
   sr->setDNSSECValidationRequested(false);
   int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Indeterminate);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Indeterminate);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     if (record.d_type == QType::SOA) {
@@ -612,34 +612,34 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_bogus)
     }
   }
   BOOST_CHECK_EQUAL(queriesCount, 1U);
-  const NegCache::NegCacheEntry* ne = nullptr;
+  NegCache::NegCacheEntry ne;
   BOOST_CHECK_EQUAL(SyncRes::t_sstorage.negcache.size(), 1U);
-  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
-  BOOST_CHECK_EQUAL(ne->d_validationState, Indeterminate);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->d_ttd, now + SyncRes::s_maxnegttl);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0U);
+  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
+  BOOST_CHECK_EQUAL(ne.d_validationState, vState::Indeterminate);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.d_ttd, now + SyncRes::s_maxnegttl);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 0U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 0U);
 
   ret.clear();
   /* second one _does_ require validation */
   sr->setDNSSECValidationRequested(true);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
   }
   BOOST_CHECK_EQUAL(queriesCount, 4U);
-  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
-  BOOST_CHECK_EQUAL(ne->d_validationState, Bogus);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->d_ttd, now + SyncRes::s_maxbogusttl);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0U);
+  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
+  BOOST_CHECK_EQUAL(ne.d_validationState, vState::Bogus);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.d_ttd, now + SyncRes::s_maxbogusttl);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 0U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 0U);
 
   ret.clear();
   /* third one _does_ not require validation, we just check that
@@ -647,19 +647,19 @@ BOOST_AUTO_TEST_CASE(test_dnssec_validation_from_negcache_bogus)
   sr->setDNSSECValidationRequested(false);
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NoError);
-  BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+  BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
   BOOST_REQUIRE_EQUAL(ret.size(), 2U);
   for (const auto& record : ret) {
     BOOST_CHECK_EQUAL(record.d_ttl, SyncRes::s_maxbogusttl);
   }
   BOOST_CHECK_EQUAL(queriesCount, 4U);
-  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), &ne), true);
-  BOOST_CHECK_EQUAL(ne->d_validationState, Bogus);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.records.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->authoritySOA.signatures.size(), 1U);
-  BOOST_CHECK_EQUAL(ne->d_ttd, now + SyncRes::s_maxbogusttl);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.records.size(), 0U);
-  BOOST_CHECK_EQUAL(ne->DNSSECRecords.signatures.size(), 0U);
+  BOOST_REQUIRE_EQUAL(SyncRes::t_sstorage.negcache.get(target, QType(QType::A), sr->getNow(), ne), true);
+  BOOST_CHECK_EQUAL(ne.d_validationState, vState::Bogus);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.records.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.authoritySOA.signatures.size(), 1U);
+  BOOST_CHECK_EQUAL(ne.d_ttd, now + SyncRes::s_maxbogusttl);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.records.size(), 0U);
+  BOOST_CHECK_EQUAL(ne.DNSSECRecords.signatures.size(), 0U);
 }
 
 BOOST_AUTO_TEST_CASE(test_lowercase_outgoing)
@@ -765,7 +765,7 @@ BOOST_AUTO_TEST_CASE(test_getDSRecords_multialgo)
 
   dsmap_t ds;
   auto state = sr->getDSRecords(target, ds, false, 0, false);
-  BOOST_CHECK_EQUAL(state, Secure);
+  BOOST_CHECK_EQUAL(state, vState::Secure);
   BOOST_REQUIRE_EQUAL(ds.size(), 1U);
   for (const auto& i : ds) {
     BOOST_CHECK_EQUAL(i.d_digesttype, DNSSECKeeper::DIGEST_SHA256);
@@ -818,7 +818,7 @@ BOOST_AUTO_TEST_CASE(test_getDSRecords_multialgo_all_sha)
 
   dsmap_t ds;
   auto state = sr->getDSRecords(target, ds, false, 0, false);
-  BOOST_CHECK_EQUAL(state, Secure);
+  BOOST_CHECK_EQUAL(state, vState::Secure);
   BOOST_REQUIRE_EQUAL(ds.size(), 1U);
   for (const auto& i : ds) {
     BOOST_CHECK_EQUAL(i.d_digesttype, DNSSECKeeper::DIGEST_SHA384);
@@ -871,7 +871,7 @@ BOOST_AUTO_TEST_CASE(test_getDSRecords_multialgo_two_highest)
 
   dsmap_t ds;
   auto state = sr->getDSRecords(target, ds, false, 0, false);
-  BOOST_CHECK_EQUAL(state, Secure);
+  BOOST_CHECK_EQUAL(state, vState::Secure);
   BOOST_REQUIRE_EQUAL(ds.size(), 2U);
   for (const auto& i : ds) {
     BOOST_CHECK_EQUAL(i.d_digesttype, DNSSECKeeper::DIGEST_SHA256);
@@ -1054,6 +1054,7 @@ BOOST_AUTO_TEST_CASE(test_records_sanitization_keep_glue)
       setLWResult(res, 0, false, false, true);
       addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns1.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
       addRecordToLW(res, "powerdns.com.", QType::NS, "pdns-public-ns2.powerdns.com.", DNSResourceRecord::AUTHORITY, 172800);
+      addRecordToLW(res, "powerdns.com.", QType::DS, "1 8 2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAA", DNSResourceRecord::AUTHORITY, 172800);
       addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::A, "192.0.2.2", DNSResourceRecord::ADDITIONAL, 172800);
       addRecordToLW(res, "pdns-public-ns1.powerdns.com.", QType::AAAA, "2001:DB8::2", DNSResourceRecord::ADDITIONAL, 172800);
       addRecordToLW(res, "pdns-public-ns2.powerdns.com.", QType::A, "192.0.2.3", DNSResourceRecord::ADDITIONAL, 172800);
@@ -1063,6 +1064,7 @@ BOOST_AUTO_TEST_CASE(test_records_sanitization_keep_glue)
     else if (ip == ComboAddress("192.0.2.2:53") || ip == ComboAddress("192.0.2.3:53") || ip == ComboAddress("[2001:DB8::2]:53") || ip == ComboAddress("[2001:DB8::3]:53")) {
       setLWResult(res, 0, true, false, true);
       addRecordToLW(res, target, QType::A, "192.0.2.4");
+      addRecordToLW(res, "powerdns.com.", QType::DS, "2 8 2 BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB BBBBBBBB", DNSResourceRecord::AUTHORITY);
       return 1;
     }
     else {
@@ -1091,6 +1093,12 @@ BOOST_AUTO_TEST_CASE(test_records_sanitization_keep_glue)
   BOOST_CHECK_GT(s_RC->get(now, DNSName("pdns-public-ns1.powerdns.com."), QType(QType::AAAA), false, &cached, who), 0);
   BOOST_CHECK_GT(s_RC->get(now, DNSName("pdns-public-ns2.powerdns.com."), QType(QType::A), false, &cached, who), 0);
   BOOST_CHECK_GT(s_RC->get(now, DNSName("pdns-public-ns2.powerdns.com."), QType(QType::AAAA), false, &cached, who), 0);
+
+  cached.clear();
+  /* check that we accepted the DS from the parent, and not from the child zone */
+  BOOST_CHECK_GT(s_RC->get(now, DNSName("powerdns.com."), QType(QType::DS), false, &cached, who), 0);
+  BOOST_REQUIRE_EQUAL(cached.size(), 1U);
+  BOOST_CHECK_EQUAL(cached.at(0).d_content->getZoneRepresentation(), "1 8 2 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
 }
 
 BOOST_AUTO_TEST_CASE(test_records_sanitization_scrubs_ns_nxd)
index 019724596c002f7797706af74e7d98739ec2fb46..8d2adbab98c72d8f68741015ace23f07d73af2d1 100644 (file)
@@ -42,10 +42,10 @@ static void insertIntoRootNSZones(const DNSName &name) {
   }
 }
 
-void primeHints(void)
+bool primeHints(void)
 {
   // prime root cache
-  const vState validationState = Insecure;
+  const vState validationState = vState::Insecure;
   vector<DNSRecord> nsset;
   t_rootNSZones.clear();
 
@@ -84,26 +84,59 @@ void primeHints(void)
     ZoneParserTNG zpt(::arg()["hint-file"]);
     zpt.setMaxGenerateSteps(::arg().asNum("max-generate-steps"));
     DNSResourceRecord rr;
+    set<DNSName> seenNS;
+    set<DNSName> seenA;
+    set<DNSName> seenAAAA;
 
     while(zpt.get(rr)) {
       rr.ttl+=time(0);
       if(rr.qtype.getCode()==QType::A) {
+        seenA.insert(rr.qname);
         vector<DNSRecord> aset;
         aset.push_back(DNSRecord(rr));
         s_RC->replace(time(0), rr.qname, QType(QType::A), aset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, boost::none, validationState); // auth, etc see above
       } else if(rr.qtype.getCode()==QType::AAAA) {
+        seenAAAA.insert(rr.qname);
         vector<DNSRecord> aaaaset;
         aaaaset.push_back(DNSRecord(rr));
         s_RC->replace(time(0), rr.qname, QType(QType::AAAA), aaaaset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), true, boost::none, boost::none, validationState);
       } else if(rr.qtype.getCode()==QType::NS) {
+        seenNS.insert(DNSName(rr.content));
         rr.content=toLower(rr.content);
         nsset.push_back(DNSRecord(rr));
       }
       insertIntoRootNSZones(rr.qname.getLastLabel());
     }
+
+    // Check reachability of A and AAAA records
+    bool reachableA = false, reachableAAAA = false;
+    for (auto const& r: seenA) {
+      if (seenNS.count(r)) {
+        reachableA = true;
+      }
+    }
+    for (auto const& r: seenAAAA) {
+      if (seenNS.count(r)) {
+        reachableAAAA = true;
+      }
+    }
+    if (SyncRes::s_doIPv4 && !SyncRes::s_doIPv6 && !reachableA) {
+      g_log<<Logger::Error<<"Running IPv4 only but no IPv4 root hints"<<endl;
+      return false;
+    }
+    if (!SyncRes::s_doIPv4 && SyncRes::s_doIPv6 && !reachableAAAA) {
+      g_log<<Logger::Error<<"Running IPv6 only but no IPv6 root hints"<<endl;
+      return false;
+    }
+    if (SyncRes::s_doIPv4 && SyncRes::s_doIPv6 && !reachableA && !reachableAAAA) {
+      g_log<<Logger::Error<<"No valid root hints"<<endl;
+      return false;
+    }
   }
+
   s_RC->doWipeCache(g_rootdnsname, false, QType::NS);
   s_RC->replace(time(0), g_rootdnsname, QType(QType::NS), nsset, vector<std::shared_ptr<RRSIGRecordContent>>(), vector<std::shared_ptr<DNSRecord>>(), false, boost::none, boost::none, validationState); // and stuff in the cache
+  return true;
 }
 
 
@@ -115,7 +148,7 @@ void primeHints(void)
 // servers are authoritative for root-servers.net, and some
 // implementations reply not with a delegation on a root-servers.net
 // DS query, but with a NODATA response (the domain is unsigned).
-void primeRootNSZones(bool dnssecmode)
+void primeRootNSZones(bool dnssecmode, unsigned int depth)
 {
   struct timeval now;
   gettimeofday(&now, 0);
@@ -132,7 +165,7 @@ void primeRootNSZones(bool dnssecmode)
   for (const auto & qname: copy) {
     s_RC->doWipeCache(qname, false, QType::NS);
     vector<DNSRecord> ret;
-    sr.beginResolve(qname, QType(QType::NS), QClass::IN, ret);
+    sr.beginResolve(qname, QType(QType::NS), QClass::IN, ret, depth + 1);
   }
 }
 
index 774fa7e56402378524de46135cc6535cedf0cb1b..423e3f90224ac4ffe24682f250f377bae58e2e69 100644 (file)
 #include "base64.hh"
 #include "dnswriter.hh"
 #include "dnsparser.hh"
-
+#include "query-local-address.hh"
 
 #include "dns_random.hh"
 #include <poll.h>
 #include "gss_context.hh"
 #include "namespaces.hh"
 
+using pdns::resolver::parseResult;
+
 int makeQuerySocket(const ComboAddress& local, bool udpOrTCP, bool nonLocalBind)
 {
   ComboAddress ourLocal(local);
@@ -100,10 +102,12 @@ Resolver::Resolver()
   locals["default4"] = -1;
   locals["default6"] = -1;
   try {
-    if(!::arg()["query-local-address"].empty())
-      locals["default4"] = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind"));
-    if(!::arg()["query-local-address6"].empty())
-      locals["default6"] = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind"));
+    if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+      locals["default4"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind"));
+    }
+    if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
+      locals["default6"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind"));
+    }
   }
   catch(...) {
     if(locals["default4"]>=0)
@@ -156,12 +160,11 @@ uint16_t Resolver::sendResolve(const ComboAddress& remote, const ComboAddress& l
   // choose socket based on local
   if (local.sin4.sin_family == 0) {
     // up to us.
-    sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
-    if (sock == -1) {
+    if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
       string ipv = remote.sin4.sin_family == AF_INET ? "4" : "6";
-      string qla = remote.sin4.sin_family == AF_INET ? "" : "6";
-      throw ResolverException("No IPv" + ipv + " socket available, is query-local-address" + qla + " unset?");
+      throw ResolverException("No IPv" + ipv + " socket available, is such an address configured in query-local-address?");
     }
+    sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
   } else {
     std::string lstr = local.toString();
     std::map<std::string, int>::iterator lptr;
@@ -188,36 +191,41 @@ uint16_t Resolver::sendResolve(const ComboAddress& remote, const ComboAddress& l
   return randomid;
 }
 
-static int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
-{
-  result->clear();
-
-  if(mdp.d_header.rcode)
-    return mdp.d_header.rcode;
-
-  if(origQname.countLabels()) {  // not AXFR
-    if(mdp.d_header.id != id) 
-      throw ResolverException("Remote nameserver replied with wrong id");
-    if(mdp.d_header.qdcount != 1)
-      throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")");
-    if(mdp.d_qname != origQname)
-      throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)");
-  }
+namespace pdns {
+  namespace resolver {
+    int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
+    {
+      result->clear();
+
+      if(mdp.d_header.rcode)
+        return mdp.d_header.rcode;
+
+      if(origQname.countLabels()) {  // not AXFR
+        if(mdp.d_header.id != id) 
+          throw ResolverException("Remote nameserver replied with wrong id");
+        if(mdp.d_header.qdcount != 1)
+          throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")");
+        if(mdp.d_qname != origQname)
+          throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)");
+      }
 
-  vector<DNSResourceRecord> ret;
-  DNSResourceRecord rr;
-  result->reserve(mdp.d_answers.size());
+      vector<DNSResourceRecord> ret;
+      DNSResourceRecord rr;
+      result->reserve(mdp.d_answers.size());
 
-  for (const auto& i: mdp.d_answers) {
-    rr.qname = i.first.d_name;
-    rr.qtype = i.first.d_type;
-    rr.ttl = i.first.d_ttl;
-    rr.content = i.first.d_content->getZoneRepresentation(true);
-    result->push_back(rr);
-  }
+      for (const auto& i: mdp.d_answers) {
+        rr.qname = i.first.d_name;
+        rr.qtype = i.first.d_type;
+        rr.ttl = i.first.d_ttl;
+        rr.content = i.first.d_content->getZoneRepresentation(true);
+        result->push_back(rr);
+      }
 
-  return 0;
-}
+      return 0;
+    }
+
+  } // namespace resolver
+} // namespace pdns
 
 bool Resolver::tryGetSOASerial(DNSName *domain, ComboAddress* remote, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
 {
@@ -362,221 +370,3 @@ void Resolver::getSoaSerial(const ComboAddress& ipport, const DNSName &domain, u
   }
 }
 
-AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
-                             const DNSName& domain,
-                             const TSIGTriplet& tt, 
-                             const ComboAddress* laddr,
-                             size_t maxReceivedBytes,
-                             uint16_t timeout)
-  : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
-{
-  ComboAddress local;
-  if (laddr != nullptr) {
-    local = ComboAddress(*laddr);
-  } else {
-    string qlas = remote.sin4.sin_family == AF_INET ? "query-local-address" : "query-local-address6";
-    if (::arg()[qlas].empty()) {
-      throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". " + qlas + " is unset");
-    }
-    local=ComboAddress(::arg()[qlas]);
-  }
-  d_sock = -1;
-  try {
-    d_sock = makeQuerySocket(local, false); // make a TCP socket
-    if (d_sock < 0)
-      throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort());
-    d_buf = shared_array<char>(new char[65536]);
-    d_remote = remote; // mostly for error reporting
-    this->connect(timeout);
-    d_soacount = 0;
-  
-    vector<uint8_t> packet;
-    DNSPacketWriter pw(packet, domain, QType::AXFR);
-    pw.getHeader()->id = dns_random_uint16();
-  
-    if(!tt.name.empty()) {
-      if (tt.algo == DNSName("hmac-md5"))
-        d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int");
-      else
-        d_trc.d_algoName = tt.algo;
-      d_trc.d_time = time(0);
-      d_trc.d_fudge = 300;
-      d_trc.d_origID=ntohs(pw.getHeader()->id);
-      d_trc.d_eRcode=0;
-      addTSIG(pw, d_trc, tt.name, tt.secret, "", false);
-    }
-  
-    uint16_t replen=htons(packet.size());
-    Utility::iovec iov[2];
-    iov[0].iov_base=reinterpret_cast<char*>(&replen);
-    iov[0].iov_len=2;
-    iov[1].iov_base=packet.data();
-    iov[1].iov_len=packet.size();
-  
-    int ret=Utility::writev(d_sock, iov, 2);
-    if(ret < 0)
-      throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
-    if(ret != (int)(2+packet.size())) {
-      throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
-    }
-  
-    int res = waitForData(d_sock, timeout, 0);
-    
-    if(!res)
-      throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
-    if(res<0)
-      throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
-  }
-  catch(...) {
-    if(d_sock >= 0)
-      close(d_sock);
-    d_sock = -1;
-    throw;
-  }
-}
-
-AXFRRetriever::~AXFRRetriever()
-{
-  close(d_sock);
-}
-
-
-
-int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed.
-{
-  if(d_soacount > 1)
-    return false;
-
-  // d_sock is connected and is about to spit out a packet
-  int len=getLength(timeout);
-  if(len<0)
-    throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
-
-  if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
-    throw ResolverException("Reached the maximum number of received bytes during AXFR");
-
-  timeoutReadn(len, timeout);
-
-  d_receivedBytes += (uint16_t) len;
-
-  MOADNSParser mdp(false, d_buf.get(), len);
-
-  int err = mdp.d_header.rcode;
-
-  if(err) {
-    throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
-  }
-
-  try {
-    d_tsigVerifier.check(std::string(d_buf.get(), len), mdp);
-  }
-  catch(const std::runtime_error& re) {
-    throw ResolverException(re.what());
-  }
-
-  if(!records) {
-    err = parseResult(mdp, DNSName(), 0, 0, &res);
-
-    if (!err) {
-      for(const auto& answer :  mdp.d_answers)
-        if (answer.first.d_type == QType::SOA)
-          d_soacount++;
-    }
-  }
-  else {
-    records->clear();
-    records->reserve(mdp.d_answers.size());
-
-    for(auto& r: mdp.d_answers) {
-      if (r.first.d_type == QType::SOA) {
-        d_soacount++;
-      }
-
-      records->push_back(std::move(r.first));
-    }
-  }
-
-  return true;
-}
-
-void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec)
-{
-  time_t start=time(nullptr);
-  int n=0;
-  int numread;
-  while(n<bytes) {
-    int res=waitForData(d_sock, timeoutsec-(time(nullptr)-start));
-    if(res<0)
-      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
-    if(!res)
-      throw ResolverException("Timeout while reading data from remote nameserver over TCP");
-
-    numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
-    if(numread<0)
-      throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
-    if(numread==0)
-      throw ResolverException("Remote nameserver closed TCP connection");
-    n+=numread;
-  }
-}
-
-void AXFRRetriever::connect(uint16_t timeout)
-{
-  setNonBlocking( d_sock );
-
-  int err;
-
-  if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
-    try {
-      closesocket(d_sock);
-    }
-    catch(const PDNSException& e) {
-      d_sock=-1;
-      throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
-    }
-
-    throw ResolverException("connect: "+stringerror());
-  }
-
-  if(!err)
-    goto done;
-
-  err=waitForRWData(d_sock, false, timeout, 0); // wait for writeability
-  
-  if(!err) {
-    try {
-      closesocket(d_sock); // timeout
-    }
-    catch(const PDNSException& e) {
-      d_sock=-1;
-      throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
-    }
-
-    d_sock=-1;
-    errno=ETIMEDOUT;
-    
-    throw ResolverException("Timeout connecting to server");
-  }
-  else if(err < 0) {
-    throw ResolverException("Error connecting: "+stringerror());
-  }
-  else {
-    Utility::socklen_t len=sizeof(err);
-    if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
-      throw ResolverException("Error connecting: "+stringerror()); // Solaris
-
-    if(err)
-      throw ResolverException("Error connecting: "+string(strerror(err)));
-  }
-  
- done:
-  setBlocking( d_sock );
-  // d_sock now connected
-}
-
-int AXFRRetriever::getLength(uint16_t timeout)
-{
-  timeoutReadn(2, timeout);
-  return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
-}
-
index 17b4b7ccdd4fc876b879ef09243b78ace0f1c46f..c78ef139f20a0a923104178acf0578c6ec852e55 100644 (file)
@@ -76,31 +76,8 @@ private:
   std::map<std::string, int> locals;
 };
 
-class AXFRRetriever : public boost::noncopyable
-{
-  public:
-    AXFRRetriever(const ComboAddress& remote,
-                  const DNSName& zone,
-                  const TSIGTriplet& tt = TSIGTriplet(),
-                  const ComboAddress* laddr = NULL,
-                  size_t maxReceivedBytes=0,
-                  uint16_t timeout=10);
-    ~AXFRRetriever();
-    int getChunk(Resolver::res_t &res, vector<DNSRecord>* records=0, uint16_t timeout=10);
-  
-  private:
-    void connect(uint16_t timeout);
-    int getLength(uint16_t timeout);
-    void timeoutReadn(uint16_t bytes, uint16_t timeoutsec=10);
-
-    shared_array<char> d_buf;
-    string d_domain;
-    int d_sock;
-    int d_soacount;
-    ComboAddress d_remote;
-    TSIGTCPVerifier d_tsigVerifier;
-
-    size_t d_receivedBytes;
-    size_t d_maxReceivedBytes;
-    TSIGRecordContent d_trc;
-};
+namespace pdns {
+  namespace resolver {
+    int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result);
+  } // namespace resolver
+} // namespace pdns
index 069671a61f7fde70ea74d1afab86e7a1e8e1e50b..15902766d44c75a379ced3011436673621a7c456 100644 (file)
@@ -16,6 +16,7 @@
 #include "dns_random.hh"
 #include "backends/gsql/ssql.hh"
 #include "communicator.hh"
+#include "query-local-address.hh"
 
 extern StatBag S;
 extern CommunicatorClass Communicator;
@@ -597,14 +598,10 @@ int PacketHandler::forwardPacket(const string &msgPrefix, const DNSPacket& p, co
   for(const auto& remote : di.masters) {
     g_log<<Logger::Notice<<msgPrefix<<"Forwarding packet to master "<<remote<<endl;
 
-    ComboAddress local;
-    if (remote.sin4.sin_family == AF_INET && !::arg()["query-local-address"].empty()) {
-      local = ComboAddress(::arg()["query-local-address"]);
-    } else if(remote.sin4.sin_family == AF_INET6 && !::arg()["query-local-address6"].empty()) {
-      local = ComboAddress(::arg()["query-local-address6"]);
-    } else {
+    if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
       continue;
     }
+    auto local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
     int sock = makeQuerySocket(local, false); // create TCP socket. RFC2136 section 6.2 seems to be ok with this.
     if(sock < 0) {
       g_log<<Logger::Error<<msgPrefix<<"Error creating socket: "<<stringerror()<<endl;
@@ -836,7 +833,7 @@ int PacketHandler::processUpdate(DNSPacket& p) {
     if (rr->d_place == DNSResourceRecord::ANSWER) {
       int res = checkUpdatePrerequisites(rr, &di);
       if (res>0) {
-        g_log<<Logger::Error<<msgPrefix<<"Failed PreRequisites check for "<<rr->d_name.toLogString()<<", returning "<<RCode::to_s(res)<<endl;
+        g_log<<Logger::Error<<msgPrefix<<"Failed PreRequisites check for "<<rr->d_name<<", returning "<<RCode::to_s(res)<<endl;
         di.backend->abortTransaction();
         return res;
       }
@@ -1094,7 +1091,7 @@ void PacketHandler::increaseSerial(const string &msgPrefix, const DomainInfo *di
       string soaEditSetting;
       d_dk.getSoaEdit(di->zone, soaEditSetting);
       if (soaEditSetting.empty()) {
-        g_log<<Logger::Error<<msgPrefix<<"Using "<<soaEdit2136<<" for SOA-EDIT-DNSUPDATE increase on DNS update, but SOA-EDIT is not set for domain \""<< di->zone.toLogString() <<"\". Using DEFAULT for SOA-EDIT-DNSUPDATE"<<endl;
+        g_log<<Logger::Error<<msgPrefix<<"Using "<<soaEdit2136<<" for SOA-EDIT-DNSUPDATE increase on DNS update, but SOA-EDIT is not set for domain \""<< di->zone <<"\". Using DEFAULT for SOA-EDIT-DNSUPDATE"<<endl;
         soaEdit2136 = "DEFAULT";
       } else
         soaEdit = soaEditSetting;
index 908c3a4384b9f493f3a236bf155fa2017a1f9372..5814f26296deba9a5c65a8534838a643edbbbc24 100644 (file)
@@ -3,12 +3,13 @@
 #include "dnsrecords.hh"
 #include "ixfr.hh"
 #include "syncres.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
 #include "logger.hh"
 #include "rec-lua-conf.hh"
 #include "rpzloader.hh"
 #include "zoneparser-tng.hh"
 #include "threadname.hh"
+#include "query-local-address.hh"
 
 Netmask makeNetmaskFromRPZ(const DNSName& name)
 {
@@ -193,7 +194,7 @@ static shared_ptr<SOARecordContent> loadRPZFromServer(const ComboAddress& master
 
   ComboAddress local(localAddress);
   if (local == ComboAddress())
-    local = getQueryLocalAddress(master.sin4.sin_family, 0);
+    local = pdns::getQueryLocalAddress(master.sin4.sin_family, 0);
 
   AXFRRetriever axfr(master, zoneName, tt, &local, maxReceivedBytes, axfrTimeout);
   unsigned int nrecords=0;
@@ -306,14 +307,14 @@ static bool dumpZoneToDisk(const DNSName& zoneName, const std::shared_ptr<DNSFil
   std::string temp = dumpZoneFileName + "XXXXXX";
   int fd = mkstemp(&temp.at(0));
   if (fd < 0) {
-    g_log<<Logger::Warning<<"Unable to open a file to dump the content of the RPZ zone "<<zoneName.toLogString()<<endl;
+    g_log<<Logger::Warning<<"Unable to open a file to dump the content of the RPZ zone "<<zoneName<<endl;
     return false;
   }
 
   auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fdopen(fd, "w+"), fclose);
   if (!fp) {
     close(fd);
-    g_log<<Logger::Warning<<"Unable to open a file pointer to dump the content of the RPZ zone "<<zoneName.toLogString()<<endl;
+    g_log<<Logger::Warning<<"Unable to open a file pointer to dump the content of the RPZ zone "<<zoneName<<endl;
     return false;
   }
   fd = -1;
@@ -322,27 +323,27 @@ static bool dumpZoneToDisk(const DNSName& zoneName, const std::shared_ptr<DNSFil
     newZone->dump(fp.get());
   }
   catch(const std::exception& e) {
-    g_log<<Logger::Warning<<"Error while dumping the content of the RPZ zone "<<zoneName.toLogString()<<": "<<e.what()<<endl;
+    g_log<<Logger::Warning<<"Error while dumping the content of the RPZ zone "<<zoneName<<": "<<e.what()<<endl;
     return false;
   }
 
   if (fflush(fp.get()) != 0) {
-    g_log<<Logger::Warning<<"Error while flushing the content of the RPZ zone "<<zoneName.toLogString()<<" to the dump file: "<<stringerror()<<endl;
+    g_log<<Logger::Warning<<"Error while flushing the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl;
     return false;
   }
 
   if (fsync(fileno(fp.get())) != 0) {
-    g_log<<Logger::Warning<<"Error while syncing the content of the RPZ zone "<<zoneName.toLogString()<<" to the dump file: "<<stringerror()<<endl;
+    g_log<<Logger::Warning<<"Error while syncing the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl;
     return false;
   }
 
   if (fclose(fp.release()) != 0) {
-    g_log<<Logger::Warning<<"Error while writing the content of the RPZ zone "<<zoneName.toLogString()<<" to the dump file: "<<stringerror()<<endl;
+    g_log<<Logger::Warning<<"Error while writing the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl;
     return false;
   }
 
   if (rename(temp.c_str(), dumpZoneFileName.c_str()) != 0) {
-    g_log<<Logger::Warning<<"Error while moving the content of the RPZ zone "<<zoneName.toLogString()<<" to the dump file: "<<stringerror()<<endl;
+    g_log<<Logger::Warning<<"Error while moving the content of the RPZ zone "<<zoneName<<" to the dump file: "<<stringerror()<<endl;
     return false;
   }
 
@@ -377,6 +378,8 @@ void RPZIXFRTracker(const std::vector<ComboAddress>& masters, boost::optional<DN
         sr = loadRPZFromServer(master, zoneName, newZone, defpol, defpolOverrideLocal, maxTTL, tt, maxReceivedBytes, localAddress, axfrTimeout);
         newZone->setSerial(sr->d_st.serial);
         newZone->setRefresh(sr->d_st.refresh);
+        // This period gets used below this loop
+        oldZone->setRefresh(sr->d_st.refresh);
         setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), true);
 
         g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
@@ -433,7 +436,7 @@ void RPZIXFRTracker(const std::vector<ComboAddress>& masters, boost::optional<DN
 
       ComboAddress local(localAddress);
       if (local == ComboAddress()) {
-        local = getQueryLocalAddress(master.sin4.sin_family, 0);
+        local = pdns::getQueryLocalAddress(master.sin4.sin_family, 0);
       }
 
       try {
@@ -452,73 +455,91 @@ void RPZIXFRTracker(const std::vector<ComboAddress>& masters, boost::optional<DN
       continue;
     }
 
-    g_log<<Logger::Info<<"Processing "<<deltas.size()<<" delta"<<addS(deltas)<<" for RPZ "<<zoneName<<endl;
-
-    oldZone = luaconfsLocal->dfe.getZone(zoneIdx);
-    /* we need to make a _full copy_ of the zone we are going to work on */
-    std::shared_ptr<DNSFilterEngine::Zone> newZone = std::make_shared<DNSFilterEngine::Zone>(*oldZone);
+    try {
+      g_log<<Logger::Info<<"Processing "<<deltas.size()<<" delta"<<addS(deltas)<<" for RPZ "<<zoneName<<endl;
+
+      oldZone = luaconfsLocal->dfe.getZone(zoneIdx);
+      /* we need to make a _full copy_ of the zone we are going to work on */
+      std::shared_ptr<DNSFilterEngine::Zone> newZone = std::make_shared<DNSFilterEngine::Zone>(*oldZone);
+      /* initialize the current serial to the last one */
+      std::shared_ptr<SOARecordContent> currentSR = sr;
+
+      int totremove=0, totadd=0;
+      bool fullUpdate = false;
+      for(const auto& delta : deltas) {
+        const auto& remove = delta.first;
+        const auto& add = delta.second;
+        if(remove.empty()) {
+          g_log<<Logger::Warning<<"IXFR update is a whole new zone"<<endl;
+          newZone->clear();
+          fullUpdate = true;
+        }
+        for(const auto& rr : remove) { // should always contain the SOA
+          if(rr.d_type == QType::NS)
+            continue;
+          if(rr.d_type == QType::SOA) {
+            auto oldsr = getRR<SOARecordContent>(rr);
+            if (oldsr && oldsr->d_st.serial == currentSR->d_st.serial) {
+              //           cout<<"Got good removal of SOA serial "<<oldsr->d_st.serial<<endl;
+            }
+            else {
+              if (!oldsr) {
+                throw std::runtime_error("Unable to extract serial from SOA record while processing the removal part of an update");
+              }
+              else {
+                throw std::runtime_error("Received an unexpected serial (" + std::to_string(oldsr->d_st.serial) + ", expecting " + std::to_string(currentSR->d_st.serial) + ") from SOA record while processing the removal part of an update");
+              }
+            }
+          }
+          else {
+            totremove++;
+            g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had removal of "<<rr.d_name<<" from RPZ zone "<<zoneName<<endl;
+            RPZRecordToPolicy(rr, newZone, false, defpol, defpolOverrideLocal, maxTTL);
+          }
+        }
 
-    int totremove=0, totadd=0;
-    bool fullUpdate = false;
-    for(const auto& delta : deltas) {
-      const auto& remove = delta.first;
-      const auto& add = delta.second;
-      if(remove.empty()) {
-        g_log<<Logger::Warning<<"IXFR update is a whole new zone"<<endl;
-        newZone->clear();
-        fullUpdate = true;
+        for(const auto& rr : add) { // should always contain the new SOA
+          if(rr.d_type == QType::NS)
+            continue;
+          if(rr.d_type == QType::SOA) {
+            auto tempSR = getRR<SOARecordContent>(rr);
+            //   g_log<<Logger::Info<<"New SOA serial for "<<zoneName<<": "<<currentSR->d_st.serial<<endl;
+            if (tempSR) {
+              currentSR = tempSR;
+            }
+          }
+          else {
+            totadd++;
+            g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had addition of "<<rr.d_name<<" to RPZ zone "<<zoneName<<endl;
+            RPZRecordToPolicy(rr, newZone, true, defpol, defpolOverrideLocal, maxTTL);
+          }
+        }
       }
-      for(const auto& rr : remove) { // should always contain the SOA
-        if(rr.d_type == QType::NS)
-          continue;
-       if(rr.d_type == QType::SOA) {
-         auto oldsr = getRR<SOARecordContent>(rr);
-         if(oldsr && oldsr->d_st.serial == sr->d_st.serial) {
-           //      cout<<"Got good removal of SOA serial "<<oldsr->d_st.serial<<endl;
-         }
-         else
-           g_log<<Logger::Error<<"GOT WRONG SOA SERIAL REMOVAL, SHOULD TRIGGER WHOLE RELOAD"<<endl;
-       }
-       else {
-          totremove++;
-         g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had removal of "<<rr.d_name<<" from RPZ zone "<<zoneName<<endl;
-         RPZRecordToPolicy(rr, newZone, false, defpol, defpolOverrideLocal, maxTTL);
-       }
+
+      /* only update sr now that all changes have been converted */
+      if (currentSR) {
+        sr = currentSR;
       }
+      g_log<<Logger::Info<<"Had "<<totremove<<" RPZ removal"<<addS(totremove)<<", "<<totadd<<" addition"<<addS(totadd)<<" for "<<zoneName<<" New serial: "<<sr->d_st.serial<<endl;
+      newZone->setSerial(sr->d_st.serial);
+      newZone->setRefresh(sr->d_st.refresh);
+      setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), fullUpdate);
+
+      /* we need to replace the existing zone with the new one,
+         but we don't want to touch anything else, especially other zones,
+         since they might have been updated by another RPZ IXFR tracker thread.
+      */
+      g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
+                          lci.dfe.setZone(zoneIdx, newZone);
+                        });
 
-      for(const auto& rr : add) { // should always contain the new SOA
-        if(rr.d_type == QType::NS)
-          continue;
-       if(rr.d_type == QType::SOA) {
-         auto newsr = getRR<SOARecordContent>(rr);
-         //      g_log<<Logger::Info<<"New SOA serial for "<<zoneName<<": "<<newsr->d_st.serial<<endl;
-         if (newsr) {
-           sr = newsr;
-         }
-       }
-       else {
-          totadd++;
-         g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had addition of "<<rr.d_name<<" to RPZ zone "<<zoneName<<endl;
-         RPZRecordToPolicy(rr, newZone, true, defpol, defpolOverrideLocal, maxTTL);
-       }
+      if (!dumpZoneFileName.empty()) {
+        dumpZoneToDisk(zoneName, newZone, dumpZoneFileName);
       }
+      refresh = std::max(refreshFromConf ? refreshFromConf :  newZone->getRefresh(), 1U);
     }
-    g_log<<Logger::Info<<"Had "<<totremove<<" RPZ removal"<<addS(totremove)<<", "<<totadd<<" addition"<<addS(totadd)<<" for "<<zoneName<<" New serial: "<<sr->d_st.serial<<endl;
-    newZone->setSerial(sr->d_st.serial);
-    newZone->setRefresh(sr->d_st.refresh);
-    setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), fullUpdate);
-
-    /* we need to replace the existing zone with the new one,
-       but we don't want to touch anything else, especially other zones,
-       since they might have been updated by another RPZ IXFR tracker thread.
-    */
-    g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
-                        lci.dfe.setZone(zoneIdx, newZone);
-                      });
-
-    if (!dumpZoneFileName.empty()) {
-      dumpZoneToDisk(zoneName, newZone, dumpZoneFileName);
+    catch (const std::exception& e) {
+      g_log << Logger::Error << "Error while applying the update received over XFR for "<<zoneName<<", skipping the update: "<< e.what() <<endl;
     }
-    refresh = std::max(refreshFromConf ? refreshFromConf :  newZone->getRefresh(), 1U);
   }
 }
index 65ab81d3521f93594413bec4e62a88ed194842f0..435051a136da4649e871bab2ceda85b2851c3935 100644 (file)
@@ -63,7 +63,7 @@ void doSecPoll(bool first)
     processSecPoll(res, ret, security_status, security_message);
   } catch(const PDNSException &pe) {
     S.set("security-status", security_status);
-    g_log<<Logger::Warning<<"Could not retrieve security status update for '" + pkgv + "' on '"+ query + "': "<<pe.reason<<endl;
+    g_log<<Logger::Warning<<"Failed to retrieve security status update for '" + pkgv + "' on '"+ query + "': "<<pe.reason<<endl;
     return;
   }
 
index 2e53ec0d474c846f902c669aad3f540cc9d069aa..eacaef09287c15ebac1f6afef6f77b0e52536f01 100644 (file)
@@ -47,7 +47,7 @@ void doSecPoll(time_t* last_secpoll)
   boost::replace_all(qstring, "+", "_");
   boost::replace_all(qstring, "~", "_");
 
-  vState state = Indeterminate;
+  vState state = vState::Indeterminate;
   DNSName query(qstring);
   int res = sr.beginResolve(query, QType(QType::TXT), 1, ret);
 
@@ -55,8 +55,8 @@ void doSecPoll(time_t* last_secpoll)
     state = sr.getValidationState();
   }
 
-  if(state == Bogus) {
-    g_log<<Logger::Error<<"Could not retrieve security status update for '" +pkgv+ "' on '"<<query<<"', DNSSEC validation result was Bogus!"<<endl;
+  if(state == vState::Bogus) {
+    g_log<<Logger::Error<<"Failed to retrieve security status update for '" +pkgv+ "' on '"<<query<<"', DNSSEC validation result was Bogus!"<<endl;
     if(g_security_status == 1) // If we were OK, go to unknown
       g_security_status = 0;
     return;
@@ -74,7 +74,7 @@ void doSecPoll(time_t* last_secpoll)
     processSecPoll(res, ret, security_status, security_message);
   } catch(const PDNSException &pe) {
     g_security_status = security_status;
-    g_log<<Logger::Warning<<"Could not retrieve security status update for '" << pkgv << "' on '"<< query << "': "<<pe.reason<<endl;
+    g_log<<Logger::Warning<<"Failed to retrieve security status update for '" << pkgv << "' on '"<< query << "': "<<pe.reason<<endl;
     return;
   }
 
index 77e6a08a4cf6c885dedf9867214538e89a1261df..e798a3b61fe3f2d0f3e2aeec11981a6f8f95b3ef 100644 (file)
@@ -40,7 +40,7 @@ void processSecPoll(const int res, const std::vector<DNSRecord> &ret, int &secPo
   secPollMessage.clear();
   if (res != 0) { // not NOERROR
     setSecPollToUnknownOnOK(secPollStatus);
-    throw PDNSException("RCODE was not NOERROR but " + RCode::to_s(res));
+    throw PDNSException("RCODE was " + RCode::to_s(res));
   }
 
   if (ret.empty()) { // empty NOERROR... wat?
index 64c4bba7dda401d4dfc4fc0725094c922382f4d4..1dd1353b0002a3a2bea02ef0db07fc37d9a7a0c3 100644 (file)
@@ -34,7 +34,7 @@ void pdns::shuffle(std::vector<DNSZoneRecord>& rrs)
 {
   std::vector<DNSZoneRecord>::iterator first, second;
 
-  // We assume the CNAMES are listed firts in the ANSWWER section and the the other records
+  // We assume the CNAMES are listed first in the ANSWER section and the the other records
   // and we want to shuffle the other records only
 
   // First we scan for the first non-CNAME ANSWER record
@@ -43,7 +43,7 @@ void pdns::shuffle(std::vector<DNSZoneRecord>& rrs)
       break;
     }
   }
-  // And then for one past the last ANSWER recordd
+  // And then for one past the last ANSWER record
   for (second = first; second != rrs.end(); ++second)
     if (second->dr.d_place != DNSResourceRecord::ANSWER)
       break;
index 4b0ade2113344aee19d851613a3da1f5a31b307a..d2d8892b86810106ef374a98f64ec605f74aa01c 100644 (file)
@@ -34,7 +34,7 @@
 #include "dnsbackend.hh"
 #include "ueberbackend.hh"
 #include "packethandler.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
 #include "logger.hh"
 #include "dns.hh"
 #include "arguments.hh"
 #include "inflighter.cc"
 #include "namespaces.hh"
 #include "common_startup.hh"
+#include "query-local-address.hh"
 
 #include "ixfr.hh"
 
-void CommunicatorClass::addSuckRequest(const DNSName &domain, const ComboAddress& master)
+void CommunicatorClass::addSuckRequest(const DNSName &domain, const ComboAddress& master, bool force)
 {
   std::lock_guard<std::mutex> l(d_lock);
   SuckRequest sr;
   sr.domain = domain;
   sr.master = master;
+  sr.force = force;
   pair<UniQueue::iterator, bool>  res;
 
   res=d_suckdomains.push_back(sr);
@@ -292,7 +294,7 @@ static vector<DNSResourceRecord> doAxfr(const ComboAddress& raddr, const DNSName
 }   
 
 
-void CommunicatorClass::suck(const DNSName &domain, const ComboAddress& remote)
+void CommunicatorClass::suck(const DNSName &domain, const ComboAddress& remote, bool force)
 {
   {
     std::lock_guard<std::mutex> l(d_lock);
@@ -313,7 +315,7 @@ void CommunicatorClass::suck(const DNSName &domain, const ComboAddress& remote)
     DNSSECKeeper dk (&B); // reuse our UeberBackend copy for DNSSECKeeper
     bool wrongDomainKind = false;
     // this checks three error conditions & sets wrongDomainKind if we hit the third
-    if(!B.getDomainInfo(domain, di) || !di.backend || (wrongDomainKind = true, di.kind != DomainInfo::Slave)) { // di.backend and B are mostly identical
+    if(!B.getDomainInfo(domain, di) || !di.backend || (wrongDomainKind = true, !force && di.kind != DomainInfo::Slave)) { // di.backend and B are mostly identical
       if(wrongDomainKind)
         g_log<<Logger::Error<<"Can't determine backend for domain '"<<domain<<"', not configured as slave"<<endl;
       else
@@ -373,15 +375,13 @@ void CommunicatorClass::suck(const DNSName &domain, const ComboAddress& remote)
         return;
       }
     } else {
-      if(remote.sin4.sin_family == AF_INET && !::arg()["query-local-address"].empty()) {
-        laddr = ComboAddress(::arg()["query-local-address"]);
-      } else if(remote.sin4.sin_family == AF_INET6 && !::arg()["query-local-address6"].empty()) {
-        laddr = ComboAddress(::arg()["query-local-address6"]);
-      } else {
-        bool isv6 = remote.sin4.sin_family == AF_INET6;
-        g_log<<Logger::Error<<"Unable to AXFR, destination address is IPv" << (isv6 ? "6" : "4") << ", but query-local-address"<< (isv6 ? "6" : "") << " is unset!"<<endl;
+      if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
+        bool isV6 = remote.sin4.sin_family == AF_INET6;
+        g_log<<Logger::Error<<"Unable to AXFR, destination address is "<<remote<<" (IPv"<< (isV6 ? "6" : "4") <<
+          ", but that address family is not enabled for outgoing traffic (query-local-address)"<<endl;
         return;
       }
+      laddr = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
     }
 
     bool hadDnssecZone = false;
@@ -713,13 +713,10 @@ struct SlaveSenderReceiver
 
   bool receive(Identifier& id, Answer& a)
   {
-    if(d_resolver.tryGetSOASerial(&(std::get<0>(id)), &(std::get<1>(id)), &a.theirSerial, &a.theirInception, &a.theirExpire, &(std::get<2>(id)))) {
-      return 1;
-    }
-    return 0;
+    return d_resolver.tryGetSOASerial(&(std::get<0>(id)), &(std::get<1>(id)), &a.theirSerial, &a.theirInception, &a.theirExpire, &(std::get<2>(id)));
   }
 
-  void deliverAnswer(DomainNotificationInfo& dni, const Answer& a, unsigned int usec)
+  void deliverAnswer(const DomainNotificationInfo& dni, const Answer& a, unsigned int usec)
   {
     d_freshness[dni.di.id]=a;
   }
@@ -900,14 +897,14 @@ void CommunicatorClass::slaveRefresh(PacketHandler *P)
   }
   g_log<<Logger::Warning<<"Received serial number updates for "<<ssr.d_freshness.size()<<" zone"<<addS(ssr.d_freshness.size())<<", had "<<ifl.getTimeouts()<<" timeout"<<addS(ifl.getTimeouts())<<endl;
 
-  typedef DomainNotificationInfo val_t;
   time_t now = time(0);
-  for(val_t& val :  sdomains) {
+  for(auto& val : sdomains) {
     DomainInfo& di(val.di);
-    DomainInfo tempdi;
     // might've come from the packethandler
-    // Please do not overwrite received DI just to make sure it exists in backend.
     if(!di.backend) {
+      // Do not overwrite received DI just to make sure it exists in backend:
+      // di.masters should contain the picked master (as first entry)!
+      DomainInfo tempdi;
       if (!B->getDomainInfo(di.zone, tempdi)) {
         g_log<<Logger::Warning<<"Ignore domain "<< di.zone<<" since it has been removed from our backend"<<endl;
         continue;
@@ -949,10 +946,12 @@ void CommunicatorClass::slaveRefresh(PacketHandler *P)
     }
     catch(...) {}
 
-    uint32_t theirserial = ssr.d_freshness[di.id].theirSerial, ourserial = sd.serial;
+    uint32_t theirserial = ssr.d_freshness[di.id].theirSerial;
+    uint32_t ourserial = sd.serial;
+    const ComboAddress remote = *di.masters.begin();
 
     if(rfc1982LessThan(theirserial, ourserial) && ourserial != 0 && !::arg().mustDo("axfr-lower-serial"))  {
-      g_log<<Logger::Error<<"Domain '"<<di.zone<<"' more recent than master, our serial " << ourserial << " > their serial "<< theirserial << endl;
+      g_log<<Logger::Error<<"Domain '" << di.zone << "' more recent than master " << remote.toStringWithPortExcept(53) << ", our serial "<< ourserial<< " > their serial "<< theirserial << endl;
       di.backend->setFresh(di.id);
     }
     else if(hasSOA && theirserial == ourserial) {
@@ -969,38 +968,38 @@ void CommunicatorClass::slaveRefresh(PacketHandler *P)
         }
       }
       if(! maxInception && ! ssr.d_freshness[di.id].theirInception) {
-        g_log<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh (no DNSSEC), serial is "<<ourserial<<endl;
+        g_log<<Logger::Info<<"Domain '"<< di.zone << "' is fresh (no DNSSEC), serial is " << ourserial << " (checked master " << remote.toStringWithPortExcept(53) << ")" << endl;
         di.backend->setFresh(di.id);
       }
       else if(maxInception == ssr.d_freshness[di.id].theirInception && maxExpire == ssr.d_freshness[di.id].theirExpire) {
-        g_log<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh and SOA RRSIGs match, serial is "<<ourserial<<endl;
+        g_log<<Logger::Info<<"Domain '"<< di.zone << "' is fresh and SOA RRSIGs match, serial is " << ourserial << " (checked master " << remote.toStringWithPortExcept(53) << ")" << endl;
         di.backend->setFresh(di.id);
       }
       else if(maxExpire >= now && ! ssr.d_freshness[di.id].theirInception ) {
-        g_log<<Logger::Info<<"Domain '"<< di.zone<<"' is fresh, master is no longer signed but (some) signatures are still vallid, serial is "<<ourserial<<endl;
+        g_log<<Logger::Info<<"Domain '"<< di.zone << "' is fresh, master " << remote.toStringWithPortExcept(53) << " is no longer signed but (some) signatures are still vallid, serial is " << ourserial << endl;
         di.backend->setFresh(di.id);
       }
       else if(maxInception && ! ssr.d_freshness[di.id].theirInception ) {
-        g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master is no longer signed and all signatures have expired, serial is "<<ourserial<<endl;
-        addSuckRequest(di.zone, *di.masters.begin());
+        g_log<<Logger::Warning<<"Domain '"<< di.zone << "' is stale, master " << remote.toStringWithPortExcept(53) << " is no longer signed and all signatures have expired, serial is " << ourserial << endl;
+        addSuckRequest(di.zone, remote);
       }
       else if(dk.doesDNSSEC() && ! maxInception && ssr.d_freshness[di.id].theirInception) {
-        g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master has signed, serial is "<<ourserial<<endl;
-        addSuckRequest(di.zone, *di.masters.begin());
+        g_log<<Logger::Warning<<"Domain '"<< di.zone << "' is stale, master " << remote.toStringWithPortExcept(53) << " has signed, serial is " << ourserial << endl;
+        addSuckRequest(di.zone, remote);
       }
       else {
-        g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is fresh, but RRSIGs differ, so DNSSEC is stale, serial is "<<ourserial<<endl;
-        addSuckRequest(di.zone, *di.masters.begin());
+        g_log<<Logger::Warning<<"Domain '"<< di.zone << "' is fresh, but RRSIGs differ on master" << remote.toStringWithPortExcept(53)<<", so DNSSEC is stale, serial is " << ourserial << endl;
+        addSuckRequest(di.zone, remote);
       }
     }
     else {
       if(hasSOA) {
-        g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is stale, master serial "<<theirserial<<", our serial "<< ourserial <<endl;
+        g_log<<Logger::Warning<<"Domain '"<< di.zone << "' is stale, master " << remote.toStringWithPortExcept(53) << " serial " << theirserial << ", our serial " << ourserial << endl;
       }
       else {
-        g_log<<Logger::Warning<<"Domain '"<< di.zone<<"' is empty, master serial "<<theirserial<<endl;
+        g_log<<Logger::Warning<<"Domain '"<< di.zone << "' is empty, master " << remote.toStringWithPortExcept(53) << " serial " << theirserial << endl;
       }
-      addSuckRequest(di.zone, *di.masters.begin());
+      addSuckRequest(di.zone, remote);
     }
   }
 }
index 9d89d15b774294904e8450527ac8a99bb35bb916..110287ae0831879ce9a2d3e805652d925bb27848 100644 (file)
@@ -97,18 +97,25 @@ string StatBag::getDescrip(const string &item)
   return d_keyDescrips[item];
 }
 
-void StatBag::declare(const string &key, const string &descrip)
+StatType StatBag::getStatType(const string &item)
+{
+  exists(item);
+  return d_statTypes[item];
+}
+
+void StatBag::declare(const string &key, const string &descrip, StatType statType)
 {
   auto i=make_unique<AtomicCounter>(0);
   d_stats[key]=std::move(i);
   d_keyDescrips[key]=descrip;
+  d_statTypes[key]=statType;
 }
 
-void StatBag::declare(const string &key, const string &descrip, StatBag::func_t func)
+void StatBag::declare(const string &key, const string &descrip, StatBag::func_t func, StatType statType)
 {
-
   d_funcstats[key]=func;
   d_keyDescrips[key]=descrip;
+  d_statTypes[key]=statType;
 }
 
           
@@ -237,8 +244,8 @@ vector<pair<T, unsigned int> >StatRing<T,Comp>::get() const
 
 void StatBag::registerRingStats(const string& name)
 {
-  declare("ring-" + name + "-size", "Number of entries in the " + name + " ring", [this,name](const std::string&) { return static_cast<uint64_t>(getRingEntriesCount(name)); });
-  declare("ring-" + name + "-capacity", "Maximum number of entries in the " + name + " ring", [this,name](const std::string&) { return static_cast<uint64_t>(getRingSize(name)); });
+  declare("ring-" + name + "-size", "Number of entries in the " + name + " ring", [this,name](const std::string&) { return static_cast<uint64_t>(getRingEntriesCount(name)); }, StatType::gauge);
+  declare("ring-" + name + "-capacity", "Maximum number of entries in the " + name + " ring", [this,name](const std::string&) { return static_cast<uint64_t>(getRingSize(name)); }, StatType::gauge);
 }
 
 void StatBag::declareRing(const string &name, const string &help, unsigned int size)
index 982f739dfbe43b5f0ac6554150486936eb6358df..1887b7833a4cc5c329b7f5599a7260a721fd6d21 100644 (file)
@@ -62,12 +62,17 @@ private:
   string d_help;
 };
 
+enum class StatType : uint8_t {
+  counter = 1,
+  gauge = 2,
+};
 
 //! use this to gather and query statistics
 class StatBag
 {
   map<string, std::unique_ptr<AtomicCounter>> d_stats;
   map<string, string> d_keyDescrips;
+  map<string, StatType> d_statTypes;
   map<string,StatRing<string, CIStringCompare> >d_rings;
   map<string,StatRing<SComboAddress> >d_comboRings;
   map<string,StatRing<std::tuple<DNSName, QType> > >d_dnsnameqtyperings;
@@ -82,8 +87,8 @@ class StatBag
 public:
   StatBag(); //!< Naked constructor. You need to declare keys before this class becomes useful
   ~StatBag();
-  void declare(const string &key, const string &descrip=""); //!< Before you can store or access a key, you need to declare it
-  void declare(const string &key, const string &descrip, func_t func); //!< Before you can store or access a key, you need to declare it
+  void declare(const string &key, const string &descrip="", StatType statType=StatType::counter); //!< Before you can store or access a key, you need to declare it
+  void declare(const string &key, const string &descrip, func_t func, StatType statType); //!< Before you can store or access a key, you need to declare it
 
   void declareRing(const string &name, const string &title, unsigned int size=10000);
   void declareComboRing(const string &name, const string &help, unsigned int size=10000);
@@ -131,6 +136,7 @@ public:
   string directory(); //!< Returns a list of all data stored
   vector<string> getEntries(); //!< returns a vector with datums (items)
   string getDescrip(const string &item); //!< Returns the description of this datum/item
+  StatType getStatType(const string &item); //!< Returns the stats type for the metrics endpoint
   void exists(const string &key); //!< call this function to throw an exception in case a key does not exist
   inline void deposit(const string &key, int value); //!< increment the statistics behind this key by value amount
   inline void inc(const string &key); //!< increase this key's value by one
index fca6650fad06b91862de034378b8d2b1b1ed3e1e..e9996a99e99952f0eaeb6319ded462ad13cba1fa 100644 (file)
@@ -53,6 +53,7 @@ unsigned int SyncRes::s_maxnegttl;
 unsigned int SyncRes::s_maxbogusttl;
 unsigned int SyncRes::s_maxcachettl;
 unsigned int SyncRes::s_maxqperq;
+unsigned int SyncRes::s_maxnsaddressqperq;
 unsigned int SyncRes::s_maxtotusec;
 unsigned int SyncRes::s_maxdepth;
 unsigned int SyncRes::s_minimumTTL;
@@ -84,6 +85,7 @@ uint8_t SyncRes::s_ecsipv6limit;
 uint8_t SyncRes::s_ecsipv4cachelimit;
 uint8_t SyncRes::s_ecsipv6cachelimit;
 
+bool SyncRes::s_doIPv4;
 bool SyncRes::s_doIPv6;
 bool SyncRes::s_nopacketcache;
 bool SyncRes::s_rootNXTrust;
@@ -130,21 +132,21 @@ SyncRes::SyncRes(const struct timeval& now) :  d_authzonequeries(0), d_outquerie
 }
 
 /** everything begins here - this is the entry point just after receiving a packet */
-int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret)
+int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret, unsigned int depth)
 {
-  vState state = Indeterminate;
+  vState state = vState::Indeterminate;
   s_queries++;
   d_wasVariable=false;
   d_wasOutOfBand=false;
 
   if (doSpecialNamesResolve(qname, qtype, qclass, ret)) {
-    d_queryValidationState = Insecure; // this could fool our stats into thinking a validation took place
+    d_queryValidationState = vState::Insecure; // this could fool our stats into thinking a validation took place
     return 0;                          // so do check before updating counters (we do now)
   }
 
   auto qtypeCode = qtype.getCode();
   /* rfc6895 section 3.1 */
-  if ((qtypeCode >= 128 && qtypeCode <= 254) || qtypeCode == QType::RRSIG || qtypeCode == QType::NSEC3 || qtypeCode == QType::OPT || qtypeCode == 65535) {
+  if (qtypeCode == 0 || (qtypeCode >= 128 && qtypeCode <= 254) || qtypeCode == QType::RRSIG || qtypeCode == QType::NSEC3 || qtypeCode == QType::OPT || qtypeCode == 65535) {
     return -1;
   }
 
@@ -154,11 +156,11 @@ int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qcl
     return -1;
 
   set<GetBestNSAnswer> beenthere;
-  int res=doResolve(qname, qtype, ret, 0, beenthere, state);
+  int res=doResolve(qname, qtype, ret, depth, beenthere, state);
   d_queryValidationState = state;
 
   if (shouldValidate()) {
-    if (d_queryValidationState != Indeterminate) {
+    if (d_queryValidationState != vState::Indeterminate) {
       g_stats.dnssecValidations++;
     }
     increaseDNSSECStateCounter(d_queryValidationState);
@@ -405,10 +407,10 @@ bool SyncRes::doOOBResolve(const DNSName &qname, const QType &qtype, vector<DNSR
   return doOOBResolve(iter->second, qname, qtype, ret, res);
 }
 
-bool SyncRes::isForwardOrAuth(const DNSName &qname) const {
+bool SyncRes::isRecursiveForwardOrAuth(const DNSName &qname) const {
   DNSName authname(qname);
   domainmap_t::const_iterator iter = getBestAuthZone(&authname);
-  return iter != t_sstorage.domainmap->end();
+  return iter != t_sstorage.domainmap->end() && (iter->second.isAuth() || iter->second.shouldRecurse());
 }
 
 uint64_t SyncRes::doEDNSDump(int fd)
@@ -640,7 +642,8 @@ int SyncRes::asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, con
 
 int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state) {
 
-  if (!getQNameMinimization() || isForwardOrAuth(qname)) {
+  // In the auth or recursive forward case, it does nt make sense to do qname-minimization
+  if (!getQNameMinimization() || isRecursiveForwardOrAuth(qname)) {
     return doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, state);
   }
 
@@ -670,7 +673,10 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
   vector<DNSRecord> retq;
   bool old = setCacheOnly(true);
   bool fromCache = false;
-  int res = doResolveNoQNameMinimization(qname, qtype, retq, depth + 1, beenthere, state, &fromCache);
+  // For cache peeking, we tell doResolveNoQNameMinimization not to consider the (non-recursive) forward case.
+  // Otherwise all queries in a forward domain will be forwarded, while we want to consult the cache.
+  // The out-of-band cases for doResolveNoQNameMinimization() should be reconsidered and redone some day.
+  int res = doResolveNoQNameMinimization(qname, qtype, retq, depth, beenthere, state, &fromCache, nullptr, false);
   setCacheOnly(old);
   if (fromCache) {
     QLOG("Step0 Found in cache");
@@ -694,7 +700,7 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
     for (int tries = 0; tries < 2 && bestns.empty(); ++tries) {
       bool flawedNSSet = false;
       set<GetBestNSAnswer> beenthereIgnored;
-      getBestNSFromCache(nsdomain, qtype, bestns, &flawedNSSet, depth + 1, beenthereIgnored);
+      getBestNSFromCache(nsdomain, qtype, bestns, &flawedNSSet, depth, beenthereIgnored);
     }
 
     if (bestns.size() == 0) {
@@ -722,7 +728,7 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
       // Step 3 resolve
       if (child == qname) {
         QLOG("Step3 Going to do final resolve");
-        res = doResolveNoQNameMinimization(qname, qtype, ret, depth + 1, beenthere, state);
+        res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, state);
         QLOG("Step3 Final resolve: " << RCode::to_s(res) << "/" << ret.size());
         return res;
       }
@@ -731,7 +737,7 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
       QLOG("Step4 Resolve A for child");
       retq.resize(0);
       StopAtDelegation stopAtDelegation = Stop;
-      res = doResolveNoQNameMinimization(child, QType::A, retq, depth + 1, beenthere, state, NULL, &stopAtDelegation);
+      res = doResolveNoQNameMinimization(child, QType::A, retq, depth, beenthere, state, NULL, &stopAtDelegation);
       QLOG("Step4 Resolve A result is " << RCode::to_s(res) << "/" << retq.size() << "/" << stopAtDelegation);
       if (stopAtDelegation == Stopped) {
         QLOG("Delegation seen, continue at step 1");
@@ -742,7 +748,7 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
         // Case 5: unexpected answer
         QLOG("Step5: other rcode, last effort final resolve");
         setQNameMinimization(false);
-        res = doResolveNoQNameMinimization(qname, qtype, ret, depth + 1, beenthere, state);
+        res = doResolveNoQNameMinimization(qname, qtype, ret, depth, beenthere, state);
 
         if(res == RCode::NoError) {
           s_qnameminfallbacksuccess++;
@@ -770,7 +776,7 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
  * \param stopAtDelegation if non-nullptr and pointed-to value is Stop requests the callee to stop at a delegation, if so pointed-to value is set to Stopped
  * \return DNS RCODE or -1 (Error)
  */
-int SyncRes::doResolveNoQNameMinimization(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state, bool *fromCache, StopAtDelegation *stopAtDelegation)
+int SyncRes::doResolveNoQNameMinimization(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state, bool *fromCache, StopAtDelegation *stopAtDelegation, bool considerforwards)
 {
   string prefix;
   if(doLog()) {
@@ -780,7 +786,7 @@ int SyncRes::doResolveNoQNameMinimization(const DNSName &qname, const QType &qty
 
   LOG(prefix<<qname<<": Wants "<< (d_doDNSSEC ? "" : "NO ") << "DNSSEC processing, "<<(d_requireAuthData ? "" : "NO ")<<"auth data in query for "<<qtype.getName()<<endl);
 
-  state = Indeterminate;
+  state = vState::Indeterminate;
 
   if (s_maxdepth && depth > s_maxdepth) {
     string msg = "More than " + std::to_string(s_maxdepth) + " (max-recursion-depth) levels of recursion needed while resolving " + qname.toLogString();
@@ -804,7 +810,7 @@ int SyncRes::doResolveNoQNameMinimization(const DNSName &qname, const QType &qty
             *fromCache = d_wasOutOfBand;
           return res;
         }
-        else {
+        else if (considerforwards) {
           const vector<ComboAddress>& servers = iter->second.d_servers;
           const ComboAddress remoteIP = servers.front();
           LOG(prefix<<qname<<": forwarding query to hardcoded nameserver '"<< remoteIP.toStringWithPort()<<"' for zone '"<<authname<<"'"<<endl);
@@ -849,8 +855,9 @@ int SyncRes::doResolveNoQNameMinimization(const DNSName &qname, const QType &qty
       }
     }
 
-    if(!d_skipCNAMECheck && doCNAMECacheCheck(qname, qtype, ret, depth, res, state, wasAuthZone, wasForwardRecurse)) { // will reroute us if needed
+    if(doCNAMECacheCheck(qname, qtype, ret, depth, res, state, wasAuthZone, wasForwardRecurse)) { // will reroute us if needed
       d_wasOutOfBand = wasAuthZone;
+      // Do not set *fromCache; res does not reflect the final result in all cases
       return res;
     }
 
@@ -885,7 +892,7 @@ int SyncRes::doResolveNoQNameMinimization(const DNSName &qname, const QType &qty
 
   state = getValidationStatus(qname, false);
 
-  LOG(prefix<<qname<<": initial validation status for "<<qname<<" is "<<vStates[state]<<endl);
+  LOG(prefix<<qname<<": initial validation status for "<<qname<<" is "<<state<<endl);
 
   if(!(res=doResolveAt(nsset, subdomain, flawedNSSet, qname, qtype, ret, depth, beenthere, state, stopAtDelegation)))
     return 0;
@@ -915,7 +922,7 @@ struct speedOrderCA
 
 /** This function explicitly goes out for A or AAAA addresses
 */
-vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly)
+vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS)
 {
   typedef vector<DNSRecord> res_t;
   typedef vector<ComboAddress> ret_t;
@@ -924,14 +931,15 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
   bool oldCacheOnly = setCacheOnly(cacheOnly);
   bool oldRequireAuthData = d_requireAuthData;
   bool oldValidationRequested = d_DNSSECValidationRequested;
+  const unsigned int startqueries = d_outqueries;
   d_requireAuthData = false;
   d_DNSSECValidationRequested = false;
 
   try {
-    vState newState = Indeterminate;
+    vState newState = vState::Indeterminate;
     res_t resv4;
     // If IPv4 ever becomes second class, we should revisit this
-    if (doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
+    if (s_doIPv4 && doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
       for (auto const &i : resv4) {
         if (i.d_type == QType::A) {
           if (auto rec = getRR<ARecordContent>(i)) {
@@ -940,10 +948,10 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
         }
       }
     }
-    if (s_doIPv6) {
+    if (s_doIPv6) { // s_doIPv6 **IMPLIES** pdns::isQueryLocalAddressFamilyEnabled(AF_INET6) returned true
       if (ret.empty()) {
         // We did not find IPv4 addresses, try to get IPv6 ones
-        newState = Indeterminate;
+        newState = vState::Indeterminate;
         res_t resv6;
         if (doResolve(qname, QType::AAAA, resv6, depth+1, beenthere, newState) == 0) {  // this consults cache, OR goes out
           for (const auto &i : resv6) {
@@ -974,6 +982,10 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
        of a NS and keep processing the current query */
   }
 
+  if (ret.empty() && d_outqueries > startqueries) {
+    // We did 1 or more outgoing queries to resolve this NS name but returned empty handed
+    addressQueriesForNS++;
+  }
   d_requireAuthData = oldRequireAuthData;
   d_DNSSECValidationRequested = oldValidationRequested;
   setCacheOnly(oldCacheOnly);
@@ -1039,10 +1051,16 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto
       for(auto k=ns.cbegin();k!=ns.cend(); ++k) {
         if(k->d_ttl > (unsigned int)d_now.tv_sec ) {
           vector<DNSRecord> aset;
+          QType nsqt{QType::ADDR};
+          if (s_doIPv4 && !s_doIPv6) {
+            nsqt = QType::A;
+          } else if (!s_doIPv4 && s_doIPv6) {
+            nsqt = QType::AAAA;
+          }
 
           const DNSRecord& dr=*k;
          auto nrr = getRR<NSRecordContent>(dr);
-          if(nrr && (!nrr->getNS().isPartOf(subdomain) || s_RC->get(d_now.tv_sec, nrr->getNS(), s_doIPv6 ? QType(QType::ADDR) : QType(QType::A),
+          if(nrr && (!nrr->getNS().isPartOf(subdomain) || s_RC->get(d_now.tv_sec, nrr->getNS(), nsqt,
                                                                     false, doLog() ? &aset : 0, d_cacheRemote, d_routingTag) > 5)) {
             bestns.push_back(dr);
             LOG(prefix<<qname<<": NS (with ip, or non-glue) in cache for '"<<subdomain<<"' -> '"<<nrr->getNS()<<"'"<<endl);
@@ -1094,11 +1112,11 @@ void SyncRes::getBestNSFromCache(const DNSName &qname, const QType& qtype, vecto
     if(subdomain.isRoot() && !brokeloop) {
       // We lost the root NS records
       primeHints();
-      primeRootNSZones(g_dnssecmode != DNSSECMode::Off);
+      primeRootNSZones(g_dnssecmode != DNSSECMode::Off, depth);
       LOG(prefix<<qname<<": reprimed the root"<<endl);
       /* let's prevent an infinite loop */
       if (!d_updatingRootNS) {
-        getRootNS(d_now, d_asyncResolve);
+        getRootNS(d_now, d_asyncResolve, depth);
       }
     }
   } while(subdomain.chopOff());
@@ -1122,42 +1140,80 @@ SyncRes::domainmap_t::const_iterator SyncRes::getBestAuthZone(DNSName* qname) co
 /** doesn't actually do the work, leaves that to getBestNSFromCache */
 DNSName SyncRes::getBestNSNamesFromCache(const DNSName &qname, const QType& qtype, NsSet& nsset, bool* flawedNSSet, unsigned int depth, set<GetBestNSAnswer>&beenthere)
 {
-  DNSName authdomain(qname);
+  string prefix;
+  if (doLog()) {
+    prefix = d_prefix;
+    prefix.append(depth, ' ');
+  }
+  DNSName authOrForwDomain(qname);
 
-  domainmap_t::const_iterator iter=getBestAuthZone(&authdomain);
-  if(iter!=t_sstorage.domainmap->end()) {
-    if( iter->second.isAuth() )
+  domainmap_t::const_iterator iter = getBestAuthZone(&authOrForwDomain);
+  // We have an auth, forwarder of forwarder-recurse
+  if (iter != t_sstorage.domainmap->end()) {
+    if (iter->second.isAuth()) {
       // this gets picked up in doResolveAt, the empty DNSName, combined with the
       // empty vector means 'we are auth for this zone'
       nsset.insert({DNSName(), {{}, false}});
+      return authOrForwDomain;
+    }
     else {
-      // Again, picked up in doResolveAt. An empty DNSName, combined with a
-      // non-empty vector of ComboAddresses means 'this is a forwarded domain'
-      // This is actually picked up in retrieveAddressesForNS called from doResolveAt.
-      nsset.insert({DNSName(), {iter->second.d_servers, iter->second.shouldRecurse() }});
+      if (iter->second.shouldRecurse()) {
+        // Again, picked up in doResolveAt. An empty DNSName, combined with a
+        // non-empty vector of ComboAddresses means 'this is a forwarded domain'
+        // This is actually picked up in retrieveAddressesForNS called from doResolveAt.
+        nsset.insert({DNSName(), {iter->second.d_servers, true }});
+        return authOrForwDomain;
+      }
     }
-    return authdomain;
   }
 
-  DNSName subdomain(qname);
+  // We might have a (non-recursive) forwarder, but maybe the cache already contains
+  // a better NS
   vector<DNSRecord> bestns;
-  getBestNSFromCache(subdomain, qtype, bestns, flawedNSSet, depth, beenthere);
+  DNSName nsFromCacheDomain(g_rootdnsname);
+  getBestNSFromCache(qname, qtype, bestns, flawedNSSet, depth, beenthere);
+
+  // Pick up the auth domain
+  for (const auto& k : bestns) {
+    const auto nsContent = getRR<NSRecordContent>(k);
+    if (nsContent) {
+      nsFromCacheDomain = k.d_name;
+      break;
+    }
+  }
+
+  if (iter != t_sstorage.domainmap->end()) {
+    if (doLog()) {
+      LOG(prefix << qname << " authOrForwDomain: " << authOrForwDomain << " nsFromCacheDomain: " << nsFromCacheDomain << " isPartof: " << authOrForwDomain.isPartOf(nsFromCacheDomain) << endl);
+    }
 
-  for(auto k=bestns.cbegin() ; k != bestns.cend(); ++k) {
+    // If the forwarder is better or equal to what's found in the cache, use forwarder. Note that name.isPartOf(name).
+    // So queries that get NS for authOrForwDomain itself go to the forwarder
+    if (authOrForwDomain.isPartOf(nsFromCacheDomain)) {
+      if (doLog()) {
+        LOG(prefix << qname << ": using forwarder as NS" << endl);
+      }
+      nsset.insert({DNSName(), {iter->second.d_servers, false }});
+      return authOrForwDomain;
+    } else {
+      if (doLog()) {
+        LOG(prefix << qname << ": using NS from cache" << endl);
+      }
+    }
+  }
+  for (auto k = bestns.cbegin(); k != bestns.cend(); ++k) {
     // The actual resolver code will not even look at the ComboAddress or bool
     const auto nsContent = getRR<NSRecordContent>(*k);
     if (nsContent) {
       nsset.insert({nsContent->getNS(), {{}, false}});
-      if(k==bestns.cbegin())
-        subdomain=k->d_name;
     }
   }
-  return subdomain;
+  return nsFromCacheDomain;
 }
 
 void SyncRes::updateValidationStatusInCache(const DNSName &qname, const QType& qt, bool aa, vState newState) const
 {
-  if (newState == Bogus) {
+  if (newState == vState::Bogus) {
     s_RC->updateValidationStatus(d_now.tv_sec, qname, qt, d_cacheRemote, d_routingTag, aa, newState, s_maxbogusttl + d_now.tv_sec);
   }
   else {
@@ -1165,6 +1221,18 @@ void SyncRes::updateValidationStatusInCache(const DNSName &qname, const QType& q
   }
 }
 
+static bool scanForCNAMELoop(const DNSName& name, const vector<DNSRecord>& records)
+{
+  for (const auto& record: records) {
+    if (record.d_type == QType::CNAME && record.d_place == DNSResourceRecord::ANSWER) {
+      if (name == record.d_name) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector<DNSRecord>& ret, unsigned int depth, int &res, vState& state, bool wasAuthZone, bool wasForwardRecurse)
 {
   string prefix;
@@ -1218,7 +1286,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector
     LOG(prefix<<qname<<": No CNAME or DNAME cache hit of '"<< qname <<"' found"<<endl);
     return false;
   }
-
+  
   for(auto const &record : cset) {
     if (record.d_class != QClass::IN) {
       continue;
@@ -1226,7 +1294,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector
 
     if(record.d_ttl > (unsigned int) d_now.tv_sec) {
 
-      if (!wasAuthZone && shouldValidate() && (wasAuth || wasForwardRecurse) && state == Indeterminate && d_requireAuthData) {
+      if (!wasAuthZone && shouldValidate() && (wasAuth || wasForwardRecurse) && state == vState::Indeterminate && d_requireAuthData) {
         /* This means we couldn't figure out the state when this entry was cached,
            most likely because we hadn't computed the zone cuts yet. */
         /* make sure they are computed before validating */
@@ -1238,12 +1306,12 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector
         computeZoneCuts(subdomain, g_rootdnsname, depth);
 
         vState recordState = getValidationStatus(foundName, false);
-        if (recordState == Secure) {
-          LOG(prefix<<qname<<": got Indeterminate state from the "<<foundQT.getName()<<" cache, validating.."<<endl);
+        if (recordState == vState::Secure) {
+          LOG(prefix<<qname<<": got vState::Indeterminate state from the "<<foundQT.getName()<<" cache, validating.."<<endl);
           state = SyncRes::validateRecordsWithSigs(depth, foundName, foundQT, foundName, cset, signatures);
-          if (state != Indeterminate) {
-            LOG(prefix<<qname<<": got Indeterminate state from the CNAME cache, new validation result is "<<vStates[state]<<endl);
-            if (state == Bogus) {
+          if (state != vState::Indeterminate) {
+            LOG(prefix<<qname<<": got vState::Indeterminate state from the CNAME cache, new validation result is "<<state<<endl);
+            if (state == vState::Bogus) {
               capTTL = s_maxbogusttl;
             }
             updateValidationStatusInCache(foundName, foundQT, wasAuth, state);
@@ -1251,7 +1319,7 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector
         }
       }
 
-      LOG(prefix<<qname<<": Found cache "<<foundQT.getName()<<" hit for '"<< foundName << "|"<<foundQT.getName()<<"' to '"<<record.d_content->getZoneRepresentation()<<"', validation state is "<<vStates[state]<<endl);
+      LOG(prefix<<qname<<": Found cache "<<foundQT.getName()<<" hit for '"<< foundName << "|"<<foundQT.getName()<<"' to '"<<record.d_content->getZoneRepresentation()<<"', validation state is "<<state<<endl);
 
       DNSRecord dr = record;
       dr.d_ttl -= d_now.tv_sec;
@@ -1313,6 +1381,11 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector
         return true;
       }
 
+      if (qtype == QType::DS || qtype == QType::DNSKEY) {
+        res = 0;
+        return true;
+      }
+
       // We have a DNAME _or_ CNAME cache hit and the client wants something else than those two.
       // Let's find the answer!
       if (foundQT == QType::CNAME) {
@@ -1323,10 +1396,23 @@ bool SyncRes::doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector
         newTarget = cnameContent->getTarget();
       }
 
+      if (qname == newTarget) {
+        string msg = "got a CNAME referral (from cache) to self";
+        LOG(prefix<<qname<<": "<<msg<<endl);
+        throw ImmediateServFailException(msg);
+      }
+
+      // Check to see if we already have seen the new target as a previous target
+      if (scanForCNAMELoop(newTarget, ret)) {
+        string msg = "got a CNAME referral (from cache) that causes a loop";
+        LOG(prefix<<qname<<": status="<<msg<<endl);
+        throw ImmediateServFailException(msg);
+      }
+
       set<GetBestNSAnswer>beenthere;
-      vState cnameState = Indeterminate;
+      vState cnameState = vState::Indeterminate;
       res = doResolve(newTarget, qtype, ret, depth+1, beenthere, cnameState);
-      LOG(prefix<<qname<<": updating validation state for response to "<<qname<<" from "<<vStates[state]<<" with the state from the DNAME/CNAME quest: "<<vStates[cnameState]<<endl);
+      LOG(prefix<<qname<<": updating validation state for response to "<<qname<<" from "<<state<<" with the state from the DNAME/CNAME quest: "<<cnameState<<endl);
       updateValidationState(state, cnameState);
 
       return true;
@@ -1375,15 +1461,14 @@ static void reapRecordsFromNegCacheEntryForValidation(tcache_t& tcache, const ve
  * \param ttl     The new TTL for these records
  * \param ret     The vector of DNSRecords that should contain the records with the modified TTL
  */
-static void addTTLModifiedRecords(const vector<DNSRecord>& records, const uint32_t ttl, vector<DNSRecord>& ret) {
-  for (const auto& rec : records) {
-    DNSRecord r(rec);
-    r.d_ttl = ttl;
-    ret.push_back(r);
+static void addTTLModifiedRecords(vector<DNSRecord>& records, const uint32_t ttl, vector<DNSRecord>& ret) {
+  for (auto& rec : records) {
+    rec.d_ttl = ttl;
+    ret.push_back(std::move(rec));
   }
 }
 
-void SyncRes::computeNegCacheValidationStatus(const NegCache::NegCacheEntry* ne, const DNSName& qname, const QType& qtype, const int res, vState& state, unsigned int depth)
+void SyncRes::computeNegCacheValidationStatus(const NegCache::NegCacheEntry& ne, const DNSName& qname, const QType& qtype, const int res, vState& state, unsigned int depth)
 {
   DNSName subdomain(qname);
   /* if we are retrieving a DS, we only care about the state of the parent zone */
@@ -1393,10 +1478,10 @@ void SyncRes::computeNegCacheValidationStatus(const NegCache::NegCacheEntry* ne,
   computeZoneCuts(subdomain, g_rootdnsname, depth);
 
   tcache_t tcache;
-  reapRecordsFromNegCacheEntryForValidation(tcache, ne->authoritySOA.records);
-  reapRecordsFromNegCacheEntryForValidation(tcache, ne->authoritySOA.signatures);
-  reapRecordsFromNegCacheEntryForValidation(tcache, ne->DNSSECRecords.records);
-  reapRecordsFromNegCacheEntryForValidation(tcache, ne->DNSSECRecords.signatures);
+  reapRecordsFromNegCacheEntryForValidation(tcache, ne.authoritySOA.records);
+  reapRecordsFromNegCacheEntryForValidation(tcache, ne.authoritySOA.signatures);
+  reapRecordsFromNegCacheEntryForValidation(tcache, ne.DNSSECRecords.records);
+  reapRecordsFromNegCacheEntryForValidation(tcache, ne.DNSSECRecords.signatures);
 
   for (const auto& entry : tcache) {
     // this happens when we did store signatures, but passed on the records themselves
@@ -1407,35 +1492,35 @@ void SyncRes::computeNegCacheValidationStatus(const NegCache::NegCacheEntry* ne,
     const DNSName& owner = entry.first.name;
 
     vState recordState = getValidationStatus(owner, false);
-    if (state == Indeterminate) {
+    if (state == vState::Indeterminate) {
       state = recordState;
     }
 
-    if (recordState == Secure) {
+    if (recordState == vState::Secure) {
       recordState = SyncRes::validateRecordsWithSigs(depth, qname, qtype, owner, entry.second.records, entry.second.signatures);
     }
 
-    if (recordState != Indeterminate && recordState != state) {
+    if (recordState != vState::Indeterminate && recordState != state) {
       updateValidationState(state, recordState);
-      if (state != Secure) {
+      if (state != vState::Secure) {
         break;
       }
     }
   }
 
-  if (state == Secure) {
-    vState neValidationState = ne->d_validationState;
-    dState expectedState = res == RCode::NXDomain ? NXDOMAIN : NXQTYPE;
-    dState denialState = getDenialValidationState(*ne, state, expectedState, false);
-    updateDenialValidationState(neValidationState, ne->d_name, state, denialState, expectedState, qtype == QType::DS || expectedState == NXDOMAIN);
+  if (state == vState::Secure) {
+    vState neValidationState = ne.d_validationState;
+    dState expectedState = res == RCode::NXDomain ? dState::NXDOMAIN : dState::NXQTYPE;
+    dState denialState = getDenialValidationState(ne, state, expectedState, false);
+    updateDenialValidationState(neValidationState, ne.d_name, state, denialState, expectedState, qtype == QType::DS || expectedState == dState::NXDOMAIN);
   }
-  if (state != Indeterminate) {
+  if (state != vState::Indeterminate) {
     /* validation succeeded, let's update the cache entry so we don't have to validate again */
     boost::optional<uint32_t> capTTD = boost::none;
-    if (state == Bogus) {
+    if (state == vState::Bogus) {
       capTTD = d_now.tv_sec + s_maxbogusttl;
     }
-    t_sstorage.negcache.updateValidationStatus(ne->d_name, ne->d_qtype, state, capTTD);
+    t_sstorage.negcache.updateValidationStatus(ne.d_name, ne.d_qtype, state, capTTD);
   }
 }
 
@@ -1455,32 +1540,32 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const DNSName& authname, bool w
   uint32_t sttl=0;
   //  cout<<"Lookup for '"<<qname<<"|"<<qtype.getName()<<"' -> "<<getLastLabel(qname)<<endl;
   vState cachedState;
-  const NegCache::NegCacheEntry* ne = nullptr;
+  NegCache::NegCacheEntry ne;
 
   if(s_rootNXTrust &&
-      t_sstorage.negcache.getRootNXTrust(qname, d_now, &ne) &&
-      ne->d_auth.isRoot() &&
+      t_sstorage.negcache.getRootNXTrust(qname, d_now, ne) &&
+      ne.d_auth.isRoot() &&
       !(wasForwardedOrAuthZone && !authname.isRoot())) { // when forwarding, the root may only neg-cache if it was forwarded to.
-    sttl = ne->d_ttd - d_now.tv_sec;
-    LOG(prefix<<qname<<": Entire name '"<<qname<<"', is negatively cached via '"<<ne->d_auth<<"' & '"<<ne->d_name<<"' for another "<<sttl<<" seconds"<<endl);
+    sttl = ne.d_ttd - d_now.tv_sec;
+    LOG(prefix<<qname<<": Entire name '"<<qname<<"', is negatively cached via '"<<ne.d_auth<<"' & '"<<ne.d_name<<"' for another "<<sttl<<" seconds"<<endl);
     res = RCode::NXDomain;
     giveNegative = true;
-    cachedState = ne->d_validationState;
-  } else if (t_sstorage.negcache.get(qname, qtype, d_now, &ne)) {
+    cachedState = ne.d_validationState;
+  } else if (t_sstorage.negcache.get(qname, qtype, d_now, ne)) {
     /* If we are looking for a DS, discard NXD if auth == qname
        and ask for a specific denial instead */
-    if (qtype != QType::DS || ne->d_qtype.getCode() || ne->d_auth != qname ||
-        t_sstorage.negcache.get(qname, qtype, d_now, &ne, true))
+    if (qtype != QType::DS || ne.d_qtype.getCode() || ne.d_auth != qname ||
+        t_sstorage.negcache.get(qname, qtype, d_now, ne, true))
     {
       res = RCode::NXDomain;
-      sttl = ne->d_ttd - d_now.tv_sec;
+      sttl = ne.d_ttd - d_now.tv_sec;
       giveNegative = true;
-      cachedState = ne->d_validationState;
-      if (ne->d_qtype.getCode()) {
-        LOG(prefix<<qname<<": "<<qtype.getName()<<" is negatively cached via '"<<ne->d_auth<<"' for another "<<sttl<<" seconds"<<endl);
+      cachedState = ne.d_validationState;
+      if (ne.d_qtype.getCode()) {
+        LOG(prefix<<qname<<": "<<qtype.getName()<<" is negatively cached via '"<<ne.d_auth<<"' for another "<<sttl<<" seconds"<<endl);
         res = RCode::NoError;
       } else {
-        LOG(prefix<<qname<<": Entire name '"<<qname<<" is negatively cached via '"<<ne->d_auth<<"' for another "<<sttl<<" seconds"<<endl);
+        LOG(prefix<<qname<<": Entire name '"<<qname<<"' is negatively cached via '"<<ne.d_auth<<"' for another "<<sttl<<" seconds"<<endl);
       }
     }
   } else if (s_hardenNXD != HardenNXD::No && !qname.isRoot() && !wasForwardedOrAuthZone) {
@@ -1489,19 +1574,19 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const DNSName& authname, bool w
     negCacheName.prependRawLabel(labels.back());
     labels.pop_back();
     while(!labels.empty()) {
-      if (t_sstorage.negcache.get(negCacheName, QType(0), d_now, &ne, true)) {
-        if (ne->d_validationState == Indeterminate && validationEnabled()) {
-          // LOG(prefix << negCacheName <<  " negatively cached and Indeterminate, trying to validate NXDOMAIN" << endl);
+      if (t_sstorage.negcache.get(negCacheName, QType(0), d_now, ne, true)) {
+        if (ne.d_validationState == vState::Indeterminate && validationEnabled()) {
+          // LOG(prefix << negCacheName <<  " negatively cached and vState::Indeterminate, trying to validate NXDOMAIN" << endl);
           // ...
           // And get the updated ne struct
-          //t_sstorage.negcache.get(negCacheName, QType(0), d_now, &ne, true);
+          //t_sstorage.negcache.get(negCacheName, QType(0), d_now, ne, true);
         }
-        if ((s_hardenNXD == HardenNXD::Yes && ne->d_validationState != Bogus) || ne->d_validationState == Secure) {
+        if ((s_hardenNXD == HardenNXD::Yes && ne.d_validationState != vState::Bogus) || ne.d_validationState == vState::Secure) {
           res = RCode::NXDomain;
-          sttl = ne->d_ttd - d_now.tv_sec;
+          sttl = ne.d_ttd - d_now.tv_sec;
           giveNegative = true;
-          cachedState = ne->d_validationState;
-          LOG(prefix<<qname<<": Name '"<<negCacheName<<"' and below, is negatively cached via '"<<ne->d_auth<<"' for another "<<sttl<<" seconds"<<endl);
+          cachedState = ne.d_validationState;
+          LOG(prefix<<qname<<": Name '"<<negCacheName<<"' and below, is negatively cached via '"<<ne.d_auth<<"' for another "<<sttl<<" seconds"<<endl);
           break;
         }
       }
@@ -1514,24 +1599,24 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const DNSName& authname, bool w
 
     state = cachedState;
 
-    if (!wasAuthZone && shouldValidate() && state == Indeterminate) {
-      LOG(prefix<<qname<<": got Indeterminate state for records retrieved from the negative cache, validating.."<<endl);
+    if (!wasAuthZone && shouldValidate() && state == vState::Indeterminate) {
+      LOG(prefix<<qname<<": got vState::Indeterminate state for records retrieved from the negative cache, validating.."<<endl);
       computeNegCacheValidationStatus(ne, qname, qtype, res, state, depth);
 
-      if (state != cachedState && state == Bogus) {
+      if (state != cachedState && state == vState::Bogus) {
         sttl = std::min(sttl, s_maxbogusttl);
       }
     }
 
     // Transplant SOA to the returned packet
-    addTTLModifiedRecords(ne->authoritySOA.records, sttl, ret);
+    addTTLModifiedRecords(ne.authoritySOA.records, sttl, ret);
     if(d_doDNSSEC) {
-      addTTLModifiedRecords(ne->authoritySOA.signatures, sttl, ret);
-      addTTLModifiedRecords(ne->DNSSECRecords.records, sttl, ret);
-      addTTLModifiedRecords(ne->DNSSECRecords.signatures, sttl, ret);
+      addTTLModifiedRecords(ne.authoritySOA.signatures, sttl, ret);
+      addTTLModifiedRecords(ne.DNSSECRecords.records, sttl, ret);
+      addTTLModifiedRecords(ne.DNSSECRecords.signatures, sttl, ret);
     }
 
-    LOG(prefix<<qname<<": updating validation state with negative cache content for "<<qname<<" to "<<vStates[state]<<endl);
+    LOG(prefix<<qname<<": updating validation state with negative cache content for "<<qname<<" to "<<state<<endl);
     return true;
   }
 
@@ -1542,11 +1627,12 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const DNSName& authname, bool w
   uint32_t ttl=0;
   uint32_t capTTL = std::numeric_limits<uint32_t>::max();
   bool wasCachedAuth;
+
   if(s_RC->get(d_now.tv_sec, sqname, sqt, !wasForwardRecurse && d_requireAuthData, &cset, d_cacheRemote, d_routingTag, d_doDNSSEC ? &signatures : nullptr, d_doDNSSEC ? &authorityRecs : nullptr, &d_wasVariable, &cachedState, &wasCachedAuth) > 0) {
 
     LOG(prefix<<sqname<<": Found cache hit for "<<sqt.getName()<<": ");
 
-    if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == Indeterminate && d_requireAuthData) {
+    if (!wasAuthZone && shouldValidate() && (wasCachedAuth || wasForwardRecurse) && cachedState == vState::Indeterminate && d_requireAuthData) {
 
       /* This means we couldn't figure out the state when this entry was cached,
          most likely because we hadn't computed the zone cuts yet. */
@@ -1559,17 +1645,22 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const DNSName& authname, bool w
       computeZoneCuts(subdomain, g_rootdnsname, depth);
 
       vState recordState = getValidationStatus(qname, false);
-      if (recordState == Secure) {
-        LOG(prefix<<sqname<<": got Indeterminate state from the cache, validating.."<<endl);
-        cachedState = SyncRes::validateRecordsWithSigs(depth, sqname, sqt, sqname, cset, signatures);
+      if (recordState == vState::Secure) {
+        LOG(prefix<<sqname<<": got vState::Indeterminate state from the cache, validating.."<<endl);
+        if (sqt == QType::DNSKEY) {
+          cachedState = validateDNSKeys(sqname, cset, signatures, depth);
+        }
+        else {
+          cachedState = SyncRes::validateRecordsWithSigs(depth, sqname, sqt, sqname, cset, signatures);
+        }
       }
       else {
         cachedState = recordState;
       }
 
-      if (cachedState != Indeterminate) {
-        LOG(prefix<<qname<<": got Indeterminate state from the cache, validation result is "<<vStates[cachedState]<<endl);
-        if (cachedState == Bogus) {
+      if (cachedState != vState::Indeterminate) {
+        LOG(prefix<<qname<<": got vState::Indeterminate state from the cache, validation result is "<<cachedState<<endl);
+        if (cachedState == vState::Bogus) {
           capTTL = s_maxbogusttl;
         }
         updateValidationStatusInCache(sqname, sqt, wasCachedAuth, cachedState);
@@ -1622,7 +1713,7 @@ bool SyncRes::doCacheCheck(const DNSName &qname, const DNSName& authname, bool w
     if(found && !expired) {
       if (!giveNegative)
         res=0;
-      LOG(prefix<<qname<<": updating validation state with cache content for "<<qname<<" to "<<vStates[cachedState]<<endl);
+      LOG(prefix<<qname<<": updating validation state with cache content for "<<qname<<" to "<<cachedState<<endl);
       state = cachedState;
       return true;
     }
@@ -1854,13 +1945,13 @@ bool SyncRes::nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAd
   return false;
 }
 
-vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly)
+vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly, unsigned int &retrieveAddressesForNS)
 {
   vector<ComboAddress> result;
 
   if(!tns->first.empty()) {
     LOG(prefix<<qname<<": Trying to resolve NS '"<<tns->first<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl);
-    result = getAddrs(tns->first, depth+2, beenthere, cacheOnly);
+    result = getAddrs(tns->first, depth, beenthere, cacheOnly, retrieveAddressesForNS);
     pierceDontQuery=false;
   }
   else {
@@ -1929,26 +2020,26 @@ uint32_t SyncRes::computeLowestTTD(const std::vector<DNSRecord>& records, const
 
 void SyncRes::updateValidationState(vState& state, const vState stateUpdate)
 {
-  LOG(d_prefix<<"validation state was "<<std::string(vStates[state])<<", state update is "<<std::string(vStates[stateUpdate]));
+  LOG(d_prefix<<"validation state was "<<state<<", state update is "<<stateUpdate);
 
-  if (stateUpdate == TA) {
-    state = Secure;
+  if (stateUpdate == vState::TA) {
+    state = vState::Secure;
   }
-  else if (stateUpdate == NTA) {
-    state = Insecure;
+  else if (stateUpdate == vState::NTA) {
+    state = vState::Insecure;
   }
-  else if (stateUpdate == Bogus) {
-    state = Bogus;
+  else if (stateUpdate == vState::Bogus) {
+    state = vState::Bogus;
   }
-  else if (state == Indeterminate) {
+  else if (state == vState::Indeterminate) {
     state = stateUpdate;
   }
-  else if (stateUpdate == Insecure) {
-    if (state != Bogus) {
-      state = Insecure;
+  else if (stateUpdate == vState::Insecure) {
+    if (state != vState::Bogus) {
+      state = vState::Insecure;
     }
   }
-  LOG(", validation state is now "<<std::string(vStates[state])<<endl);
+  LOG(", validation state is now "<<state<<endl);
 }
 
 vState SyncRes::getTA(const DNSName& zone, dsmap_t& ds)
@@ -1958,18 +2049,18 @@ vState SyncRes::getTA(const DNSName& zone, dsmap_t& ds)
   if (luaLocal->dsAnchors.empty()) {
     LOG(d_prefix<<": No trust anchors configured, everything is Insecure"<<endl);
     /* We have no TA, everything is insecure */
-    return Insecure;
+    return vState::Insecure;
   }
 
   std::string reason;
   if (haveNegativeTrustAnchor(luaLocal->negAnchors, zone, reason)) {
     LOG(d_prefix<<": got NTA for '"<<zone<<"'"<<endl);
-    return NTA;
+    return vState::NTA;
   }
 
   if (getTrustAnchor(luaLocal->dsAnchors, zone, ds)) {
     LOG(d_prefix<<": got TA for '"<<zone<<"'"<<endl);
-    return TA;
+    return vState::TA;
   }
   else {
     LOG(d_prefix<<": no TA found for '"<<zone<<"' among "<< luaLocal->dsAnchors.size()<<endl);
@@ -1977,10 +2068,10 @@ vState SyncRes::getTA(const DNSName& zone, dsmap_t& ds)
 
   if (zone.isRoot()) {
     /* No TA for the root */
-    return Insecure;
+    return vState::Insecure;
   }
 
-  return Indeterminate;
+  return vState::Indeterminate;
 }
 
 static size_t countSupportedDS(const dsmap_t& dsmap)
@@ -2000,36 +2091,38 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi
 {
   vState result = getTA(zone, ds);
 
-  if (result != Indeterminate || taOnly) {
+  if (result != vState::Indeterminate || taOnly) {
     if (foundCut) {
-      *foundCut = (result != Indeterminate);
+      *foundCut = (result != vState::Indeterminate);
     }
 
-    if (result == TA) {
+    if (result == vState::TA) {
       if (countSupportedDS(ds) == 0) {
         ds.clear();
-        result = Insecure;
+        result = vState::Insecure;
       }
       else {
-        result = Secure;
+        result = vState::Secure;
       }
     }
-    else if (result == NTA) {
-      result = Insecure;
+    else if (result == vState::NTA) {
+      result = vState::Insecure;
     }
 
     return result;
   }
 
-  bool oldSkipCNAME = d_skipCNAMECheck;
-  d_skipCNAMECheck = true;
-
   std::set<GetBestNSAnswer> beenthere;
   std::vector<DNSRecord> dsrecords;
 
-  vState state = Indeterminate;
+  vState state = vState::Indeterminate;
+  const bool oldCacheOnly = setCacheOnly(false);
   int rcode = doResolve(zone, QType(QType::DS), dsrecords, depth + 1, beenthere, state);
-  d_skipCNAMECheck = oldSkipCNAME;
+  setCacheOnly(oldCacheOnly);
+
+  if (rcode == RCode::ServFail) {
+    throw ImmediateServFailException("Server Failure while retrieving DS records for " + zone.toLogString());
+  }
 
   if (rcode == RCode::NoError || (rcode == RCode::NXDomain && !bogusOnNXD)) {
     uint8_t bestDigestType = 0;
@@ -2091,7 +2184,7 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi
            - a signed zone (Secure) to an unsigned one (Insecure)
            - an unsigned zone to another unsigned one (Insecure stays Insecure, Bogus stays Bogus)
         */
-        return state == Secure ? Insecure : state;
+        return state == vState::Secure ? vState::Insecure : state;
       } else {
         /* we have a DS */
         if (foundCut) {
@@ -2104,7 +2197,7 @@ vState SyncRes::getDSRecords(const DNSName& zone, dsmap_t& ds, bool taOnly, unsi
   }
 
   LOG(d_prefix<<": returning Bogus state from "<<__func__<<"("<<zone<<")"<<endl);
-  return Bogus;
+  return vState::Bogus;
 }
 
 bool SyncRes::haveExactValidationStatus(const DNSName& domain)
@@ -2121,7 +2214,7 @@ bool SyncRes::haveExactValidationStatus(const DNSName& domain)
 
 vState SyncRes::getValidationStatus(const DNSName& subdomain, bool allowIndeterminate)
 {
-  vState result = Indeterminate;
+  vState result = vState::Indeterminate;
 
   if (!shouldValidate()) {
     return result;
@@ -2130,8 +2223,8 @@ vState SyncRes::getValidationStatus(const DNSName& subdomain, bool allowIndeterm
   do {
     const auto& it = d_cutStates.find(name);
     if (it != d_cutStates.cend()) {
-      if (allowIndeterminate || it->second != Indeterminate) {
-        LOG(d_prefix<<": got status "<<vStates[it->second]<<" for name "<<subdomain<<" (from "<<name<<")"<<endl);
+      if (allowIndeterminate || it->second != vState::Indeterminate) {
+        LOG(d_prefix<<": got status "<<it->second<<" for name "<<subdomain<<" (from "<<name<<")"<<endl);
         return it->second;
       }
     }
@@ -2145,9 +2238,9 @@ bool SyncRes::lookForCut(const DNSName& qname, unsigned int depth, const vState
 {
   bool foundCut = false;
   dsmap_t ds;
-  vState dsState = getDSRecords(qname, ds, newState == Bogus || existingState == Insecure || existingState == Bogus, depth, false, &foundCut);
+  vState dsState = getDSRecords(qname, ds, newState == vState::Bogus || existingState == vState::Insecure || existingState == vState::Bogus, depth, false, &foundCut);
 
-  if (dsState != Indeterminate) {
+  if (dsState != vState::Indeterminate) {
     newState = dsState;
   }
 
@@ -2169,7 +2262,7 @@ void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned
 
   dsmap_t ds;
   vState cutState = getDSRecords(end, ds, false, depth);
-  LOG(d_prefix<<": setting cut state for "<<end<<" to "<<vStates[cutState]<<endl);
+  LOG(d_prefix<<": setting cut state for "<<end<<" to "<<cutState<<endl);
   d_cutStates[end] = cutState;
 
   if (!shouldValidate()) {
@@ -2180,9 +2273,6 @@ void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned
   DNSName qname(end);
   std::vector<string> labelsToAdd = begin.makeRelative(end).getRawLabels();
 
-  bool oldSkipCNAME = d_skipCNAMECheck;
-  d_skipCNAMECheck = true;
-
   while(qname != begin) {
     if (labelsToAdd.empty())
       break;
@@ -2193,7 +2283,7 @@ void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned
 
     const auto cutIt = d_cutStates.find(qname);
     if (cutIt != d_cutStates.cend()) {
-      if (cutIt->second != Indeterminate) {
+      if (cutIt->second != vState::Indeterminate) {
         LOG(d_prefix<<": - Cut already known at "<<qname<<endl);
         cutState = cutIt->second;
         continue;
@@ -2203,14 +2293,14 @@ void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned
     /* no need to look for NS and DS if we are already insecure or bogus,
        just look for (N)TA
     */
-    if (cutState == Insecure || cutState == Bogus) {
+    if (cutState == vState::Insecure || cutState == vState::Bogus) {
       dsmap_t cutDS;
       vState newState = getDSRecords(qname, cutDS, true, depth);
-      if (newState == Indeterminate) {
+      if (newState == vState::Indeterminate) {
         continue;
       }
 
-      LOG(d_prefix<<": New state for "<<qname<<" is "<<vStates[newState]<<endl);
+      LOG(d_prefix<<": New state for "<<qname<<" is "<<newState<<endl);
       cutState = newState;
 
       d_cutStates[qname] = cutState;
@@ -2218,17 +2308,17 @@ void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned
       continue;
     }
 
-    vState newState = Indeterminate;
-    /* temporarily mark as Indeterminate, so that we won't enter an endless loop
+    vState newState = vState::Indeterminate;
+    /* temporarily mark as vState::Indeterminate, so that we won't enter an endless loop
        trying to determine that zone cut again. */
     d_cutStates[qname] = newState;
-    bool foundCut = lookForCut(qname, depth + 1, cutState, newState);
+    bool foundCut = lookForCut(qname, depth, cutState, newState);
     if (foundCut) {
       LOG(d_prefix<<": - Found cut at "<<qname<<endl);
-      if (newState != Indeterminate) {
+      if (newState != vState::Indeterminate) {
         cutState = newState;
       }
-      LOG(d_prefix<<": New state for "<<qname<<" is "<<vStates[cutState]<<endl);
+      LOG(d_prefix<<": New state for "<<qname<<" is "<<cutState<<endl);
       d_cutStates[qname] = cutState;
     }
     else {
@@ -2238,12 +2328,10 @@ void SyncRes::computeZoneCuts(const DNSName& begin, const DNSName& end, unsigned
     }
   }
 
-  d_skipCNAMECheck = oldSkipCNAME;
-
   LOG(d_prefix<<": list of cuts from "<<begin<<" to "<<end<<endl);
   for (const auto& cut : d_cutStates) {
     if (cut.first.isRoot() || (begin.isPartOf(cut.first) && cut.first.isPartOf(end))) {
-      LOG(" - "<<cut.first<<": "<<vStates[cut.second]<<endl);
+      LOG(" - "<<cut.first<<": "<<cut.second<<endl);
     }
   }
   setCacheOnly(oldCacheOnly);
@@ -2258,7 +2346,7 @@ vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord
     if (!signer.empty() && zone.isPartOf(signer)) {
       vState state = getDSRecords(signer, ds, false, depth);
 
-      if (state != Secure) {
+      if (state != vState::Secure) {
         return state;
       }
     }
@@ -2289,10 +2377,10 @@ vState SyncRes::validateDNSKeys(const DNSName& zone, const std::vector<DNSRecord
      covering this set, this looks Bogus. */
   if (validatedKeys.size() != tentativeKeys.size()) {
     LOG(d_prefix<<": returning Bogus state from "<<__func__<<"("<<zone<<")"<<endl);
-    return Bogus;
+    return vState::Bogus;
   }
 
-  return Secure;
+  return vState::Secure;
 }
 
 vState SyncRes::getDNSKeys(const DNSName& signer, skeyset_t& keys, unsigned int depth)
@@ -2301,15 +2389,17 @@ vState SyncRes::getDNSKeys(const DNSName& signer, skeyset_t& keys, unsigned int
   std::set<GetBestNSAnswer> beenthere;
   LOG(d_prefix<<"Retrieving DNSKeys for "<<signer<<endl);
 
-  vState state = Indeterminate;
-  /* following CNAME might lead to us to the wrong DNSKEY */
-  bool oldSkipCNAME = d_skipCNAMECheck;
-  d_skipCNAMECheck = true;
+  vState state = vState::Indeterminate;
+  const bool oldCacheOnly = setCacheOnly(false);
   int rcode = doResolve(signer, QType(QType::DNSKEY), records, depth + 1, beenthere, state);
-  d_skipCNAMECheck = oldSkipCNAME;
+  setCacheOnly(oldCacheOnly);
+
+  if (rcode == RCode::ServFail) {
+    throw ImmediateServFailException("Server Failure while retrieving DNSKEY records for " + signer.toLogString());
+  }
 
   if (rcode == RCode::NoError) {
-    if (state == Secure) {
+    if (state == vState::Secure) {
       for (const auto& key : records) {
         if (key.d_type == QType::DNSKEY) {
           auto content = getRR<DNSKEYRecordContent>(key);
@@ -2319,12 +2409,12 @@ vState SyncRes::getDNSKeys(const DNSName& signer, skeyset_t& keys, unsigned int
         }
       }
     }
-    LOG(d_prefix<<"Retrieved "<<keys.size()<<" DNSKeys for "<<signer<<", state is "<<vStates[state]<<endl);
+    LOG(d_prefix<<"Retrieved "<<keys.size()<<" DNSKeys for "<<signer<<", state is "<<state<<endl);
     return state;
   }
 
   LOG(d_prefix<<"Returning Bogus state from "<<__func__<<"("<<signer<<")"<<endl);
-  return Bogus;
+  return vState::Bogus;
 }
 
 vState SyncRes::validateRecordsWithSigs(unsigned int depth, const DNSName& qname, const QType& qtype, const DNSName& name, const std::vector<DNSRecord>& records, const std::vector<std::shared_ptr<RRSIGRecordContent> >& signatures)
@@ -2335,21 +2425,23 @@ vState SyncRes::validateRecordsWithSigs(unsigned int depth, const DNSName& qname
     if (!signer.empty() && name.isPartOf(signer)) {
       if ((qtype == QType::DNSKEY || qtype == QType::DS) && signer == qname) {
         /* we are already retrieving those keys, sorry */
-        if (qtype == QType::DS) {
-          /* something is very wrong */
+        if (qtype == QType::DS && !signer.isRoot()) {
+          /* Unless we are getting the DS of the root zone, we should never see a
+             DS (or a denial of a DS) signed by the DS itself, since we should be
+             requesting it from the parent zone. Something is very wrong */
           LOG(d_prefix<<"The DS for "<<qname<<" is signed by itself, going Bogus"<<endl);
-          return Bogus;
+          return vState::Bogus;
         }
-        return Indeterminate;
+        return vState::Indeterminate;
       }
       vState state = getDNSKeys(signer, keys, depth);
-      if (state != Secure) {
+      if (state != vState::Secure) {
         return state;
       }
     }
   } else {
     LOG(d_prefix<<"Bogus!"<<endl);
-    return Bogus;
+    return vState::Bogus;
   }
 
   sortedRecords_t recordcontents;
@@ -2360,11 +2452,11 @@ vState SyncRes::validateRecordsWithSigs(unsigned int depth, const DNSName& qname
   LOG(d_prefix<<"Going to validate "<<recordcontents.size()<< " record contents with "<<signatures.size()<<" sigs and "<<keys.size()<<" keys for "<<name<<endl);
   if (validateWithKeySet(d_now.tv_sec, name, recordcontents, signatures, keys, false)) {
     LOG(d_prefix<<"Secure!"<<endl);
-    return Secure;
+    return vState::Secure;
   }
 
   LOG(d_prefix<<"Bogus!"<<endl);
-  return Bogus;
+  return vState::Bogus;
 }
 
 static bool allowAdditionalEntry(std::unordered_set<DNSName>& allowedAdditionals, const DNSRecord& rec)
@@ -2540,11 +2632,13 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
   const unsigned int labelCount = qname.countLabels();
   bool isCNAMEAnswer = false;
   bool isDNAMEAnswer = false;
-  for(const auto& rec : lwr.d_records) {
-    if (rec.d_class != QClass::IN) {
+  for (auto& rec : lwr.d_records) {
+    if (rec.d_type == QType::OPT || rec.d_class != QClass::IN) {
       continue;
     }
 
+    rec.d_ttl = min(s_maxcachettl, rec.d_ttl);
+
     if(!isCNAMEAnswer && rec.d_place == DNSResourceRecord::ANSWER && rec.d_type == QType::CNAME && (!(qtype==QType(QType::CNAME))) && rec.d_name == qname && !isDNAMEAnswer) {
       isCNAMEAnswer = true;
     }
@@ -2567,7 +2661,7 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
         }
       }
     }
-    if(rec.d_type == QType::RRSIG) {
+    if (rec.d_type == QType::RRSIG) {
       auto rrsig = getRR<RRSIGRecordContent>(rec);
       if (rrsig) {
         /* As illustrated in rfc4035's Appendix B.6, the RRSIG label
@@ -2625,10 +2719,13 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
     }
 
     if(rec.d_name.isPartOf(auth)) {
-      if(rec.d_type == QType::RRSIG) {
+      if (rec.d_type == QType::RRSIG) {
         LOG("RRSIG - separate"<<endl);
       }
-      else if(lwr.d_aabit && lwr.d_rcode==RCode::NoError && rec.d_place==DNSResourceRecord::ANSWER && ((rec.d_type != QType::DNSKEY && rec.d_type != QType::DS) || rec.d_name != auth) && s_delegationOnly.count(auth)) {
+      else if (rec.d_type == QType::DS && rec.d_name == auth) {
+        LOG("NO - DS provided by child zone"<<endl);
+      }
+      else if (lwr.d_aabit && lwr.d_rcode==RCode::NoError && rec.d_place==DNSResourceRecord::ANSWER && ((rec.d_type != QType::DNSKEY && rec.d_type != QType::DS) || rec.d_name != auth) && s_delegationOnly.count(auth)) {
         LOG("NO! Is from delegation-only zone"<<endl);
         s_nodelegated++;
         return RCode::NXDomain;
@@ -2739,9 +2836,9 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
     }
 
     vState recordState = getValidationStatus(i->first.name, false);
-    LOG(d_prefix<<": got initial zone status "<<vStates[recordState]<<" for record "<<i->first.name<<"|"<<DNSRecordContent::NumberToType(i->first.type)<<endl);
+    LOG(d_prefix<<": got initial zone status "<<recordState<<" for record "<<i->first.name<<"|"<<DNSRecordContent::NumberToType(i->first.type)<<endl);
 
-    if (shouldValidate() && recordState == Secure) {
+    if (shouldValidate() && recordState == vState::Secure) {
       vState initialState = recordState;
 
       if (expectSignature) {
@@ -2771,15 +2868,15 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
               LOG(d_prefix<<"Validating non-additional record for "<<i->first.name<<endl);
               recordState = validateRecordsWithSigs(depth, qname, qtype, i->first.name, i->second.records, i->second.signatures);
               /* we might have missed a cut (zone cut within the same auth servers), causing the NS query for an Insecure zone to seem Bogus during zone cut determination */
-              if (qtype == QType::NS && i->second.signatures.empty() && recordState == Bogus && haveExactValidationStatus(i->first.name) && getValidationStatus(i->first.name) == Indeterminate) {
-                recordState = Indeterminate;
+              if (qtype == QType::NS && i->second.signatures.empty() && recordState == vState::Bogus && haveExactValidationStatus(i->first.name) && getValidationStatus(i->first.name) == vState::Indeterminate) {
+                recordState = vState::Indeterminate;
               }
             }
           }
         }
       }
       else {
-        recordState = Indeterminate;
+        recordState = vState::Indeterminate;
 
         /* in a non authoritative answer, we only care about the DS record (or lack of)  */
         if ((i->first.type == QType::DS || i->first.type == QType::NSEC || i->first.type == QType::NSEC3) && i->first.place == DNSResourceRecord::AUTHORITY) {
@@ -2788,17 +2885,17 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
         }
       }
 
-      if (initialState == Secure && state != recordState && expectSignature) {
+      if (initialState == vState::Secure && state != recordState && expectSignature) {
         updateValidationState(state, recordState);
       }
     }
     else {
       if (shouldValidate()) {
-        LOG(d_prefix<<"Skipping validation because the current state is "<<vStates[recordState]<<endl);
+        LOG(d_prefix<<"Skipping validation because the current state is "<<recordState<<endl);
       }
     }
 
-    if (recordState == Bogus) {
+    if (recordState == vState::Bogus) {
       /* this is a TTD by now, be careful */
       for(auto& record : i->second.records) {
         record.d_ttl = std::min(record.d_ttl, static_cast<uint32_t>(s_maxbogusttl + d_now.tv_sec));
@@ -2852,10 +2949,10 @@ RCode::rcodes_ SyncRes::updateCacheFromRecords(unsigned int depth, LWResult& lwr
 void SyncRes::updateDenialValidationState(vState& neValidationState, const DNSName& neName, vState& state, const dState denialState, const dState expectedState, bool allowOptOut)
 {
   if (denialState == expectedState) {
-    neValidationState = Secure;
+    neValidationState = vState::Secure;
   }
   else {
-    if (denialState == OPTOUT && allowOptOut) {
+    if (denialState == dState::OPTOUT && allowOptOut) {
       LOG(d_prefix<<"OPT-out denial found for "<<neName<<endl);
       /* rfc5155 states:
          "The AD bit, as defined by [RFC4035], MUST NOT be set when returning a
@@ -2871,15 +2968,15 @@ void SyncRes::updateDenialValidationState(vState& neValidationState, const DNSNa
          At best the Opt-Out NSEC3 RR proves that there is no signed DS (so no
          secure delegation).
       */
-      neValidationState = Insecure;
+      neValidationState = vState::Insecure;
     }
-    else if (denialState == INSECURE) {
+    else if (denialState == dState::INSECURE) {
       LOG(d_prefix<<"Insecure denial found for "<<neName<<", returning Insecure"<<endl);
-      neValidationState = Insecure;
+      neValidationState = vState::Insecure;
     }
     else {
       LOG(d_prefix<<"Invalid denial found for "<<neName<<", returning Bogus, res="<<denialState<<", expectedState="<<expectedState<<endl);
-      neValidationState = Bogus;
+      neValidationState = vState::Bogus;
     }
     updateValidationState(state, neValidationState);
   }
@@ -2888,7 +2985,7 @@ void SyncRes::updateDenialValidationState(vState& neValidationState, const DNSNa
 dState SyncRes::getDenialValidationState(const NegCache::NegCacheEntry& ne, const vState state, const dState expectedState, bool referralToUnsigned)
 {
   cspmap_t csp = harvestCSPFromNE(ne);
-  return getDenial(csp, ne.d_name, ne.d_qtype.getCode(), referralToUnsigned, expectedState == NXQTYPE);
+  return getDenial(csp, ne.d_name, ne.d_qtype.getCode(), referralToUnsigned, expectedState == dState::NXQTYPE);
 }
 
 bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, const QType& qtype, const DNSName& auth, LWResult& lwr, const bool sendRDQuery, vector<DNSRecord>& ret, set<DNSName>& nsset, DNSName& newtarget, DNSName& newauth, bool& realreferral, bool& negindic, vState& state, const bool needWildcardProof, const bool gatherWildcardProof, const unsigned int wildcardLabelsCount)
@@ -2927,15 +3024,15 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
       ne.d_auth = rec.d_name;
       harvestNXRecords(lwr.d_records, ne, d_now.tv_sec, &lowestTTL);
 
-      if (state == Secure) {
-        dState denialState = getDenialValidationState(ne, state, NXDOMAIN, false);
-        updateDenialValidationState(ne.d_validationState, ne.d_name, state, denialState, NXDOMAIN, true);
+      if (state == vState::Secure) {
+        dState denialState = getDenialValidationState(ne, state, dState::NXDOMAIN, false);
+        updateDenialValidationState(ne.d_validationState, ne.d_name, state, denialState, dState::NXDOMAIN, true);
       }
       else {
         ne.d_validationState = state;
       }
 
-      if (ne.d_validationState == Bogus) {
+      if (ne.d_validationState == vState::Bogus) {
         lowestTTL = min(lowestTTL, s_maxbogusttl);
       }
 
@@ -3010,7 +3107,7 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
 
       done=true;
 
-      if (state == Secure && needWildcardProof) {
+      if (state == vState::Secure && needWildcardProof) {
         /* We have a positive answer synthesized from a wildcard, we need to check that we have
            proof that the exact name doesn't exist so the wildcard can be used,
            as described in section 5.3.4 of RFC 4035 and 5.3 of RFC 7129.
@@ -3024,12 +3121,12 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
 
         cspmap_t csp = harvestCSPFromNE(ne);
         dState res = getDenial(csp, qname, ne.d_qtype.getCode(), false, false, false, wildcardLabelsCount);
-        if (res != NXDOMAIN) {
-          vState st = Bogus;
-          if (res == INSECURE) {
+        if (res != dState::NXDOMAIN) {
+          vState st = vState::Bogus;
+          if (res == dState::INSECURE) {
             /* Some part could not be validated, for example a NSEC3 record with a too large number of iterations,
                this is not enough to warrant a Bogus, but go Insecure. */
-            st = Insecure;
+            st = vState::Insecure;
             LOG(d_prefix<<"Unable to validate denial in wildcard expanded positive response found for "<<qname<<", returning Insecure, res="<<res<<endl);
           }
           else {
@@ -3072,7 +3169,7 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
     }
     else if(realreferral && rec.d_place==DNSResourceRecord::AUTHORITY && (rec.d_type==QType::NSEC || rec.d_type==QType::NSEC3) && newauth.isPartOf(auth)) {
       /* we might have received a denial of the DS, let's check */
-      if (state == Secure) {
+      if (state == vState::Secure) {
         NegCache::NegCacheEntry ne;
         ne.d_auth = auth;
         ne.d_name = newauth;
@@ -3081,13 +3178,13 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
         uint32_t lowestTTL = rec.d_ttl;
         harvestNXRecords(lwr.d_records, ne, d_now.tv_sec, &lowestTTL);
 
-        dState denialState = getDenialValidationState(ne, state, NXQTYPE, true);
+        dState denialState = getDenialValidationState(ne, state, dState::NXQTYPE, true);
 
-        if (denialState == NXQTYPE || denialState == OPTOUT || denialState == INSECURE) {
+        if (denialState == dState::NXQTYPE || denialState == dState::OPTOUT || denialState == dState::INSECURE) {
           ne.d_ttd = lowestTTL + d_now.tv_sec;
-          ne.d_validationState = Secure;
-          if (denialState == OPTOUT) {
-            ne.d_validationState = Insecure;
+          ne.d_validationState = vState::Secure;
+          if (denialState == dState::OPTOUT) {
+            ne.d_validationState = vState::Insecure;
           }
           LOG(prefix<<qname<<": got negative indication of DS record for '"<<newauth<<"'"<<endl);
 
@@ -3120,14 +3217,14 @@ bool SyncRes::processRecords(const std::string& prefix, const DNSName& qname, co
         ne.d_qtype = qtype;
         harvestNXRecords(lwr.d_records, ne, d_now.tv_sec, &lowestTTL);
 
-        if (state == Secure) {
-          dState denialState = getDenialValidationState(ne, state, NXQTYPE, false);
-          updateDenialValidationState(ne.d_validationState, ne.d_name, state, denialState, NXQTYPE, qtype == QType::DS);
+        if (state == vState::Secure) {
+          dState denialState = getDenialValidationState(ne, state, dState::NXQTYPE, false);
+          updateDenialValidationState(ne.d_validationState, ne.d_name, state, denialState, dState::NXQTYPE, qtype == QType::DS);
         } else {
           ne.d_validationState = state;
         }
 
-        if (ne.d_validationState == Bogus) {
+        if (ne.d_validationState == vState::Bogus) {
           lowestTTL = min(lowestTTL, s_maxbogusttl);
           rec.d_ttl = min(rec.d_ttl, s_maxbogusttl);
         }
@@ -3348,7 +3445,7 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
 
   if(done){
     LOG(prefix<<qname<<": status=got results, this level of recursion done"<<endl);
-    LOG(prefix<<qname<<": validation status is "<<vStates[state]<<endl);
+    LOG(prefix<<qname<<": validation status is "<<state<<endl);
     *rcode = RCode::NoError;
     return true;
   }
@@ -3356,6 +3453,7 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
   if(!newtarget.empty()) {
     if(newtarget == qname) {
       LOG(prefix<<qname<<": status=got a CNAME referral to self, returning SERVFAIL"<<endl);
+      ret.clear();
       *rcode = RCode::ServFail;
       return true;
     }
@@ -3366,8 +3464,16 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
       return true;
     }
 
-    if (qtype == QType::DS) {
-      LOG(prefix<<qname<<": status=got a CNAME referral, but we are looking for a DS"<<endl);
+    // Check to see if we already have seen the new target as a previous target
+    if (scanForCNAMELoop(newtarget, ret)) {
+      LOG(prefix<<qname<<": status=got a CNAME referral that causes a loop, returning SERVFAIL"<<endl);
+      ret.clear();
+      *rcode = RCode::ServFail;
+      return true;
+    }
+
+    if (qtype == QType::DS || qtype == QType::DNSKEY) {
+      LOG(prefix<<qname<<": status=got a CNAME referral, but we are looking for a DS or DNSKEY"<<endl);
 
       if(d_doDNSSEC)
         addNXNSECS(ret, lwr.d_records);
@@ -3379,9 +3485,9 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
       LOG(prefix<<qname<<": status=got a CNAME referral, starting over with "<<newtarget<<endl);
 
       set<GetBestNSAnswer> beenthere2;
-      vState cnameState = Indeterminate;
+      vState cnameState = vState::Indeterminate;
       *rcode = doResolve(newtarget, qtype, ret, depth + 1, beenthere2, cnameState);
-      LOG(prefix<<qname<<": updating validation state for response to "<<qname<<" from "<<vStates[state]<<" with the state from the CNAME quest: "<<vStates[cnameState]<<endl);
+      LOG(prefix<<qname<<": updating validation state for response to "<<qname<<" from "<<state<<" with the state from the CNAME quest: "<<cnameState<<endl);
       updateValidationState(state, cnameState);
       return true;
     }
@@ -3390,6 +3496,10 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
   if(lwr.d_rcode == RCode::NXDomain) {
     LOG(prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl);
 
+    if (state == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
+      updateValidationState(state, vState::Bogus);
+    }
+
     if(d_doDNSSEC)
       addNXNSECS(ret, lwr.d_records);
 
@@ -3400,8 +3510,8 @@ bool SyncRes::processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qn
   if(nsset.empty() && !lwr.d_rcode && (negindic || lwr.d_aabit || sendRDQuery)) {
     LOG(prefix<<qname<<": status=noerror, other types may exist, but we are done "<<(negindic ? "(have negative SOA) " : "")<<(lwr.d_aabit ? "(have aa bit) " : "")<<endl);
 
-    if(state == Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
-      updateValidationState(state, Bogus);
+    if(state == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
+      updateValidationState(state, vState::Bogus);
     }
 
     if(d_doDNSSEC)
@@ -3462,10 +3572,24 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
 
   LOG(endl);
 
+  unsigned int addressQueriesForNS = 0;
   for(;;) { // we may get more specific nameservers
     auto rnameservers = shuffleInSpeedOrder(nameservers, doLog() ? (prefix+qname.toString()+": ") : string() );
 
+    // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
+    // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
+    // This is to "punish" zones that publish many non-resolving NS names.
+    // We always allow 5 NS name resolving attempts with empty results.
+    unsigned int nsLimit = s_maxnsaddressqperq;
+    if (rnameservers.size() > nsLimit) {
+      int newLimit = static_cast<int>(nsLimit) - (rnameservers.size() - nsLimit);
+      nsLimit = std::max(5, newLimit);
+    }
+
     for(auto tns=rnameservers.cbegin();;++tns) {
+      if (addressQueriesForNS >= nsLimit) {
+        throw ImmediateServFailException(std::to_string(nsLimit)+" (adjusted max-ns-address-qperq) or more queries with empty results for NS addresses sent resolving "+qname.toLogString());
+      }
       if(tns==rnameservers.cend()) {
         LOG(prefix<<qname<<": Failed to resolve via any of the "<<(unsigned int)rnameservers.size()<<" offered NS at level '"<<auth<<"'"<<endl);
         if(!auth.isRoot() && flawedNSSet) {
@@ -3501,7 +3625,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
         LOG(prefix<<qname<<": Domain is out-of-band"<<endl);
         /* setting state to indeterminate since validation is disabled for local auth zone,
            and Insecure would be misleading. */
-        state = Indeterminate;
+        state = vState::Indeterminate;
         d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, lwr.d_rcode);
         lwr.d_tcbit=false;
         lwr.d_aabit=true;
@@ -3521,7 +3645,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
       }
       else {
         /* if tns is empty, retrieveAddressesForNS() knows we have hardcoded servers (i.e. "forwards") */
-        remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly);
+        remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
 
         if(remoteIPs.empty()) {
           LOG(prefix<<qname<<": Failed to get IP for NS "<<tns->first<<", trying next if available"<<endl);
@@ -3688,33 +3812,33 @@ int directResolve(const DNSName& qname, const QType& qtype, int qclass, vector<D
   SyncRes sr(now);
   int res = -1;
   try {
-    res = sr.beginResolve(qname, QType(qtype), qclass, ret);
+    res = sr.beginResolve(qname, QType(qtype), qclass, ret, 0);
   }
   catch(const PDNSException& e) {
-    g_log<<Logger::Error<<"Failed to resolve "<<qname.toLogString()<<", got pdns exception: "<<e.reason<<endl;
+    g_log<<Logger::Error<<"Failed to resolve "<<qname<<", got pdns exception: "<<e.reason<<endl;
     ret.clear();
   }
   catch(const ImmediateServFailException& e) {
-    g_log<<Logger::Error<<"Failed to resolve "<<qname.toLogString()<<", got ImmediateServFailException: "<<e.reason<<endl;
+    g_log<<Logger::Error<<"Failed to resolve "<<qname<<", got ImmediateServFailException: "<<e.reason<<endl;
     ret.clear();
   }
   catch(const PolicyHitException& e) {
-    g_log<<Logger::Error<<"Failed to resolve "<<qname.toLogString()<<", got a policy hit"<<endl;
+    g_log<<Logger::Error<<"Failed to resolve "<<qname<<", got a policy hit"<<endl;
     ret.clear();
   }
   catch(const std::exception& e) {
-    g_log<<Logger::Error<<"Failed to resolve "<<qname.toLogString()<<", got STL error: "<<e.what()<<endl;
+    g_log<<Logger::Error<<"Failed to resolve "<<qname<<", got STL error: "<<e.what()<<endl;
     ret.clear();
   }
   catch(...) {
-    g_log<<Logger::Error<<"Failed to resolve "<<qname.toLogString()<<", got an exception"<<endl;
+    g_log<<Logger::Error<<"Failed to resolve "<<qname<<", got an exception"<<endl;
     ret.clear();
   }
   
   return res;
 }
 
-int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback) {
+int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth) {
   SyncRes sr(now);
   sr.setDoEDNS0(true);
   sr.setUpdatingRootNS();
@@ -3725,10 +3849,10 @@ int SyncRes::getRootNS(struct timeval now, asyncresolve_t asyncCallback) {
   vector<DNSRecord> ret;
   int res=-1;
   try {
-    res=sr.beginResolve(g_rootdnsname, QType(QType::NS), 1, ret);
+    res=sr.beginResolve(g_rootdnsname, QType(QType::NS), 1, ret, depth + 1);
     if (g_dnssecmode != DNSSECMode::Off && g_dnssecmode != DNSSECMode::ProcessNoValidate) {
       auto state = sr.getValidationState();
-      if (state == Bogus)
+      if (state == vState::Bogus)
         throw PDNSException("Got Bogus validation result for .|NS");
     }
     return res;
index 9bbadcdb426868112256394c6ca6cefaada403dd..c327df0150e7fdc9b71bc914a6a36542ab16e5e1 100644 (file)
@@ -416,7 +416,7 @@ public:
   static uint64_t doDumpNSSpeeds(int fd);
   static uint64_t doDumpThrottleMap(int fd);
   static uint64_t doDumpFailedServers(int fd);
-  static int getRootNS(struct timeval now, asyncresolve_t asyncCallback);
+  static int getRootNS(struct timeval now, asyncresolve_t asyncCallback, unsigned int depth);
   static void clearDelegationOnly()
   {
     s_delegationOnly.clear();
@@ -604,7 +604,8 @@ public:
 
   explicit SyncRes(const struct timeval& now);
 
-  int beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret);
+  int beginResolve(const DNSName &qname, const QType &qtype, uint16_t qclass, vector<DNSRecord>&ret, unsigned int depth = 0);
+
   void setId(int id)
   {
     if(doLog())
@@ -698,11 +699,6 @@ public:
     return d_now;
   }
 
-  void setSkipCNAMECheck(bool skip = false)
-  {
-    d_skipCNAMECheck = skip;
-  }
-
   void setQuerySource(const ComboAddress& requestor, boost::optional<const EDNSSubnetOpts&> incomingECS);
 
 #ifdef HAVE_PROTOBUF
@@ -757,6 +753,7 @@ public:
   static unsigned int s_minimumTTL;
   static unsigned int s_minimumECSTTL;
   static unsigned int s_maxqperq;
+  static unsigned int s_maxnsaddressqperq;
   static unsigned int s_maxtotusec;
   static unsigned int s_maxdepth;
   static unsigned int s_maxnegttl;
@@ -771,6 +768,7 @@ public:
   static uint8_t s_ecsipv6limit;
   static uint8_t s_ecsipv4cachelimit;
   static uint8_t s_ecsipv6cachelimit;
+  static bool s_doIPv4;
   static bool s_doIPv6;
   static bool s_noEDNSPing;
   static bool s_noEDNS;
@@ -826,10 +824,10 @@ private:
   bool processAnswer(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, DNSName& auth, bool wasForwarded, const boost::optional<Netmask> ednsmask, bool sendRDQuery, NsSet &nameservers, std::vector<DNSRecord>& ret, const DNSFilterEngine& dfe, bool* gotNewServers, int* rcode, vState& state);
 
   int doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state);
-  int doResolveNoQNameMinimization(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state, bool* fromCache = NULL, StopAtDelegation* stopAtDelegation = NULL);
+  int doResolveNoQNameMinimization(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, set<GetBestNSAnswer>& beenthere, vState& state, bool* fromCache = NULL, StopAtDelegation* stopAtDelegation = NULL, bool considerforwards = true);
   bool doOOBResolve(const AuthDomain& domain, const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, int& res);
   bool doOOBResolve(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, int &res);
-  bool isForwardOrAuth(const DNSName &qname) const;
+  bool isRecursiveForwardOrAuth(const DNSName &qname) const;
   domainmap_t::const_iterator getBestAuthZone(DNSName* qname) const;
   bool doCNAMECacheCheck(const DNSName &qname, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, int &res, vState& state, bool wasAuthZone, bool wasForwardRecurse);
   bool doCacheCheck(const DNSName &qname, const DNSName& authname, bool wasForwardedOrAuthZone, bool wasAuthZone, bool wasForwardRecurse, const QType &qtype, vector<DNSRecord>&ret, unsigned int depth, int &res, vState& state);
@@ -839,13 +837,13 @@ private:
   inline vector<std::pair<DNSName, float>> shuffleInSpeedOrder(NsSet &nameservers, const string &prefix);
   inline vector<ComboAddress> shuffleForwardSpeed(const vector<ComboAddress> &rnameservers, const string &prefix, const bool wasRd);
   bool moreSpecificThan(const DNSName& a, const DNSName &b) const;
-  vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly);
+  vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS);
 
   bool nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers);
   bool nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress&);
   bool throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType& qtype, bool pierceDontQuery);
 
-  vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly);
+  vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly, unsigned int& addressQueriesForNS);
 
   void sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, bool rdQuery);
   RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool sendRDQuery);
@@ -865,7 +863,7 @@ private:
   vState getDNSKeys(const DNSName& signer, skeyset_t& keys, unsigned int depth);
   dState getDenialValidationState(const NegCache::NegCacheEntry& ne, const vState state, const dState expectedState, bool referralToUnsigned);
   void updateDenialValidationState(vState& neValidationState, const DNSName& neName, vState& state, const dState denialState, const dState expectedState, bool allowOptOut);
-  void computeNegCacheValidationStatus(const NegCache::NegCacheEntry* ne, const DNSName& qname, const QType& qtype, const int res, vState& state, unsigned int depth);
+  void computeNegCacheValidationStatus(const NegCache::NegCacheEntry& ne, const DNSName& qname, const QType& qtype, const int res, vState& state, unsigned int depth);
   vState getTA(const DNSName& zone, dsmap_t& ds);
   bool haveExactValidationStatus(const DNSName& domain);
   vState getValidationStatus(const DNSName& subdomain, bool allowIndeterminate=true);
@@ -891,7 +889,7 @@ private:
   asyncresolve_t d_asyncResolve{nullptr};
   struct timeval d_now;
   string d_prefix;
-  vState d_queryValidationState{Indeterminate};
+  vState d_queryValidationState{vState::Indeterminate};
 
   /* When d_cacheonly is set to true, we will only check the cache.
    * This is set when the RD bit is unset in the incoming query
@@ -901,7 +899,6 @@ private:
   bool d_DNSSECValidationRequested{false};
   bool d_doEDNS0{true};
   bool d_requireAuthData{true};
-  bool d_skipCNAMECheck{false};
   bool d_updatingRootNS{false};
   bool d_wantsRPZ{true};
   bool d_wasOutOfBand{false};
@@ -1087,7 +1084,6 @@ extern bool g_lowercaseOutgoing;
 
 std::string reloadAuthAndForwards();
 ComboAddress parseIPAndPort(const std::string& input, uint16_t port);
-ComboAddress getQueryLocalAddress(int family, uint16_t port);
 typedef boost::function<void*(void)> pipefunc_t;
 void broadcastFunction(const pipefunc_t& func);
 void distributeAsyncFunction(const std::string& question, const pipefunc_t& func);
@@ -1112,8 +1108,8 @@ uint64_t* pleaseWipeCache(const DNSName& canon, bool subtree=false, uint16_t qty
 uint64_t* pleaseWipePacketCache(const DNSName& canon, bool subtree, uint16_t qtype=0xffff);
 uint64_t* pleaseWipeAndCountNegCache(const DNSName& canon, bool subtree=false);
 void doCarbonDump(void*);
-void primeHints(void);
-void primeRootNSZones(bool);
+bool primeHints(void);
+void primeRootNSZones(bool, unsigned int depth);
 
 extern __thread struct timeval g_now;
 
index 30314221331e077a11e79cacb6df1b62e81c4da2..6dea329261a4db8624204365e74ecf0d9754760d 100644 (file)
@@ -53,7 +53,6 @@
 #include "common_startup.hh"
 #include "packethandler.hh"
 #include "statbag.hh"
-#include "resolver.hh"
 #include "communicator.hh"
 #include "namespaces.hh"
 #include "signingpipe.hh"
@@ -1058,8 +1057,10 @@ int TCPNameserver::doIXFR(std::unique_ptr<DNSPacket>& q, int outsock)
 
   g_log<<Logger::Error<<"IXFR of domain '"<<q->qdomain<<"' initiated by "<<q->getRemote()<<" with serial "<<serial<<endl;
 
-  // determine if zone exists and AXFR is allowed using existing backend before spawning a new backend.
+  // determine if zone exists, XFR is allowed, and if IXFR can proceed using existing backend before spawning a new backend.
   SOAData sd;
+  bool securedZone;
+  bool serialPermitsIXFR;
   {
     std::lock_guard<std::mutex> l(s_plock);
     DLOG(g_log<<"Looking for SOA"<<endl); // find domain_id via SOA and list complete domain. No SOA, no IXFR
@@ -1075,39 +1076,33 @@ int TCPNameserver::doIXFR(std::unique_ptr<DNSPacket>& q, int outsock)
       sendPacket(outpacket,outsock);
       return 0;
     }
-  }
-
-  DNSSECKeeper dk;
-  NSEC3PARAMRecordContent ns3pr;
-  bool narrow;
 
-  DNSSECKeeper::clearCaches(q->qdomain);
-  bool securedZone = dk.isSecuredZone(q->qdomain);
-  if(dk.getNSEC3PARAM(q->qdomain, &ns3pr, &narrow)) {
-    if(narrow) {
-      g_log<<Logger::Error<<"Not doing IXFR of an NSEC3 narrow zone."<<endl;
-      g_log<<Logger::Error<<"IXFR of domain '"<<q->qdomain<<"' denied to "<<q->getRemote()<<endl;
-      outpacket->setRcode(RCode::Refused);
-      sendPacket(outpacket,outsock);
-      return 0;
+    DNSSECKeeper dk(s_P->getBackend());
+    DNSSECKeeper::clearCaches(q->qdomain);
+    bool narrow;
+    securedZone = dk.isSecuredZone(q->qdomain);
+    if(dk.getNSEC3PARAM(q->qdomain, nullptr, &narrow)) {
+      if(narrow) {
+        g_log<<Logger::Error<<"Not doing IXFR of an NSEC3 narrow zone."<<endl;
+        g_log<<Logger::Error<<"IXFR of domain '"<<q->qdomain<<"' denied to "<<q->getRemote()<<endl;
+        outpacket->setRcode(RCode::Refused);
+        sendPacket(outpacket,outsock);
+        return 0;
+      }
     }
-  }
-
-  DNSName target = q->qdomain;
 
-  UeberBackend db;
-  if(!db.getSOAUncached(target, sd)) {
-    g_log<<Logger::Error<<"IXFR of domain '"<<target<<"' failed: not authoritative in second instance"<<endl;
-    outpacket->setRcode(RCode::NotAuth);
-    sendPacket(outpacket,outsock);
-    return 0;
+    serialPermitsIXFR = !rfc1982LessThan(serial, calculateEditSOA(sd.serial, dk, sd.qname));
   }
 
-  if (!rfc1982LessThan(serial, calculateEditSOA(sd.serial, dk, sd.qname))) {
+  if (serialPermitsIXFR) {
+    DNSName target = q->qdomain;
     TSIGRecordContent trc;
     DNSName tsigkeyname;
     string tsigsecret;
 
+    UeberBackend db;
+    DNSSECKeeper dk(&db);
+
     bool haveTSIGDetails = q->getTSIGDetails(&trc, &tsigkeyname);
 
     if(haveTSIGDetails && !tsigkeyname.empty()) {
@@ -1115,8 +1110,7 @@ int TCPNameserver::doIXFR(std::unique_ptr<DNSPacket>& q, int outsock)
       DNSName algorithm=trc.d_algoName; // FIXME400: was toLowerCanonic, compare output
       if (algorithm == DNSName("hmac-md5.sig-alg.reg.int"))
         algorithm = DNSName("hmac-md5");
-      std::lock_guard<std::mutex> l(s_plock);
-      if(!s_P->getBackend()->getTSIGKey(tsigkeyname, &algorithm, &tsig64)) {
+      if(!db.getTSIGKey(tsigkeyname, &algorithm, &tsig64)) {
         g_log<<Logger::Error<<"TSIG key '"<<tsigkeyname<<"' for domain '"<<target<<"' not found"<<endl;
         return 0;
       }
@@ -1126,8 +1120,6 @@ int TCPNameserver::doIXFR(std::unique_ptr<DNSPacket>& q, int outsock)
       }
     }
 
-    UeberBackend signatureDB;
-
     // SOA *must* go out first, our signing pipe might reorder
     DLOG(g_log<<"Sending out SOA"<<endl);
     DNSZoneRecord soa = makeEditedDNSZRFromSOAData(dk, sd);
@@ -1135,7 +1127,7 @@ int TCPNameserver::doIXFR(std::unique_ptr<DNSPacket>& q, int outsock)
     if(securedZone && outpacket->d_dnssecOk) {
       set<DNSName> authSet;
       authSet.insert(target);
-      addRRSigs(dk, signatureDB, authSet, outpacket->getRRS());
+      addRRSigs(dk, db, authSet, outpacket->getRRS());
     }
 
     if(haveTSIGDetails && !tsigkeyname.empty())
@@ -1148,7 +1140,7 @@ int TCPNameserver::doIXFR(std::unique_ptr<DNSPacket>& q, int outsock)
     return 1;
   }
 
-  g_log<<Logger::Error<<"IXFR fallback to AXFR for domain '"<<target<<"' our serial "<<sd.serial<<endl;
+  g_log<<Logger::Error<<"IXFR fallback to AXFR for domain '"<<q->qdomain<<"' our serial "<<sd.serial<<endl;
   return doAXFR(q->qdomain, q, outsock);
 }
 
index e655ce6b52aeced4aad7aa07bc8ddd718300a42c..52ac44ff74553a72f274f4155cb7d8194237fb76 100644 (file)
@@ -213,4 +213,18 @@ BOOST_AUTO_TEST_CASE(test_reverse_name_to_ip)
   BOOST_CHECK_EQUAL(reverseNameFromIP(v6).toString(), "2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.");
 }
 
+BOOST_AUTO_TEST_CASE(test_getCarbonHostName)
+{
+  char buffer[4096];
+
+  BOOST_CHECK_EQUAL(gethostname(buffer, sizeof buffer), 0);
+  std::string my_hostname(buffer);
+  boost::replace_all(my_hostname, ".", "_");
+
+  std::string hostname = getCarbonHostName();
+  // ensure it matches what we get
+  BOOST_CHECK_EQUAL(my_hostname, hostname);
+  BOOST_CHECK_EQUAL(my_hostname.size(), hostname.size());
+}
+
 BOOST_AUTO_TEST_SUITE_END()
index 8555f6c59117fb68fdd656855405a8ebcfbc281d..b8038dc56e435e4a6d8838f3dc6c8695611fc5fc 100644 (file)
@@ -45,16 +45,16 @@ BOOST_AUTO_TEST_CASE(test_recPacketCacheSimple) {
   pw.commit();
   string rpacket((const char*)&packet[0], packet.size());
 
-  rpc.insertResponsePacket(tag, qhash, string(qpacket), qname, QType::A, QClass::IN, string(rpacket), time(0), ttd, Indeterminate, 0, 0, boost::none);
+  rpc.insertResponsePacket(tag, qhash, string(qpacket), qname, QType::A, QClass::IN, string(rpacket), time(0), ttd, vState::Indeterminate, 0, 0, boost::none);
   BOOST_CHECK_EQUAL(rpc.size(), 1U);
   rpc.doPruneTo(0);
   BOOST_CHECK_EQUAL(rpc.size(), 0U);
-  rpc.insertResponsePacket(tag, qhash, string(qpacket), qname, QType::A, QClass::IN, string(rpacket), time(0), ttd, Indeterminate, 0, 0, boost::none);
+  rpc.insertResponsePacket(tag, qhash, string(qpacket), qname, QType::A, QClass::IN, string(rpacket), time(0), ttd, vState::Indeterminate, 0, 0, boost::none);
   BOOST_CHECK_EQUAL(rpc.size(), 1U);
   rpc.doWipePacketCache(qname);
   BOOST_CHECK_EQUAL(rpc.size(), 0U);
 
-  rpc.insertResponsePacket(tag, qhash, string(qpacket), qname, QType::A, QClass::IN, string(rpacket), time(0), ttd, Indeterminate, 0, 0, boost::none);
+  rpc.insertResponsePacket(tag, qhash, string(qpacket), qname, QType::A, QClass::IN, string(rpacket), time(0), ttd, vState::Indeterminate, 0, 0, boost::none);
   BOOST_CHECK_EQUAL(rpc.size(), 1U);
   uint32_t qhash2 = 0;
   bool found = rpc.getResponsePacket(tag, qpacket, time(nullptr), &fpacket, &age, &qhash2);
@@ -140,11 +140,11 @@ BOOST_AUTO_TEST_CASE(test_recPacketCache_Tags) {
   BOOST_CHECK(r1packet != r2packet);
 
   /* inserting a response for tag1 */
-  rpc.insertResponsePacket(tag1, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r1packet), time(0), ttd, Indeterminate, 0, 0, boost::none);
+  rpc.insertResponsePacket(tag1, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r1packet), time(0), ttd, vState::Indeterminate, 0, 0, boost::none);
   BOOST_CHECK_EQUAL(rpc.size(), 1U);
 
   /* inserting a different response for tag2, should not override the first one */
-  rpc.insertResponsePacket(tag2, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r2packet), time(0), ttd, Indeterminate, 0, 0, boost::none);
+  rpc.insertResponsePacket(tag2, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r2packet), time(0), ttd, vState::Indeterminate, 0, 0, boost::none);
   BOOST_CHECK_EQUAL(rpc.size(), 2U);
 
   /* remove all responses from the cache */
@@ -152,10 +152,10 @@ BOOST_AUTO_TEST_CASE(test_recPacketCache_Tags) {
   BOOST_CHECK_EQUAL(rpc.size(), 0U);
 
   /* reinsert both */
-  rpc.insertResponsePacket(tag1, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r1packet), time(0), ttd, Indeterminate, 0, 0, boost::none);
+  rpc.insertResponsePacket(tag1, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r1packet), time(0), ttd, vState::Indeterminate, 0, 0, boost::none);
   BOOST_CHECK_EQUAL(rpc.size(), 1U);
 
-  rpc.insertResponsePacket(tag2, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r2packet), time(0), ttd, Indeterminate, 0, 0, boost::none);
+  rpc.insertResponsePacket(tag2, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r2packet), time(0), ttd, vState::Indeterminate, 0, 0, boost::none);
   BOOST_CHECK_EQUAL(rpc.size(), 2U);
 
   /* remove the responses by qname, should remove both */
@@ -163,7 +163,7 @@ BOOST_AUTO_TEST_CASE(test_recPacketCache_Tags) {
   BOOST_CHECK_EQUAL(rpc.size(), 0U);
 
   /* insert the response for tag1 */
-  rpc.insertResponsePacket(tag1, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r1packet), time(0), ttd, Indeterminate, 0, 0, boost::none);
+  rpc.insertResponsePacket(tag1, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r1packet), time(0), ttd, vState::Indeterminate, 0, 0, boost::none);
   BOOST_CHECK_EQUAL(rpc.size(), 1U);
 
   /* we can retrieve it */
@@ -182,7 +182,7 @@ BOOST_AUTO_TEST_CASE(test_recPacketCache_Tags) {
   BOOST_CHECK_EQUAL(temphash, qhash);
 
   /* adding a response for the second tag */
-  rpc.insertResponsePacket(tag2, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r2packet), time(0), ttd, Indeterminate, 0, 0, boost::none);
+  rpc.insertResponsePacket(tag2, qhash, string(qpacket), qname, QType::A, QClass::IN, string(r2packet), time(0), ttd, vState::Indeterminate, 0, 0, boost::none);
   BOOST_CHECK_EQUAL(rpc.size(), 2U);
 
   /* We still get the correct response for the first tag */
diff --git a/pdns/test-ueberbackend_cc.cc b/pdns/test-ueberbackend_cc.cc
new file mode 100644 (file)
index 0000000..2ca4a56
--- /dev/null
@@ -0,0 +1,1126 @@
+#define BOOST_TEST_DYN_LINK
+#define BOOST_TEST_NO_MAIN
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <unordered_map>
+
+#include <boost/test/unit_test.hpp>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+#include <boost/multi_index/key_extractors.hpp>
+
+#include "arguments.hh"
+#include "auth-querycache.hh"
+#include "ueberbackend.hh"
+
+class SimpleBackend : public DNSBackend
+{
+public:
+  struct SimpleDNSRecord
+  {
+    SimpleDNSRecord(const DNSName& name, uint16_t type, const std::string& content, uint32_t ttl): d_content(content), d_name(name), d_ttl(ttl), d_type(type)
+    {
+    }
+
+    std::string d_content;
+    DNSName d_name;
+    uint32_t d_ttl;
+    uint16_t d_type;
+  };
+
+  struct OrderedNameTypeTag;
+
+  typedef multi_index_container<
+    SimpleDNSRecord,
+    indexed_by <
+      ordered_non_unique<tag<OrderedNameTypeTag>,
+                         composite_key<
+                           SimpleDNSRecord,
+                           member<SimpleDNSRecord, DNSName, &SimpleDNSRecord::d_name>,
+                           member<SimpleDNSRecord, uint16_t, &SimpleDNSRecord::d_type>
+                           >,
+                         composite_key_compare<CanonDNSNameCompare, std::less<uint16_t> >
+                         >
+      >
+    > RecordStorage;
+
+  struct SimpleDNSZone
+  {
+    SimpleDNSZone(const DNSName& name, uint64_t id): d_records(std::make_shared<RecordStorage>()), d_name(name), d_id(id)
+    {
+    }
+    std::shared_ptr<RecordStorage> d_records;
+    DNSName d_name;
+    uint64_t d_id;
+  };
+
+  struct HashedNameTag {};
+  struct IDTag {};
+
+  typedef multi_index_container<
+    SimpleDNSZone,
+    indexed_by <
+      ordered_unique<tag<IDTag>, member<SimpleDNSZone, uint64_t, &SimpleDNSZone::d_id> >,
+      hashed_unique<tag<HashedNameTag>, member<SimpleDNSZone, DNSName, &SimpleDNSZone::d_name> >
+      >
+    > ZoneStorage;
+
+  struct SimpleMetaData
+  {
+    SimpleMetaData(const DNSName& name, const std::string& kind, const std::vector<std::string>& values): d_name(name), d_kind(kind), d_values(values)
+    {
+    }
+
+    DNSName d_name;
+    std::string d_kind;
+    std::vector<std::string> d_values;
+  };
+
+  struct OrderedNameKindTag {};
+
+  typedef multi_index_container<
+    SimpleMetaData,
+    indexed_by <
+      ordered_unique<tag<OrderedNameKindTag>,
+                     composite_key<
+                       SimpleMetaData,
+                       member<SimpleMetaData, DNSName, &SimpleMetaData::d_name>,
+                       member<SimpleMetaData, std::string, &SimpleMetaData::d_kind>
+                       >,
+                     composite_key_compare<CanonDNSNameCompare, std::less<std::string> >
+                     >
+      >
+    > MetaDataStorage;
+
+  // Initialize our backend ID from the suffix, skipping the '-' that DNSBackend adds there
+  SimpleBackend(const std::string& suffix): d_suffix(suffix), d_backendId(pdns_stou(suffix.substr(1)))
+  {
+  }
+
+  bool findZone(const DNSName& qdomain, int zoneId, std::shared_ptr<RecordStorage>& records, uint64_t& currentZoneId) const
+  {
+    currentZoneId = -1;
+    records.reset();
+
+    if (zoneId != -1) {
+      const auto& idx = boost::multi_index::get<IDTag>(s_zones.at(d_backendId));
+      auto it = idx.find(zoneId);
+      if (it == idx.end()) {
+        return false;
+      }
+      records = it->d_records;
+      currentZoneId = it->d_id;
+    }
+    else {
+      const auto& idx = boost::multi_index::get<HashedNameTag>(s_zones.at(d_backendId));
+      auto it = idx.find(qdomain);
+      if (it == idx.end()) {
+        return false;
+      }
+      records = it->d_records;
+      currentZoneId = it->d_id;
+    }
+
+    return true;
+  }
+
+  void lookup(const QType& qtype, const DNSName& qdomain, int zoneId = -1, DNSPacket *pkt_p = nullptr) override
+  {
+    d_currentScopeMask = 0;
+    findZone(qdomain, zoneId, d_records, d_currentZone);
+
+    if (d_records) {
+      if (qdomain == DNSName("geo.powerdns.com.") && pkt_p != nullptr && pkt_p->getRealRemote() == Netmask("192.0.2.1")) {
+        d_currentScopeMask = 32;
+      }
+
+      auto& idx = d_records->get<OrderedNameTypeTag>();
+      if (qtype == QType::ANY) {
+        auto range = idx.equal_range(qdomain);
+        d_iter = range.first;
+        d_end = range.second;
+      }
+      else {
+        auto range = idx.equal_range(boost::make_tuple(qdomain, qtype.getCode()));
+        d_iter = range.first;
+        d_end = range.second;
+      }
+    }
+  }
+
+  bool get(DNSResourceRecord& drr) override
+  {
+    if (!d_records) {
+      return false;
+    }
+
+    if (d_iter == d_end) {
+      return false;
+    }
+
+    drr.qname = d_iter->d_name;
+    drr.domain_id = d_currentZone;
+    drr.content = d_iter->d_content;
+    drr.qtype = d_iter->d_type;
+    drr.ttl = d_iter->d_ttl;
+
+    // drr.auth = d_iter->auth; might bring pain at some point, let's not cross that bridge until then
+    drr.auth = true;
+    drr.scopeMask = d_currentScopeMask;
+
+    ++d_iter;
+    return true;
+  }
+
+  bool list(const DNSName& target, int zoneId, bool include_disabled = false) override
+  {
+    findZone(target, zoneId, d_records, d_currentZone);
+
+    if (d_records) {
+      d_iter = d_records->begin();
+      d_end = d_records->end();
+      return true;
+    }
+
+    return false;
+  }
+
+  bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta) override
+  {
+    const auto& idx = boost::multi_index::get<OrderedNameKindTag>(s_metadata.at(d_backendId));
+    auto it = idx.find(boost::make_tuple(name, kind));
+    if (it == idx.end()) {
+      /* funnily enough, we are expected to return true even though we might not know that zone */
+      return true;
+    }
+
+    meta = it->d_values;
+    return true;
+  }
+
+  bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta) override
+  {
+    auto& idx = boost::multi_index::get<OrderedNameKindTag>(s_metadata.at(d_backendId));
+    auto it = idx.find(boost::make_tuple(name, kind));
+    if (it == idx.end()) {
+      s_metadata.at(d_backendId).insert(SimpleMetaData(name, kind, meta));
+      return true;
+    }
+    idx.replace(it, SimpleMetaData(name, kind, meta));
+    return true;
+  }
+
+  /* this is not thread-safe */
+  static std::unordered_map<uint64_t, ZoneStorage> s_zones;
+  static std::unordered_map<uint64_t, MetaDataStorage> s_metadata;
+
+protected:
+  std::string d_suffix;
+  std::shared_ptr<RecordStorage> d_records{nullptr};
+  RecordStorage::index<OrderedNameTypeTag>::type::const_iterator d_iter;
+  RecordStorage::index<OrderedNameTypeTag>::type::const_iterator d_end;
+  const uint64_t d_backendId;
+  uint64_t d_currentZone{0};
+  uint8_t d_currentScopeMask{0};
+};
+
+class SimpleBackendBestAuth : public SimpleBackend
+{
+public:
+  SimpleBackendBestAuth(const std::string& suffix): SimpleBackend(suffix)
+  {
+  }
+
+  bool getAuth(const DNSName& target, SOAData* sd) override
+  {
+    static const DNSName best("d.0.1.0.0.2.ip6.arpa.");
+
+    ++d_authLookupCount;
+
+    if (target.isPartOf(best)) {
+      /* return the best SOA right away */
+      std::shared_ptr<RecordStorage> records;
+      uint64_t zoneId;
+      if (!findZone(best, -1, records, zoneId)) {
+        return false;
+      }
+
+      auto& idx = records->get<OrderedNameTypeTag>();
+      auto range = idx.equal_range(boost::make_tuple(best, QType::SOA));
+      if (range.first == range.second) {
+        return false;
+      }
+
+      fillSOAData(range.first->d_content, *sd);
+      sd->ttl = range.first->d_ttl;
+      sd->qname = best;
+      sd->domain_id = zoneId;
+      return true;
+    }
+
+    return getSOA(target, *sd);
+  }
+
+  size_t d_authLookupCount{0};
+};
+
+class SimpleBackendNoMeta : public SimpleBackend
+{
+public:
+  SimpleBackendNoMeta(const std::string& suffix): SimpleBackend(suffix)
+  {
+  }
+
+  bool getDomainMetadata(const DNSName& name, const std::string& kind, std::vector<std::string>& meta) override
+  {
+    return false;
+  }
+
+  bool setDomainMetadata(const DNSName& name, const std::string& kind, const std::vector<std::string>& meta) override
+  {
+    return false;
+  }
+};
+
+std::unordered_map<uint64_t, SimpleBackend::ZoneStorage> SimpleBackend::s_zones;
+std::unordered_map<uint64_t, SimpleBackend::MetaDataStorage> SimpleBackend::s_metadata;
+
+class SimpleBackendFactory : public BackendFactory
+{
+public:
+  SimpleBackendFactory(): BackendFactory("SimpleBackend")
+  {
+  }
+
+  DNSBackend *make(const string& suffix="") override
+  {
+    return new SimpleBackend(suffix);
+  }
+};
+
+class SimpleBackendBestAuthFactory : public BackendFactory
+{
+public:
+  SimpleBackendBestAuthFactory(): BackendFactory("SimpleBackendBestAuth")
+  {
+  }
+
+  DNSBackend *make(const string& suffix="") override
+  {
+    return new SimpleBackendBestAuth(suffix);
+  }
+};
+
+class SimpleBackendNoMetaFactory : public BackendFactory
+{
+public:
+  SimpleBackendNoMetaFactory(): BackendFactory("SimpleBackendNoMeta")
+  {
+  }
+
+  DNSBackend *make(const string& suffix="") override
+  {
+    return new SimpleBackendNoMeta(suffix);
+  }
+};
+
+struct UeberBackendSetupArgFixture {
+  UeberBackendSetupArgFixture() {
+    extern AuthQueryCache QC;
+    ::arg().set("query-cache-ttl")="0";
+    ::arg().set("negquery-cache-ttl")="0";
+    QC.cleanup();
+    BackendMakers().clear();
+    SimpleBackend::s_zones.clear();
+    SimpleBackend::s_metadata.clear();
+  };
+};
+
+static void testWithoutThenWithCache(std::function<void(UeberBackend& ub)> func)
+{
+  extern AuthQueryCache QC;
+
+  {
+    /* disable the cache */
+    ::arg().set("query-cache-ttl")="0";
+    ::arg().set("negquery-cache-ttl")="0";
+    QC.cleanup();
+
+    UeberBackend ub;
+    func(ub);
+  }
+
+  {
+    /* enable the cache */
+    ::arg().set("query-cache-ttl")="20";
+    ::arg().set("negquery-cache-ttl")="60";
+    QC.cleanup();
+
+    UeberBackend ub;
+    /* a first time to fill the cache */
+    func(ub);
+    /* a second time to make sure every call has been tried with the cache filled */
+    func(ub);
+  }
+}
+
+BOOST_FIXTURE_TEST_SUITE(test_ueberbackend_cc, UeberBackendSetupArgFixture)
+
+static std::vector<DNSZoneRecord> getRecords(UeberBackend& ub, const DNSName& name, uint16_t qtype, int zoneId, const DNSPacket* pkt)
+{
+  std::vector<DNSZoneRecord> result;
+
+  ub.lookup(QType(qtype), name, zoneId, const_cast<DNSPacket*>(pkt));
+
+  DNSZoneRecord dzr;
+  while (ub.get(dzr))
+  {
+    result.push_back(std::move(dzr));
+  }
+
+  return result;
+}
+
+static void checkRecordExists(const std::vector<DNSZoneRecord>& records, const DNSName& name, uint16_t type, int zoneId, uint8_t scopeMask, bool auth)
+{
+  BOOST_REQUIRE_GE(records.size(), 1);
+  for (const auto& record : records) {
+    if (record.domain_id == zoneId &&
+        record.dr.d_type == type &&
+        record.dr.d_name == name &&
+        record.auth == auth &&
+        record.scopeMask == scopeMask) {
+      return;
+    }
+  }
+  BOOST_CHECK_MESSAGE(false, "Record " + name.toString() + "/" + QType(type).getName() + " - " + std::to_string(zoneId) + " not found");
+}
+
+BOOST_AUTO_TEST_CASE(test_simple) {
+
+  try {
+    SimpleBackend::SimpleDNSZone zoneA(DNSName("powerdns.com."), 1);
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60));
+    SimpleBackend::s_zones[1].insert(zoneA);
+
+    BackendMakers().report(new SimpleBackendFactory());
+    BackendMakers().launch("SimpleBackend:1");
+    UeberBackend::go();
+
+    auto testFunction = [](UeberBackend& ub) -> void {
+    {
+      // test SOA with unknown zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+    }
+
+    {
+      // test ANY with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 2);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test AAAA with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test NODATA with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test ANY with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 2);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test AAAA with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test NODATA with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test ANY with wrong zone id
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 65535, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test a DNS packet is correctly passed and that the corresponding scope is passed back
+      DNSPacket pkt(true);
+      ComboAddress remote("192.0.2.1");
+      pkt.setRemote(&remote);
+      auto records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 32, true);
+      // and that we don't get the same result for a different client
+      remote = ComboAddress("192.0.2.2");
+      pkt.setRemote(&remote);
+      records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 0, true);
+    }
+
+    };
+    testWithoutThenWithCache(testFunction);
+  }
+  catch(const PDNSException& e) {
+    cerr<<e.reason<<endl;
+    throw;
+  }
+  catch(const std::exception& e) {
+    cerr<<e.what()<<endl;
+    throw;
+  }
+  catch(...) {
+    cerr<<"An unexpected error occurred.."<<endl;
+    throw;
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_multi_backends_separate_zones) {
+  // one zone in backend 1, a second zone in backend 2
+  // no overlap
+
+  try {
+    SimpleBackend::SimpleDNSZone zoneA(DNSName("powerdns.com."), 1);
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60));
+    SimpleBackend::s_zones[1].insert(zoneA);
+
+    SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.org."), 2);
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.org."), QType::SOA, "ns1.powerdns.org. powerdns.org. 3 600 600 3600000 604800", 3600));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.org."), QType::AAAA, "2001:db8::2", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.org."), QType::AAAA, "2001:db8::2", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.org."), QType::AAAA, "2001:db8::42", 60));
+    SimpleBackend::s_zones[2].insert(zoneB);
+
+    BackendMakers().report(new SimpleBackendFactory());
+    BackendMakers().launch("SimpleBackend:1, SimpleBackend:2");
+    UeberBackend::go();
+
+    auto testFunction = [](UeberBackend& ub) -> void {
+    {
+      // test SOA with unknown zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+
+      records = getRecords(ub, DNSName("powerdns.org."), QType::SOA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.org."), QType::SOA, 2, 0, true);
+    }
+
+    {
+      // test ANY with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 2);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+
+      records = getRecords(ub, DNSName("powerdns.org."), QType::ANY, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 2);
+      checkRecordExists(records, DNSName("powerdns.org."), QType::SOA, 2, 0, true);
+      checkRecordExists(records, DNSName("powerdns.org."), QType::AAAA, 2, 0, true);
+    }
+
+    {
+      // test AAAA with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+
+      records = getRecords(ub, DNSName("powerdns.org."), QType::AAAA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.org."), QType::AAAA, 2, 0, true);
+    }
+
+    {
+      // test NODATA with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+
+      records = getRecords(ub, DNSName("powerdns.org."), QType::PTR, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test ANY with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 2);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+
+      records = getRecords(ub, DNSName("powerdns.org."), QType::ANY, 2, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 2);
+      checkRecordExists(records, DNSName("powerdns.org."), QType::SOA, 2, 0, true);
+      checkRecordExists(records, DNSName("powerdns.org."), QType::AAAA, 2, 0, true);
+    }
+
+    {
+      // test AAAA with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+
+      records = getRecords(ub, DNSName("www.powerdns.org."), QType::AAAA, 2, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("www.powerdns.org."), QType::AAAA, 2, 0, true);
+    }
+
+    {
+      // test NODATA with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+
+      records = getRecords(ub, DNSName("powerdns.org."), QType::PTR, 2, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test ANY with wrong zone id
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 2, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+
+      records = getRecords(ub, DNSName("powerdns.org."), QType::ANY, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+
+      records = getRecords(ub, DNSName("not-powerdns.com."), QType::ANY, 65535, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test a DNS packet is correctly passed and that the corresponding scope is passed back
+      DNSPacket pkt(true);
+      ComboAddress remote("192.0.2.1");
+      pkt.setRemote(&remote);
+      auto records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 32, true);
+      // and that we don't get the same result for a different client
+      remote = ComboAddress("192.0.2.2");
+      pkt.setRemote(&remote);
+      records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 0, true);
+    }
+
+    };
+    testWithoutThenWithCache(testFunction);
+  }
+  catch(const PDNSException& e) {
+    cerr<<e.reason<<endl;
+    throw;
+  }
+  catch(const std::exception& e) {
+    cerr<<e.what()<<endl;
+    throw;
+  }
+  catch(...) {
+    cerr<<"An unexpected error occurred.."<<endl;
+    throw;
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_multi_backends_overlay) {
+  // one backend holds the SOA, NS and one A
+  // a second backend holds another A and AAAA
+  try {
+    SimpleBackend::SimpleDNSZone zoneA(DNSName("powerdns.com."), 1);
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::A, "192.168.0.1", 60));
+    SimpleBackend::s_zones[1].insert(zoneA);
+
+    SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.com."), 1);
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::A, "192.168.0.2", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60));
+    SimpleBackend::s_zones[2].insert(zoneB);
+
+    BackendMakers().report(new SimpleBackendFactory());
+    BackendMakers().launch("SimpleBackend:1, SimpleBackend:2");
+    UeberBackend::go();
+
+    auto testFunction = [](UeberBackend& ub) -> void {
+    {
+      // test SOA with unknown zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+    }
+
+    {
+      // test ANY with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr);
+      // /!\ only 3 records are returned since we don't allow spreading the same name over several backends
+      BOOST_REQUIRE_EQUAL(records.size(), 3);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::A, 1, 0, true);
+      //checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test AAAA with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, -1, nullptr);
+      // /!\ the AAAA will be found on an exact search, but not on an ANY one
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test NODATA with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test ANY with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 1, nullptr);
+      // /!\ only 3 records are returned since we don't allow spreading the same name over several backends
+      BOOST_REQUIRE_EQUAL(records.size(), 3);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::A, 1, 0, true);
+      //checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test AAAA with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, 1, nullptr);
+      // /!\ the AAAA will be found on an exact search, but not on an ANY one
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test www - A with zone id set (only in the second backend)
+      auto records = getRecords(ub, DNSName("www.powerdns.com."), QType::A, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("www.powerdns.com."), QType::A, 1, 0, true);
+    }
+
+    {
+      // test NODATA with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test ANY with wrong zone id
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 2, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test a DNS packet is correctly passed and that the corresponding scope is passed back
+      DNSPacket pkt(true);
+      ComboAddress remote("192.0.2.1");
+      pkt.setRemote(&remote);
+      auto records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 32, true);
+      // and that we don't get the same result for a different client
+      remote = ComboAddress("192.0.2.2");
+      pkt.setRemote(&remote);
+      records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 0, true);
+    }
+
+    };
+    testWithoutThenWithCache(testFunction);
+  }
+  catch(const PDNSException& e) {
+    cerr<<e.reason<<endl;
+    throw;
+  }
+  catch(const std::exception& e) {
+    cerr<<e.what()<<endl;
+    throw;
+  }
+  catch(...) {
+    cerr<<"An unexpected error occurred.."<<endl;
+    throw;
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_multi_backends_overlay_name) {
+  // one backend holds the apex with SOA, NS and one A
+  // a second backend holds others names
+  try {
+    SimpleBackend::SimpleDNSZone zoneA(DNSName("powerdns.com."), 1);
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::A, "192.168.0.1", 60));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::A, "192.168.0.2", 60));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60));
+    SimpleBackend::s_zones[1].insert(zoneA);
+
+    SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.com."), 1);
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::AAAA, "192.168.0.1", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60));
+    SimpleBackend::s_zones[2].insert(zoneB);
+
+    BackendMakers().report(new SimpleBackendFactory());
+    BackendMakers().launch("SimpleBackend:1, SimpleBackend:2");
+    UeberBackend::go();
+
+    auto testFunction = [](UeberBackend& ub) -> void {
+    {
+      // test SOA with unknown zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+    }
+
+    {
+      // test ANY with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 5);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::A, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test AAAA with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test NODATA with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test ANY with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 5);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::A, 1, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test AAAA with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::AAAA, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 1, 0, true);
+    }
+
+    {
+      // test www - A with zone id set (only in the second backend)
+      auto records = getRecords(ub, DNSName("www.powerdns.com."), QType::A, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("www.powerdns.com."), QType::A, 1, 0, true);
+    }
+
+    {
+      // test NODATA with zone id set
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::PTR, 1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test ANY with wrong zone id
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, 2, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 0);
+    }
+
+    {
+      // test a DNS packet is correctly passed and that the corresponding scope is passed back
+      DNSPacket pkt(true);
+      ComboAddress remote("192.0.2.1");
+      pkt.setRemote(&remote);
+      auto records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 32, true);
+      // and that we don't get the same result for a different client
+      remote = ComboAddress("192.0.2.2");
+      pkt.setRemote(&remote);
+      records = getRecords(ub, DNSName("geo.powerdns.com."), QType::ANY, 1, &pkt);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("geo.powerdns.com."), QType::A, 1, 0, true);
+    }
+
+    };
+    testWithoutThenWithCache(testFunction);
+  }
+  catch(const PDNSException& e) {
+    cerr<<e.reason<<endl;
+    throw;
+  }
+  catch(const std::exception& e) {
+    cerr<<e.what()<<endl;
+    throw;
+  }
+  catch(...) {
+    cerr<<"An unexpected error occurred.."<<endl;
+    throw;
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_child_zone) {
+  // Backend 1 holds zone A "com" while backend 2 holds zone B "powerdns.com"
+  // Check that DS queries are correctly handled
+
+  try {
+    SimpleBackend::SimpleDNSZone zoneA(DNSName("com."), 1);
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("com."), QType::SOA, "a.gtld-servers.net. nstld.verisign-grs.com. 3 600 600 3600000 604800", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::DS, "44030 8 3 7DD75AE1565051F9563CF8DF976AC99CDCA51E3463019C81BD2BB083 82F3854E", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("ns1.powerdns.com."), QType::A, "192.0.2.1", 3600));
+    SimpleBackend::s_zones[1].insert(zoneA);
+
+    SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.com."), 2);
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::2", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::NS, "ns1.powerdns.com.", 3600));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("ns1.powerdns.com."), QType::A, "192.0.2.1", 3600));
+    SimpleBackend::s_zones[2].insert(zoneB);
+
+    BackendMakers().report(new SimpleBackendFactory());
+    BackendMakers().launch("SimpleBackend:1, SimpleBackend:2");
+    UeberBackend::go();
+
+    auto testFunction = [](UeberBackend& ub) -> void {
+    {
+      // test SOA with unknown zone id == -1
+      auto records = getRecords(ub, DNSName("com."), QType::SOA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("com."), QType::SOA, 1, 0, true);
+
+      records = getRecords(ub, DNSName("powerdns.com."), QType::SOA, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 1);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 2, 0, true);
+    }
+
+    {
+      // test ANY with zone id == -1
+      auto records = getRecords(ub, DNSName("powerdns.com."), QType::ANY, -1, nullptr);
+      BOOST_REQUIRE_EQUAL(records.size(), 3);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::SOA, 2, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::NS, 2, 0, true);
+      checkRecordExists(records, DNSName("powerdns.com."), QType::AAAA, 2, 0, true);
+    }
+
+    {
+      // test getAuth() for DS
+      SOAData sd;
+      BOOST_REQUIRE(ub.getAuth(DNSName("powerdns.com."), QType::DS, &sd));
+      BOOST_CHECK_EQUAL(sd.qname.toString(), "com.");
+      BOOST_CHECK_EQUAL(sd.domain_id, 1);
+    }
+
+    {
+      // test getAuth() for A
+      SOAData sd;
+      BOOST_REQUIRE(ub.getAuth(DNSName("powerdns.com."), QType::A, &sd));
+      BOOST_CHECK_EQUAL(sd.qname.toString(), "powerdns.com.");
+      BOOST_CHECK_EQUAL(sd.domain_id, 2);
+    }
+
+    };
+    testWithoutThenWithCache(testFunction);
+  }
+  catch(const PDNSException& e) {
+    cerr<<e.reason<<endl;
+    throw;
+  }
+  catch(const std::exception& e) {
+    cerr<<e.what()<<endl;
+    throw;
+  }
+  catch(...) {
+    cerr<<"An unexpected error occurred.."<<endl;
+    throw;
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_multi_backends_best_soa) {
+  // several backends, one returns the best SOA it has right away
+  // while the others do simple lookups
+
+  try {
+    SimpleBackend::SimpleDNSZone zoneA(DNSName("d.0.1.0.0.2.ip6.arpa."), 1);
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("d.0.1.0.0.2.ip6.arpa."), QType::SOA, "ns.apnic.net. read-txt-record-of-zone-first-dns-admin.apnic.net. 3005126844 7200 1800 604800 3600", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa."), QType::PTR, "a.reverse.", 3600));
+    SimpleBackend::s_zones[1].insert(zoneA);
+
+    SimpleBackend::SimpleDNSZone zoneB(DNSName("0.1.0.0.2.ip6.arpa."), 2);
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("0.1.0.0.2.ip6.arpa."), QType::SOA, "ns.apnic.net. read-txt-record-of-zone-first-dns-admin.apnic.net. 3005126844 7200 1800 604800 3600", 3600));
+    SimpleBackend::s_zones[2].insert(zoneB);
+
+    BackendMakers().report(new SimpleBackendFactory());
+    BackendMakers().report(new SimpleBackendBestAuthFactory());
+    BackendMakers().launch("SimpleBackendBestAuth:1, SimpleBackend:2");
+    UeberBackend::go();
+
+    auto testFunction = [](UeberBackend& ub) -> void {
+    {
+      auto sbba = dynamic_cast<SimpleBackendBestAuth*>(ub.backends.at(0));
+      BOOST_REQUIRE(sbba != nullptr);
+      sbba->d_authLookupCount = 0;
+
+      // test getAuth()
+      SOAData sd;
+      BOOST_REQUIRE(ub.getAuth(DNSName("2.4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa."), QType::PTR, &sd));
+      BOOST_CHECK_EQUAL(sd.qname.toString(), "d.0.1.0.0.2.ip6.arpa.");
+      BOOST_CHECK_EQUAL(sd.domain_id, 1);
+      // check that only one auth lookup occurred to this backend
+      BOOST_CHECK_EQUAL(sbba->d_authLookupCount, 1);
+    }
+
+    };
+
+    testWithoutThenWithCache(testFunction);
+  }
+  catch(const PDNSException& e) {
+    cerr<<e.reason<<endl;
+    throw;
+  }
+  catch(const std::exception& e) {
+    cerr<<e.what()<<endl;
+    throw;
+  }
+  catch(...) {
+    cerr<<"An unexpected error occurred.."<<endl;
+    throw;
+  }
+}
+
+BOOST_AUTO_TEST_CASE(test_multi_backends_metadata) {
+  // we have metadata stored in the first and second backend.
+  // We can read from the first backend but not from the second, since the first will return "true" even though it has nothing
+  // Updating will insert into the first backend, leaving the first one untouched
+
+  try {
+    SimpleBackend::SimpleDNSZone zoneA(DNSName("powerdns.com."), 1);
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::SOA, "ns1.powerdns.com. powerdns.com. 3 600 600 3600000 604800", 3600));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.com."), QType::AAAA, "2001:db8::1", 60));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.com."), QType::A, "192.168.0.1", 60));
+    zoneA.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.com."), QType::A, "192.168.0.42", 60));
+    SimpleBackend::s_zones[1].insert(zoneA);
+    SimpleBackend::s_metadata[1].insert(SimpleBackend::SimpleMetaData(DNSName("powerdns.com."), "test-data-a", { "value1", "value2"}));
+
+    SimpleBackend::SimpleDNSZone zoneB(DNSName("powerdns.org."), 2);
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.org."), QType::SOA, "ns1.powerdns.org. powerdns.org. 3 600 600 3600000 604800", 3600));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("powerdns.org."), QType::AAAA, "2001:db8::2", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("www.powerdns.org."), QType::AAAA, "2001:db8::2", 60));
+    zoneB.d_records->insert(SimpleBackend::SimpleDNSRecord(DNSName("geo.powerdns.org."), QType::AAAA, "2001:db8::42", 60));
+    SimpleBackend::s_zones[2].insert(zoneB);
+    SimpleBackend::s_metadata[2].insert(SimpleBackend::SimpleMetaData(DNSName("powerdns.org."), "test-data-b", { "value1", "value2"}));
+
+    BackendMakers().report(new SimpleBackendFactory());
+    BackendMakers().launch("SimpleBackend:1, SimpleBackend:2");
+    UeberBackend::go();
+
+    auto testFunction = [](UeberBackend& ub) -> void {
+    {
+      // check the initial values
+      std::vector<std::string> values;
+      BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.com."), "test-data-a", values));
+      BOOST_REQUIRE_EQUAL(values.size(), 2);
+      BOOST_CHECK_EQUAL(values.at(0), "value1");
+      BOOST_CHECK_EQUAL(values.at(1), "value2");
+      values.clear();
+      BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.com."), "test-data-b", values));
+      BOOST_CHECK_EQUAL(values.size(), 0);
+      values.clear();
+      BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.org."), "test-data-a", values));
+      BOOST_CHECK_EQUAL(values.size(), 0);
+      values.clear();
+      BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.org."), "test-data-b", values));
+      BOOST_CHECK_EQUAL(values.size(), 0);
+    }
+
+    {
+      // update the values
+      BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.com."), "test-data-a", { "value3" }));
+      BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-a", { "value4" }));
+      BOOST_CHECK(ub.setDomainMetadata(DNSName("powerdns.org."), "test-data-b", { "value5" }));
+    }
+
+    // check the updated values
+    {
+      std::vector<std::string> values;
+      BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.com."), "test-data-a", values));
+      BOOST_REQUIRE_EQUAL(values.size(), 1);
+      BOOST_CHECK_EQUAL(values.at(0), "value3");
+      values.clear();
+      BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.org."), "test-data-a", values));
+      BOOST_REQUIRE_EQUAL(values.size(), 1);
+      BOOST_CHECK_EQUAL(values.at(0), "value4");
+      values.clear();
+      BOOST_CHECK(ub.getDomainMetadata(DNSName("powerdns.org."), "test-data-b", values));
+      BOOST_REQUIRE_EQUAL(values.size(), 1);
+      BOOST_CHECK_EQUAL(values.at(0), "value5");
+    }
+
+    {
+      // check that it has not been updated in the second backend
+      const auto& it = SimpleBackend::s_metadata[2].find(boost::make_tuple(DNSName("powerdns.org."), "test-data-b"));
+      BOOST_REQUIRE(it != SimpleBackend::s_metadata[2].end());
+      BOOST_REQUIRE_EQUAL(it->d_values.size(), 2);
+      BOOST_CHECK_EQUAL(it->d_values.at(0), "value1");
+      BOOST_CHECK_EQUAL(it->d_values.at(1), "value2");
+    }
+    };
+
+    UeberBackend ub;
+    testFunction(ub);
+  }
+  catch(const PDNSException& e) {
+    cerr<<e.reason<<endl;
+    throw;
+  }
+  catch(const std::exception& e) {
+    cerr<<e.what()<<endl;
+    throw;
+  }
+  catch(...) {
+    cerr<<"An unexpected error occurred.."<<endl;
+    throw;
+  }
+}
+
+
+BOOST_AUTO_TEST_SUITE_END();
index c453f0e3b94ab66f0cf5ed1fd666b17a6054adb4..b2e93c30a67545770d90c56e9bc4cda45aac980e 100644 (file)
@@ -145,7 +145,7 @@ try
       for(const auto& sig : csp.second.signatures) {
        cerr<<"got rrsig "<<sig->d_signer<<"/"<<sig->d_tag<<endl;
        vState state = getKeysFor(tro, sig->d_signer, keys);
-       cerr<<"! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys at "<<qname<<endl;
+       cerr<<"! state = "<<state<<", now have "<<keys.size()<<" keys at "<<qname<<endl;
         // dsmap.insert(make_pair(dsrc.d_tag, dsrc));
       }
     }
@@ -155,7 +155,7 @@ try
   else {
     cerr<<"no sigs, hoping for Insecure"<<endl;
     vState state = getKeysFor(tro, qname, keys);
-    cerr<<"! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys at "<<qname<<endl;
+    cerr<<"! state = "<<state<<", now have "<<keys.size()<<" keys at "<<qname<<endl;
   }
   cerr<<"! validated "<<validrrsets.size()<<" RRsets out of "<<cspmap.size()<<endl;
 
index 3a705096cd5369949b86a083677d973daccbf5f6..e63e29ebc5ecebbf617d1ce73f8f01acba19e58f 100644 (file)
 #include "digests.hh"
 #include "base64.hh"
 #include "dnssecinfra.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
 #include "arguments.hh"
 #include "dns_random.hh"
+#include "query-local-address.hh"
 
 StatBag S;
 
@@ -26,8 +27,7 @@ ArgvMap& arg()
 int main(int argc, char** argv)
 try
 {
-  ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
-  ::arg().set("query-local-address6","Source IPv6 address for sending queries")="::";
+  pdns::parseQueryLocalAddress(":: 0.0.0.0");
 
   reportAllTypes();
 
index 45cbe9aab13176fdacdb869d5c58c1bfe7e4eed7..38717e0f3402a42a0f6566fa672e5ed328daf80a 100644 (file)
@@ -109,10 +109,10 @@ bool UeberBackend::getDomainInfo(const DNSName &domain, DomainInfo &di, bool get
   return false;
 }
 
-bool UeberBackend::createDomain(const DNSName &domain)
+bool UeberBackend::createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account)
 {
   for(DNSBackend* mydb :  backends) {
-    if(mydb->createDomain(domain)) {
+    if(mydb->createDomain(domain, kind, masters, account)) {
       return true;
     }
   }
@@ -322,7 +322,7 @@ bool UeberBackend::getAuth(const DNSName &target, const QType& qtype, SOAData* s
         DLOG(g_log<<Logger::Error<<"has pos cache entry: "<<shorter<<endl);
         fillSOAData(d_answers[0], *sd);
 
-        sd->db = 0;
+        sd->db = nullptr;
         sd->qname = shorter;
         goto found;
       } else if(cstat == 0 && d_negcache_ttl) {
@@ -416,7 +416,7 @@ bool UeberBackend::getSOA(const DNSName &domain, SOAData &sd)
     fillSOAData(d_answers[0],sd);
     sd.domain_id=d_answers[0].domain_id;
     sd.ttl=d_answers[0].dr.d_ttl;
-    sd.db=0;
+    sd.db = nullptr;
     return true;
   }
 
@@ -454,6 +454,14 @@ bool UeberBackend::getSOAUncached(const DNSName &domain, SOAData &sd)
   return false;
 }
 
+bool UeberBackend::superMasterAdd(const string &ip, const string &nameserver, const string &account) 
+{
+  for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
+    if((*i)->superMasterAdd(ip, nameserver, account)) 
+      return true;
+  return false;
+}
+
 bool UeberBackend::superMasterBackend(const string &ip, const DNSName &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **db)
 {
   for(vector<DNSBackend *>::const_iterator i=backends.begin();i!=backends.end();++i)
@@ -476,7 +484,7 @@ UeberBackend::UeberBackend(const string &pname)
   d_cache_ttl = ::arg().asNum("query-cache-ttl");
   d_negcache_ttl = ::arg().asNum("negquery-cache-ttl");
 
-  d_stale=false;
+  d_stale = false;
 
   backends=BackendMakers().all(pname=="key-only");
 }
index 48db857d9904b85fa37725e07bab6bbb794f5bfa..4a21569ca05aa64ae99684655c4f5fefde531d29 100644 (file)
 #include <map>
 #include <string>
 #include <algorithm>
-#include <semaphore.h>
 #include <mutex>
 #include <condition_variable>
 
-#include <unistd.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
 #include <boost/utility.hpp>
+
 #include "dnspacket.hh"
 #include "dnsbackend.hh"
-
 #include "namespaces.hh"
 
 /** This is a very magic backend that allows us to load modules dynamically,
@@ -53,6 +48,8 @@ public:
 
   bool superMasterBackend(const string &ip, const DNSName &domain, const vector<DNSResourceRecord>&nsset, string *nameserver, string *account, DNSBackend **db);
 
+  bool superMasterAdd(const string &ip, const string &nameserver, const string &account); 
+
   /** Tracks all created UeberBackend instances for us. We use this vector to notify
       existing threads of new modules 
   */
@@ -109,7 +106,7 @@ public:
   void getUnfreshSlaveInfos(vector<DomainInfo>* domains);
   void getUpdatedMasters(vector<DomainInfo>* domains);
   bool getDomainInfo(const DNSName &domain, DomainInfo &di, bool getSerial=true);
-  bool createDomain(const DNSName &domain);
+  bool createDomain(const DNSName &domain, const DomainInfo::DomainKind kind, const vector<ComboAddress> &masters, const string &account);
   
   bool doesDNSSEC();
   bool addDomainKey(const DNSName& name, const DNSBackend::KeyData& key, int64_t& id);
index 58aa6713279e25b4251d31159e0079d6c494ac56..8a0c92fd938522021095adc19dfdcfbf606a8c97 100644 (file)
@@ -11,15 +11,47 @@ uint16_t g_maxNSEC3Iterations{0};
 
 #define LOG(x) if(g_dnssecLOG) { g_log <<Logger::Warning << x; }
 
-const char *dStates[]={"nodata", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
-const char *vStates[]={"Indeterminate", "Bogus", "Insecure", "Secure", "NTA", "TA"};
+static bool isAZoneKey(const DNSKEYRecordContent& key)
+{
+  /* rfc4034 Section 2.1.1:
+     "Bit 7 of the Flags field is the Zone Key flag.  If bit 7 has value 1,
+     then the DNSKEY record holds a DNS zone key, and the DNSKEY RR's
+     owner name MUST be the name of a zone.  If bit 7 has value 0, then
+     the DNSKEY record holds some other type of DNS public key and MUST
+     NOT be used to verify RRSIGs that cover RRsets."
+
+     Let's check that this is a ZONE key, even though there is no other
+     types of DNSKEYs at the moment.
+  */
+  return (key.d_flags & 256) != 0;
+}
+
+static bool isRevokedKey(const DNSKEYRecordContent& key)
+{
+  /* rfc5011 Section 3 */
+  return (key.d_flags & 128) != 0;
+}
 
 static vector<shared_ptr<DNSKEYRecordContent > > getByTag(const skeyset_t& keys, uint16_t tag, uint8_t algorithm)
 {
   vector<shared_ptr<DNSKEYRecordContent>> ret;
-  for(const auto& key : keys)
-    if(key->d_protocol == 3 && key->getTag() == tag && key->d_algorithm == algorithm)
+
+  for (const auto& key : keys) {
+    if (!isAZoneKey(*key)) {
+      LOG("Key for tag "<<std::to_string(tag)<<" and algorithm "<<std::to_string(algorithm)<<" is not a zone key, skipping"<<endl;);
+      continue;
+    }
+
+    if (isRevokedKey(*key)) {
+      LOG("Key for tag "<<std::to_string(tag)<<" and algorithm "<<std::to_string(algorithm)<<" has been revoked, skipping"<<endl;);
+      continue;
+    }
+
+    if (key->d_protocol == 3 && key->getTag() == tag && key->d_algorithm == algorithm) {
       ret.push_back(key);
+    }
+  }
+
   return ret;
 }
 
@@ -334,7 +366,7 @@ static bool provesNSEC3NoWildCard(DNSName wildcard, uint16_t const qtype, const
 /*
   This function checks whether the existence of qname|qtype is denied by the NSEC and NSEC3
   in validrrsets.
-  - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODATA
+  - If `referralToUnsigned` is true and qtype is QType::DS, this functions returns NODENIAL
   if a NSEC or NSEC3 proves that the name exists but no NS type exists, as specified in RFC 5155 section 8.9.
   - If `wantsNoDataProof` is set but a NSEC proves that the whole name does not exist, the function will return
   NXQTYPE is the name is proven to be ENT and NXDOMAIN otherwise.
@@ -374,14 +406,14 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
           LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec->isSet(QType::NS))<<", SOA is "<<std::to_string(nsec->isSet(QType::SOA))<<", signer is "<<signer<<", owner name is "<<owner<<endl);
           /* this is an "ancestor delegation" NSEC RR */
           LOG("An ancestor delegation NSEC RR can only deny the existence of a DS"<<endl);
-          return NODATA;
+          return dState::NODENIAL;
         }
 
         /* check if the type is denied */
         if(qname == owner) {
           if (nsec->isSet(qtype)) {
             LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<endl);
-            return NODATA;
+            return dState::NODENIAL;
           }
 
           LOG("Denies existence of type "<<QType(qtype).getName()<<endl);
@@ -389,7 +421,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
           /* RFC 6840 section 4.3 */
           if (nsec->isSet(QType::CNAME)) {
             LOG("However a CNAME exists"<<endl);
-            return NODATA;
+            return dState::NODENIAL;
           }
 
           /*
@@ -400,7 +432,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
            */
           if (referralToUnsigned && qtype == QType::DS && !nsec->isSet(QType::NS)) {
             LOG("However, no NS record exists at this level!"<<endl);
-            return NODATA;
+            return dState::NODENIAL;
           }
 
           /* we know that the name exists (but this qtype doesn't) so except
@@ -411,44 +443,52 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
           }
 
           if (!needWildcardProof || provesNoWildCard(qname, qtype, validrrsets)) {
-            return NXQTYPE;
+            return dState::NXQTYPE;
           }
 
           LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<endl);
-          return NODATA;
+          return dState::NODENIAL;
         }
 
         /* check if the whole NAME is denied existing */
-        if(isCoveredByNSEC(qname, owner, nsec->d_next)) {
+        if (isCoveredByNSEC(qname, owner, nsec->d_next)) {
           LOG(qname<<" is covered ");
-          /* if the name is an ENT and we received a NODATA answer,
-             we are fine with a NSEC proving that the name does not exist. */
-          if (wantsNoDataProof && nsecProvesENT(qname, owner, nsec->d_next)) {
-            LOG("Denies existence of type "<<qname<<"/"<<QType(qtype).getName()<<" by proving that "<<qname<<" is an ENT"<<endl);
-            return NXQTYPE;
+
+          if (nsecProvesENT(qname, owner, nsec->d_next)) {
+            if (wantsNoDataProof) {
+              /* if the name is an ENT and we received a NODATA answer,
+                 we are fine with a NSEC proving that the name does not exist. */
+              LOG("Denies existence of type "<<qname<<"/"<<QType(qtype).getName()<<" by proving that "<<qname<<" is an ENT"<<endl);
+              return dState::NXQTYPE;
+            }
+            else {
+              /* but for a NXDOMAIN proof, this doesn't make sense! */
+              LOG("but it tries to deny the existence of "<<qname<<" by proving that "<<qname<<" is an ENT, this does not make sense!"<<endl);
+              return dState::NODENIAL;
+            }
           }
 
           if (!needWildcardProof) {
             LOG("and we did not need a wildcard proof"<<endl);
-            return NXDOMAIN;
+            return dState::NXDOMAIN;
           }
 
           LOG("but we do need a wildcard proof so ");
           if (wantsNoDataProof) {
             LOG("looking for NODATA proof"<<endl);
             if (provesNoDataWildCard(qname, qtype, validrrsets)) {
-              return NXQTYPE;
+              return dState::NXQTYPE;
             }
           }
           else {
             LOG("looking for NO wildcard proof"<<endl);
             if (provesNoWildCard(qname, qtype, validrrsets)) {
-              return NXDOMAIN;
+              return dState::NXDOMAIN;
             }
           }
 
           LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<QType(qtype).getName()<<endl);
-          return NODATA;
+          return dState::NODENIAL;
         }
 
         LOG("Did not deny existence of "<<QType(qtype).getName()<<", "<<owner<<"?="<<qname<<", "<<nsec->isSet(qtype)<<", next: "<<nsec->d_next<<endl);
@@ -469,7 +509,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
         string h = getHashFromNSEC3(qname, nsec3);
         if (h.empty()) {
           LOG("Unsupported hash, ignoring"<<endl);
-          return INSECURE;
+          return dState::INSECURE;
         }
 
         nsec3Seen = true;
@@ -488,14 +528,14 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
           LOG("type is "<<QType(qtype).getName()<<", NS is "<<std::to_string(nsec3->isSet(QType::NS))<<", SOA is "<<std::to_string(nsec3->isSet(QType::SOA))<<", signer is "<<signer<<", owner name is "<<v.first.first<<endl);
           /* this is an "ancestor delegation" NSEC3 RR */
           LOG("An ancestor delegation NSEC3 RR can only deny the existence of a DS"<<endl);
-          return NODATA;
+          return dState::NODENIAL;
         }
 
         // If the name exists, check if the qtype is denied
         if(beginHash == h) {
           if (nsec3->isSet(qtype)) {
             LOG("Does _not_ deny existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
-            return NODATA;
+            return dState::NODENIAL;
           }
 
           LOG("Denies existence of type "<<QType(qtype).getName()<<" for name "<<qname<<" (not opt-out)."<<endl);
@@ -503,7 +543,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
           /* RFC 6840 section 4.3 */
           if (nsec3->isSet(QType::CNAME)) {
             LOG("However a CNAME exists"<<endl);
-            return NODATA;
+            return dState::NODENIAL;
           }
 
           /*
@@ -515,10 +555,10 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
            */
           if (referralToUnsigned && qtype == QType::DS && !nsec3->isSet(QType::NS)) {
             LOG("However, no NS record exists at this level!"<<endl);
-            return NODATA;
+            return dState::NODENIAL;
           }
 
-          return NXQTYPE;
+          return dState::NXQTYPE;
         }
       }
     }
@@ -526,7 +566,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
 
   /* if we have no NSEC3 records, we are done */
   if (!nsec3Seen) {
-    return NODATA;
+    return dState::NODENIAL;
   }
 
   DNSName closestEncloser(qname);
@@ -556,7 +596,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
 
             string h = getHashFromNSEC3(closestEncloser, nsec3);
             if (h.empty()) {
-              return INSECURE;
+              return dState::INSECURE;
             }
 
             string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
@@ -617,7 +657,7 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
 
             string h = getHashFromNSEC3(nextCloser, nsec3);
             if (h.empty()) {
-              return INSECURE;
+              return dState::INSECURE;
             }
 
             string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
@@ -650,24 +690,24 @@ dState getDenial(const cspmap_t &validrrsets, const DNSName& qname, const uint16
     if (needWildcardProof && !provesNSEC3NoWildCard(closestEncloser, qtype, validrrsets, &wildcardExists)) {
       if (!isOptOut) {
         LOG("But the existence of a wildcard is not denied for "<<qname<<"/"<<QType(qtype).getName()<<endl);
-        return NODATA;
+        return dState::NODENIAL;
       }
     }
 
     if (isOptOut) {
-      return OPTOUT;
+      return dState::OPTOUT;
     }
     else {
       if (wildcardExists) {
-        return NXQTYPE;
+        return dState::NXQTYPE;
       }
-      return NXDOMAIN;
+      return dState::NXDOMAIN;
     }
   }
 
   // There were no valid NSEC(3) records
   // XXX maybe this should be INSECURE... it depends on the semantics of this function
-  return NODATA;
+  return dState::NODENIAL;
 }
 
 /*
@@ -743,17 +783,17 @@ bool validateWithKeySet(time_t now, const DNSName& name, const sortedRecords_t&
       LOG(name<<": Discarding invalid RRSIG whose label count is "<<signature->d_labels<<" while the RRset owner name has only "<<labelCount<<endl);
     }
 
-    auto r = getByTag(keys, signature->d_tag, signature->d_algorithm);
+    auto keysMatchingTag = getByTag(keys, signature->d_tag, signature->d_algorithm);
 
-    if(r.empty()) {
+    if (keysMatchingTag.empty()) {
       LOG("No key provided for "<<signature->d_tag<<" and algorithm "<<std::to_string(signature->d_algorithm)<<endl;);
       continue;
     }
 
-    string msg=getMessageForRRSET(name, *signature, toSign, true);
-    for(const auto& l : r) {
-      bool signIsValid = checkSignatureWithKey(now, signature, l, msg);
-      if(signIsValid) {
+    string msg = getMessageForRRSET(name, *signature, toSign, true);
+    for(const auto& key : keysMatchingTag) {
+      bool signIsValid = checkSignatureWithKey(now, signature, key, msg);
+      if (signIsValid) {
         isValid = true;
         LOG("Validated "<<name<<"/"<<DNSRecordContent::NumberToType(signature->d_type)<<endl);
         //       cerr<<"valid"<<endl;
@@ -924,7 +964,7 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
   auto luaLocal = g_luaconfs.getLocal();
   const auto anchors = luaLocal->dsAnchors;
   if (anchors.empty()) // Nothing to do here
-    return Insecure;
+    return vState::Insecure;
 
   // Determine the lowest (i.e. with the most labels) Trust Anchor for zone
   DNSName lowestTA(".");
@@ -953,7 +993,7 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
        */
       if(lowestTA.countLabels() <= lowestNTA.countLabels()) {
         LOG("marking answer Insecure"<<endl);
-        return NTA; // Not Insecure, this way validateRecords() can shortcut
+        return vState::NTA; // Not Insecure, this way validateRecords() can shortcut
       }
       LOG("but a Trust Anchor for "<<lowestTA<<" is configured, continuing validation."<<endl);
     }
@@ -1020,14 +1060,14 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
     if(validkeys.empty())
     {
       LOG("ended up with zero valid DNSKEYs, going Bogus"<<endl);
-      return Bogus;
+      return vState::Bogus;
     }
     LOG("situation: we have one or more valid DNSKEYs for ["<<*zoneCutIter<<"] (want ["<<zone<<"])"<<endl);
 
     if(zoneCutIter == zoneCuts.cend()-1) {
       LOG("requested keyset found! returning Secure for the keyset"<<endl);
       keyset.insert(validkeys.cbegin(), validkeys.cend());
-      return Secure;
+      return vState::Secure;
     }
 
     // We now have the DNSKEYs, use them to validate the DS records at the next zonecut
@@ -1050,10 +1090,10 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
     if(r.first == r.second) {
       LOG("No DS for "<<*(zoneCutIter+1)<<", now look for a secure denial"<<endl);
       dState res = getDenial(validrrsets, *(zoneCutIter+1), QType::DS, true, true);
-      if (res == INSECURE || res == NXDOMAIN)
-        return Bogus;
-      if (res == NXQTYPE || res == OPTOUT)
-        return Insecure;
+      if (res == dState::INSECURE || res == dState::NXDOMAIN)
+        return vState::Bogus;
+      if (res == dState::NXQTYPE || res == dState::OPTOUT)
+        return vState::Insecure;
     }
 
     /*
@@ -1070,7 +1110,7 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, skeyset_t& keyset)
     }
   }
   // There were no zone cuts (aka, we should never get here)
-  return Bogus;
+  return vState::Bogus;
 }
 
 bool isSupportedDS(const DSRecordContent& ds)
@@ -1098,3 +1138,22 @@ DNSName getSigner(const std::vector<std::shared_ptr<RRSIGRecordContent> >& signa
 
   return DNSName();
 }
+
+const std::string& vStateToString(vState state)
+{
+  static const std::vector<std::string> vStates = {"Indeterminate", "Bogus", "Insecure", "Secure", "NTA", "TA"};
+  return vStates.at(static_cast<size_t>(state));
+}
+
+std::ostream& operator<<(std::ostream &os, const vState d)
+{
+  os<<vStateToString(d);
+  return os;
+}
+
+std::ostream& operator<<(std::ostream &os, const dState d)
+{
+  static const std::vector<std::string> dStates = {"no denial", "nxdomain", "nxqtype", "empty non-terminal", "insecure", "opt-out"};
+  os<<dStates.at(static_cast<size_t>(d));
+  return os;
+}
index 76e9de3d046ab204d1da8c0bb92cd33c44492b30..60054aa1303e88b70558185c7d5fb63a1bfa7d4a 100644 (file)
@@ -33,12 +33,14 @@ extern time_t g_signatureInceptionSkew;
 extern uint16_t g_maxNSEC3Iterations;
 
 // 4033 5
-enum vState { Indeterminate, Bogus, Insecure, Secure, NTA, TA };
-extern const char *vStates[];
+enum class vState : uint8_t { Indeterminate, Bogus, Insecure, Secure, NTA, TA };
+const std::string& vStateToString(vState state);
 
 // NSEC(3) results
-enum dState { NODATA, NXDOMAIN, NXQTYPE, ENT, INSECURE, OPTOUT};
-extern const char *dStates[];
+enum class dState : uint8_t { NODENIAL, NXDOMAIN, NXQTYPE, ENT, INSECURE, OPTOUT};
+
+std::ostream& operator<<(std::ostream &os, const vState d);
+std::ostream& operator<<(std::ostream &os, const dState d);
 
 class DNSRecordOracle
 {
index de5fa008405e1a019be87da67208bb0fec535081..574798f351ee590f76ef97e37236c0184b08bfaf 100644 (file)
@@ -615,27 +615,54 @@ static void throwUnableToSecure(const DNSName& zonename) {
       + "capable backends are loaded, or because the backends have DNSSEC disabled. Check your configuration.");
 }
 
-static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo& di, const DNSName& zonename, const Json document, bool rectifyTransaction=true) {
-  vector<string> zonemaster;
-  bool shouldRectify = false;
-  for(auto value : document["masters"].array_items()) {
-    string master = value.string_value();
-    if (master.empty())
-      throw ApiException("Master can not be an empty string");
-    try {
-      ComboAddress m(master);
-    } catch (const PDNSException &e) {
-      throw ApiException("Master (" + master + ") is not an IP address: " + e.reason);
+
+static void extractDomainInfoFromDocument(const Json document, boost::optional<DomainInfo::DomainKind>& kind, boost::optional<vector<ComboAddress>>& masters, boost::optional<string>& account) {
+  if (document["kind"].is_string()) {
+    kind = DomainInfo::stringToKind(stringFromJson(document, "kind"));
+  } else {
+    kind = boost::none;
+  }
+
+  if (document["masters"].is_array()) {
+    masters = vector<ComboAddress>();
+    for(auto value : document["masters"].array_items()) {
+      string master = value.string_value();
+      if (master.empty())
+        throw ApiException("Master can not be an empty string");
+      try {
+        masters->emplace_back(master, 53);
+      } catch (const PDNSException &e) {
+        throw ApiException("Master (" + master + ") is not an IP address: " + e.reason);
+      }
     }
-    zonemaster.push_back(master);
+  } else {
+    masters = boost::none;
+  }
+
+  if (document["account"].is_string()) {
+    account = document["account"].string_value();
+  } else {
+    account = boost::none;
   }
+}
 
-  if (zonemaster.size()) {
-    di.backend->setMaster(zonename, boost::join(zonemaster, ","));
+static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo& di, const DNSName& zonename, const Json document, bool rectifyTransaction=true) {
+  boost::optional<DomainInfo::DomainKind> kind;
+  boost::optional<vector<ComboAddress>> masters;
+  boost::optional<string> account;
+
+  extractDomainInfoFromDocument(document, kind, masters, account);
+
+  if (kind) {
+    di.backend->setKind(zonename, *kind);
   }
-  if (document["kind"].is_string()) {
-    di.backend->setKind(zonename, DomainInfo::stringToKind(stringFromJson(document, "kind")));
+  if (masters) {
+    di.backend->setMasters(zonename, *masters);
   }
+  if (account) {
+    di.backend->setAccount(zonename, *account);
+  }
+
   if (document["soa_edit_api"].is_string()) {
     di.backend->setDomainMetadataOne(zonename, "SOA-EDIT-API", document["soa_edit_api"].string_value());
   }
@@ -648,13 +675,13 @@ static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo&
   }
   catch (const JsonException&) {}
 
-  if (document["account"].is_string()) {
-    di.backend->setAccount(zonename, document["account"].string_value());
-  }
 
   DNSSECKeeper dk(&B);
+  bool shouldRectify = false;
   bool dnssecInJSON = false;
   bool dnssecDocVal = false;
+  bool nsec3paramInJSON = false;
+  string nsec3paramDocVal;
 
   try {
     dnssecDocVal = boolFromJson(document, "dnssec");
@@ -662,6 +689,13 @@ static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo&
   }
   catch (const JsonException&) {}
 
+  try {
+    nsec3paramDocVal = stringFromJson(document, "nsec3param");
+    nsec3paramInJSON = true;
+  }
+  catch (const JsonException&) {}
+
+
   bool isDNSSECZone = dk.isSecuredZone(zonename);
 
   if (dnssecInJSON) {
@@ -712,19 +746,30 @@ static void updateDomainSettingsFromDocument(UeberBackend& B, const DomainInfo&
     }
   }
 
-  if(document["nsec3param"].string_value().length() > 0) {
+  if (nsec3paramInJSON) {
     shouldRectify = true;
-    NSEC3PARAMRecordContent ns3pr(document["nsec3param"].string_value());
-    string error_msg = "";
     if (!isDNSSECZone) {
       throw ApiException("NSEC3PARAMs provided for zone '"+zonename.toString()+"', but zone is not DNSSEC secured.");
     }
-    if (!dk.checkNSEC3PARAM(ns3pr, error_msg)) {
-      throw ApiException("NSEC3PARAMs provided for zone '"+zonename.toString()+"' are invalid. " + error_msg);
+
+    if (nsec3paramDocVal.length() == 0) {
+      // Switch to NSEC
+      if (!dk.unsetNSEC3PARAM(zonename)) {
+        throw ApiException("Unable to remove NSEC3PARAMs from zone '" + zonename.toString());
+      }
     }
-    if (!dk.setNSEC3PARAM(zonename, ns3pr, boolFromJson(document, "nsec3narrow", false))) {
-      throw ApiException("NSEC3PARAMs provided for zone '" + zonename.toString() +
-          "' passed our basic sanity checks, but cannot be used with the current backend.");
+
+    if (nsec3paramDocVal.length() > 0) {
+      // Set the NSEC3PARAMs
+      NSEC3PARAMRecordContent ns3pr(nsec3paramDocVal);
+      string error_msg = "";
+      if (!dk.checkNSEC3PARAM(ns3pr, error_msg)) {
+        throw ApiException("NSEC3PARAMs provided for zone '"+zonename.toString()+"' are invalid. " + error_msg);
+      }
+      if (!dk.setNSEC3PARAM(zonename, ns3pr, boolFromJson(document, "nsec3narrow", false))) {
+        throw ApiException("NSEC3PARAMs provided for zone '" + zonename.toString() +
+            "' passed our basic sanity checks, but cannot be used with the current backend.");
+      }
     }
   }
 
@@ -1650,8 +1695,13 @@ static void apiServerZones(HttpRequest* req, HttpResponse* resp) {
       }
     }
 
+    boost::optional<DomainInfo::DomainKind> kind;
+    boost::optional<vector<ComboAddress>> masters;
+    boost::optional<string> account;
+    extractDomainInfoFromDocument(document, kind, masters, account);
+
     // no going back after this
-    if(!B.createDomain(zonename))
+    if(!B.createDomain(zonename, kind.get_value_or(DomainInfo::Native), masters.get_value_or(vector<ComboAddress>()), account.get_value_or("")))
       throw ApiException("Creating domain '"+zonename.toString()+"' failed");
 
     if(!B.getDomainInfo(zonename, di))
@@ -1860,11 +1910,8 @@ static void apiServerZoneRectify(HttpRequest* req, HttpResponse* resp) {
 
   DNSSECKeeper dk(&B);
 
-  if (!dk.isSecuredZone(zonename))
-    throw ApiException("Zone '" + zonename.toString() + "' is not DNSSEC signed, not rectifying.");
-
-  if (di.kind == DomainInfo::Slave)
-    throw ApiException("Zone '" + zonename.toString() + "' is a slave zone, not rectifying.");
+  if (dk.isPresigned(zonename))
+    throw ApiException("Zone '" + zonename.toString() + "' is pre-signed, not rectifying.");
 
   string error_msg = "";
   string info;
@@ -1970,7 +2017,7 @@ static void patchZone(UeberBackend& B, HttpRequest* req, HttpResponse* resp) {
     di.backend->getDomainMetadataOne(zonename, "SOA-EDIT", soa_edit_kind);
     bool soa_edit_done = false;
 
-    set<pair<DNSName, QType>> seen;
+    set<tuple<DNSName, QType, string>> seen;
 
     for (const auto& rrset : rrsets.array_items()) {
       string changetype = toUpper(stringFromJson(rrset, "changetype"));
@@ -1982,11 +2029,11 @@ static void patchZone(UeberBackend& B, HttpRequest* req, HttpResponse* resp) {
         throw ApiException("RRset "+qname.toString()+" IN "+stringFromJson(rrset, "type")+": unknown type given");
       }
 
-      if(seen.count({qname, qtype}))
+      if(seen.count({qname, qtype, changetype}))
       {
-        throw ApiException("Duplicate RRset "+qname.toString()+" IN "+qtype.getName());
+        throw ApiException("Duplicate RRset "+qname.toString()+" IN "+qtype.getName()+" with changetype: "+changetype);
       }
-      seen.insert({qname, qtype});
+      seen.insert({qname, qtype, changetype});
 
       if (changetype == "DELETE") {
         // delete all matching qname/qtype RRs (and, implicitly comments).
@@ -2253,6 +2300,39 @@ static void apiServerCacheFlush(HttpRequest* req, HttpResponse* resp) {
   });
 }
 
+static std::ostream& operator<<(std::ostream& os, StatType statType)
+{
+  switch (statType)
+  {
+    case StatType::counter: return os << "counter";
+    case StatType::gauge: return os << "gauge";
+  };
+  return os << static_cast<uint16_t>(statType);
+}
+
+static void prometheusMetrics(HttpRequest* req, HttpResponse* resp) {
+  if (req->method != "GET")
+    throw HttpMethodNotAllowedException();
+
+  std::ostringstream output;
+  for (const auto &metricName : S.getEntries()) {
+    // Prometheus suggest using '_' instead of '-'
+    std::string prometheusMetricName = "pdns_auth_" + boost::replace_all_copy(metricName, "-", "_");
+
+    output << "# HELP " << prometheusMetricName << " " << S.getDescrip(metricName) << "\n";
+    output << "# TYPE " << prometheusMetricName << " " << S.getStatType(metricName) << "\n";
+    output << prometheusMetricName << " " << S.read(metricName) << "\n";
+  }
+
+  output << "# HELP pdns_auth_info " << "Info from PowerDNS, value is always 1" << "\n";
+  output << "# TYPE pdns_auth_info " << "gauge" << "\n";
+  output << "pdns_auth_info{version=\"" << VERSION << "\"} " << "1" << "\n";
+
+  resp->body = output.str();
+  resp->headers["Content-Type"] = "text/plain";
+  resp->status = 200;
+}
+
 void AuthWebServer::cssfunction(HttpRequest* req, HttpResponse* resp)
 {
   resp->headers["Cache-Control"] = "max-age=86400";
@@ -2317,6 +2397,7 @@ void AuthWebServer::webThread()
     if (::arg().mustDo("webserver")) {
       d_ws->registerWebHandler("/style.css", std::bind(&AuthWebServer::cssfunction, this, std::placeholders::_1, std::placeholders::_2));
       d_ws->registerWebHandler("/", std::bind(&AuthWebServer::indexfunction, this, std::placeholders::_1, std::placeholders::_2));
+      d_ws->registerWebHandler("/metrics", prometheusMetrics);
     }
     d_ws->go();
   }
index 79f8f2f6b6fd38af83a687ced2ff21aa43b2a710..2cd127345fadae064a349f2585dc6c61669f5c70 100644 (file)
@@ -21,6 +21,7 @@
  */
 #pragma once
 #include <string>
+#include <tuple>
 #include <map>
 #include <time.h>
 #include <pthread.h>
index 7ed832bd17e4f7eb0bb3339b64a9ae898cab981b..83e3e43af3b7f47bb97745b969e0999863ab56d5 100644 (file)
@@ -458,6 +458,10 @@ static void prometheusMetrics(HttpRequest *req, HttpResponse *resp) {
         output << prometheusMetricName << " " << tup.second << "\n";
     }
 
+    output << "# HELP pdns_recursor_info " << "Info from pdns_recursor, value is always 1" << "\n";
+    output << "# TYPE pdns_recursor_info " << "gauge" << "\n";
+    output << "pdns_recursor_info{version=\"" << VERSION << "\"} " << "1" << "\n";
+
     resp->body = output.str();
     resp->headers["Content-Type"] = "text/plain";
     resp->status = 200;
@@ -674,6 +678,10 @@ void AsyncServer::newConnection()
 
 // This is an entry point from FDM, so it needs to catch everything.
 void AsyncWebServer::serveConnection(std::shared_ptr<Socket> client) const {
+  if (!client->acl(d_acl)) {
+    return;
+  }
+
   const string logprefix = d_logprefix + to_string(getUniqueID()) + " ";
 
   HttpRequest req(logprefix);
index 897316b647274f0df6f8f399654f314409cb8651..f09177222d57d8e37643f00fbf5c9dc5b70bb832 100644 (file)
@@ -30,7 +30,10 @@ class HttpResponse;
 
 class AsyncServer : public Server {
 public:
-  AsyncServer(const string &localaddress, int port) : Server(localaddress, port) { };
+  AsyncServer(const string &localaddress, int port) : Server(localaddress, port)
+  {
+    d_server_socket.setNonBlocking();
+  };
 
   friend void AsyncServerNewConnectionMT(void *p);
 
index ca372f124a5c2026d613988e9d84ae2264628930..e2069d8efa43e8317818acd018f58e57a587ff89 100644 (file)
@@ -539,6 +539,21 @@ class AuthZones(ApiTestCase, AuthZonesHelperMixin):
         self.assertEquals(data['kind'], 'NSEC3NARROW')
         self.assertEquals(data['metadata'][0], '1')
 
+    def test_create_zone_with_nsec3param_switch_to_nsec(self):
+        """
+        Create a zone with "nsec3param", then remove the params
+        """
+        name, payload, data = self.create_zone(dnssec=True,
+                                               nsec3param='1 0 1 ab')
+        self.session.put(self.url("/api/v1/servers/localhost/zones/" + name),
+                         data=json.dumps({'nsec3param': ''}))
+        r = self.session.get(
+            self.url("/api/v1/servers/localhost/zones/" + name))
+        data = r.json()
+
+        self.assertEquals(r.status_code, 200)
+        self.assertEquals(data['nsec3param'], '')
+
     def test_create_zone_dnssec_serial(self):
         """
         Create a zone set/unset "dnssec" and see if the serial was increased
@@ -1184,6 +1199,35 @@ $ORIGIN %NAME%
         data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json()
         self.assertIsNone(get_rrset(data, name, 'NS'))
 
+    def test_zone_rr_update_rrset_combine_replace_and_delete(self):
+        name, payload, zone = self.create_zone()
+        rrset1 = {
+            'changetype': 'delete',
+            'name': 'sub.' + name,
+            'type': 'CNAME',
+        }
+        rrset2 = {
+            'changetype': 'replace',
+            'name': 'sub.' + name,
+            'type': 'CNAME',
+            'ttl': 500,
+            'records': [
+                {
+                    "content": "www.example.org.",
+                    "disabled": False
+                }
+            ]
+        }
+        payload = {'rrsets': [rrset1, rrset2]}
+        r = self.session.patch(
+            self.url("/api/v1/servers/localhost/zones/" + name),
+            data=json.dumps(payload),
+            headers={'content-type': 'application/json'})
+        self.assert_success(r)
+        # verify that (only) the new record is there
+        data = self.session.get(self.url("/api/v1/servers/localhost/zones/" + name)).json()
+        self.assertEquals(get_rrset(data, 'sub.' + name, 'CNAME')['records'], rrset2['records'])
+
     def test_zone_disable_reenable(self):
         # This also tests that SOA-EDIT-API works.
         name, payload, zone = self.create_zone(soa_edit_api='EPOCH')
@@ -1916,6 +1960,27 @@ $ORIGIN %NAME%
         dbrecs = get_db_records(name, 'AAAA')
         self.assertIsNone(dbrecs[0]['ordername'])
 
+    def test_explicit_rectify_success(self):
+        name, _, data = self.create_zone = self.create_zone(api_rectify=False, dnssec=True, nsec3param='1 0 1 ab')
+        dbrecs = get_db_records(name, 'SOA')
+        self.assertIsNone(dbrecs[0]['ordername'])
+        r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/rectify"))
+        self.assertEquals(r.status_code, 200)
+        dbrecs = get_db_records(name, 'SOA')
+        self.assertIsNotNone(dbrecs[0]['ordername'])
+
+    def test_explicit_rectify_slave(self):
+        # Some users want to move a zone to kind=Slave and then rectify, without a re-transfer.
+        name, _, data = self.create_zone = self.create_zone(api_rectify=False, dnssec=True, nsec3param='1 0 1 ab')
+        r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id']),
+            data=json.dumps({'kind': 'Slave'}),
+            headers={'content-type': 'application/json'})
+        self.assertEquals(r.status_code, 204)
+        r = self.session.put(self.url("/api/v1/servers/localhost/zones/" + data['id'] + "/rectify"))
+        self.assertEquals(r.status_code, 200)
+        dbrecs = get_db_records(name, 'SOA')
+        self.assertIsNotNone(dbrecs[0]['ordername'])
+
     def test_cname_at_ent_place(self):
         name, payload, zone = self.create_zone(dnssec=True, api_rectify=True)
         rrset = {
index 39ab51acf90d1afaa1cb366b46d469cdc2425577..d0ba5ed039117b2a348ceac8d1284e4d308b2ccc 100644 (file)
@@ -117,6 +117,8 @@ any              IN           TXT "hello there"
 
 resolve          IN    LUA    A   ";local r=resolve('localhost', 1) local t={{}} for _,v in ipairs(r) do table.insert(t, v:toString()) end return t"
 
+*.createforward  IN    LUA    A   "createForward()"
+
         """,
     }
     _web_rrsets = []
@@ -611,6 +613,41 @@ resolve          IN    LUA    A   ";local r=resolve('localhost', 1) local t={{}}
         self.assertRcodeEqual(res, dns.rcode.NOERROR)
         self.assertEqual(res.answer, response.answer)
 
+    def testCreateForward(self):
+        name_suffix = '.createforward.example.org.'
+
+        name_expected = {
+            "1.2.3.4": "1.2.3.4",
+            "1.2.3.4.static": "1.2.3.4",
+            "1.2.3.4.5.6": "1.2.3.4",
+            "invalid.1.2.3.4": "0.0.0.0",
+            "invalid": "0.0.0.0",
+            "1-2-3-4": "1.2.3.4",
+            "1-2-3-4.foo": "1.2.3.4",
+            "1-2-3-4.foo.bar": "0.0.0.0",
+            "1-2-3-4.foo.bar.baz": "0.0.0.0",
+            "1-2-3-4.foo.bar.baz.quux": "0.0.0.0",
+            "ip-1-2-3-4": "1.2.3.4",
+            "ip-is-here-for-you-1-2-3-4": "1.2.3.4",
+            "ip40414243": "64.65.66.67",
+            "ipp40414243": "0.0.0.0",
+            "ip4041424": "0.0.0.0",
+        }
+
+        for k, v in name_expected.items():
+            name = k + name_suffix
+
+            query = dns.message.make_query(name, 'A')
+            response = dns.message.make_response(query)
+            response.answer.append(dns.rrset.from_text(
+                name, 0, dns.rdataclass.IN, dns.rdatatype.A, v))
+
+            res = self.sendUDPQuery(query)
+            print(res)
+            self.assertRcodeEqual(res, dns.rcode.NOERROR)
+            self.assertEqual(res.answer, response.answer)
+
+
 if __name__ == '__main__':
     unittest.main()
     exit(0)
index 8765ea58445658ea0ac95288b08f19d6933c98f8..773d5732ffa486e62c757f2e8116d8642fd1fb48 100644 (file)
@@ -58,6 +58,7 @@ class DNSDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
     _healthCheckCounter = 0
     _answerUnexpected = True
     _checkConfigExpectedOutput = None
+    _verboseMode = False
 
     @classmethod
     def startResponders(cls):
@@ -82,6 +83,9 @@ class DNSDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
 
         dnsdistcmd = [os.environ['DNSDISTBIN'], '--supervised', '-C', confFile,
                       '-l', '%s:%d' % (cls._dnsDistListeningAddr, cls._dnsDistPort) ]
+        if cls._verboseMode:
+            dnsdistcmd.append('-v')
+
         for acl in cls._acl:
             dnsdistcmd.extend(['--acl', acl])
         print(' '.join(dnsdistcmd))
@@ -96,7 +100,7 @@ class DNSDistTest(AssertEqualDNSMessageMixin, unittest.TestCase):
           expectedOutput = cls._checkConfigExpectedOutput
         else:
           expectedOutput = ('Configuration \'%s\' OK!\n' % (confFile)).encode()
-        if output != expectedOutput:
+        if not cls._verboseMode and output != expectedOutput:
             raise AssertionError('dnsdist --check-config failed: %s' % output)
 
         logFile = os.path.join('configs', 'dnsdist_%s.log' % (cls.__name__))
index afb54015105409ec5fbf67d28bd52305274f1378..b894323cb4cd59d71272ae91beb0278a455aceb9 100644 (file)
@@ -236,7 +236,8 @@ class TestAPIBasics(DNSDistTest):
                     'noncompliant-responses', 'rdqueries', 'empty-queries', 'cache-hits',
                     'cache-misses', 'cpu-iowait', 'cpu-steal', 'cpu-sys-msec', 'cpu-user-msec', 'fd-usage', 'dyn-blocked',
                     'dyn-block-nmg-size', 'rule-servfail', 'security-status',
-                    'udp-in-errors', 'udp-noport-errors', 'udp-recvbuf-errors', 'udp-sndbuf-errors']
+                    'udp-in-errors', 'udp-noport-errors', 'udp-recvbuf-errors', 'udp-sndbuf-errors',
+                    'doh-query-pipe-full', 'doh-response-pipe-full']
 
         for key in expected:
             self.assertIn(key, values)
@@ -516,3 +517,39 @@ class TestAPIAuth(DNSDistTest):
 
         r = requests.get(url, headers=headers, timeout=self._webTimeout)
         self.assertEquals(r.status_code, 401)
+
+class TestAPIACL(DNSDistTest):
+
+    _webTimeout = 2.0
+    _webServerPort = 8083
+    _webServerBasicAuthPassword = 'secret'
+    _webServerAPIKey = 'apisecret'
+    _consoleKey = DNSDistTest.generateConsoleKey()
+    _consoleKeyB64 = base64.b64encode(_consoleKey).decode('ascii')
+    _config_params = ['_consoleKeyB64', '_consolePort', '_testServerPort', '_webServerPort', '_webServerBasicAuthPassword', '_webServerAPIKey']
+    _config_template = """
+    setKey("%s")
+    controlSocket("127.0.0.1:%s")
+    setACL({"127.0.0.1/32", "::1/128"})
+    newServer{address="127.0.0.1:%s"}
+    webserver("127.0.0.1:%s", "%s", "%s", {}, "192.0.2.1")
+    """
+
+    def testACLChange(self):
+        """
+        API: Should be denied by ACL then allowed
+        """
+
+        url = 'http://127.0.0.1:' + str(self._webServerPort) + "/"
+        try:
+            r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+            self.assertTrue(False)
+        except requests.exceptions.ConnectionError as exp:
+            pass
+
+        # reset the ACL
+        self.sendConsoleCommand('setWebserverConfig({acl="127.0.0.1"})')
+
+        r = requests.get(url, auth=('whatever', self._webServerBasicAuthPassword), timeout=self._webTimeout)
+        self.assertTrue(r)
+        self.assertEquals(r.status_code, 200)
index ec20c929f5966591f857c55e9e0d9a2e52730f37..17bc9a5fb1094ee4b829ecb3c895b6bac00ca2d5 100644 (file)
@@ -28,6 +28,7 @@ class TestTCPLimits(DNSDistTest):
     setMaxTCPConnectionDuration(%s)
     """
     _config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxTCPQueriesPerConn', '_maxTCPConnsPerClient', '_maxTCPConnDuration']
+    _verboseMode = True
 
     def testTCPQueriesPerConn(self):
         """
@@ -91,6 +92,11 @@ class TestTCPLimits(DNSDistTest):
         for conn in conns:
             conn.close()
 
+        # wait a bit to be sure that dnsdist closed the connections
+        # and decremented the counters on its side, otherwise subsequent
+        # connections will be dropped
+        time.sleep(1)
+
         self.assertEqual(count, self._maxTCPConnsPerClient)
         self.assertEqual(failed, 1)
 
index 917a0bc9293df287defa39d215a65ff149536bd0..bb6d17232750f3e4a9e4f2cadabafe45b27d0b15 100644 (file)
@@ -10,7 +10,7 @@ dnsupdate-queries=0
 dnsupdate-refused=0
 incoming-notifications=0
 key-cache-size=0
-meta-cache-size=3
+meta-cache-size=1
 open-tcp-connections=0
 overload-drops=0
 packetcache-size=4
index 83322a86a7cf09415576d39ff6639afa5f3ba141..e321d22fd982078a04cfc7fb81b10a59e8ca6280 100644 (file)
@@ -15,6 +15,21 @@ import dns.message
 
 from eqdnsmessage import AssertEqualDNSMessageMixin
 
+
+def have_ipv6():
+    """
+    Try to make an IPv6 socket and bind it, if it fails, no ipv6...
+    """
+    try:
+        sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+        sock.bind(('::1', 56581))
+        sock.close()
+        return True
+    except:
+        return False
+    return False
+
+
 class RecursorTest(AssertEqualDNSMessageMixin, unittest.TestCase):
     """
     Setup all recursors and auths required for the tests
@@ -50,6 +65,7 @@ log-common-errors=yes
     _roothints = """
 .                        3600 IN NS  ns.root.
 ns.root.                 3600 IN A   %s.8
+ns.root.                 3600 IN AAAA ::1
 """ % _PREFIX
     _root_DS = "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a"
 
@@ -498,7 +514,10 @@ distributor-threads={threads}""".format(confdir=confdir,
         authcmd = list(cls._auth_cmd)
         authcmd.append('--config-dir=%s' % confdir)
         authcmd.append('--local-address=%s' % ipaddress)
-        authcmd.append('--local-ipv6=')
+        if (confdir[-4:] == "ROOT") and have_ipv6():
+            authcmd.append('--local-ipv6=::1')
+        else:
+            authcmd.append('--local-ipv6=')
         print(' '.join(authcmd))
 
         logFile = os.path.join(confdir, 'pdns.log')
diff --git a/regression-tests.recursor-dnssec/test_API.py b/regression-tests.recursor-dnssec/test_API.py
new file mode 100644 (file)
index 0000000..ec275dd
--- /dev/null
@@ -0,0 +1,72 @@
+import os
+import requests
+
+from recursortests import RecursorTest
+
+class APIRecursorTest(RecursorTest):
+
+    @classmethod
+    def setUpClass(cls):
+
+        # we don't need all the auth stuff
+        cls.setUpSockets()
+        cls.startResponders()
+
+        confdir = os.path.join('configs', cls._confdir)
+        cls.createConfigDir(confdir)
+
+        cls.generateRecursorConfig(confdir)
+        cls.startRecursor(confdir, cls._recursorPort)
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.tearDownRecursor()
+
+class APIAllowedRecursorTest(APIRecursorTest):
+    _confdir = 'API'
+    _wsPort = 8042
+    _wsTimeout = 2
+    _wsPassword = 'secretpassword'
+    _apiKey = 'secretapikey'
+
+    _config_template = """
+webserver=yes
+webserver-port=%d
+webserver-address=127.0.0.1
+webserver-password=%s
+webserver-allow-from=127.0.0.1
+api-key=%s
+""" % (_wsPort, _wsPassword, _apiKey)
+
+    def testAPI(self):
+        headers = {'x-api-key': self._apiKey}
+        url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+        r = requests.get(url, headers=headers, timeout=self._wsTimeout)
+        self.assertTrue(r)
+        self.assertEquals(r.status_code, 200)
+        self.assertTrue(r.json())
+
+class APIDeniedRecursorTest(APIRecursorTest):
+    _confdir = 'API'
+    _wsPort = 8042
+    _wsTimeout = 2
+    _wsPassword = 'secretpassword'
+    _apiKey = 'secretapikey'
+
+    _config_template = """
+webserver=yes
+webserver-port=%d
+webserver-address=127.0.0.1
+webserver-password=%s
+webserver-allow-from=192.0.2.1
+api-key=%s
+""" % (_wsPort, _wsPassword, _apiKey)
+
+    def testAPI(self):
+        headers = {'x-api-key': self._apiKey}
+        url = 'http://127.0.0.1:' + str(self._wsPort) + '/api/v1/servers/localhost/statistics'
+        try:
+            r = requests.get(url, headers=headers, timeout=self._wsTimeout)
+            self.assertTrue(False)
+        except requests.exceptions.ConnectionError as exp:
+            pass
index 26038091e8e18cb70e872ae4458a585266a75412..d1b80505fa6f409d621ac3ef60c33fc7e1c7db51 100644 (file)
@@ -5,7 +5,8 @@ import struct
 import threading
 import time
 import clientsubnetoption
-from recursortests import RecursorTest
+import unittest
+from recursortests import RecursorTest, have_ipv6
 from twisted.internet.protocol import DatagramProtocol
 from twisted.internet import reactor
 
@@ -14,6 +15,7 @@ nameECS = 'ecs-echo.example.'
 nameECSInvalidScope = 'invalid-scope.ecs-echo.example.'
 ttlECS = 60
 ecsReactorRunning = False
+ecsReactorv6Running = False
 
 class ECSTest(RecursorTest):
     _config_template_default = """
@@ -62,6 +64,7 @@ disable-syslog=yes
     @classmethod
     def startResponders(cls):
         global ecsReactorRunning
+        global ecsReactorv6Running
         print("Launching responders..")
 
         address = cls._PREFIX + '.21'
@@ -71,6 +74,10 @@ disable-syslog=yes
             reactor.listenUDP(port, UDPECSResponder(), interface=address)
             ecsReactorRunning = True
 
+        if not ecsReactorv6Running and have_ipv6():
+            reactor.listenUDP(53000, UDPECSResponder(), interface='::1')
+            ecsReactorv6Running = True
+
         if not reactor.running:
             cls._UDPResponder = threading.Thread(name='UDP Responder', target=reactor.run, args=(False,))
             cls._UDPResponder.setDaemon(True)
@@ -341,6 +348,7 @@ ecs-ipv6-cache-bits=128
         query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
         self.sendECSQuery(query, expected, ttlECS)
 
+@unittest.skipIf(not have_ipv6(), "No IPv6")
 class testIncomingECSByNameV6(ECSTest):
     _confdir = 'ECSIncomingByNameV6'
 
@@ -349,9 +357,9 @@ use-incoming-edns-subnet=yes
 ecs-ipv6-bits=128
 ecs-ipv4-cache-bits=32
 ecs-ipv6-cache-bits=128
-forward-zones=ecs-echo.example=%s.21
-query-local-address6=::1
-    """ % (os.environ['PREFIX'])
+query-local-address=::1
+forward-zones=ecs-echo.example=[::1]:53000
+    """
 
     def testSendECS(self):
         expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '2001:db8::1/128')
@@ -367,8 +375,7 @@ query-local-address6=::1
         self.sendECSQuery(query, expected, ttlECS)
 
     def testRequireNoECS(self):
-        # we should get ::1/128 because neither ecs-scope-zero-addr nor query-local-address are set,
-        # but query-local-address6 is set to ::1
+        # we should get ::1/128 because ecs-scope-zero-addr is unset and query-local-address is set to ::1
         expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', "::1/128")
 
         ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
index a305184c5e68fb7c8d3a1644e66cad4589ba843e..593db437689b7d085f8ae8970c371e1c690a85a0 100644 (file)
@@ -28,7 +28,7 @@ class RPZServer(object):
             return False
 
         if newSerial != self._currentSerial + 1:
-            raise AssertionError("Asking the RPZ server to server serial %d, already serving %d" % (newSerial, self._currentSerial))
+            raise AssertionError("Asking the RPZ server to serve serial %d, already serving %d" % (newSerial, self._currentSerial))
         self._targetSerial = newSerial
         return True
 
@@ -52,7 +52,8 @@ class RPZServer(object):
         elif message.question[0].rdtype == dns.rdatatype.IXFR:
             oldSerial = message.authority[0][0].serial
 
-            if oldSerial != self._currentSerial:
+            # special case for the 9th update, which might get skipped
+            if oldSerial != self._currentSerial and self._currentSerial != 9:
                 print('Received an IXFR query with an unexpected serial %d, expected %d' % (oldSerial, self._currentSerial))
                 return (None, self._currentSerial)
 
@@ -124,6 +125,38 @@ class RPZServer(object):
                     dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
                     dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
                     ]
+            elif newSerial == 9:
+                # IXFR inserting a duplicate, we should not crash and skip it
+                records = [
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+                    dns.rrset.from_text('dup.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-passthru.'),
+                    dns.rrset.from_text('dup.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-passthru.'),
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
+                    ]
+            elif newSerial == 10:
+                # full AXFR to make sure we are removing the duplicate, adding a record, to check that the update was correctly applied
+                records = [
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+                    dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
+                    ]
+            elif newSerial == 11:
+                # IXFR with two deltas, the first one adding a 'g' and the second one removing 'f'
+                records = [
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1)),
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+                    dns.rrset.from_text('g.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+                    dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1)),
+                    dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1))
+                    ]
+                # this one has two updates in one
+                newSerial = newSerial + 1
+                self._targetSerial = self._targetSerial + 1
 
         response.answer = records
         return (newSerial, response)
@@ -457,6 +490,36 @@ e 3600 IN A 192.0.2.42
         self.checkNXD('tc.example.')
         self.checkNXD('drop.example.')
 
+        # 9th zone is a duplicate, it might get skipped
+        global rpzServer
+        rpzServer.moveToSerial(9)
+        time.sleep(3)
+        self.waitUntilCorrectSerialIsLoaded(10)
+        self.checkRPZStats(10, 1, 4, self._xfrDone)
+        self.checkNotBlocked('a.example.')
+        self.checkNotBlocked('b.example.')
+        self.checkNotBlocked('c.example.')
+        self.checkNotBlocked('d.example.')
+        self.checkNotBlocked('e.example.')
+        self.checkBlocked('f.example.')
+        self.checkNXD('tc.example.')
+        self.checkNXD('drop.example.')
+
+        # the next update will update the zone twice
+        rpzServer.moveToSerial(11)
+        time.sleep(3)
+        self.waitUntilCorrectSerialIsLoaded(12)
+        self.checkRPZStats(12, 1, 4, self._xfrDone)
+        self.checkNotBlocked('a.example.')
+        self.checkNotBlocked('b.example.')
+        self.checkNotBlocked('c.example.')
+        self.checkNotBlocked('d.example.')
+        self.checkNotBlocked('e.example.')
+        self.checkNXD('f.example.')
+        self.checkBlocked('g.example.')
+        self.checkNXD('tc.example.')
+        self.checkNXD('drop.example.')
+
 class RPZFileRecursorTest(RPZRecursorTest):
     """
     This test makes sure that we correctly load RPZ zones from a file
index 76db3bc0015727300aa46251ec2a0fb32bc24c5a..cd02160e628366339a1bd950192e8009e82a9c7c 100644 (file)
@@ -132,7 +132,7 @@ end
         query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
         self.sendECSQuery(query, expected1)
 
-        # Now check a cache hit withg the same routingTag (but no ECS)
+        # Now check a cache hit with the same routingTag (but no ECS)
         query = dns.message.make_query(nameECS, 'TXT', 'IN')
         self.checkECSQueryHit(query, expected1)
 
@@ -142,7 +142,7 @@ end
         query = dns.message.make_query(nameECS, 'TXT', 'IN')
         self.sendECSQuery(query, expected2)
 
-        # And see if a *no* tag does *not* hit the firts one
+        # And see if a *no* tag does *not* hit the first one
         expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.3.0/24')
         self.setRoutingTag(None)
         ecso = clientsubnetoption.ClientSubnetOption('192.0.3.1', 32)
@@ -170,7 +170,7 @@ end
                           '--config-dir=%s' % 'configs/' + self._confdir,
                           'dump-cache x']
         try:
-            expected = 'dumped 6 records\n'
+            expected = 'dumped 7 records\n'
             ret = subprocess.check_output(rec_controlCmd, stderr=subprocess.STDOUT)
             self.assertEqual(ret, expected)
 
@@ -214,7 +214,7 @@ end
         query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
         self.sendECSQuery(query, expected1)
 
-        # Now check a cache hit withg the same routingTag (but no ECS)
+        # Now check a cache hit with the same routingTag (but no ECS)
         query = dns.message.make_query(nameECS, 'TXT', 'IN')
         self.checkECSQueryHit(query, expected1)
 
@@ -224,7 +224,7 @@ end
         query = dns.message.make_query(nameECS, 'TXT', 'IN')
         self.sendECSQuery(query, expected2)
 
-        # And see if a *no* tag does *not* hit the firts one
+        # And see if a *no* tag does *not* hit the first one
         expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.3.0/24')
         self.setRoutingTag(None)
         ecso = clientsubnetoption.ClientSubnetOption('192.0.3.1', 32)
index 3ce84f19da619c3385372b1e66ce22cb137ba127..ec9757c21eb96960fcd15de2db6c123cdeaae514 100644 (file)
@@ -52,7 +52,7 @@ __EOF__
        while [ $loopcount -lt 30 ]
        do
                sleep 5
-               todo=$(mysql --user="$GMYSQL2USER" --password="$GMYSQL2PASSWD" --host="$GMYSQl2HOST" \
+               todo=$(mysql --user="$GMYSQL2USER" --password="$GMYSQL2PASSWD" --host="$GMYSQL2HOST" \
                        "$GMYSQL2DB" -ss -e 'SELECT COUNT(id) FROM domains WHERE last_check IS NULL')
                if [ $todo = 0 ]
                then
index 4d38c9fc673636576bffad1170808f431f82143d..280d578a0258fb011a19d056a1b97ff6b457fe68 100755 (executable)
@@ -4,9 +4,9 @@ port=$1
 limit=$2
 [ -z "$limit" ] && limit=100000
 threads=$3
-[ -z "$threads" ] && threads=2
+[ -z "$threads" ] && threads=8
 mthreads=$4
-[ -z "$mthreads" ] && mthreads=100
+[ -z "$mthreads" ] && mthreads=2048
 shards=$5
 [ -z "$shards" ] && shards=1024
 
@@ -19,7 +19,7 @@ shards=$5
 
 if [ $IPv6 = 1 ]
 then
-       QLA6="::"
+       QLA6=" ::"
 else
        QLA6=""
 fi
@@ -31,7 +31,7 @@ rm -f recursor.pid pdns_recursor.pid
 <measurement><name>system CPU seconds</name><value>%S</value></measurement>
 <measurement><name>wallclock seconds</name><value>%e</value></measurement>
 <measurement><name>%% CPU used</name><value>%P</value></measurement>
-'         ${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --max-mthreads=$mthreads --query-local-address6="${QLA6}" --threads=$threads --cache-shards=$shards --disable-packetcache > recursor.log 2>&1 &
+'         ${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --max-mthreads=$mthreads --query-local-address="0.0.0.0${QLA6}" --threads=$threads --record-cache-shards=$shards --disable-packetcache > recursor.log 2>&1 &
 sleep 3
 
 # warm up the cache
index 5cdda716803695243e04e30a1f513e053db8b07c..47ec529df0716cfd188012716dca0d983fdc4c36 100644 (file)
@@ -12,13 +12,13 @@ limit=$2
 
 if [ $IPv6 = 1 ]
 then
-       QLA6="::"
+       QLA6=" ::"
 else
        QLA6=""
 fi
 
 rm -f recursor.pid pdns_recursor.pid
-${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --local-address=0.0.0.0 --allow-from=0.0.0.0/0 --query-local-address6="${QLA6}" > recursor.log 2>&1 &
+${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --local-address=0.0.0.0 --allow-from=0.0.0.0/0 --query-local-address="0.0.0.0${QLA6}" > recursor.log 2>&1 &
 sleep 3
 ./dnsbulktest -qe 37.252.127.190 $port $limit < ${CSV} > bulktest.results
 kill $(cat pdns_recursor.pid)
index 96044e25f09a1f749f4ace22d54a6d98916d21d7..f5f2fb5a54d50358aae520ef63b2aad3ab359607 100755 (executable)
@@ -129,22 +129,24 @@ securezone ()
        else
                # check if PKCS#11 should be used
                if [ "$pkcs11" -eq 1 ]; then
-                  if [ "$slot" == "" ]; then
-                    slot=0
-                  else
-                    slot=$((slot+1))
-                  fi
-                  sudo softhsm --init-token --slot $slot --label label$slot --pin 123$slot --so-pin 123$slot
-                  kid=`$PDNSUTIL --config-dir=. $configname hsm assign $zone ecdsa256 zsk softhsm label$slot 123$slot label$slot 2>&1 | grep softhsm | awk '{ print $NF }'`
-                  kid=`$PDNSUTIL --config-dir=. $configname show-zone $zone | grep 'ID =.*ZSK' | awk '{ print $3 }'`
-                  $PDNSUTIL --config-dir=. $configname hsm create-key $zone $kid
-                else
-                  $PDNSUTIL --config-dir=. $configname secure-zone $zone 2>&1
-                  if [ "${zone: 0:20}" = "cdnskey-cds-test.com" ]; then
-                    $PDNSUTIL --config-dir=. $configname set-publish-cds $zone 2>&1
-                    $PDNSUTIL --config-dir=. $configname set-publish-cdnskey $zone 2>&1
-                  fi
-                fi
+                       if [ "$slot" == "" ]; then
+                               slot=0
+                       else
+                               slot=$((slot+1))
+                       fi
+                       label=pdnstest-${EPOCHSECONDS}-${slot}
+                       softhsm2-util --delete-token --label $label 2> /dev/null || true
+                       softhsm2-util --init-token --label $label --free --pin 1234 --so-pin 1234
+                       kid=`$PDNSUTIL --config-dir=. $configname hsm assign $zone ecdsa256 ksk softhsm2 $label 1234 $label 2>&1 | grep softhsm | awk '{ print $NF }'`
+                       $PDNSUTIL --config-dir=. $configname hsm create-key $zone $kid
+                       $PDNSUTIL --config-dir=. $configname rectify-zone $zone 2>&1
+               else
+                       $PDNSUTIL --config-dir=. $configname secure-zone $zone 2>&1
+               fi
+               if [ "${zone: 0:20}" = "cdnskey-cds-test.com" ]; then
+                       $PDNSUTIL --config-dir=. $configname set-publish-cds $zone 2>&1
+                       $PDNSUTIL --config-dir=. $configname set-publish-cdnskey $zone 2>&1
+               fi
        fi
 }