]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #9157 from rgacogne/reuseport-lb
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 21 Aug 2020 07:59:23 +0000 (09:59 +0200)
committerGitHub <noreply@github.com>
Fri, 21 Aug 2020 07:59:23 +0000 (09:59 +0200)
Add support for FreeBSD's SO_REUSEPORT_LB

218 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/expect.txt
.github/actions/spell-check/only.txt [new file with mode: 0644]
.github/workflows/spelling.yml
README.md
build-scripts/docker/generate-repo-files.sh
build-scripts/test-recursor
build-scripts/test-recursor-bulk
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/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/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-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/ixfrutils.cc
pdns/lua-auth4.cc
pdns/lua-base4.cc
pdns/lua-record.cc
pdns/lua-recursor4.cc
pdns/lua-recursor4.hh
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/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/configure.ac
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/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-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/test-negcache_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/rfc2136handler.cc
pdns/rpzloader.cc
pdns/secpoll-recursor.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/ueberbackend.cc
pdns/ueberbackend.hh
pdns/validate.cc
pdns/validate.hh
pdns/webserver.cc
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/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
index 551f45ce9f148d8db81a6058f30b799334be89cd..2775eb9554723edc7ba034e880f87e62b0eb2206 100644 (file)
-aaaarc
-aaaarec
 aaaarecord
-aaaarr
-aaaaset
-aab
-aabbccddeeff
-aabit
-aac
 aadaf
 aadceba
 aae
-aafd
-AAg
 aaldering
-Aand
-Aaq
-Aarbp
-aarch
 ababd
 abbb
-abca
-abcabcabcabacabac
-abcb
-abcd
-abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq
+ABCD
 abcde
 abcdef
-abcdefgh
-abcdefghijklmnopq
-abcdefghijklmnopqrstuv
-abcdefghijklmnopqrstuvwxyz
-abcdefghj
 abfe
 abi
 aborttransaction
-abq
+ABQ
 Abraitis
 abspath
-ACAABIBg
 acb
-acceptspace
-accountname
 acd
 acf
-aci
 acl
-aclocal
-ACLTOK
 ACPI
-Acpvl
 activatedomainkey
-Acu
 ada
 adadab
-adata
 adb
 adcb
-addaction
 addc
 adddomainkey
-addgroup
 addingrecords
-addnode
-ADDO
-ADDOCD
 addprefix
-addrbuf
-ADDRCONFIG
-addrinfo
-addrlabel
-addrlen
-addrringbuf
 addsuffix
-adduser
-addzone
 adfdffa
-adifferentlabel
 Adiscon
-advsys
 aee
 aeefcf
 aef
-aerique
-afc
 afdf
-afe
 Afek
-Affero
-afilias
-Afio
 afl
 afnic
-afpx
 afsdb
-afternm
 AFYER
 agentx
 agentxperms
-aglu
 AHM
-ahudns
 ahupowerdns
-aio
 Aips
 Aki
-Akkermann
-akxe
 Alenichev
 alexa
-Alexey
-algname
 algo
-algonum
-algotype
 aliceblue
-alldown
-allemaal
-ALLEXTERNALS
-allmakers
-alloc
-allocflux
-alloptions
-alloutqueries
-allowedips
-allownooptinar
-allowns
-allowonear
-allowtwoan
-allowtwoarnoopt
-allowwnooptinar
-alnum
-alphtype
-alpn
-ALSONOTIFYTOK
-altfrac
-altmeters
+allocs
 Altpeter
-altsign
-Amau
-amazonaws
-amazonlinux
-Amc
 amd
-amet
-Amf
-AMFLAGS
-Ampl
-anaccount
-anadns
-aname
-ANANSWER
-ancount
+ANCOUNT
 Anderton
-andnot
-ANDQ
 anewid
 anid
-anl
-anonpdns
-anonscm
 anonymization
 Anonymize
-anonymizing
 anotherid
 ANOTHERIPADDRESS
 anothertype
 ansible
 ANSSI
-answercount
-answermask
-answername
-answersslow
-answertime
 Antoin
 anycast
-ANYId
-ANYNo
-anytruncatetcp
-ao
-aoh
-aorudp
-AOver
 api
 apikey
-apipassword
-apisecret
 APIv
-apix
-apk
-apos
-apowerdnscom
-appliedpolicy
-appliedpolicytype
-appname
-APTR
-APublic
 AQAB
-Aqerz
-AQEX
 AQTQ
 ARCHFLAGS
-arcount
-arec
+ARCOUNT
 arecord
 arecvfrom
-arecvtcp
 Arentz
-argc
-arglock
-argp
-argparse
-args
-Argumentsv
-argv
-argvec
+ARGS
 arial
 Arjen
 Arjo
-arl
 Arnoud
-aroot
-arp
 arpa
-arpos
-arrs
 Arsen
-ARSH
 aruba
-asan
 asc
 Ascio
 ASd
-asendtcp
-asendto
 Asenov
 ASEP
-aset
 Ashish
 ASIN
-asn
-asnr
 asnum
 aspx
 associateddomain
-AStats
 asyncresolve
-athread
-atid
-ATime
-ATj
 Atlassian
-Ato
-atof
 atoi
 Atomia
 aton
-atruncatetc
-attodot
 attr
-attrany
-attributetype
-attrname
-attronly
 atype
-Auous
-authbind
-authcid
-authcmd
-authconfdir
-authdir
-authdomain
 AUTHIP
-authlog
 authmethod
-authname
 Authoritativedoc
-AUTHRUN
 auths
-authstorage
-authtests
-authzid
 authzone
-authzonepath
-authzonequeries
-AUTOBRIEF
-Autobuilder
 autobuilt
-autocalculating
 autocalculation
-autocommit
 autocomplete
 autoconf
 autodetect
 autodetecting
 autodoc
-autogen
 autogenerated
-autom
 automagically
 automake
 Automattic
 autoptr
 autoreconf
-autorev
-autorr
 autoserial
-autosetup
 autoslave
-autosplit
 autotools
-avect
-AWbqt
-AWIBIQ
-AWith
-awithecs
-AWORD
-aww
 AXF
 axfer
 axfr
 axfrfilter
-AXFRGET
-axfrqlen
-Ayea
-AZWLVw
 Baan
 bacc
-bacf
 backend
-BACKENDADDRESS
-backendfactory
-BACKENDID
-BACKENDLATENCY
-backendloader
-backendmaker
-backendname
-BACKENDORDER
-BACKENDOUTSTANDING
-BACKENDPOOLS
-BACKENDQPS
-BACKENDQPSLIMIT
-BACKENDQUERIES
-BACKENDREUSED
-BACKENDSTATE
-BACKENDWEIGHT
 backgrounding
 backport
 Backtick
 backtraces
 BADALG
-BADALGO
 BADCOOKIE
 badips
 BADKEY
-badmac
 BADMODE
-badname
-badrequests
+BADNAME
 badserver
 BADSIG
 BADTIME
@@ -364,287 +146,117 @@ BADTRUNC
 BADVERS
 baf
 Baj
-bak
 Bakker
 Baltus
-BAQEB
 basedn
 basepath
-baseurl
-BASICRESP
 Bastiaan
-batchmode
 bayour
-baz
 bba
 bbb
 bbc
 bbcbbbe
 bbd
-bbnew
-bbold
-bbsv
 bc
 bca
 bcb
-bcbsks
 bcc
 bccd
 bcce
 bce
-Bchl
 bda
 bdd
 bddd
 bded
-Bdn
-bdr
-bdsqlodbc
 bea
 bearggg
 beb
 beda
 beenthere
-befe
-beforenm
-BEFORENMBYTES
-begiter
-Beh
 bellis
 Belyshev
 benchmarketing
 Benetasso
-bereturnscookiesecscookies
-bereturnscookiesthenecs
-bereturnsecs
-bereturnsecsthencookies
 Bernd
 bert
-berval
 Besselink
-bestmatch
-bestns
-bestpos
 bestwho
 bfa
 bfb
 bfc
 bfcada
-Bfcw
 bfe
-bff
 bffa
-BFr
 bgcolor
-BGs
-bh
-bhartvigsen
 Bheca
-BHQk
-bidir
 Biege
 bigbank
 bigint
-BIGNUM
 BIGSERIAL
 Bilik
-BINARYPATH
-binascii
-bincount
-BINDANY
 bindbackend
-BINDCONF
-binddirectory
 binddn
-binddnssec
-binderror
-bindir
-bindlexer
-bindmethod
-bindnow
-bindparser
-bindparserclasses
-bindpw
 BINDTODEVICE
-bindwait
 Binero
 binlog
-bitcstr
-bitmembers
-bitpointers
-bitset
-bitsizes
-bitsleft
-bitstr
-Bj
-BKCNF
-bkoz
-BKU
 bla
-blablabla
-blahb
-bleh
 Bleker
-blen
-blhc
 blockfilter
-blockfor
 blockquote
-blockset
-bloeh
 blog
 blogpost
 blogspot
-blxn
-bmc
-bmgul
-BMh
 bmigrate
-BNA
-BNEh
-BNX
 bodyfont
 bodysize
 bodywrapper
-bogusqueryring
-bogusremotering
-bogusremotes
-BOIWc
 bolditalic
 bonafide
-boostorg
-booststringappend
 Bortzmeyer
-Bostock
 botnet
 bpf
-BPHYx
 bpo
-bpowerdns
-bpowerdnscom
-bq
 Brainspark
 Braunoeder
 breadcrumb
-BREHMDq
 Bremler
 brendangregg
 Briley
 Broens
 broer
-brokeloop
 Bromwich
-broot
 Brownworth
-BRRRRR
-BRu
 Brynjar
 Brzeski
 bsd
-bsqlodbc
-BSTUNE
-BTf
-btoe
 Btw
-buf
-buffersize
-buflen
+Buf
 bufsize
 bugfix
 bugfixes
-BUGLIST
-bugreport
 bugzilla
-builddir
-buildflags
-buildroot
-builtins
+BUILDDIR
 bulc
-bulktest
 bulletinc
 burstable
-burtle
-burtlemix
-bval
-Bvc
-bvect
-Bvy
 bw
-BWdx
 BXvs
-byport
-bytag
-bytearray
-byteorder
-byterate
-bytesread
+Byterate
 bytestring
 bzero
 bzip
-BZq
 caa
 caad
-cacheable
-cachebase
-cachecache
-cachecachevalid
-cachecleaner
-cacheda
-cachedecreasettl
-cachedifferentcase
-cachedifferentedns
-cachedir
-cachedoesntdecreasettl
-cachedqname
-cachedrtag
-cacheecsttl
-cacheentries
-cacheexpiration
-cacheexpirationdifferentsets
-cachefull
-cachehandleiter
-cachehit
-cachehitresponses
-cachehitresprulactions
 cachekey
-cachelimit
-cachemisses
-cachenotfullyet
-cacheonly
-cachettl
-cacheweekno
-CAcreateserial
-caddyfile
-caddyserver
 cae
-cafile
-CAINFO
 Cairney
 calculatesoaserial
 calidns
-callbackfunc
-callbackmap
-callee
-callgraph
-CANTOPEN
-capair
-caplen
-carbonname
-carbonthread
-cas
-cassert
 Cauquil
 cbb
 cbc
-cbe
-cbegin
-cbf
+CBF
 Cbjr
-CBlock
-cbmap
-CBTd
-cbuf
-cbufsize
 ccac
 ccache
 ccb
@@ -652,29 +264,13 @@ ccbd
 ccc
 ccd
 cce
-ccf
-ccname
-ccontrols
 ccounts
-cctype
-ccv
-cda
 cdb
-cdbbc
 cdbe
-cdbf
-cdbfile
-cdbinit
 CDBKV
-cdbm
-CDBQ
-cdc
-cdd
-cdde
 cde
 cdece
 cdeede
-cdef
 cdnskey
 cds
 ceb
@@ -683,1098 +279,379 @@ cece
 cef
 cefcf
 Cegetel
-cellpadding
-cend
 Cerb
-cerr
-cerrno
-CERTID
-CERTSTATUS
 certusage
-Cexternal
-Cfb
 cfe
 cfea
 cfeb
 CFLAGS
-cft
-Cfy
 cgi
-cgit
 CGroup
-CHACHA
 changelog
 changeme
 changeset
 changetype
 charset
-chartocode
 chashed
 chbruyand
 chdir
-checkbox
-checkfunc
-checkglue
-checkinterval
-checknow
-Checkof
-checkrr
-checkzone
 Chiavacci
-childstat
-chkconfig
 chmod
 chopoff
 chown
 Chqt
-chr
 Christof
-chrono
 chroot
 chrooting
 CHz
 ci
 CIDR
-cin
-cinttypes
-ciphersuites
-circleci
-citmp
-Cj
 cjf
-cka
-Ckey
-CKF
-CKK
-CKM
-CKO
-CKR
-cku
 classmethod
-classname
-classnum
-CLASSTOK
-cleandig
-CLEANFILES
-cleaninterval
-cleannsupdate
-cleanskipped
-clearrd
-clearrdviaaction
-cleartext
-clearthensetrules
-clen
-clientdiff
-CLIENTIP
-clientparseerrors
-clientsubnet
-clientsubnetoption
-climits
-clmn
-CLOEXEC
+CLASSNUM
 Cloos
-closedir
-closelog
 closesocket
-cls
 clusions
-Clvv
-cmake
-cmap
-cmatchlen
-cmath
-cmdline
-cmdomains
 cmouse
-cmp
 cmsg
-cmsgbuf
 cmsghdr
-Cmu
-cmval
 cn
-cnam
 cname
-cnameaction
-CNAMEAt
 cnamechainresolution
+CNAMEd
 CNAMEDNS
-CNAMENo
-CNAMENTA
-cnamerec
 cnamerecord
-cnamespoof
-cnamespoofaction
-cnamewildcard
-cnamewildcardnxdomain
 cnf
 Cnma
 cnn
-cnt
-Cnxd
+cockroachlabs
 Cockroft
 codebgcolor
-codedocs
 codeninja
 codetextcolor
-coldata
 Colemarcus
 colgroup
 collapsiblesidebar
 colm
-colnum
-colspan
-columncount
 comboaddress
-combovar
-comfun
 commandline
 committransaction
-compat
-compgen
-compilerflags
-COMPREPLY
 conaxis
-concat
-concurrentqueries
-condrestart
-confbasename
-confdir
-confdirname
-conffile
 config
-configdir
-configentry
 configfile
 configname
-configpath
 configsetting
-configstring
 configurability
-conflictor's
-confx
-connectionroom
-connectlogstr
-connectstr
-connstr
 conntrack
 Conntracking
-conscli
-consequenses
 Consolas
 constexpr
-contentlen
-contentstr
-contenttype
 controllen
 controlsocket
-cookiesoption
-coproc
 coprocess
 coprocesses
-copyable
-copyfile
 coredumps
 cornercases
 corpit
-correctpackets
-cors
+CORS
 costypetrisor
 cout
-Covcbz
 coverity
-cparts
-cpid
-cplusplus
-cpng
-cpnmu
-cpnmuog
-cpnmuoj
-cpos
 cpp
 cppcheck
-CPPFLAGS
-cptr
-cpuchart
-cpugraph
-CPUIO
-cpuload
-cpuset
-cpuy
-Cqg
-CQq
-crbegin
 createdb
-createdomainentry
 createslavedomain
-createzones
-creativecommons
-cred
 Cremers
-crend
-Crhs
 CRn
 cron
-crr
-crt
-cruft
+Cruft
 crv
 cryptokey
 Cryptoki
 cryptopp
 cryptoshop
-CSAx
-Csemi
-Cserver
-cset
-csignal
-Csmtp
-cso
-cspiter
-cspmap
-csr
 css
-cssfunction
-Cstart
-cstat
-cstddef
-cstdint
-cstdio
-cstdlib
-cstr
-cstring
-csu
 csv
-CSVE
-ctag
 ctime
-ctive
-ctl
-ctlun
 ctor
-Ctoroot
 ctx
-ctxarg
-ctype
-CUDJFRFI
-cumul
-cumulstats
-Cunauth
-CURLE
-CURLINFO
-CURLOPT
-CURLPROTO
-curtime
 Cuz
-CVbx
 cve
 cvename
 cvs
 cvstrac
-Cw
 CWD
-Cwithin
-CWORD
-Cwww
-Cxk
-cxx
 CXXFLAGS
 cz
-CZENW
 daa
 dac
 daee
-daemonize
 daemonizing
 daemontools
 daf
-DAFB
 Daganoto
 Danerklint
 dankamongmen
 Darilion
 darix
 Darron
-dasharray
-databuffer
-datafmt
 dataformat
-datalen
-datapos
-dataret
 datasource
 datastore
 datatracker
-datestr
-datetime
 Daugaard
 Davids
 Dayneko
 dbaec
-DBC
-dbd
-dbddb
-dbdnssec
-dbdnsseckeeper
 dbe
 dbedfc
 dbf
 dbfile
-dbg
-DBHOST
-dbi
-DBIf
-dbkey
-dbkeyset
-dblacka
 dblfilename
-dbmode
 dbname
-Dbolui
-DBp
-DBPASS
-dbpath
 dbpf
-DBPORT
 dbr
-dbrec
-dbrg
-DBSERVER
-DBT
-DBUSER
 DBX
-DCBE
 dccc
 dcd
 dcde
 dce
-dcec
 DCF
-Dcg
-DCHVORt
-Dcity
-dcke
-DCMAKE
 dcobject
-Dcode
 ddaab
-Ddata
-ddb
-DDct
-Ddcy
-dddx
 dde
-ddeb
-ddepth
 ddf
 ddns
 DDo
-DDWN
 deactivatedomainkey
-deadbeef
-deadc
-debbuild
-debconf
-DEBEMAIL
-DEBFULLNAME
-debhelper
 debian
 deboynepollard
-debtest
-decafsigners
-decerement
-declarearguments
-declarestats
 decls
-decltype
-deconfigure
-deconfigured's
 ded
 Deduktiva
 dedup
-deduplicate
-deepcopy
-defaultmap
-defaultport
-defaultttl
-defaultvalue
+Deduplicate
 defcontent
 defpol
-defport
-defstr
 defttl
-deinit
-delaypipe
-delcount
-delcounter
-deleteme
-deletetsigkey
-delnonterm
 DENIC
-depcomp
-DEPRECATEDLIST
-deps
-deque
 deref
-derp
 descclassname
 descname
-descr
-descrip
-deserialize
 Dessel
 dest
-destaddr
-destdir
 destname
 Detlef
-devent
-deviceid
 devicename
-devnull
-devpoll
-devpollfd
-devpollmplexer
-devscripts
 devtoolset
 df
 dfb
 dfd
-dfe
 dff
-DFL
-dfsdfsdf
-dgn
-dgram
-DGUX
 dh
 DHCID
 DHCP
 dhcpd
 dhcpdupdate
-DHE
-Dhh
-DHTML
-Dhydb
-dietlibc
-difflib
-diffopts
 diffs
-difft
-diffto
 DIGESTALGOS
-digesttype
 Digitalus
 dijk
 dilinger
-dimissing
-dimitri
-directbackendcmd
 Directi
-DIRECTORYTOK
-dirent
-dirfile
-dirhdl
-dirname
-dirp
-dirs
-dirstamp
-disabledvialua
-diskspace
 Disqus
-distdir
-disthashseed
-distributo
 distro
-DIXl
-DIy
 djbdns
-djlr
-DJYNFFq
-dkc
-dke
 DKIM
-dkrc
-DLCC
-DLD
-DLDAP
-dlen
 dlerror
-dlfcn
 dlg
-dlib
 DLLs
 dlmalloc
-DLOCALSTATEDIR
-DLOG
-dlopen
-DLQ
 DLV
-DMd
-DMEd
 dmesg
-dmi
 Dmitry
-dmp
-dmq
-DNAM
 dname
-DNAMETo
-DNb
-dndist
-dnf
-dni
-dnl
-dnmatch
 Dnn
-DNODCACHEDIR
-DNRSx
 dns
 dnsapi
-dnsbackend
 dnsbulktest
 dnscache
-dnsclass
 dnscrypt
 dnsdemog
 dnsdist
-DNSDISTBIN
-dnsdistcache
-dnsdistclient
-dnsdistcmd
 dnsdistconf
 dnsdistdist
 dnsdistdoc
-dnsdistdynblocks
-dnsdistkvs
-dnsdistlbpolicies
-dnsdistluarules
-dnsdistpacketcache
-dnsdistrings
-dnsdistrules
-dnsdisttests
 dnsdomain
-dnserrors
 dnsext
 dnsgram
 dnsheader
-DNSID
-DNSIP
 dnskey
-dnskeyr
-dnslabeltext
-dnslen
-dnsmasq
-dnsmaster
 dnsmessage
 dnsname
-dnsnameqtyperings
-dnsnameraw
 dnsnameset
 dnsop
-dnsp
 dnspacket
 dnsparser
 dnspcap
-dnsperf
-dnspkt
-DNSPORT
-dnsproxy
-dnspython
-dnsq
+DNSQ
 dnsquestion
 DNSR
 dnsrecord
-dnsrecordcontent
-dnsrecordheader
 dnsreplay
 dnsresourcerecord
-DNSRR
-dnsrule
 dnsscan
 dnsscope
-dnsscript
-DNSSE
 dnssec
-dnssecdb
 dnssecfromexisting
-dnssecinfra
-dnsseckeeper
-dnssecmode
-dnssecok
-DNSSECOn
-dnssecsigner
 DNSSERVER
 dnsspoof
-dnsstats
-dnsstrings
 dnstap
-dnstcpb
 dnstcpbench
-dnstext
 dnstree
 dnsttl
-dnstype
 dnsupdate
-dnsutils
 dnswasher
-dnswriter
 dnszone
-DNSZR
 dnt
 Dobrawy
-docbits
-docblock
-DOCD
-docdir
-Dockerfile
 docnamecachelookup
-docstring
 doctrees
-DOCTYPE
 documentclass
 documentwrapper
 docutils
-doent
 doesnotexist
-doesnotmatter
 dofile
-DOHFFI
-dohlocals
 Dohmen
-dohquerypair
-dohresponsepair
-DOIx
-dokill
-dolog
-domaindetails
-domainid
-domainidindex
-domainidmetaindex
 domaininfo
-domainmap
 domainmetadata
-domainmetaidindex
-domainmetanameindex
 domainname
-domainnameindex
 domainrelatedobject
-domainsdone
-domcount
 Donatas
-dononterm
-dontallow
 dontcare
-dontdrop
-dontinclude
-dontqueries
-DONTWAIT
-doquery
-dosec
-dotests
-dotfile
-downcase
 downsides
 downstreams
-Doxyfile
-doxygen
-doxyrules
-DPGZA
-dpk
-dpkg
-DPKGLIBDIR
-dpm
 dport
-dpos
-dprefix
-dptr
-dpw
 dq
-dqcount
-dqffi
 drafiei
 Draschl
-drc
-drh
-drillchase
-DRmcx
-dro
-Dropbox
-dropdb
 droprate
-dropset
-dropwhencached
-drr
-drs
-drsoa
-dsa
-DSANSEC
-dsc
+DRR
 dscontent
-dsdelegation
-dsdigestalgorithm
-dses
-DSfor
-dskey
-dsmap
-dsn
-dspk
-dsrc
-dsrec
 dsrecord
-dss
-dsset
+DSs
 dst
-dstates
-dstportrule
-DSYSCONFDIR
-dtime
-dtn
-dtor
 DTS
-dtv
-dtxn
 Dufberg
 dumresp
-DUPSORT
-DUv
-dval
-DVGN
-dvi
-DVIUi
-dvp
-dvpoll
-dw
-Dwaoc
-DWITH
-DXagbsuz
-dylib
 dynblock
 dynblocklist
 dynblocksref
 dynbpf
 dyndns
 dynhandler
-DYNLINKFLAGS
-dynlistener
-dynloader
-dynmessenger
-dynmetrics
-dynmetricslock
 dynmodules
-dzr
 eaa
-eaaecdabe
-eab
-EABy
 eac
-eacdd
 eachother
-EADDRINUSE
-EADDRNOTAVAIL
-EAFNOSUPPORT
 EAGAIN
 easydns
 eb
 ebaf
 ebd
-ebda
 ebe
 ebeb
-EBERb
 ebf
 ebfd
-Ebgy
 ebpf
-ebpfblock
 ebpfblocklist
-EBRACE
-ebuf
-EBUSY
 EBXN
-EByvht
 ecbf
 ecc
-ECCGOST
 ECCN
-ecdh
-ECDHE
 ecdsa
-ecdsap
-ecf
-ecgroup
-eckey
+ECDSAP
 econds
 ECONNRESET
-ecount
-ECP
-ecparam
 ecs
-ECSBy
-ecscachelimitttl
 ECSDA
-ECSIn
-ECSIP
-ecsipv
-ecso
-ecsqueries
-ecsresponses
-ecsrules
-ECSTo
-ECSTTL
 ecswho
-ectx
 eda
 edb
 edc
-eddsa
 ede
-EDEADLK
 edfa
 editline
-editttl
-edkey
-edn
-ednscookies
-ednsdomains
-EDNSECS
-EDNSFFI
-ednsflag
-EDNSIGNORANT
-ednsip
-ednslocalsubnets
-ednsmap
-ednsmask
-ednsnm
-EDNSNo
-EDNSOK
-ednsoptcode
-ednsoption
-ednsoptionrule
+edns
+ednsoptions
 ednsoptionview
-EDNSR
-ednsrcode
-ednsremotesubnets
-EDNSRR
-ednsstatus
 ednssubnet
 EDNSTo
-EDNSUDP
-ednsversion
-ednsversionrule
-EDNSZ
-edo
-EDR
-EDSIGNORANT
 edu
 eea
 eeb
 eec
-eecfe
 EED
 eef
-Eelapsed
-eevent
-EEXIST
-EEy
 efb
-efbd
 efbf
 efc
 efd
-EFGH
-efmq
-EFw
-egrep
-EHADv
-Ei
-eid
 Eieb
-EILq
-EINPROGRESS
 EINTR
-EINVAL
-Eips
-EISCONN
-eiter
-Ejf
-ejkmcpqxot
 ejones
 EJUGg
 ek
-Ekfq
 Ekkelenkamp
-Eky
-elabel
-elems
 elgoog
-elif
-Elr
-elseif
-elsif
-emailbx
-emap
-EMD
-EMERG
-EMFILE
 Emph
-enableval
-Enames
-encaps
 endblock
-endcode
 Enden
 endian
-endianness
 endif
 endl
-endnode
-endpos
-endptr
-endswith
-ENETUNREACH
-Enj
-ENOBUFS
-ENODEV
 ENOENT
-ENOSPC
-ENOSYS
 ENOTCONN
 ent
 entrypoint
 enum
 envoutput
-Enx
-enzo
-eol
-EOu
+EOL
 epel
-EPIPE
-epita
-eply
 epoll
-epollfd
-EPOLLIN
-epollmplexer
-EPOLLOUT
-eptr
 epub
-EPVHU
-eqd
-eqdnsmessage
 eqno
-equivs
-Eqy
-Erc
-erca
-ercode
-ercursor
 Eriksson
-ERKSs
-errcode
-errfds
-errlen
 errlog
-errmsg
 errno
-Erroring
 errorlevels
-errorresponses
-errorresult
-errorstring
-errstr
-esac
-escapedtext
-escdecb
-eso
-esow
 esr
-ESRCH
-esyscmd
-Etcu
-ETEQw
-ETIME
-ETIMEDOUT
-Eto
-etree
-etry
-eturn
-eui
+EUI
 EUips
-evah
-eventkey
-everytime
-EVFILT
-Evi
-evilapikey
 evildomain
-evilsecret
-evloop
 EVMu
-evp
-ewma
-EWOULDBLOCK
-EWPk
-Ewr
+EWMA
 examplekey
-examplenet
 exceedfuncs
-exe
 execfile
-execinfo
-execv
-execvp
-EXEEXT
-EXITCODE
-exitvalue
-exky
 Exort
-Expat
-expectedlen
-expf
-expr
-expungebyname
-expungebynameandtype
-expungebynameother
-extdir
 externalrefs
-extfile
-extracontext
 extrahead
-EXTRAOPTS
-extrasmn
-extrcode
-exu
-Eyd
-EZ
 Ezb
 Ezbu
 ezdns
-EZg
 fabf
-fabs
 fadec
 FAEEBC
 Faerch
 faf
-FAFAFA
-FAHr
 failedservers
-FAILONERROR
 failover
-fakeroot
-faketime
-fallthrough
-FASTOPEN
 favicon
 FBAE
-FBB
 fbc
 fbe
 fbf
-Fbo
-fbr
-Fbx
 fcbd
 fcc
 fcd
@@ -1782,3468 +659,1086 @@ fcde
 fcf
 fcff
 fcgi
-fclose
-fcnt
-fcntl
 fcontext
-fctx
 fd
 fda
-fdb
+fdc
 fdce
 fdd
 fdde
-fdf
-fdm
-fdmultiplexer
 fdopen
-fdset
-feb
 fedc
 fedoraproject
 feedents
 feedrecord
 feef
-fernando
-fetchall
 ffaae
 ffb
 ffd
 ffdd
-FFEE
 fff
 ffi
-ffilb
 ffipolicy
-ffipolicyfunc
-fflush
-fgets
-fh
-Fibl
-fieldname
-filebasename
 filedescriptor
-filenam
-fileno
-Filenum
-filestate
-filesystem
-FILETOK
-FILETYPE
-filtermap
-filtername
-filterpo
-findall
+Filesystem
 findclientpolicy
-findinit
-findnext
 Firefox
 firewalled
 firewalls
-FIRSTHDR
-firstquestiontime
 fixednow
-fixme
-fixperms
-FJHL
+FIXME
 FJZ
-Fk
-FKFy
 Fki
-flawednsset
-FLHY
-FLJ
 FLln
 Florus
-flowinfo
-FLrjot
-flt
 flto
-flushname
-FLYU
-Fmajor
-fmod
-fmri
-fmter
-fnamearg
-fnd
-fndhemi
-FNHYc
-fno
-fns
-fnum
-followedbyanother
-FOLLOWLOCATION
+FNs
 fontname
-foob
-fooba
 footerbgcolor
 footertextcolor
-fopen
-forcesafesearch
 forfun
-formask
-formerr
+FORMERR
 Fortiguard
 Fortinet's
 forwardzone
-foundct
-fournosoa
-fournosoainfirst
-foursecondsoainsecond
-fpacket
-Fpb
-fpc
-Fpk
-fprintf
-fptype
-fq
-fqdn
-Fqgo
-FQk
 framestream
-fread
 freakshow
-freeaddrinfo
-FREEBIND
 freebsd
 freedesktop
 Freenet
 freesans
 freetds
 freshports
-fri
 Froemel
-fromaddr
-fromlen
-fromport
-fromportstr
-fromserial
-fromstdin
-fromtimestamp
-fromtosockets
-fromvalue
 frontend
-frontsbase
-fsanitize
-fsck
-FSDB
-fsf
-fsl
-fslu
-fslutest
-fstream
 fstrm
-fstrmlogger
-fsync
-ftf
-ftp
-FTXp
 fullname
 fulltoc
-funarg
 func
-funcdb
-funcparam
-funcstats
-funkdb
-funkwithusage
-funnytext
 Furnell
 Fusl
-fuzzer
-FVM
-Fvn
-FVr
-FVrip
-Fvuj
-FWG
-fwopt
-fwparams
-fwrite
-fx
-FXc
-fxl
-Fxpgs
-FYd
 FYhvws
-Fym
-Fyq
 FZq
 gaba
 gacogne
-gai
-gaierror
-gamesgiroll
-garblewarble
 gatech
-gatewaytype
-gatherwildcard
 Gavarret
-gbv
-Gcached
 gcc
-GCCPACKATTRIBUTE
-gcda
 Gci
-GClj
-gcno
-gcount
-Gcountry
-gcov
-gdata
-gdate
-gdb
 gdpr
-gecos
 Geijn
-Gemfile
-gencontrol
-GENERALIZEDTIME
-GENERR
-genhook
 genindex
-genlog
 geobackend
 geoip
 geoipbackend
-geoipdatabase
-geoipdosec
-geoipgraphic
-geoipinterface
-geoipkeydir
-geoiploader
-geoiplookup
-geoiprecord
-geoipregion
-geoipregionip
 geolocated
-geoname
-geosec
 Gergely
 Gerritsen
 Gervai
 Gerwin
 getaddrinfo
 getaddrs
-getaftername
 getalldomainmetadata
-getalldomains
-getatomics
-getattr
-getbeforeandafternames
 getbeforeandafternamesabsolute
-getbeforename
-getchar
-getcommonlabels
-getcontext
-getcursor
-getcwd
-getdiagrec
-getdn
 getdomaininfo
 getdomainkeys
 getdomainmetadata
-getegid
-getent
-getenv
-geteuid
-GETFL
-getgrnam
-gethostbyname
 gethostname
-getinfo
-getlastlabel
-getline
 getlocaladdress
-getlock
 getn
-getnameinfo
-GETNEXT
-getopt
-getpagesize
-getpass
-getpeername
-getpid
-GETPIPE
-getpwnam
-getpwuid
-getqueries
-GETQUESTION
 getrandom
-getrawlabel
 getregisteredname
-getrlimit
-getroot
-getrusage
-getserial
-getsocket
-getsockname
-getsockopt
-getstack
-getstdout
 gettag
 gettext
 gettime
 gettimeofday
 gettsigkey
-getupdatedmasters
-getvalue
-getvars
 Geuze
-gf
-GFi
 GFm
 gh
-ghostscript
 Gibheer
 Gieben
 Gillstrom
-gir
 github
 githubusercontent
-gitlab
-GIZI
-GJPRf
-GK
 Gkey
 Gkkq
 glibc
-GLIBCXX
-globalconf
-globfree
-Glq
-glueless
-Glxb
 gmail
 gmake
 Gmb
-gmd
-gmt
 gmtime
-GMTOFF
 GMy
 gmysql
 gmysqlbackend
-GMYSQLDB
-GMYSQLHOST
-gmysqlloader
-GMYSQLPASSWD
-GMYSQLUSER
-Gnomovision
-gnuc
-gnupg
 gnutls
-GOceania
 godbc
 godbcbackend
-godbcloader
-godns
-GOMRf
 goodmatch
 google
 googleapis
-googledomains
 goracle
 goraclebackend
-gost
-gotdomaindetails
-gotipdetails
-gotit
-gotoline
-gotsome
+GOST
 gouv
 Goxz
-GPflpm
-gpg
-GPGSQ
 gpgsql
 gpgsqlbackend
-GPGSQLDB
-gpgsqlloader
-GPGSQLUSER
-gpl
-GPLv
-GPOS
-GPRINT
 gprof
 gpsqlbackend
-Gqhx
 GQj
 GQNy
-Gqxdqt
-grabkeys
-GRAPHOPTS
-Graphviz
-greg
 grep
 grepping
 grepq
-grok
-groupadd
-groupinstall
-grp
-gruleactions
-gsort
 GSQ
 gsql
-gsqlbackend
 gsqlite
-gsqlitebackend
 gss
 gssapi
-gssctx
 gsub
-gtar
 gtld
-gtm
-GTNi
-GTXTk
-gtzero
-guido
 guilabel
-GVs
-GWa
-GWTy
-Gwy
-Gx
-GXX
 gy
 Gyh
-GYK
 Gyselinck
 gz
-GZha
 gzip
 gzipped
-haas
-habbie
 hackerone
 Hakulinen
-halen
 Hannu
-HAPB
 haproxy
 hardcode
 hardcoded
 hardcoding
-hardlimit
 hardlink
 Harker
-Hartvigsen
-hasattr
-hashedidx
-hashindex
-hashperturb
-hatype
-havedollarttl
-haveednssection
-haveednssubnet
-HAVENSEC
-havetsig
-HBB
-Hc
-HCID
-HCIEg
-HCNUM
-HCo
-hcode
-HCPD
-hctx
-Hcu
-hdr
-Hdv
 headbgcolor
-headerfmt
-HEADERFUNCTION
 headerlink
-headersize
 headfont
-headl
 headlinkcolor
-headr
 headtextcolor
 healthcheck
-healththread
 Heimhilcher
 Helbekkmo
 HELO
-helpmap
-helpstring
 Hendriks
 Henk
 Hensbergen
 Heredoc
-herokuapp
-Heu
 Heuer
 Hev
-hexlify
-hextodec
-HFtab
 hh
-hhc
-hhp
-hhx
-hiddencryptokeys
-hideinitializer
 hidesoadetails
-hidesoaserial
 hidettl
 highlighttable
-hightxt
-hightype
-HIHEe
-hijackme
 Hiljanen
 hinfo
-hintfile
-Hiso
-histfile
-histo
-histog
 hitrate
-HJpbmcg
-hk
-HKm
 hkraal
 HKSKRWu
-hkw
-hlapi
-Hlatitudeh
-HLEN
 hll
-Hlll
 hlmann
-Hlocation
 hmac
-hmacsha
-hmech
 Hmi
-Hmisix
-HMrc
-HMukilteo
-Hn
-HNk
 Hoentjen
 Hofstaedtler
-holelock
-homedir
 homepage
 Hooimeijer
-horiz
-horizpre
-hostconf
-hostlist
 hostmaster
-hostmastercom
 hostname
-hoststr
 Hotmail
 howto
 hpecorp
 hpiers
-Hpj
 hpp
-hppa
 HPx
-hqp
 href
-HResearch
-hsa
-HSIZE
 hsm
-HSmu
-hsould
-HSw
 htbp
 htm
 html
 htmlescape
-htmlfiles
 htmlhelp
-htobe
-htole
-htonl
-htons
-htons'ed
 http
 httpapi
-httpconnector
-httpconnects
-httpd
 httpdomain
-HTTPHEADER
-httpversion
 hubert
-HUr
-HVeu
-Hvw
-hwinfo
-Hx
-hxx
 hyperlink
-HYrl
-HZFV
 HZQ
-HZXIZh
 iana
-iarchive
-IAustralia
-Iav
-ibfk
-IBMR
-IBRw
 icann
-ICANON
-ICASE
-ICNOKr
 ico
-icontent
-Icontinent
 ict
-idindex
-idl
-idmanager
-IDOID
-idonotexist
-IDont
-idpool
 idprotect
 idq
-idqueue
-Idret
-idserver
-idstate
 idx
-iends
-iequals
 iers
 ietf
-IEUW
-iface
-ifarch
 ifdef
-ifeq
-ifindex
-ifl
-ifndef
 ifportup
-IFSOCK
-ifstream
-ifupport
-ifupurl
 ifurlup
 IFV
-ifw
-Iga
-ignorebogus
-igoy
-IH
-IHOST
-Ihw
-IImrg
-iinfo
-Iinputs
-iix
-IIY
-IJ
+ihsinme
 IJajghd
 IKOYz
-Ilanguages
-ilexicographical
-ILggb
 illumos
-Ilongitudeh
-imap
-IMEI
 img
 Imhard
-Imiw
-impl
-inbytes
 incbin
-Incd
-inceptionday
-incfiles
 includeboilerplate
-includedir
 includerings
 indexa
 indexassociated
-indexfunction
 indextable
-inescape
 inet
-inext
-infd
-inflighter
-infodir
 infolog
 infosecinstitute
-infostream
 ini
-initctl
-initgroups
-initialrequestid
-initialrequestidstr
-initparams
-initrddir
 initscript
-initval
-inkey
-inl
-inlined
 Inno
 innodb
-ino
 inode
-inp
-inprogress
-inputkey
-inputlen
-inputname
-inqueries
-insn
-insnonterm
-insserv
 installable
-installdeb
-installdox
-installexamples
-installinit
-INTERNETTRAFFIC
 interop
 interoperability
 interoperation
-intervalcount
-intptr
-intransaction
-ints
-inttypes
-intxn
 inzk
-ioctl
-iomanip
-ioqueue
-iostate
 iostream
-iothr
-iothropt
-iov
-iovec
-iovlen
 iowait
-IOz
 ip
-ipaddress
-ipairs
+IPADDRESS
 IPbackend
 ipc
 ipcipher
-ipcipheripcipher
 ipcom
 ipcrypt
 ipdecrypt
-IPDNS
 ipencrypt
 ipfilter
-ipfromstr
-IPg
-iphdr
-ipi
-iplist
-ipmap
-ipo
-ipp
-ipparts
-ipport
-IPPROTO
 IPSECKEY
-ipsum
 iptables
-IPTo
-iptostr
-ipunitlist
 iputils
 ipv
 IQIT
-iqmp
 IQuery
-Iqw
 irc
-irz
-isa
-isalnum
-isalpha
 isane
-isatty
-isbase
 isc
-isdigit
-ISDIR
-isfile
-isinstance
-islandofsecurity
-isleap
 ismaster
 isoc
 isp
 ispell
 isql
-ISREG
-Isrqzjh
-isru
-isrunning
-iss
 isse
-isset
-isspace
-istream
-istreambuf
-istringstream
-isxdigit
-iteritems
-itf
-Itg
-ITIMER
-itimerval
-itmp
-Itni
-itoa
-Iu
-IUIu
-IUt
-ival
-Iwlx
-IWN
-IWR
-ixes
-IXF
+issuecomment
 ixfr
-ixfrdiff
 ixfrdist
-IXFRDISTBIN
-ixfrdistcmd
-ixfrdistdomain
-ixfrdisttests
-ixfrinfo
-ixfrutils
 ixplore
-ixx
-iy
-Izd
-IZws
 Jakub
 Jakum
-JAmk
-jan
 janeczku
 Jatko
 Jaury
 Jauvin
-JAVADOC
 javascript
-Jb
-JBnu
 JCf
-Jcj
 Jck
-jcong
-JCUw
-jdnssec
-jdthood
 Jeftovic
 Jelte
-jelu
-JEQ
 Jermar
 Jeroen
 jessie
-JFo
-Jgeoname
-JGKj
-JGT
-Jhb
-Jhdz
-JHm
-Jhpj
-Jip
-Jiy
 jj
-Jjbq
-jlist
-JLQ
-jm
-Jmdll
-Jmj
-Jn
-JNE
-Jnode
-JNTJMMHZDO
 Joaqu
 Jong
 Jorn
 journalctl
 journald
-journalmode
 jp
-jpg
 jpmens
 jq
-JQTNLZDBh
-jquery
-jre
-JSGT
 json
 jsondomain
-jsonp
+JSONP
 jsonstat
-JSONTSIG
-jsr
-JTf
-Jtv
 ju
 Juergen
-Juhf
-jul
 jumpbox
-Jungermann
 Juraj
-Jwmtfu
-JWndz
-Jx
 jye
-JZ
-JZIA
-JZte
-kamago
 Kaminsky
 Kaseorg
-kauq
-kbcafe
-KBo
-Kbuild
-kce
 KCtsq
 kd
-kdb
-kdbarg
-Kdescription
 Kdhcp
 Kdhcpdupdate
-Kdismbe
-keepnocd
-keeprd
-keeprecurse
 Kees
 kerberos
 Kerkhof
-kevent
-keyalgorithm
 KEYBITS
 keyblock
-KEYBYTES
-keycache
-keycachelock
-keydata
 keydir
 keyfile
 keygen
-keyid
-keylen
-keylog
-keymeta
-keymetadb
 keyname
-keyout
 keypair
-keypos
-keyring
+keypairgen
 keyroll
 keysearch
-keyserver
 keysize
-keystr
-keystring
 keytab
-keytag
+KEYTAG
 keytype
-keyv
 keywordmatches
-Kfq
-Kgvf
-kh
-Khk
 kickdaddy
-killall
-killproc
 Kirill
-Kj
-KJPKEd
-kk
-Kkfp
-KKl
-KLa
 Klebermass
-klen
-KMBT
-kmd
-KMP
-Knj
 koch
 Kockum
-Kok
 Kolkman
 kom
 Konqueror
 Koos
 koqv
-Kosnik
 Kovacic
-KOw
 kp
-kq
-kqevent
-KQF
-Kqim
 kqueue
-kqueuefd
-kqueuemplexer
 KQZX
 krb
-Krecord
-KRIEGER
 Krist
 Krul
-Ksj
 ksk
-kskds
-kskeys
 kskroll
 kskrollcdnskey
-KStream
-Ksy
 Kuehrer
 KUXs
-kvo
-kvresp
 kvs
-kwargs
-KWw
-kx
-KXu
+KX
 Kxxnux
 Kyc
-kz
-KZR
-labellen
-labelparts
-Labelreverse
-labelscount
-labelscountadvanced
-labeltok
-laddr
-LADIP
 Ladot
 Lafon
 Lakkas
 largeanswer
-largeanswerremotes
-largernumberofconnections
-largettl
 Laros
-lastanswer
 lastcheck
-lastclean
-lastcounts
 Lastdrager
-lastget
-lastline
-lastmod
 lastnotified
-lastperc
-lastpos
-lastquestion
-lastreport
-lastsec
-lastsum
-lastval
-latdeg
-latdiff
-latexmk
 latexpdf
-lathem
 latlon
 latlonloc
-latlonstrptr
-latmin
 latomic
-latsec
 lauch
-Launcheable
 Laurient
 Laursen
-lauxlib
-layj
 Lbackend
-lber
-lbpolicies
-lcc
-lci
-Lcode
-lcontent
-lcrypto
 LCUP
-lczonename
 LDA
-LDADD
 ldap
-ldapadd
-ldapauthenticator
 ldapbackend
-LDAPBASEDN
-ldapbinddn
-ldapdelete
-LDAPHOST
-ldaploader
-ldapmodify
-LDAPNo
-LDAPPASSWD
-LDAPRELATTR
-ldapsecret
-ldapuris
-LDAPUSER
-ldaputils
-LDAPv
-LDB
-ldd
 ldflags
 ldif
 ldns
-ldnsutils
 LDR
 Leen
-leftcolumn
-leftiter
-lels
-LEMLD
 Lemoine
 len
-lenpos
-lentry
 lessthan
 Lesuisse
 lethalgroup
 letsencrypt
 letterpaper
-lflag
 LFya
-LGCe
-LGeo
-lgnutls
-LGPL
-LHav
-lhead
-LHq
-lhs
-Lhwzi
-LIBADD
-LIBASAN
 libatomic
-libauthbind
-libbindbackend
-libboost
-libbpf
 libc
-libcap
-libcdb
 libcrypto
 libcryptopp
 libcurl
 libdecaf
 libdir
-libdl
 libedit
-libexecdir
-libfaketime
 libfstrm
 libgcc
-libgen
 libgeoip
-libgeoipbackend
-libgmysqlbackend
-libgnutls
-libgodbcbackend
-libgpgsqlbackend
-libgsqlite
 libh
-libipcrypt
-libjson
-libkern
-libkrb
-libldap
-libldapbackend
-liblmdb
-liblmdbbackend
-liblua
-libluajit
 libmaxminddb
 libmongo
 libmysqlclient
-libnacl
-libnet
 libnss
-libopie
-libp
 libpcap
-libpipebackend
 libpq
 libpqpp
-libprobds
-libprotobuf
-librandombackend
-libremotebackend
 libresolv
 libressl
 librt
-libsnmp
 libsodium
 libsofthsm
-libsqlite
-libsqliteodbc
 libssl
 libsystemd
 libtdsodbc
-libtestremotebackend
-libtinydnsbackend
-libtolua
-libtool
-libunbound
-libwslay
-libyahttp
 libyaml
 libzmq
-licensedir
-lightuserdata
 Lindqvist
-lineno
+linenos
 linenum
-linespoints
-lineterm
 linkcolor
-linktype
 lintian
 linux
 linuxnetworks
 Lior
-LISi
-listenaddress
-listerner
 listinfo
-listname
-listset
-listx
 literalinclude
-Lj
-lk
-lkjhgf
-lld
-LLL
-lltemp
-llu
 llvm
 lmdb
 lmdbbackend
 LMDBKV
-LMDBQ
-Lmg
-lnc
 lnsl
 loadbalancer
 loadbalancing
-loaderdecaf
-loadersodium
-loadmodule
-locala
 localaddr
-localaddress
-localaddresses
-localbind
 localhost
 localip
-localname
-localsock
-localstatedir
+LOCALSTATEDIR
 localtime
 localtoc
 locaweb
 lochiiconnectivity
-loctext
-locwild
-lodbc
-logaction
 logfile
-LOGGINGTOK
-Loginfo
 loglevel
-logline
 logmessage
-logprefix
 logrotate
-logscale
-logstream
-lol
 lon
-londeg
-londiff
-longenough
-longindex
-LONGLONG
-longopts
-LONGTEXT
-longttl
-lonhem
-lonmin
-lonsec
-Lookaside
-lookedup
-lookupdomain
-loopcount
 Loopia
 Lorbach
 lordievader
-Lorem
 Louwers
 loweralpha
-lowercasequery
-lowercasing
 lowerroman
-Lpat
-LPi
-lptr
-lpz
-Lq
-Lqcha
-Lqm
-Lqr
-lrde
 lresolv
 Lrhazi
 lrt
-lru
-LSA
-lsb
-LSH
-Lsl
 lsock
 lsocket
-lssl
-lstr
-LString
-Lsubdivisions
-LSzyzd
 lte
-LTest
-LTGp
-lthread
-LTLIBRARIES
-ltmain
-ltoptions
-ltsugar
-Ltuxc
-ltversion
-LTvniq
 lua
 luaaction
 luabackend
 luac
-luacall
-luaconf
-luaconfpath
-luadnsrule
-luaffi
-luaffiactionfunction
-luaffiactionsettag
-luaffirulefunction
-luahooks
 luajit
-LUAJITPC
-lualib
-luamaintenance
-luamutex
-LUAPC
 luaroundrobin
 luarule
-luarulefunction
-luaruleparams
-luascript
-luascriptpath
-luasmn
-luaspoof
-luaspoofwithstats
-luatarget
 luawrapper
 Lutter
 Luuk
-lw
 Lwc
-lwn
-lwr
-lwres
-lwslay
 Lwz
 LYg
 lz
-LZrd
-lzrp
-LZz
-MACBYTES
-maclen
-madname
 Maik
 Maikel
 MAILA
 MAILB
-mailinglist
-mailserver
-mainfilter
-mainloop
-mainpage
-mainthread
-maj
 Majer
-makebackend
-makecontext
 Makefiles
-makeindex
-makemetadataonly
-MAKEOPTS
-makerfunc
-MAKEVAR
 malcrafted
-mallinfo
 malloc
-Mallocated
-malloctrace
 malware
-malwareset
 Mamane
 Mandriva
 manpage
-manpath
 mapasync
 mariadb
 Markmann
-marvin
-maskl
-maskr
 maskv
 Massar
-mastercommunicator
-mastermake
-masterplan
-masterschanged
-MASTERTOK
-matchkey
-matchlen
 matchtype
-matthijs
-maxanswersize
-maxbodysize
-maxbogusttl
-maxcachesize
-maxcachettl
-maxchunkrecords
-maxcleaninterval
-maxconnsperclient
-maxcp
+Matthijs
 maxdepth
-maxent
-maxevents
-maxfd
-MAXHOSTNAMELEN
 MAXINT
 maxlistdepth
 maxmind
-maxminddb
-maxnegttl
-maxnsaddressqperq
-maxobjects
-maxqperq
 maxqps
-maxqueriesperconn
-maxreplylen
-maxrss
-maxthreads
-maxtid
-maxtotusec
+MAXVALUE
 mbed
 mbedtls
-mbox
 MBOXFW
 mbytes
-MBZ
-mcgrof
-mch
-mcontext
-mcp
-Mcu
-Mdatabase
-mday
-mdb
-MDBIn
-MDBRO
-MDBRW
-mdctx
-mde
-mdiff
-mdname
-mdp
-MDQx
-MDSV
-mediumsizedlabel
+MDB
 Meerwald
 Mekking
-MEMB
-Memcheck
-memchr
-memcmp
-memcpy
-memfree
-memis
 MEMLOCK
-memmove
-memset
 Memusage
-memzero
 menuselection
-MERCHANTAPILITY
-mesg
-mesgsize
-messageid
-messageidstr
-metacache
-metacachelock
 metadata
 metadatabase
-metadatadn
 metainformation
-metamap
-metavar
 metricnames
 metricscarbon
 Meulen
-MEY
-Mfc
-mflags
-mgmname
-Mgo
-MHd
-mic
-michel
 Michiel
-microsoft
+Microsoft
 Miek
 Miell
 Mieslinger
-might've
-migweb
 Milas
 Mimimization
 minbody
-mincleaninterval
 mindex
 MINFO
-minicurl
-MINIMIZATON
 minipatch
-mintime
-minttl
-MIPSEB
-mipsel
 misconfigured
-mistmatching
-Mixin
-Miy
-Mj
-Mjgw
 mjt
-mkbindist
-mkdir
-mkinstalldirs
-mkpubsuffixcc
-mkquery
-mkstemp
-mktemp
-mktime
 mkuchar
-mlen
-Mlj
-Mlkroefk
-mlock
-mman
 mmap
-Mmax
-mmc
 mmdb
-mmsghdr
 mname
-Mngwcrq
 mnordhoff
-MNorth
 MOADNS
-moadnsparser
-moby
 Modderman
 modifyingpolicydecisions
 modindex
-moduledirs
-modulelibs
-moduleobjects
-mohta
 monshouwer
 Moq
-moreutils
 motherboards
-moz
 mozilla
-MPkb
-Mpl
 mplexer
 Mpqhbg
-Mps
-MPSC
-mq
-mqalatency
-Mqas
-MQlel
-Mqoh
+MQ
 mrtg
-MRUBY
-MRw
 msdcs
-msdn
-msecmatch
-mseconds
-msgfree
-msgh
-msghdr
-msgid
-msocket
+MSDNS
 msphinx
 mssql
-mstr
-MSVC
-msysmsec
 mtasker
-MTASKERTIMING
-MTest
 MTEUl
 mthread
-mtime
-mtracer
 MUar
-mul
 Mulholland
-multialgo
-MULTIARCH
 multiline
-multimaint
-multimap
 multimaster
-multiplecookies
-Multiplexermap
-multispoof
-multitext
 multithreading
 mundsson
-munlock
 munmap
 Muraro
-musermsec
 musl
-mustlog
 mutex
-mutexes
-mval
 Mwaikambo
-MWJu
 mx
-MXB
-mxname
 mxrecord
-mxtics
-MXZQm
 mybackend
 mycompany
-mydata
-mydb
 mydns
 mydnsbackend
 mydomain
 MYec
-myemailhere
-myfile
 myhost
-myinitlock
 myinstance
-MYMETA
 myname
-mynewkey
 mypassword
 mypgsql
-mypool
-myproject
-mysecretauthkey
-mysecretenckey
 mysecretpassword
 myset
 myspecialmetric
 mysql
-mysqladmin
 mysqlbackend
 mysqld
-mysqldiff
-mytag
-mytics
 mytsigkey
 myuser
 mywebapp
-Mzlw
-NAGLE
 NAi
-namealgoindex
-namebuf
-namecount
-namedconf
-namedfile
 namedroppers
-namehash
-nameindex
-namelen
-namemap
-namenum
-namepositions
-Nameret
-nameser
 nameserver
-nameservername
-nameservice
 nameserving
 namespace
-namesseen
-namestocheck
-nametoindex
-nametype
-namq
 namserver
-nanosleep
 naptr
-nargs
-narrowbool
 Nauck
 Navarrete
-nbb
 nc
-nce
-NCx
-ndays
 Ndd
-NDEBUG
-NDELAY
-ndiff
-NDIy
-NDUx
 nearmiss
 nearmisses
 Nederlandse
 nedmalloc
-needcdb
-needldap
-needlmdb
-needres
-needsqlite
-Negativerustanchor
 negativetrustanchor
-negativetrustanchorserver
-negativettl
 negcache
-negcached
-negcacheentries
-negindic
 negquery
 neheb
 Nelless
 neosystem
-NERUUDNz
 Netblock
-netdb
 netfilter
-netflix
 netherlabs
 netinet
 netmask
 netmaskgroup
-Netscape
 netsnmp
-netstat
-netw
 NETWORKMASK
 Neue
 Neuf
-newalgo
-newargv
-newauth
-newconnectioncb
 newcontent
-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
+nextval
 nf
-nfc
-nfds
-NFLSd
-NGtr
-nh
-NHo
 nic
-nif
 nimber
-nint
 Nixu
-Njd
-NJKw
-nju
-nk
 nkey
-NKm
-nlaunch
-Nld
-nlen
-nline
-NLips
-nlnen
-nlnetlabs
-nmasters
-nmemb
 nmg
-nmgrule
-nmin
-Nml
-nmmatch
-nmp
-nmpair
-nmt
 nn
 Nncqx
-NNl
-NOACK
 noaction
 noad
-noaddrs
-noalias
 noall
-noarch
-noaxfr
-nobackend
-NOBLOCK
-noc
 nocache
-nocachevialua
-NOCASE
-NOCERTS
-nocheck
-nocn
-nocontext
-nocontinue
 nocookie
-nocreate
 nodata
-nodcachedir
-noddb
-noddbp
 NODELAY
-nodelegated
-nodist
-nodnssec
-NODNSTAPTESTS
-nodot
-nodownstream
-NODUPDATA
-nodyndns
-noecs
 noedns
-noent
-noerroranswers
-noerrorcount
 noerrors
-noexcept
-NOEXIST
-nof
-nogpgcheck
-NOi
-noinline
-noinput
-noinst
-nologin
-nolua
-nometa
 nometasync
 Nominet
-Nominum
-nonarrow
-NONBLOCK
-NONCEBYTES
-noncnames
-noncopyable
-nonetheripudp
-noneup
 nonexist
-NONINFRINGEMENT
-nonleap
 Nonnekes
-nonsec
 noout
-nop
-nopacketcache
-noparse
 noping
 noport
-NOQUOTES
-nora
-norbert
-nord
-norecurse
-norecursionavailable
-noreplace
-noreply
-noreturn
-norrsig
-noserver
-noservfailstats
-nosetests
 nosniff
-nostale
 nostrip
-NOSUB
 NOSUBDIR
-NOSUCHOBJECT
-nosuchpool
 nosync
-notallowed
 Notaras
 NOTAUTH
-notdisabled
-NOTFOUND
-notimp
-notimpl
-notincludedir
-notoverridden
-notoverriddenprefixlength
+NOTIMP
 NOTPARALLEL
-notpool
-notpowerdns
 notrack
-NOTRUNNING
-notsetecsaction
-notwcard
-notyouroffset
 NOTZONE
-nounput
 Novell
-nowait
-nowildcard
-NOWPLUSTEN
-nowrap
-noyy
-nparams
-npos
 nproxy
 NPTL
-NPxzx
-nq
-nqk
-nqueue
-nrc
-nread
-nrecords
-nrep
-Nro
-nrr
-NRus
-nsa
-nscount
+NSCOUNT
 NSCx
 nsd
-nsdname
-nsdomain
-nseconds
-nsecrecords
-NSECs
-nsecx
-nsecxrepo
+NSECx
 NSes
 nsid
-NSIDTCP
-NSIDUDP
-nsip
-nsiploop
 nsis
 nsname
-nsock
-nsone
-nsq
+NSQ
 nsrecord
-nsrr
-nss
+NSS
 nsset
-nsspeed
-nsspeedsentries
-nst
-nstld
+nsspeeds
 NSTTL
 nsupdate
 nta
-ntk
+ntei
 ntlworld
-ntohl
-ntohs
-ntop
-nts
-Ntx
 nullptr
 NULs
 NUMA
-numanswers
-numcores
-numdomains
-numentries
-NUMERICHOST
-numerrors
-numevents
-NUMFAILED
-numloops
-numread
 numreceived
-numreceiveddo
-numrecords
-numsigs
-numsocks
-NUMTESTS
-numthreads
-numwarnings
-numworkers
-Nury
-NUx
-NUxt
-Nvd
-nvect
-NVQ
-nw
-nwaiters
-NWSIW
-Nwv
 nx
-Nxbulf
-NXCFg
 nxd
 NXDATA
-nxdo
-NXDOMAI
 nxdomain
-nxdomainanswers
-nxdomainme
-nxdomainsuffix
-NXNSECS
-NXQ
-nxqtype
-NXRR
-nxrrset
-nxt
-NXTHDR
-nxwithnorr
-Nyeq
-nz
-nzg
-nztest
-oa
-Oaccuracy
-OAfx
+NXRRSET
 oarc
-oarchive
 oauth
 Obermayer
-obf
 obidos
 objectclass
-OBJECTFILES
-OBJECTLIBS
-OBJECTPROPERTY
-OBJEXT
-OBRACE
 Obser
 obspm
-Obsu
-OCc
-ocn
-ocontent
 ocsp
-octetsin
-octetsout
-octx
-ODAx
 odbc
 odbcbackend
 odbcinst
 Oddy
-ODESC
 Odintsov
-ODIy
-odl
-odo
-ODQ
 Oestreicher
-OEVLOOP
-Oew
-ofc
-ofconf
-Ofdvp
-OFFMASK
-offsetof
 offsite
-ofile
-OFJJXk
-OFkp
 Ofpy
-ofstream
 oftc
-ofzone
-OGhh
-OGlnb
-OGR
-ohn
-oi
-oid
-OJ
-oknodo
-OKPERCENTAGE
-OKPERCENTAGEINT
-oks
+OIDs
 Olafur
-olc
-oldmode
-oldmsg
-oldnames
-oldrr
-oldsr
-oldt
-olen
-OMG's
-omoerbeek
-OMPQK
-Omqq
 Omroep
-oneline
-onenosoa
-oneshot
-onexit
-ONXJQQ
-Oo
-oob
-oobar
-OOO
-OOOTCP
-oopts
-oor
-Ooutputs
-oowerdns
-opacket
-opcodenotify
-opcodeupdate
-OPEI
 openapi
 openbsd
 opendbx
-opendir
-opendns
-openf
-openldap
-openlog
 openpgpkey
 opensc
-opensource
 openssl
-openssllocks
-opensslsigners
-opensslv
 opensuse
 openwall
-operat
-opie
-OPj
 Opmeer
-opname
-opos
-optarg
 optcode
 Opteron
-OPTIn
-optind
-OPTIONSTOK
 optmem
-optname
 optout
-optret
-optsize
-optstring
-optvect
-oq
-oqtaen
 oraclebackend
-orderindex
 ordername
-origanswers
-origbetter
-origbetterset
-origctx
-origdnserrors
-origfd
-origid
-originalrequestorsubnet
-originalttl
-origlate
-orignever
-origtimedout
-origunmatched
 orsn
 Oservers
-osixia
-oss
-ostr
-ostream
 ostringstream
-osubgrouping
 OSX
-OTAy
-ote
-otherdatalen
 otherdomain
 otherpool
-otherprovider
-otherrcode
-othersize
-othertag
-Otkjt
-otype
 ou
-OUh
-OUHh
 OUHTU
-ourcount
-ourdomain
-ourhelpfulservice
 ourname
-ournum
-ourpos
 ourserial
-oursr
 ourtime
-ourttl
-outerpost
-outfd
-outfile
-outgoingtimeouts
-outlen
-outofzone
+OUTFILE
 outpacket
 outputbuffer
 outqueries
-outsigned
-outsock
-overriddenprefixlength
-overriddenprefixlengthvialua
-overriddenvialua
-OWJ
-OXd
-OYna
-ozz
 PACKAGEVERSION
 packetcache
-packetcacheentries
-packetcachehits
-packetcachemisses
-packetcacheservfailttl
-packetcachettl
-packetcaching
 packethandler
-packetheader
-packetperc
-packetsize
-packetwriter
-padawan
-Paf
-pagefaults
-pak
-panix
 papersize
 paramater
-paramcount
-PARAMDOC
 PARAMKEYWORDS
 params
-paridx
-parm
-parnum
-parsecheck
-parsefail
-parsertest
-passhtru
-passlen
 passphrase
 passthrough
 passthru
-passwd
 PATC
 patchlevels
-pathbuf
-pathc
-pathconf
 pathconfig
 pathto
-pathv
-patsubst
 pawal
 pb
 Pbackend
-PBDNS
-pbegin
-PBh
-Pbi
-PBKDF
-PBpq
-pbtag
 pcap
 PCAPFILE
-PCas
-PCDNSSEC
-PCKS
-PCmissing
-pcomp
-pcount
-PCPU
-pctx
-pddkcrxy
-pddrw
 pdf
-pdflatex
-Pdk
 pdns
 pdnsbackend
-pdnsbackendmysql
-pdnsbackendpgsql
-pdnsconf
 pdnscontrol
-pdnsdbi
-PDNSDEBUG
-pdnsdist
-pdnsdomains
-pdnsexception
-pdnsfeatures
-pdnsinfo
 pdnsldap
-pdnsload
 pdnslog
 pdnsmgrd
 pdnsodbx
-PDNSPB
 pdnsrandom
-PDNSRECCONTROL
-PDNSRECURSOR
 pdnssec
-PDNSSERVER
-pdnssocket
 pdnstest
 pdnsutil
-pdp
 Peeters
 Pels
 pem
 Penev
-perc
-periodicall
 perl
-PERLMOD
-Perror
 Perroud
-pershard
-pertub
 Pertubation
-pevents
-pex
 pez
 Pfetzing
-pfh
-PFm
-pfs
-pfsbox
-PGconn
-PGHOST
 pgmysql
 pgmysqlbackend
 pgp
-PGPASSWORD
-pgpsigurlmangle
 pgpsql
-PGr
-PGRES
-PGresult
 pgsql
-PGUe
-Pgv
-PGw
-pheader
 phishing
-phitrate
 phonedph
 php
 pickclosest
 pickrandom
 pickwhashed
 pickwrandom
-PICOTLS
 pid
 piddir
-pident
 pidfile
-pidfname
 pidof
 pieter
 pieterlexis
 pilindex
 Pinski
 pipebackend
-pipeconnector
-pipefail
-pipefd
-pipefunc
-pipeloader
 pipermail
-PIPESTATUS
-piter
 PIV
-PKCKQAu
 pkcs
-pkey
-pkgconfig
-pkglib
-pkglibdir
-pkgname
-PKgogeu
-pkgv
+PKGLIBDIR
 PKI
-pkill
-pkthdr
-pktinfo
-pktlen
-pkttype
+PKTINFO
 placeholders
-pldap
-pleasequeryfunc
-pleasequit
-pleaseremotefunc
-pleft
-plen
-plenus
-pline
 PLt
-plugin
-plumgrid
 Plusnet
 plzz
-pmap
-PMQ
 pmtmr
-PMTU
-PMTUDISC
-Pn
-pname
 pnds
 png
-PNPr
 Poelov
 pointsize
 polarssl
 policyactions
-policyfunc
 policykinds
 policyname
-policystr
-policytag
-POLLERR
-pollfd
-POLLHUP
-pollin
-pollitem
 pollmplexer
-POLLOUT
-POLLREMOVE
-polmap
 Ponomarev
-poolaction
+poolers
 poolname
-popen
-popisort
-portev
-portfd
 portnum
 portnumber
-portsmplexer
-posbegin
-posend
-posix
-postdata
-POSTFIELDS
-POSTFIELDSIZE
 postgre
 postgresql
 postinst
-postoutquery
-postpol
-postprepare
-postqueries
 postresolve
-postun
-postvars
-potentialsupermasters
-powerdn
+powerdns
 powerdnsrecursor
 powerdnssec
 powerldap
-poweroften
 powerpc
-pparent
-pparts
 ppc
-ppid
-pply
-pprint
-PQclear
-PQconnectdb
-PQerror
-PQescape
-PQexec
-PQfinish
-PQfreemem
-PQftype
-PQgetisnull
-PQgetvalue
-PQnfields
-PQntuples
-PQreset
-PQresult
-PQsocket
-PQstatus
 pragma
-prc
-PRecord
-precsize
-PREDEF
 Predota
-preg
-PRELOAD
-preout
 preoutquery
-preparse
 Preproc
 prequery
 prereleases
-prereq
 prerpz
-presignatures
 presigned
-presignedcontext
 presignedness
-preun
-prevprev
-prevqname
-prfds
 PRId
 primetime
-primev
 princ
-printargs
-printf
-printlogs
-printtable
-printvars
-PRIu
-privatedns
 privatekey
-privateoid
 privs
 PRNG
-proba
-probds
-probs
-processname
+Proba
 progid
-progname
-PROGRAMLISTING
-programname
-progrm
-progsarray
-promtool
-propol
-PROT
-protbuf
 protobuf
-protoc
-protostr
+PROTOC
 providername
-proxyheader
-proxymagic
-PROXYMAGICLEN
 proxyprotocol
-PROXYPROTOCOLHEADER
 proxyprotocolvalues
 PROXYv
-PRsm
-prv
-psbf
 pseudonymize
-pseudonymized
 pseudorecord
-PSEUDOSECTION
 psql
-Pstmt
-psy
-ptcp
 pthread
-Pthv
-pton
 ptr
 ptrrecord
-pubkey
-publabel
-publicdomain
-publickey
-PUBLICKEYBYTES
-publicsuffix
 Publieke
 publishdomainkey
-pubsuffix
-pubsuffixloader
-puk
 pullreq
-pullrequest
-pushlightuserdata
-pvars
-pvect
-pw
-pwd
-pwent
 pwfm
-pwgen
-pwtkey
 px
 py
-pyc
-pycache
-pycurl
 pygments
 pypi
 Pyry
-pysnmp
-PYTHONUNBUFFERED
 PYv
 PZFX
 qa
 Qag
-qaint
-qalatency
-qame
-QBD
-qc
-qcachehits
-qcachemisses
-Qccuox
-qckzu
 qclass
-qclasschaos
-QClasses
-qclassin
-QCmissing
-qcount
-qcounter
-qd
 qdcount
-QDDt
 qdomain
-qdomainwild
-qdomainzone
-qesc
-qf
-qfonh
 qgen
-QGy
-qhash
-qi
-qids
-Qj
-qk
-qkey
 Qkj
-qla
-qlass
 qlen
 Qlim
 Qll
-qlog
-Qlolbd
-Qlq
-QLs
-qmail
-qmin
-Qmsa
 qname
-qnamefilter
-qnamelen
-qnamemap
-qnameminfallbacksuccess
-qnameminimization
-qnamewirelength
-QOP
-qoutq
-qowerdns
-qpacket
-qpe
 qperq
 Qpkv
-qpol
-qpos
 qps
-qpschart
-qpsgraph
 QPSIP
-qpslimit
-qpsnone
-qpspoolaction
-qpsstart
-qpsy
-QPTk
-QPv
-qq
-qrate
-qrateactionnxd
-qrateactionrefused
-qrateactiontruncated
-qraterefused
-qrset
-QSarv
+qpslimits
+QRate
 qsize
 QSLj
-qsock
-qstats
-qstr
-qstring
-QSvh
-QSy
-QTag
 qthread
-qtid
-qtnull
-qttl
-qtun
 qtype
-qtypecounters
 qtypelist
-qtypenums
-quantcast
-queryb
 querycache
 querycount
-queryfd
-queryring
-querystr
-querytimesec
-querytimeusec
-Queuedo
-queuelength
-queuetimeout
-qufnk
-Qug
 quickstart
-quotedname
-QUOTEDWORD
-quux
-qvalue
-qw
-QWN
-qx
-Qxh
 QYCIHp
 qytpe
-raddr
 ragel
-raisd
 randombackend
 randombit
 randombytes
-randomid
 randomisation
 randomises
 randomloader
 rapidjson
-rarg
 raspbian
-rattr
-RAv
-rawpacket
 rb
-rbegin
 RBL
-RBu
 RBUb
-rca
-rcc
-rclass
 rcode
-rcodecount
-rcodecounters
 rcodezero
-rcontent
-rcounts
-rcp
-rcpgenerator
-rcv
-rcvbuf
+Rcvbuf
 rcvmmsg
-RCVTIMEO
-rdacounts
 rdata
-rdataclass
-rdataset
-rdatastr
-rdatatype
-rdclass
-rdev
-Rdl
-rdlen
-RDLENGTH
-rdlock
-rdnonra
-rdnonrafs
-rdoc
-rdomains
-RDONLY
-rdqaplot
-rdqcounts
 rdqueries
-rds
-rdtype
-RDWR
 rdynamic
-readdir
 readline
-readlock
 README
-readn
 readonly
-realinput
-realname
-realnow
-realpath
-realqps
-realreferral
-realrr
 realtime
-realzone
-Rebm
-RECCONTROL
-reccount
-receiveerrors
-recloc
 reconnections
-recordbuffer
-recordcomment
-recordcontent
-recorddata
-recordheader
-recordlen
-recordname
-recordorder
-recordplace
-recordscount
-recordstart
-recordstorage
-recordttl
-recpacketcache
-recparts
-RECRUN
-recsig
-rectrc
 recursor
-recursorcache
-recursorcmd
-recursorconf
 recursordist
 Recursordoc
-recursorlog
-recursortests
 Recuweb
 recv
 recvbuf
-recvbytes
-recvcounter
-RECVDSTADDR
-recvec
 recverr
-recverrors
 recvfrom
 recvmmsg
 recvmsg
-RECVPKTINFO
-recvtv
-reczones
 redelegations
 redhat
-redirectresponses
-redistributors
 redjack
 reentrantly
 refactor
 Refactoring
-refcnt
 refcount
-refcursor
-referals
-refferals
 refman
 refreh
 refuseds
-refuseemptyar
-refusefouran
-refusenoan
-refusens
-refuseoptinar
-refusetwoar
-regcomp
 regex
-regexec
-regexp
-regexstr
-regfree
-reginfo
-regist
-regm
-regmatch
 reid
 reimplementation
 Reinier
-reinit
 Rejo
 relbar
 relbarbgcolor
 relbarlinkcolor
 relbartextcolor
-reldir
-relqname
 relro
 Remco
-remdomains
 remi
-remlat
-remlen
-remlong
 remoteaddr
 remotebackend
-remotedosec
 remoteip
-remotelen
-remoteloader
-remotelogger
-remotename
-remotering
-remotesec
-remotetype
 remoting
 removedomainkey
-rentry
-reparse
 replacerrset
-replen
-reqinfo
-reqs
 requery
-requestbuilder
-requestorid
-requestorstr
-requestvb
-requeue
-requeueing
-resanswers
-resetring
-residx
-resizering
-resnum
-reso
 resolv
-resolvconf
-resolveret
-RESOLVERIP
-resourcelimits
 respawn
 respawning
-responsebyterate
-RESPONSEIP
-responsestats
 respout
-resprulactions
-respsize
-resquestions
-ress
-restfunc
-restoreflags
-resv
+respsizes
 Resync
 resynchronise
-retargetcount
-retargeted
-retkeyset
-retlen
-retline
-retq
 retransfering
-retre
-returncode
-retval
-reuseaddr
 reuseds
 reuseport
 Reuwiei
-revents
-revsets
-revzone
-revzonedata
 rfc
-rfds
-rfind
-Rfv
-RFZJVWl
-rgacogne
-rgba
-rhandle
 rhel
-rhs
-rhscount
-rhsoopts
-rhspos
-ri
 Rietz
-rightcolumn
-rightiter
 rightsidebar
 Rijsdijk
 ringbuffer
-ringmeta
-ringname
-ringsize
-riter
-Rj
-Rjd
-Rjk
-rk
-Rkc
-RKEe
 rkey
-Rkx
-rl
-rlen
-rlim
-rlimit
-RLNl
-rlocks
-rmailbx
-RMCD
-rmd
-rmdir
-Rminor
-rmtree
-RMz
+RLIMIT
 rname
-rnameservers
 rng
-rnow
-RNWc
 rocommunity
 Roel
-rollbackmarker
-romap
-RONLY
-rootdnsname
-roothints
-roothintspath
-rootkey
-rootnodot
-rootoid
-rootptr
-rootupdate
-rootzone
-ROqu
 Rosmalen
-rotxn
 roundrobin
-roundtrip
-roundtripped
-routingtag
-rowid
-roystgnr
 rp
-Rpa
-rpacket
 RPATH
-rpc
-rpi
 rping
-rplookup
-rpmbuild
-rpmdev
-rpmdevtools
 RPMS
-rpmtest
-Rprj
-Rptim
-rpu
 rpz
-RPZIXFR
-rpzloader
-RPZNSDNAME
-RPZNSIP
 rpzstatistics
-RPZXFR
-rqclass
-rqname
-rqtype
 Rqvg
-RQw
 rr
-RRA
-rrc
-rrclass
 rrcontent
 rrd
 rrdata
 rrdtool
-rrget
-rrhs
-RRIn
 rrname
-rrout
-rrscount
 rrset
 rrsig
-rrsigds
-RRSIGIn
-rrsigkey
-rrsigncomp
-RRSIGTTL
-rrterm
-rrthrowaway
-RRto
 rrtype
-rrudr
-rrvalue
 rsa
-rsakey
-rsamd
 rsasha
-rsautl
-rset
-RShr
-RSIG
-Rsjs
-rsock
 RSP
 rst
-rstrip
 rsync
-rtag
-rtf
-rthreads
-RTLD
-rtr
-rttl
-rtype
 ru
-ruben
-rubenkerkhof
-rubygems
 Rueckert
-RUFM
 Rul
-rulaction
-ruleparams
-ruleresult
 rulesets
-Rumu
-runcond
-runing
-runlevel
-runtests
-RUNWRAPPER
-rusage
 Ruthensteiner
-RUTQ
 rv
-rval
-RVARS
 Rvd
-RVe
-RVel
-RVM
-RVpn
-RVSBz
 rw
 Rwgj
-rwl
 rwlock
-rwtxn
-rwxr
-Rxgrk
-RXXw
-RY
-rz
-rzrp
-SAccept
-saccount
-saddr
-safesearch
+Rzs
 Sakaguchi
-salen
-saltlen
 saltsa
 sandboxing
 Sangwhan
-sanitizerflags
-sanitizers
-sargs
-sasl
+SASL
 Saunalahti
-savederrno
 saxfr
-sbf
+SBF
 sbin
-SBINARYPATH
-SBind
-sbindir
-Sbn
-sbuf
 Sbvka
 scalability
-scalarmult
-scert
-sched
+SCHED
 Scheffler
-schemaversion
 Schlich
 Scholten
 Schryver
 Schueler
 schwer
-scl
 SCn
-SCombo
-SConnect
-scontrols
 scopebits
 scopemask
 scriptable
 scriptlets
-SCRIPTNAME
-SCSV
-sdata
 sdb
-sdfdhhgj
 sdfoijdfio
-sdfsdfs
-sdfsdfsfd
 sdig
-SDIGBUFSIZE
-sdist
-sdns
-sdomains
-sdynamic
-searchclass
-SEARCHENGINE
-searchkey
-seckey
-secp
 secpoll
-secpollthread
-secretapikey
-secretbox
-secretcommunity
-SECRETKEYBYTES
-secretpassword
-secretuser
-secsfrac
-sectionname
-SECUREDOFFERS
 securesphere
-securezone
 securitypolicy
 securitypolling
 seealso
-SEEDBYTES
-seekg
-seenauthsoa
 segfault
 selectmplexer
-selfanswered
-selfansweredresprulactions
-selfstat
 selinux
-sendall
 senderrors
 Sendetzky
-sendfromto
-sendit
-sendlines
-sendmmsg
 sendmsg
-sendout
-SENDSRCADDR
-sendto
-sendupdate
-Sensi
 sensistive
-sepa
-seqinit
-seqnext
-Sequanux
 Sergey
-serialtweaker
-servercmd
-serverdiff
-serverdownmaxfails
-serverdownthrottletime
-serverid
-serveridentity
-serveridstr
-serverlist
 servername
-serverparseerrors
 serverpools
-serverproc
 serverselection
-serverset
 servfail
-servfailanswers
-servfailps
-servfailqueryring
-servfailrate
-servfailratio
-servfailremotering
-servfailremotes
-Servlet
 setaffinity
-setbuf
-setcd
-setcdviaaction
 setcontent
 setdomainmetadata
-setecsaction
-setenv
-setf
-SETFD
-SETFL
 setgid
-setgroups
-sethook
-SETID
 seting
-setitimer
 setkey
-setmetatable
-setname
-setnegativeandsoa
 setnotified
-SETOF
-setopt
 SETPIPE
-setprecision
-setrlimit
-setscheduler
-setsid
-SETSIZE
-setsockopt
-settimeout
-settsigkey
 settting
 setuptools
-setuptree
 setvariable
-SEv
-SEZ
-sformat
 Sgs
 Shafir
 shantikulkarni
-SHBt
-SHELTEK
-SHFP
 shinsterneck
-Shk
-shlibs
-shm
-sholder
+Shm
 showdetails
 showflags
-showinitializer
-showserversopts
-showvar
-shrinked
-shuf
 Shukla
-shutil
-Shutterstock
 sid
 SIddm
 sidebarbgcolor
@@ -5254,169 +1749,67 @@ sidebarlogo
 sidebarsourcelink
 sidebartextcolor
 sidebarwidth
-sideeffect
 sidn
-sidx
 SIGABR
-SIGABRT
-SIGALARM
-SIGALRM
-SIGCHLD
-sigdr
-sigexpire
-sigfigs
-SIGFPE
-SIGHUP
-siginception
 sigint
-SIGKILL
-siglen
 Sigmod
-signaturecache
 signedness
-signingpipe
 Signingpiper
 signpipe
 signttl
 signzone
-Sigoure
 SIGPIPE
 sigs
-SIGSEGV
-sigset
-sigterm
+SIGTERM
 SIGUSR
-SIGVTALRM
-sillyrecords
-simplea
-simplebind
 singlethreaded
 Sinstallation
 Sipek
-siz
-sizecounters
 sizeof
-Sj
 Sjoerd
-skb
-skeyset
-SKIPIT
-skiplua
-skipreasons
-skiprow
-skiptests
-SKnd
 slapd
 slapindex
-slavecommunicator
 slaveness
-slaveport
-slaveschanged
-slen
 SLES
-slist
-SListen
-slo
-Slp
-SLQ
-smallquerylargeresponse
-smaps
 smartcard
 smb
-smech
 smellyspice
 smfgo
 smimea
-Smirnov
-smlen
 smn
-smokeyjoe
-smp
-smt
-smtarg
 smtp
 Smurthwaite
-SMy
-smysql
-Smz
-sname
-snaplen
 Snarked
-snd
 sndbuf
-SNDTIMEO
-sni
+SNI
 snmp
 snmpd
-snmpv
+SNMPv
 snprintf
-Snv
 soa
-SOAAXFR
-soacount
 soadata
-SOAIn
-SOANo
-SOANXD
 soarecord
-soaret
-soarr
-soatimes
-SOATTL
-socat
 sockaddr
-socketaddress
-socketclose
 socketdir
-socketfamily
-socketname
-socketpair
-SOCKETPATH
-socketprotocol
-sockfd
-sockgroup
-socklen
-sockmode
-sockname
-sockowner
-socktype
-sodbc
-sodcrypto
-sodiumsigners
 softhsm
-sokolov
 solaris
 SOLc
 Soldaat
 SOMAXCONN
-somedata
 somedomain
-someiostream
-somekey
 Sonix
-sor
 Soref
 Soroceanu
-sortcname
 sortlist
 sourcecode
 SOURCEDIR
 sourceforge
-sourceip
 sourceware
 Spaans
-spacelen
 Spackages
-spacket
 spam
-sparam
-sparc
-specifictest
-spectest
-speedtest
+Sparc
 spf
-SPg
-spgsql
 SPHINXBUILD
 sphinxcontrib
 sphinxjsondomain
@@ -5424,1458 +1817,424 @@ SPHINXOPTS
 SPHINXPROJ
 sphinxsidebar
 sphinxsidebarwrapper
-splitlines
 splitsetup
-splot
-spm
-spongerng
-spoofaction
-spoofedcname
-spoofrawrule
-spoofrule
-spos
 sprezzos
 Spruyt
-SQda
 SQk
 sql
-SQLCHAR
-sqlcmd
-sqlext
-SQLHANDLE
-SQLHDBC
-SQLHENV
-SQLHSTMT
-SQLINTEGER
 sqlite
-SQLLEN
-SQLPOINTER
-SQLRETURN
-SQLSMALLINT
-sqlstate
-sqlstr
-SQLTCHAR
-SQLULEN
-sqname
-sqt
-sqtype
 srandom
 src
-srcdir
-srcmask
 srcname
-SRd
 SRecord
-sresult
-srl
-Sro
-SRPMS
-SRSLY
 Srule
 srv
-SRx
-Ssb
-sscanf
-SSetsockopt
-ssh
+SSH
 sshfp
 ssi
-ssize
 ssl
-sslctx
-SSLECDSADNS
-SSLEDDSADNS
 sslmode
 sslrootcert
-SSLRSADNS
-sslsock
-SSLTLS
-SSLTLSIO
-SSLv
-SSocket
 SSQ
-ssql
-ssqlite
-ssr
-sstorage
-sstream
-sstuff
-Ssystem
-stackoverflow
+SSql
 stacksize
-stacktrace
 standalone
-Starovoitov
-startdir
-startpos
-startqueries
-startrecord
-startrecordpos
-startswith
-starttime
 starttls
 starttransaction
 Stasic
 statbag
 statbas
-stateenum
-statemachine
-statesbase
-statfmt
-statfunction
-staticmethod
 statisticitem
 statm
-statmap
-statnames
-statnode
-statnodesince
-statnumentries
-statnumhit
-statnummiss
-Statsv
-stattid
-statuscode
-statvisitor
 stbuehler
-stdcxx
-stddev
 stderr
-stdev
-stdexcept
 stdin
-stdint
 stdio
-stdlib
 stdout
 Stef
 Steinbuch
-stest
-stex
 Stichting
 stickysidebar
 Stillaway
 Stirnimann
 stmt
-Stogner
-stoi
 Stol
-stoll
 Storbeck
-stormap
-storvect
-storvector
 stou
 stoul
-stoull
-strart
-strbind
-strcasecmp
 strcasestr
-strchr
-strcmp
-strdup
-strerr
-strerror
-strftime
-stringappend
-stringbuffer
-stringerror
-stringfgets
 stringmatch
-stringstream
-stringtok
-stringvalue
 strlen
-STRLIT
-strncasecmp
-strncmp
-strncpy
 strpos
-strptime
-strptr
-strres
-strsignal
-strstr
-strtod
-strtol
-structs
-sttl
 stubquery
 stubresolver
 stunnel
 Stussy
 stutiredboy
-STX
 stylesheet
-stype
-subdir
 subdomain
-subgrouping
 subkey
-Subnetcheck
-subnetlist
 subnetmask
-subqueries
-substr
-SUBSTVARS
-subsys
-suckdomains
 sudo
-suffixmatch
 suffixmatchtree
-suffixother
-suffixtype
-SUh
 supermaster
 supermasterbackend
-SUPERMASTERCONF
 supernotification
 supersecretpassword
 superslave
 superslaving
 supervisord
-suppliedrecords
 Surfnet
-suse
-suseconds
-sval
-svg
-SVj
-svn
-svr
-svstat
+SUSE
 swapcontext
 swe
-Swfi
 swoga
-SWUOQ
 sx
-SXRFNm
-SYlm
-symlink
+Symlink
 syncres
-Syq
 sys
 sysadmin
 syscall
-sysconf
-sysconfdir
+SYSCONFDIR
 sysctl
 Sysdream
 syslog
-sysmsec
-sysobjects
-sysperc
 systemcall
 systemctl
 systemd
-systemdsystemunit
-systemdsystemunitdir
-systm
 sysv
 SYW
-sz
-Szd
-Szps
+SZ
 szw
-tac
-TAEw
-tagfile
-tagp
-tagsstr
-tagthis
-tahoe
-taiepoch
-TAMg
-tanhqgv
 tarball
-TARBALLFILE
-TARBALLPREFIX
-TARBALLVERSION
-tardirname
-targetlen
 Tarjei
 Tarnell
-tbaggery
 tbhandler
 tbody
-Tbz
-tcache
-tcbit
-TCEDNS
 tcely
-tcgetattr
-TCO
 tcp
-tcpa
-tcpaaaa
-tcpavgconnduration
-tcpavgconnectionduration
-tcpavgqueriesperconn
-tcpavgqueriesperconnection
-tcpbench
-tcpbytesanswered
-tcpclient
-tcpclientimeouts
-tcpclientthreads
-tcpcurrentconnections
-tcpdiedreaddingresponse
-tcpdiedreadingquery
-tcpdiedreadingresponse
-tcpdiedsendingquery
-tcpdiedsendingresponse
-tcpdownstreamtimeouts
-tcpdrops
 tcpdump
-TCPEDNS
-tcpgaveup
-tcphdr
-TCPIO
-tcpiohandler
-tcpka
 TCPKEEPALIVE
-tcpnameser
-TCPNo
-tcpnumanswered
-tcpoutqueries
-tcppacket
-tcpqcounter
-tcpquestions
-tcprange
-tcpreadimeouts
-tcpreadtimeouts
-tcpreceiver
-tcpspeeds
-TCPTLS
 TCPv
-tcpwritetimeouts
-TCSANOW
-tcsetattr
 Tctk
 td
-tdate
-tdi
-TDIBy
-TDja
-TDn
-tdomains
-tdsmap
-Tdsza
-TDt
-TDVXa
 teeaction
-Telekom
 Telenet
-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
 testrunner
-testschema
 testsdir
-testsuffix
-testsuite
-testt
-testuser
 texinfo
-texlive
 textcolor
-tf
-tfh
-TFILE
-TFN
-tfo
 tfoot
 Tful
-tfunc
 TGJGVc
-THandler
 thead
-theirserial
 thel
 thelog
 Thessalonikefs
 Thiago
 thinko
-thislock
-thisupd
-thiszone
 Thomassen
-thr
-threadcloser
-THREADFLAGS
-threadname
 threadsafe
-threadwrapper
-throttledout
-throttledqueries
-throttleentries
 throttlemap
 thrysoee
-THTM
-thu
-tickinterval
-tidx
-timedelta
-timediff
+Thu
 timedipsetrule
 timedout
 timeframe
-timegm
 timeline
-timeoutsec
-timeoutspec
-timersonly
-timesec
 timesource
-Timespan
-timespec
-timespent
-timeusec
 timeval
 timezone
-tinfo
 tinycdb
 tinydns
 tinydnsbackend
-TINYDNSDATA
-tinydnsloader
-TINYINT
 tisr
-TIsy
-Tj
-TJR
-tkdb
-tkey
-TKllk
-tkrc
-tl
+TKEY
 tld
-tlen
-Tlh
-TLKA
+tls
 tlsa
-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
 tmp
-tmpbuf
-tmpdh
-tmpdir
-tmpfd
-tmpfile
 tmpfs
-tmpkey
-tmpnam
-tmpname
-tmpstr
-tmsg
-tname
-tnameservers
-Tno
-tnode
-tnow
-tns
-toaddr
 tobool
 toc
-tocheck
 toctree
 todo
-TODOLIST
-toh
 toint
-tok
-tokenizer
-tokill
-TOLO
+tokenuser
 tolower
 Tolstov
-tonumber
-toolong
-toolongtobevalid
-toomuchinfo
-topbar
-toport
-toportstr
-toroot
-TOSBy
-toserial
 Toshifumi
-toskip
-tosql
 tostring
-totadd
-totar
-totcount
-totcumul
-totlat
-totmperc
-totpairs
-totpcache
-totrdatalen
-totremove
-tottime
-toupper
-toxml
-toysdig
-tozero
-TPL
-tpos
-tptr
-TQBv
-TQJv
-transactiondomain
-transactiondomainid
 Travaille
-trc
-trecords
-treeview
 tribool
-trilab
-trillian
-trl
-tro
-trollololoooolll
 trunc
-Truncateds
-truncatemarker
 trustanchor
-trustanchorserver
-trustedkeys
 trusteer
-TRw
 trx
 trxid
-tryrdlock
-tryrwlock
-trysuperdomains
-trywait
-trywrlock
 tsc
-tscomp
-tsdelta
 tsig
-tsigalgname
 tsigalgo
-tsigalgorithm
 tsigkey
-tsigkeyname
 tsigname
-tsigprevious
 tsigsecret
-TSIGTCP
-tsigtimersonly
-tsigutils
-tsigverifier
-tsstorage
 tstamp
-Tstbt
 TSU
-tsuna
 tt
-ttd
-ttdi
-ttdindex
-ttdwatch
 ttl
-ttllimit
-TTLNo
-TTLNX
-TTLRPZ
-ttltooshort
-ttsig
 Tuinder
-Tuk
 tunables
 tuomi
 Tushuizen
 Tuu
 Tuxis
-TUz
 TVJRU
-Tvq
-TVU
 tw
-TWk
-TWl
-twopt
-txn
-ty
 tylerneylon
-typedef
-typedns
-typeenum
-typeid
-typeinfo
-typemap
-typename
-typestr
-TYPETOK
+typedefs
+typenames
 tyu
-TYX
-tz
-TZDU
-TZOFF
-TZud
+TZ
 ualberta
-uapi
-ub
-UBIGINT
 Ubo
-ubsan
 ubuntu
-UBXc
-ucf
-ucfq
-ucfr
-uchar
-UCLIBC
-ucontext
-UCPEd
-ucspi
-Uctchk
-udiff
-Udipd
-Udm
 udp
-udpanswers
-udpbytesanswered
-udpclientsocks
-UDPECS
-UDPEDNS
-udphdr
-udpin
-udpnumanswered
-udpoverruns
 udpqueryresponse
-udpsize
-udpsock
-udpspeeds
-udpv
+UDPv
 udr
-udrdbp
-UDRYNm
-UDWORD
-ue
 Ueber
-ueberbackend
 Ueli
-Ueuwr
-ufc
-UFt
-ufx
-uglifyjs
-uhry
 uid
 uint
-uintptr
-uio
 Uisms
-uitoa
 uj
-Ujd
 uk
-uki
-Ukj
-Ukvz
-UKyg
 ul
-ulen
 ulimit
-ulong
-Ulws
-UML
 Umq
-ums
-uname
 unauth
-unauthtcp
-unauthudp
-unavailables
-unboundhost
 unbreak
 uncached
-uncanon
-uncomment
-uncompress
-unconfigured
-undef
-UNDOC
-unescape
 unescaping
 unfresh
 unhash
-unhexlify
 unicode
 uninett
 uninitialised
-uninstall
 Uninstaller
-uniq
-uniquw
-UNIREGISTRYMARKET
 unistd
-unitdir
 unitialized
-unittest
-unixconnector
 unixodbc
-Unixsocket
 unixtime
-Unknownqueries
-unknownrecordcontent
-unlicense
-unloadable
-unmap
 unparseable
 Unprocessable
 unpublish
 unpublishdomainkey
-Unpublishing
-unquotify
 unreachables
-unregist
 unregister
-unreport
 unshadowing
-UNsockaddr
-UNSPEC
-Unthrottling
 untruncated
-UNUTTe
 unzero
-uo
-UOHk
-uom
-Uor
-uordblks
 upd
 updatepolicy
-UPi
-Uploaders
-UPnhs
 upperalpha
-uppercasing
 upperroman
-upq
-upto
 urandom
-urc
 uri
 url
 urlencoded
-urljoin
-urllib
-urlmap
-urlparse
-urlsafe
-uroot
-usazzz
 usec
 usecase
-useconds
-uselessdrc
-useradd
 useragent
 userbase
-userdata
-userfriendly
-usermsec
 username
-userperc
 userspace
-USHRT
-usleep
-usm
 usr
-USs
-ustar
-utexas
 utf
-utils
-utime
-Utmc
-utype
 UUHJWZg
 uuid
-uupdate
-Uuser
-UVARIABLES
 uwaterloo
 Uwcjbp
-Uwhjf
 Uwi
-uwisza
-uwopt
-UXsnr
-Uxt
-Uy
-UYx
 Uyypn
-vab
 Valentei
 Valentini
 valgrind
 validationstates
-validator
-validkeys
-validns
-validpacket
-validresponses
-validrrsets
-valign
-valiter
+validators
 Valkenburg
-vallid
-valmask
-valr
-valrandom
-vals
 Vandalon
 vandergaast
 Vandoren
-varbinary
-varbind
-varchar
-varlist
-varmap
+VARCHAR
 varname
-varval
 Vasiliy
 VBG
-Vbi
 Vbt
-vby
-Vcs
-VDLs
 VDRAW
 VDz
-vec
-vect
 Veldhuyzen
 venv
-veorq
-verboselog
-verhaaltje
 Verheijen
-VERIFYHOST
-VERIFYPEER
-verifyzone
-verisign
-verisignlabs
-VERq
+Verisign
 Verschuren
 versionadded
-versionbind
 versionchanged
-versioncommand
-versionmangle
 versionmodified
-versionpdns
-vertpre
-verylongstringlongerthan
-verysecret
-vf
-vfree
 vh
 viewcode
-VInf
-vinfolog
 virtualenv
 virtualized
 visitedlinkcolor
-VIuk
-VIW
 vixie
-Vj
-Vjda
-VK
-vkuiv
-vla
-vlen
-Vlh
-Vll
 vm
-vmbe
-vml
-Vmm
-Vmp
 Vn
 Voegeli
 Volker
 voxel
-VPA
-vpacket
-vpos
-VPQ
-vptr
-VQBWQ
-VQda
-Vra
 Vranken
-Vrbp
-Vre
-vrooooom
-vrr
-Vscj
 Vsgoi
-vstate
-vstring
-vstringtok
-vtable
-VTd
-vtnq
-Vuux
-Vw
 Vwgbclzx
-VWlr
-vww
-Vx
-vy
-VYBP
-Vyl
-Vz
-Vzd
+VY
 vzu
-WAITALL
 WAITFORONE
-waitpid
-waitpoint
-waitstatus
-waitstatusenum
-waitval
-WAja
 wal
 wallclock
-wantsnsid
 warnlog
 Wascwa
-wastcp
-wattr
-Waxyl
-wbaw
 Wbq
-wcard
-wcarddomain
-wcmatch
-WCN
-wcname
-WCOREDUMP
-wcplusencloser
-wctx
-wday
-weanswers
 webbased
 webdocs
-webetter
 webhandler
-webhndlr
-webkit
 webpassword
-WEBPORT
-webrick
-webserv
 webserver
-webserveropts
 website
 Webspider
-wednserrors
-weekno
 weightparams
 Weimer
-weirdtxt
 Welzel
-wenever
-Werror
 Wessels
 westes
-wetimedout
-weunmatched
-WEV
 Wevers
-WEXITSTATUS
 wextra
-wfile
-WFQTVE
-Wfxq
-wget
-Wgp
-WGqnuy
 Wgx
 whashed
-WHg
-Whij
 whitelist
-whitelisting
-whoami
 Wia
-wichert
+Wichert
 Wieger
 Wielicki
-WIFEXITED
-WIFSIGNALED
 Wijk
 Wijnand
 Wijngaards
 wiki
 wikipedia
 wil
-wildcarddnsname
 wildcarded
-wildcardname
 wildcards
-wildzone
 Willcott
 windr
 Winfried
-wiplist
 wireformat
-wirelen
 wirelength
-wireshark
 Wisiol
-withecs
-withedns
-withednsecs
-withednsnoecs
-withoutedns
-withport
-withval
-Wj
-WJj
-WJO
-Wklm
-wlat
-wli
-wlist
-Wll
-wlocks
-wlon
 wmc
 Wmissing
-Wmpt
-Wmpx
-WNfpw
-Wno
-WNOHANG
-Woi
 Wojas
-Wor
-wordpad
 workaround
-workdir
-workflow
 Worldnic
 would've
 wouter
-Wowie
 wpad
 wproduction
-Wpv
-WQ
-wrand
 wrandom
 Wrange
 Wredundant
-wret
-Wri
-writeability
-WRITEDATA
-WRITEFUNCTION
-writen
 writev
-wrlock
-WRONLY
-Wsg
 Wshadow
-wshash
-WSIZE
-Wsqvx
-Wswaps
-WTCSr
-WTERMSIG
-wtest
-Wtl
-Wtqlj
 wuh
-wv
-WVZHd
 ww
-wwa
-wwho
-wwjb
-WWo
-wwv
 www
-wwwds
-wwwezdnsit
-wwwpowerdnscom
-WWWPREFIX
-wwx
-wwz
-WX
-WXA
-Wxb
-Wxe
-Wxm
-WXTAH
 WYbw
-Wyc
-WYjld
-wz
-WZERM
 Wzqf
-xa
-xaa
-xaaa
-xad
-XADD
-xaf
+Wzs
 Xander
-XAPI
-xargs
-Xautonomous
-Xbz
-xca
-xce
-xception
 xchacha
-xchange
-Xcml
-xcrypt
-xcy
+Xchange
 xdb
-xdigits
-XDqdg
 Xek
 Xeon
-XEz
 xf
-XFDj
-xff
-xffverylongstring
 xfr
-XFRM
-xfrserver
-XFv
-xfz
-xg
-Xga
-xgp
 xh
-XHhl
-xhr
 xhtml
-xib
-xit
-xj
-Xk
-XKpw
-Xkqi
-xlabel
-xluajit
-xm
+XM
 xml
-XMy
-xn
-XNGCH
-xno
-Xnv
-Xo
-Xof
-xor
 xorbooter
 xpf
-xpfcode
-XPFDATA
-xpfdst
-xpfproto
-xpfsrc
-xpfversion
-Xpk
-xpong
+XPFCODE
+XPFDST
+XPFPROTO
+XPFSRC
+XPFVERSION
 Xpw
-Xq
-xrange
 XRecord
-XRL
-XRq
-xsalsa
-XSKk
+XSalsa
 xss
-XSyam
-Xtext
-xtics
-xtrue
-xugtk
-XUgz
-xunit
-xv
-xvf
-XXh
-XXxqrt
-XXXXg
-xy
-xyes
-XYk
 XYR
-xz
-xzvf
 yahttp
 yaml
 yb
-ybndrfg
-YBSI
-yc
-yday
 YDyzc
-Yegst
 Yehuda
-YEQRBVK
-yetanother
-Yf
-yfb
-YFLAGS
 YG
-YGu
-yh
-Yhh
 YHk
 YIBo
-yiss
 Yiu
 Yj
-YJou
-yjxe
 yk
-YKMY
-ykyb
-YLa
-ylabel
 YLCOy
-YLGg
-Ylh
 Ylitalo
-ylwrap
-Ymf
 yml
 YMMV
-YNBIs
-Yng
-Yogz
-YOia
-yop
-YOUNAMEIT
 yourcompany
 yourdomain
 yourorganization
 yoursecret
-Yoyodyne
 yp
-yq
-Yqi
-YSa
-YSfp
-YTg
-ytics
-YTyj
+YQ
 yubikey
-YUTh
-YVs
-Yw
-YWA
-YWJx
-YWJYRXp
-YWls
-YWQ
-ywu
-YWVJf
-yx
-yxdomain
-YXos
-YXRR
-yxrrset
+Yx
+YXDOMAIN
+YXRRSET
 yy
-yydebug
-yyerror
-yyin
-yylex
-yylval
-yyparse
-yyrestart
-YYSTYPE
-yyterminate
-yytext
-yywrap
-yyy
 YYYYMMD
 YYYYMMDD
-YYYYMMDDH
 YYYYMMDDSS
-yz
-zackw
-ZAWs
-zbefore
-zc
-Zcdnskey
-ZCLASS
-Zd
-Zdelegated
-zdf
-ZDFi
-Zdnssec
-ZDV
 Zealey
 zeha
 Zengers
 Zengin
 zeromq
-zeromqrb
-zeroport
-Zexample
 Zfndz
-Zg
-ZGq
-Zgta
-Zgw
-Zhc
-ZHE
 Zhf
-ZHJp
-ZHml
-ZIf
 zilopbg
-Zinsecure
-ziter
-ZJA
 ZJad
-Zjmco
-Zjq
-zl
-zlib
-Zm
-zmakerfunc
-zmakermap
 Zmd
-Zminimal
-zmq
-zmqconnector
-zname
 ZNLY
-Znztest
 Zoag
-ZOMG
-zonecontent
-zonecount
 zonecryptokey
-zonecut
-zonedata
-zonedataline
-zonedomain
 zonefile
-zonefilename
-zoneid
-zoneinfo
-zonekind
-zonelevel
-zonelist
-zonemaster
 zonemetadata
 zonename
 zoneparser
-zoneparsertng
-zonestring
-ZONETOK
 zonetransfer
 Zonneveld
-zp
-zpt
-ZQ
-ZQOZ
-ZQSUOf
-zr
 ZRev
-Zrm
-zrr
-Zsecure
 zsk
-zskds
-zskeys
 zskroll
-Zstest
-Zsu
-Zsub
-zt
-Ztest
-ZTh
-Ztsig
-ztype
-zu
-zugschlus
-Zuhz
+Zt
 Zumstrull
-Zun
-Zv
-ZVNIQn
-Zw
 Zwane
-Zwtest
-ZWTQ
-ZWxz
-Zx
-ZXJETl
 zz
-ZZj
 zzyzz
-zzz
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/
index abc15f239510cdb81a486fb82134205cde246189..d3dcbfaa958baee41658aa50a13b066c1bb8873a 100644 (file)
@@ -5,9 +5,18 @@ on:
       - "**"
     tags-ignore:
       - "**"
-  schedule:
-    # * is a special character in YAML so you have to quote this string
-    - cron: '15 * * * *'
+    paths:
+      - "docs/**"
+      - "**/docs/**"
+  pull_request:
+    branches:
+      - "**"
+    tags-ignore:
+      - "**"
+    paths:
+      - "docs/**"
+      - "**/docs/**"
+    types: ['opened', 'reopened', 'synchronize']
 
 jobs:
   build:
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 9f7b35a672a15f25f18f7e9586bd8f7716fd7ea4..e0bda5d9d2ec777cfaa3fc6edee06bdf55aaf4d8 100755 (executable)
@@ -29,7 +29,6 @@ done
 [ -z $MODULES ] && echo "No module directory found" >&2 && exit 1
 
 # Symlink the modules on the system
-cd regression-tests/modules
 for backend in *.so; do
   ln -sf $MODULES/$backend $backend
 done
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 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 91d7b98e488dcaa7a7e8bd153be0172114e2c3ba..304916cf0263a82d3e3c73ffa05fa20fe6c5b023 100755 (executable)
@@ -55,7 +55,7 @@ override_dh_installinit:
 
 override_dh_install:
        dh_install
-       ./pdns/pdns_server --no-config --config=default | 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 8aca8fae2724736681c1d01b7092ee396cb797ab..5cc5cbb7c92d5194476907e021c1e06bdf101d17 100755 (executable)
@@ -52,7 +52,7 @@ override_dh_installinit:
 
 override_dh_install:
        dh_install
-       ./pdns/pdns_server --no-config --config=default | 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 a1d62ac1fa7160e111d5c1e2447ee82f0178fe4f..28e42be93b5e535fba3384df67adde3181c33e48 100755 (executable)
@@ -51,7 +51,7 @@ override_dh_installinit:
 
 override_dh_install:
        dh_install
-       ./pdns/pdns_server --no-config --config=default | 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 43099f8cf36d98f6a4c2cc11b8bbd33287c58982..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=default | 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!' \
index f3f30aa5fcf6e981b12cc138c213bca9b64d8498..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=default | 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!' \
index 43099f8cf36d98f6a4c2cc11b8bbd33287c58982..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=default | 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!' \
index 92150ef77484bb1b37df96903c4f2662977e7f8c..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=default | 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 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 4f0ebdb3c0cce96bcbb0c0fc95b17eb655bee077..7212a97ee7fc8c0abe122da63a54e0265f96eb39 100644 (file)
@@ -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 ab3ba11690e460c4bfec74a34c60ca178088390d..f933370da766d34c2f9c658a2ed47ee64c099bb2 100644 (file)
@@ -1,4 +1,4 @@
-@       86400   IN  SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020052000 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.
 
@@ -202,14 +202,17 @@ recursor-4.1.12.security-status                         60 IN TXT "3 Upgrade now
 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 "1 OK"
+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 "1 OK"
+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)"
@@ -218,8 +221,11 @@ recursor-4.3.0-beta2.security-status                    60 IN TXT "3 Unsupported
 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 "1 OK"
-recursor-4.4.0-alpha1.security-status                   60 IN TXT "1 OK"
+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/"
@@ -349,4 +355,7 @@ dnsdist-1.4.0-rc5.security-status                          60 IN TXT "2 Unsuppor
 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 "2 Unsupported pre-release (no known vulnerabilities)"
-dnsdist-1.5.0-rc2.security-status                          60 IN TXT "1 OK"
+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 8de9398fbc311e22e5a5bab4630c67eb732a7b9c..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:
 
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 27b4b81d1130a3dab394fb60d0bc78eb6ebe5df1..71a3fe86c98b3c6a6bbf16cc9190ad4342d65747 100644 (file)
@@ -1265,7 +1265,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 \
@@ -1344,11 +1344,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
 
@@ -1626,6 +1627,7 @@ 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 5fd968c82cb55095b9516d3009988d3226a07803..3f839d2b6bd2a7c907264de4291b7b4c11b83635 100644 (file)
@@ -40,16 +40,13 @@ try
 
   string namespace_name=arg()["carbon-namespace"];
   string hostname=arg()["carbon-ourname"];
-  if(hostname.empty()) {
-    char tmp[HOST_NAME_MAX+1];
-    memset(tmp, 0, sizeof(tmp));
-    if (gethostname(tmp, sizeof(tmp)) != 0) {
-      throw std::runtime_error("The carbon-ourname setting has not been set and we are unable to determine the system's hostname: " + stringerror());
+  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());
     }
-    char *p = strchr(tmp, '.');
-    if(p) *p=0;
-    hostname=tmp;
-    boost::replace_all(hostname, ".", "_");
   }
   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");
 
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 f5a777c1435c59713b50dc6a80e280ba5982915f..fe4994cc1392e12ad86babd5a76d95682f7e0ba1 100644 (file)
@@ -333,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.");
@@ -344,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");
index a58d35224d5eb5b23baa060e485efb25e1650b1d..fd1f168f0bfd66e75991ee03755324bf302ccdaa 100644 (file)
@@ -54,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 c5c1381d0b5ac09dcfe3052a32663919304c25a1..53475dfa9e60823920bdc088113fd1becd457443 100644 (file)
@@ -58,16 +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[HOST_NAME_MAX+1];
-        memset(tmp, 0, sizeof(tmp));
-        if (gethostname(tmp, sizeof(tmp)) != 0) {
-          throw std::runtime_error("The 'ourname' setting in 'carbonServer()' has not been set and we are unable to determine the system's hostname: " + stringerror());
+      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());
         }
-        char *p = strchr(tmp, '.');
-        if(p) *p=0;
-        hostname=tmp;
-        boost::replace_all(hostname, ".", "_");
       }
       const std::string& instance_name = conf.instance_name;
 
@@ -89,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";
@@ -113,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 a49a9f913605d645acedac8d74aa95dc20d83253..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");
@@ -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 8bf331ca365e9133935f6c968850ffe70f98426b..52d05107f5599c507d1509940eb4e0fe1bf3f340 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 a66086d89b4dc122618b3cc10b9d2b90fcef1d4c..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 \
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());
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 544e6157c202ec903eb01b644f614e8ee16f2dd5..7dc7a90e9241367e1dc92d70d146248771dd1cb0 100644 (file)
@@ -1,6 +1,124 @@
 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
@@ -758,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
@@ -1273,7 +1391,7 @@ Changelog
     :pullreq: 7585
     :tickets: 7534
 
-     Prevent 0-ttl cache hits
+    Prevent 0-ttl cache hits
 
   .. change::
     :tags: Improvements
@@ -1451,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
@@ -1476,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
@@ -1634,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
@@ -1696,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 aa17af85f1bbdd90430b62e00d7320e3e859292f..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);
   }
@@ -55,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();
   
@@ -231,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()
@@ -246,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 48b3748d7fb0688d1386483536886fa8b3566a3d..b8ea84fab3d599d8b3f47e2d183a634bac959c70 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 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 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 445092448ed0985ddaa58e3c17e0a1703c4dc357..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});
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 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 c2cab50064bcdaed5eac56730f1ee3999390b215..8d1d1686191d250f2feb2a1052cc270fca459071 100644 (file)
@@ -173,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) */
@@ -1038,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)) {
@@ -1047,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;
     }
@@ -1062,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;
@@ -1073,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;
     }
@@ -1234,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;
@@ -1428,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);
     }
@@ -1449,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)) {
 
@@ -1513,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) {
@@ -1527,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;
             }
@@ -1589,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;
             }
@@ -1598,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)
@@ -1719,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
@@ -1812,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--;
@@ -1870,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) {
@@ -1929,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) {
@@ -2561,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)
@@ -2823,7 +2855,8 @@ static void handleNewUDPQuestion(int fd, FDMultiplexer::funcparam_t& var)
           }
 
           if(g_weDistributeQueries) {
-            distributeAsyncFunction(data, boost::bind(doProcessUDPQuestion, data, fromaddr, dest, source, destination, tv, fd, proxyProtocolValues));
+            distributeAsyncFunction(data, [data = data, fromaddr, dest, source, destination, tv, fd, proxyProtocolValues]() mutable
+              { return doProcessUDPQuestion(data, fromaddr, dest, source, destination, tv, fd, proxyProtocolValues); });
           }
           else {
             ++s_threadInfos[t_id].numberOfDistributedQueries;
@@ -3189,11 +3222,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;
+          }
         }
       }
 
@@ -3201,22 +3252,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;
         }
@@ -3238,14 +3289,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()
@@ -3515,7 +3572,7 @@ template<class T> T broadcastAccFunction(const boost::function<T*()>& func)
 
     const auto& tps = threadInfo.pipes;
     ThreadMSG* tmsg = new ThreadMSG();
-    tmsg->func = boost::bind(voider<T>, func);
+    tmsg->func = [func]{ return voider<T>(func); };
     tmsg->wantAnswer = true;
 
     if(write(tps.writeToThread, &tmsg, sizeof(tmsg)) != sizeof(tmsg)) {
@@ -3805,7 +3862,7 @@ catch(PDNSException& ae)
 
 string doTraceRegex(vector<string>::const_iterator begin, vector<string>::const_iterator end)
 {
-  return broadcastAccFunction<string>(boost::bind(pleaseUseNewTraceRegex, begin!=end ? *begin : ""));
+  return broadcastAccFunction<string>([=]{ return pleaseUseNewTraceRegex(begin!=end ? *begin : ""); });
 }
 
 static void checkLinuxIPv6Limits()
@@ -3926,7 +3983,7 @@ void parseACLs()
   }
 
   g_initialAllowFrom = allowFrom;
-  broadcastFunction(boost::bind(pleaseSupplantACLs, allowFrom));
+  broadcastFunction([=]{ return pleaseSupplantACLs(allowFrom); });
   oldAllowFrom = nullptr;
 
   l_initialized = true;
@@ -4094,6 +4151,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"));
@@ -4124,6 +4183,15 @@ 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;
@@ -4132,6 +4200,11 @@ static int serviceMain(int argc, char*argv[])
     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;
@@ -4601,6 +4674,9 @@ static int serviceMain(int argc, char*argv[])
     recursorThread(currentThreadId++, "worker");
     
     handlerInfos.thread.join();
+    if (handlerInfos.exitCode != 0) {
+      ret = handlerInfos.exitCode;
+    }
   }
   else {
 
@@ -4645,13 +4721,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)
@@ -4668,11 +4747,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)
@@ -4790,7 +4877,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);
     }
 
@@ -5067,7 +5156,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";
@@ -5166,7 +5255,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");
 
@@ -5178,7 +5267,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;
index ef1204d6ec1d3231742bf7df74f6835143a64b8e..c03bae308214a1f64a6734782ccc7c2d1a6eaa6e 100644 (file)
@@ -32,17 +32,13 @@ try
   if(namespace_name.empty()) {
     namespace_name="pdns";
   }
-  if(hostname.empty()) {
-    char tmp[HOST_NAME_MAX+1];
-    memset(tmp, 0, sizeof(tmp));
-    if (gethostname(tmp, sizeof(tmp)) != 0) {
-      throw std::runtime_error("The 'carbon-ourname' setting has not been set and we are unable to determine the system's hostname: " + stringerror());
+  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());
     }
-    char *p = strchr(tmp, '.');
-    if(p) *p=0;
-
-    hostname=tmp;
-    boost::replace_all(hostname, ".", "_");    
   }
   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 489e81083f5b230af119b0919a8f593736a9b572..b692e076457f301b990d5f94bd3af92229139fce 100644 (file)
@@ -250,7 +250,7 @@ static string doDumpNSSpeeds(T begin, T end)
     return "Error opening dump file for writing: "+stringerror()+"\n";
   uint64_t total = 0;
   try {
-    total = broadcastAccFunction<uint64_t>(boost::bind(pleaseDumpNSSpeeds, fd));
+    total = broadcastAccFunction<uint64_t>([=]{ return pleaseDumpNSSpeeds(fd); });
   }
   catch(std::exception& e)
   {
@@ -281,7 +281,7 @@ static string doDumpCache(T begin, T end)
     return "Error opening dump file for writing: "+stringerror()+"\n";
   uint64_t total = 0;
   try {
-    total = s_RC->doDump(fd) + broadcastAccFunction<uint64_t>(boost::bind(pleaseDump, fd));
+    total = s_RC->doDump(fd) + broadcastAccFunction<uint64_t>([=]{ return pleaseDump(fd); });
   }
   catch(...){}
   
@@ -303,7 +303,7 @@ static string doDumpEDNSStatus(T begin, T end)
     return "Error opening dump file for writing: "+stringerror()+"\n";
   uint64_t total = 0;
   try {
-    total = broadcastAccFunction<uint64_t>(boost::bind(pleaseDumpEDNSMap, fd));
+    total = broadcastAccFunction<uint64_t>([=]{ return pleaseDumpEDNSMap(fd); });
   }
   catch(...){}
 
@@ -364,7 +364,7 @@ static string doDumpThrottleMap(T begin, T end)
     return "Error opening dump file for writing: "+stringerror()+"\n";
   uint64_t total = 0;
   try {
-    total = broadcastAccFunction<uint64_t>(boost::bind(pleaseDumpThrottleMap, fd));
+    total = broadcastAccFunction<uint64_t>([=]{ return pleaseDumpThrottleMap(fd); });
   }
   catch(...){}
 
@@ -386,7 +386,7 @@ static string doDumpFailedServers(T begin, T end)
     return "Error opening dump file for writing: "+stringerror()+"\n";
   uint64_t total = 0;
   try {
-    total = broadcastAccFunction<uint64_t>(boost::bind(pleaseDumpFailedServers, fd));
+    total = broadcastAccFunction<uint64_t>([=]{ return pleaseDumpFailedServers(fd); });
   }
   catch(...){}
 
@@ -435,9 +435,9 @@ static string doWipeCache(T begin, T end, uint16_t qtype)
 
   int count=0, pcount=0, countNeg=0;
   for (auto wipe : toWipe) {
-    count+= broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, wipe.first, wipe.second, qtype));
-    pcount+= broadcastAccFunction<uint64_t>(boost::bind(pleaseWipePacketCache, wipe.first, wipe.second, qtype));
-    countNeg+=broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, wipe.first, wipe.second));
+    count+= broadcastAccFunction<uint64_t>([=]{ return pleaseWipeCache(wipe.first, wipe.second, qtype);});
+    pcount+= broadcastAccFunction<uint64_t>([=]{ return pleaseWipePacketCache(wipe.first, wipe.second, qtype);});
+    countNeg+=broadcastAccFunction<uint64_t>([=]{ return pleaseWipeAndCountNegCache(wipe.first, wipe.second);});
   }
 
   return "wiped "+std::to_string(count)+" records, "+std::to_string(countNeg)+" negative records, "+std::to_string(pcount)+" packets\n";
@@ -538,9 +538,9 @@ static string doAddNTA(T begin, T end)
   g_luaconfs.modify([who, why](LuaConfigItems& lci) {
       lci.negAnchors[who] = why;
       });
-  broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, who, true, 0xffff));
-  broadcastAccFunction<uint64_t>(boost::bind(pleaseWipePacketCache, who, true, 0xffff));
-  broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, who, true));
+  broadcastAccFunction<uint64_t>([=]{return pleaseWipeCache(who, true, 0xffff);});
+  broadcastAccFunction<uint64_t>([=]{return pleaseWipePacketCache(who, true, 0xffff);});
+  broadcastAccFunction<uint64_t>([=]{return pleaseWipeAndCountNegCache(who, true);});
   return "Added Negative Trust Anchor for " + who.toLogString() + " with reason '" + why + "'\n";
 }
 
@@ -586,9 +586,9 @@ static string doClearNTA(T begin, T end)
     g_luaconfs.modify([entry](LuaConfigItems& lci) {
         lci.negAnchors.erase(entry);
       });
-    broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, entry, true, 0xffff));
-    broadcastAccFunction<uint64_t>(boost::bind(pleaseWipePacketCache, entry, true, 0xffff));
-    broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, entry, true));
+    broadcastAccFunction<uint64_t>([=]{return pleaseWipeCache(entry, true, 0xffff);});
+    broadcastAccFunction<uint64_t>([=]{return pleaseWipePacketCache(entry, true, 0xffff);});
+    broadcastAccFunction<uint64_t>([=]{return pleaseWipeAndCountNegCache(entry, true);});
     if (!first) {
       first = false;
       removed += ",";
@@ -643,9 +643,9 @@ static string doAddTA(T begin, T end)
       auto ds=std::dynamic_pointer_cast<DSRecordContent>(DSRecordContent::make(what));
       lci.dsAnchors[who].insert(*ds);
       });
-    broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, who, true, 0xffff));
-    broadcastAccFunction<uint64_t>(boost::bind(pleaseWipePacketCache, who, true, 0xffff));
-    broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, who, true));
+    broadcastAccFunction<uint64_t>([=]{return pleaseWipeCache(who, true, 0xffff);});
+    broadcastAccFunction<uint64_t>([=]{return pleaseWipePacketCache(who, true, 0xffff);});
+    broadcastAccFunction<uint64_t>([=]{return pleaseWipeAndCountNegCache(who, true);});
     g_log<<Logger::Warning<<endl;
     return "Added Trust Anchor for " + who.toStringRootDot() + " with data " + what + "\n";
   }
@@ -689,9 +689,9 @@ static string doClearTA(T begin, T end)
     g_luaconfs.modify([entry](LuaConfigItems& lci) {
         lci.dsAnchors.erase(entry);
       });
-    broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, entry, true, 0xffff));
-    broadcastAccFunction<uint64_t>(boost::bind(pleaseWipePacketCache, entry, true, 0xffff));
-    broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, entry, true));
+    broadcastAccFunction<uint64_t>([=]{return pleaseWipeCache(entry, true, 0xffff);});
+    broadcastAccFunction<uint64_t>([=]{return pleaseWipePacketCache(entry, true, 0xffff);});
+    broadcastAccFunction<uint64_t>([=]{return pleaseWipeAndCountNegCache(entry, true);});
     if (!first) {
       first = false;
       removed += ",";
@@ -1112,13 +1112,13 @@ void registerAllStats()
   addGetStat("ecs-queries", &SyncRes::s_ecsqueries);
   addGetStat("ecs-responses", &SyncRes::s_ecsresponses);
   addGetStat("chain-resends", &g_stats.chainResends);
-  addGetStat("tcp-clients", boost::bind(TCPConnection::getCurrentConnections));
+  addGetStat("tcp-clients", []{return TCPConnection::getCurrentConnections();});
 
 #ifdef __linux__
-  addGetStat("udp-recvbuf-errors", boost::bind(udpErrorStats, "udp-recvbuf-errors"));
-  addGetStat("udp-sndbuf-errors", boost::bind(udpErrorStats, "udp-sndbuf-errors"));
-  addGetStat("udp-noport-errors", boost::bind(udpErrorStats, "udp-noport-errors"));
-  addGetStat("udp-in-errors", boost::bind(udpErrorStats, "udp-in-errors"));
+  addGetStat("udp-recvbuf-errors", []{return udpErrorStats("udp-recvbuf-errors");});
+  addGetStat("udp-sndbuf-errors", []{return udpErrorStats("udp-sndbuf-errors");});
+  addGetStat("udp-noport-errors", []{return udpErrorStats("udp-noport-errors");});
+  addGetStat("udp-in-errors", []{return udpErrorStats("udp-in-errors");});
 #endif
 
   addGetStat("edns-ping-matches", &g_stats.ednsPingMatches);
@@ -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 4fcb19ca723520bcb18a11f8998ad4a449916fa1..573047c75eec4a66bbcf773835a3b7c42553b4e4 100644 (file)
@@ -306,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 \
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 94b031fdac9a872a102ae666e0a9cb1a43eda327..67a25ac7a64cc4119676952d173084a81ca1c6a2 100644 (file)
@@ -1,13 +1,31 @@
 Changelogs for 4.1.x
 ====================
 
+.. changelog::
+  :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:
+     :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.
index 58aad41fa2869b1b53d9b0150ad41913ed07b095..1da98268fb2a4a96893f6eb0ef5d34fb118ef18b 100644 (file)
@@ -1,5 +1,74 @@
 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
@@ -7,7 +76,7 @@ Changelogs for 4.2.x
 
   .. change::
      :tags: Bug Fixes
-     :pullreq:
+     :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.
@@ -25,7 +94,7 @@ Changelogs for 4.2.x
     Update gen-version to use latest tag for version number.
 
   .. change::
-    :tags:  
+    :tags: Internals
     :pullreq: 8964, 8752
     :tickets: 8875
 
index 06f4a768d4194cd45a5c29f6ae7a4406ebeec5c8..acb4ae32b02f4d0ab11f3837823faac9f8bbe274 100644 (file)
 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:
+     :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.
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 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 93a2e3a6d95c1be381966006f593a451a6681446..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.
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 689d09cc8e5ee4d2530f8b40407f0c7fc39c6014..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,7 +90,7 @@ 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>[,address...]
+--query-local-address=<address[,address...]>
     Use *address* as Source IP address when sending queries.
 --quiet
     Suppress logging of questions and answers.
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-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 cb1ec523cb434b65ed6cebff2f58feb14e1069fc..d82c616988a284d07b14a0f22e0b7fbf6dfa6a7d 100644 (file)
@@ -16,7 +16,7 @@ As an example:
 
 ``allow-from``
 --------------
--  IP ranges, separated by commas
+-  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.
@@ -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
@@ -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``
 ----------------------------------------------
@@ -1246,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:
 
@@ -1268,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.
@@ -1347,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``
@@ -1354,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:
 
@@ -1830,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:
 
@@ -1878,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:
 
@@ -1913,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);
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 ac95f95d3cf9250e2579c7fa1f735cca643b9b92..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()
@@ -118,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 2e19ce8a828d491b016496dca826790d36c53b3c..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);
@@ -1314,7 +1314,7 @@ BOOST_AUTO_TEST_CASE(test_completely_flawed_big_nsset)
     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, 11);
+    BOOST_CHECK_EQUAL(queriesCount, 11U);
   }
 }
 
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 d1ef7a7a0c0ea44c5a7ee72861640ef2881f607c..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,7 @@ 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);
@@ -1441,7 +1441,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_nxdomain)
   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(), 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);
@@ -1450,7 +1450,7 @@ BOOST_AUTO_TEST_CASE(test_dnssec_bogus_nxdomain)
   ret.clear();
   res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
   BOOST_CHECK_EQUAL(res, RCode::NXDomain);
-  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);
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 5b65d121673f9c7fd07e3300588bf3c6221854ef..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);
   }
 }
 
@@ -333,12 +366,12 @@ string reloadAuthAndForwards()
     }
 
     for(const auto& i : oldAndNewDomains) {
-      broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeCache, i, true, 0xffff));
-      broadcastAccFunction<uint64_t>(boost::bind(pleaseWipePacketCache, i, true, 0xffff));
-      broadcastAccFunction<uint64_t>(boost::bind(pleaseWipeAndCountNegCache, i, true));
+      broadcastAccFunction<uint64_t>([&]{return pleaseWipeCache(i, true, 0xffff);});
+      broadcastAccFunction<uint64_t>([&]{return pleaseWipePacketCache(i, true, 0xffff);});
+      broadcastAccFunction<uint64_t>([&]{return pleaseWipeAndCountNegCache(i, true);});
     }
 
-    broadcastFunction(boost::bind(pleaseUseNewSDomainsMap, newDomainMap));
+    broadcastFunction([=]{return pleaseUseNewSDomainsMap(newDomainMap);});
     return "ok\n";
   }
   catch(std::exception& e) {
index 95cfebd93ae26f0ed577f99c833a55e8d45ac662..15902766d44c75a379ced3011436673621a7c456 100644 (file)
@@ -833,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;
       }
@@ -1091,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 f397e0ce3c86397c2d775762f5a665800596fb09..5814f26296deba9a5c65a8534838a643edbbbc24 100644 (file)
@@ -307,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;
@@ -323,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;
   }
 
@@ -378,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) {
@@ -459,7 +461,8 @@ void RPZIXFRTracker(const std::vector<ComboAddress>& masters, boost::optional<DN
       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);
-      std::shared_ptr<SOARecordContent> newSR{nullptr};
+      /* initialize the current serial to the last one */
+      std::shared_ptr<SOARecordContent> currentSR = sr;
 
       int totremove=0, totadd=0;
       bool fullUpdate = false;
@@ -476,11 +479,17 @@ void RPZIXFRTracker(const std::vector<ComboAddress>& masters, boost::optional<DN
             continue;
           if(rr.d_type == QType::SOA) {
             auto oldsr = getRR<SOARecordContent>(rr);
-            if(oldsr && oldsr->d_st.serial == sr->d_st.serial) {
+            if (oldsr && oldsr->d_st.serial == currentSR->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 {
+              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++;
@@ -494,9 +503,9 @@ void RPZIXFRTracker(const std::vector<ComboAddress>& masters, boost::optional<DN
             continue;
           if(rr.d_type == QType::SOA) {
             auto tempSR = getRR<SOARecordContent>(rr);
-            //   g_log<<Logger::Info<<"New SOA serial for "<<zoneName<<": "<<newsr->d_st.serial<<endl;
+            //   g_log<<Logger::Info<<"New SOA serial for "<<zoneName<<": "<<currentSR->d_st.serial<<endl;
             if (tempSR) {
-              newSR = tempSR;
+              currentSR = tempSR;
             }
           }
           else {
@@ -508,8 +517,8 @@ void RPZIXFRTracker(const std::vector<ComboAddress>& masters, boost::optional<DN
       }
 
       /* only update sr now that all changes have been converted */
-      if (newSR) {
-        sr = newSR;
+      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);
index dbb4aa78428746082f54c3597a2d12dc4d85c328..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,7 +55,7 @@ void doSecPoll(time_t* last_secpoll)
     state = sr.getValidationState();
   }
 
-  if(state == Bogus) {
+  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;
index 1794cc025464fc66893ead9b866d679c2f073862..d2d8892b86810106ef374a98f64ec605f74aa01c 100644 (file)
 
 #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);
@@ -293,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);
@@ -314,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
@@ -712,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;
   }
@@ -899,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;
@@ -948,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) {
@@ -968,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 3fde0ef257060ea7116080ff1eba002e39bd482f..e9996a99e99952f0eaeb6319ded462ad13cba1fa 100644 (file)
@@ -85,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;
@@ -131,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;
   }
 
@@ -155,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);
@@ -406,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)
@@ -641,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);
   }
 
@@ -671,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");
@@ -695,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) {
@@ -723,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;
       }
@@ -732,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");
@@ -743,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++;
@@ -771,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()) {
@@ -781,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();
@@ -805,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);
@@ -850,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;
     }
 
@@ -886,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;
@@ -930,10 +936,10 @@ vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth,
   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)) {
@@ -942,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) {
@@ -1045,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);
@@ -1100,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());
@@ -1128,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 {
@@ -1171,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;
@@ -1224,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;
@@ -1232,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 */
@@ -1244,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);
@@ -1257,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;
@@ -1319,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) {
@@ -1329,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;
@@ -1381,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 */
@@ -1399,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
@@ -1413,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);
   }
 }
 
@@ -1461,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) {
@@ -1495,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;
         }
       }
@@ -1520,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;
   }
 
@@ -1548,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. */
@@ -1565,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);
@@ -1628,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;
     }
@@ -1866,7 +1951,7 @@ vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix,
 
   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, retrieveAddressesForNS);
+    result = getAddrs(tns->first, depth, beenthere, cacheOnly, retrieveAddressesForNS);
     pierceDontQuery=false;
   }
   else {
@@ -1935,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)
@@ -1964,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);
@@ -1983,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)
@@ -2006,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;
@@ -2097,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) {
@@ -2110,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)
@@ -2127,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;
@@ -2136,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;
       }
     }
@@ -2151,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;
   }
 
@@ -2175,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()) {
@@ -2186,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;
@@ -2199,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;
@@ -2209,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;
@@ -2224,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 {
@@ -2244,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);
@@ -2264,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;
       }
     }
@@ -2295,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)
@@ -2307,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);
@@ -2325,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)
@@ -2341,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;
@@ -2366,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)
@@ -2546,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;
     }
@@ -2573,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
@@ -2631,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;
@@ -2745,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) {
@@ -2777,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) {
@@ -2794,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));
@@ -2858,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
@@ -2877,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);
   }
@@ -2894,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)
@@ -2933,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);
       }
 
@@ -3016,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.
@@ -3030,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 {
@@ -3078,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;
@@ -3087,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);
 
@@ -3126,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);
         }
@@ -3354,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;
   }
@@ -3362,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;
     }
@@ -3372,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);
@@ -3385,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;
     }
@@ -3396,8 +3496,8 @@ 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 == 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)
@@ -3410,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)
@@ -3525,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;
@@ -3712,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();
@@ -3749,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 8ff14ddf68fab389378a779c03cc00146d605dea..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
@@ -772,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;
@@ -827,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);
@@ -866,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);
@@ -892,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
@@ -902,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};
@@ -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 7e93964e0c5b42e908c666a16e332cbb3b3c222b..6dea329261a4db8624204365e74ecf0d9754760d 100644 (file)
@@ -1057,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
@@ -1074,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()) {
@@ -1114,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;
       }
@@ -1125,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);
@@ -1134,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())
@@ -1147,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 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 b47cc6d7e00d095f73bda70b21b3358679b515e4..ca10668787c54e0110bc5de5b000134a55150d48 100644 (file)
@@ -107,7 +107,7 @@ static void bareHandlerWrapper(WebServer::HandlerFunction handler, YaHTTP::Reque
 
 void WebServer::registerBareHandler(const string& url, HandlerFunction handler)
 {
-  YaHTTP::THandlerFunction f = std::bind(&bareHandlerWrapper, handler, std::placeholders::_1, std::placeholders::_2);
+  YaHTTP::THandlerFunction f = [=](YaHTTP::Request* req, YaHTTP::Response* resp){return bareHandlerWrapper(handler, req, resp);};
   YaHTTP::Router::Any(url, f);
 }
 
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 a074460c2f1f587191feefbaff1c052194981273..83e3e43af3b7f47bb97745b969e0999863ab56d5 100644 (file)
@@ -382,9 +382,9 @@ static void apiServerCacheFlush(HttpRequest* req, HttpResponse* resp) {
   DNSName canon = apiNameToDNSName(req->getvars["domain"]);
   bool subtree = (req->getvars.count("subtree") > 0 && req->getvars["subtree"].compare("true") == 0);
 
-  int count = broadcastAccFunction<uint64_t>(std::bind(pleaseWipeCache, canon, subtree, 0xffff));
-  count += broadcastAccFunction<uint64_t>(std::bind(pleaseWipePacketCache, canon, subtree, 0xffff));
-  count += broadcastAccFunction<uint64_t>(std::bind(pleaseWipeAndCountNegCache, canon, subtree));
+  int count = broadcastAccFunction<uint64_t>([=]{return pleaseWipeCache(canon, subtree, 0xffff);});
+  count += broadcastAccFunction<uint64_t>([=]{return pleaseWipePacketCache(canon, subtree, 0xffff);});
+  count += broadcastAccFunction<uint64_t>([=]{return pleaseWipeAndCountNegCache(canon, subtree);});
   resp->setBody(Json::object {
     { "count", count },
     { "result", "Flushed cache." }
@@ -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 6d746f5abb19d381e4b486227147c6b6daca688b..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-address=::1
-    """ % (os.environ['PREFIX'])
+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')
index 8341a8cd5a593ba2d254a66b591bc2835cd71629..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
 
@@ -136,12 +136,27 @@ 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)
                     ]
             elif newSerial == 10:
-                # full AXFR to make sure we are removing the duplicate, adding a record, to the that the update was correctly applied
+                # 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)
@@ -490,6 +505,21 @@ e 3600 IN A 192.0.2.42
         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 63f6c75f8c54cf03a6c0c9a5a9c229d71c3c7e19..cd02160e628366339a1bd950192e8009e82a9c7c 100644 (file)
@@ -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)
 
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 e65bbc257c8517a14fe98b1f6da96d16d39e0cea..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
 
@@ -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-address="0.0.0.0${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 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
 }