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"
libldap2-dev \
liblmdb-dev \
libluajit-5.1-dev \
+ libp11-kit-dev \
libpq-dev \
libsodium-dev \
libsqlite3-dev \
--enable-unit-tests \
--enable-backend-unit-tests \
--enable-fuzz-targets \
+ --enable-experimental-pkcs11 \
--with-lmdb=/usr \
--with-libsodium \
--prefix=/opt/pdns-auth \
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:
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:
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
--- /dev/null
+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
--- /dev/null
+(?:^|/)ext/
+(?:^|/)go\.mod$
+(?:^|/)go\.sum$
+(?:^|/)m4/
+(?:^|/)package-lock\.json$
+/expected_result
+/test-dnsrecords_cc\.cc$
+SUMS$
+\.ai$
+\.asc$
+\.bmp$
+\.cer$
+\.class$
+\.crl$
+\.crt$
+\.csr$
+\.dll$
+\.DS_Store$
+\.eot$
+\.eps$
+\.exe$
+\.gif$
+\.graffle$
+\.gz$
+\.icns$
+\.ico$
+\.jar$
+\.jpeg$
+\.jpg$
+\.keys?$
+\.lib$
+\.lock$
+\.map$
+\.min\..
+\.mp3$
+\.mp4$
+\.nsec3(?:-optout|)$
+\.otf$
+\.pdf$
+\.pem$
+\.png$
+\.psd$
+\.sig$
+\.so$
+\.svg$
+\.svgz$
+\.tar$
+\.tgz$
+\.ttf$
+\.woff
+\.xcf$
+\.xls
+\.xpm$
+\.yml$
+\.zip$
+^modules/remotebackend/example\.rb$
+^modules/tinydnsbackend/data\.cdb$
+^pdns/dnsdistdist/src_js/
+^pdns/recursordist/html/js/
+^\.github/actions/spell-check/
--- /dev/null
+aaaarecord
+aadaf
+aadceba
+aae
+aaldering
+ababd
+abbb
+ABCD
+abcde
+abcdef
+abfe
+abi
+aborttransaction
+ABQ
+Abraitis
+abspath
+acb
+acd
+acf
+acl
+ACPI
+activatedomainkey
+ada
+adadab
+adb
+adcb
+addc
+adddomainkey
+addingrecords
+addprefix
+addsuffix
+adfdffa
+Adiscon
+aee
+aeefcf
+aef
+afdf
+Afek
+afl
+afnic
+afsdb
+AFYER
+agentx
+agentxperms
+AHM
+ahupowerdns
+Aips
+Aki
+Alenichev
+alexa
+algo
+aliceblue
+allocs
+Altpeter
+amd
+ANCOUNT
+Anderton
+anewid
+anid
+anonymization
+Anonymize
+anotherid
+ANOTHERIPADDRESS
+anothertype
+ansible
+ANSSI
+Antoin
+anycast
+api
+apikey
+APIv
+AQAB
+AQTQ
+ARCHFLAGS
+ARCOUNT
+arecord
+arecvfrom
+Arentz
+ARGS
+arial
+Arjen
+Arjo
+Arnoud
+arpa
+Arsen
+aruba
+asc
+Ascio
+ASd
+Asenov
+ASEP
+Ashish
+ASIN
+asnum
+aspx
+associateddomain
+asyncresolve
+Atlassian
+atoi
+Atomia
+aton
+attr
+atype
+AUTHIP
+authmethod
+Authoritativedoc
+auths
+authzone
+autobuilt
+autocalculation
+autocomplete
+autoconf
+autodetect
+autodetecting
+autodoc
+autogenerated
+automagically
+automake
+Automattic
+autoptr
+autoreconf
+autoserial
+autoslave
+autotools
+AXF
+axfer
+axfr
+axfrfilter
+Baan
+bacc
+backend
+backgrounding
+backport
+Backtick
+backtraces
+BADALG
+BADCOOKIE
+badips
+BADKEY
+BADMODE
+BADNAME
+badserver
+BADSIG
+BADTIME
+BADTRUNC
+BADVERS
+baf
+Baj
+Bakker
+Baltus
+basedn
+basepath
+Bastiaan
+bayour
+bba
+bbb
+bbc
+bbcbbbe
+bbd
+bc
+bca
+bcb
+bcc
+bccd
+bcce
+bce
+bda
+bdd
+bddd
+bded
+bea
+bearggg
+beb
+beda
+beenthere
+bellis
+Belyshev
+benchmarketing
+Benetasso
+Bernd
+bert
+Besselink
+bestwho
+bfa
+bfb
+bfc
+bfcada
+bfe
+bffa
+bgcolor
+Bheca
+Biege
+bigbank
+bigint
+BIGSERIAL
+Bilik
+bindbackend
+binddn
+BINDTODEVICE
+Binero
+binlog
+bla
+Bleker
+blockfilter
+blockquote
+blog
+blogpost
+blogspot
+bmigrate
+bodyfont
+bodysize
+bodywrapper
+bolditalic
+bonafide
+Bortzmeyer
+botnet
+bpf
+bpo
+Brainspark
+Braunoeder
+breadcrumb
+Bremler
+brendangregg
+Briley
+Broens
+broer
+Bromwich
+Brownworth
+Brynjar
+Brzeski
+bsd
+Btw
+Buf
+bufsize
+bugfix
+bugfixes
+bugzilla
+BUILDDIR
+bulc
+bulletinc
+burstable
+bw
+BXvs
+Byterate
+bytestring
+bzero
+bzip
+caa
+caad
+cachekey
+cae
+Cairney
+calculatesoaserial
+calidns
+Cauquil
+cbb
+cbc
+CBF
+Cbjr
+ccac
+ccache
+ccb
+ccbd
+ccc
+ccd
+cce
+ccounts
+cdb
+cdbe
+CDBKV
+cde
+cdece
+cdeede
+cdnskey
+cds
+ceb
+cec
+cece
+cef
+cefcf
+Cegetel
+Cerb
+certusage
+cfe
+cfea
+cfeb
+CFLAGS
+cgi
+CGroup
+changelog
+changeme
+changeset
+changetype
+charset
+chashed
+chbruyand
+chdir
+Chiavacci
+chmod
+chopoff
+chown
+Chqt
+Christof
+chroot
+chrooting
+CHz
+ci
+CIDR
+cjf
+classmethod
+CLASSNUM
+Cloos
+closesocket
+clusions
+cmouse
+cmsg
+cmsghdr
+cn
+cname
+cnamechainresolution
+CNAMEd
+CNAMEDNS
+cnamerecord
+cnf
+Cnma
+cnn
+cockroachlabs
+Cockroft
+codebgcolor
+codeninja
+codetextcolor
+Colemarcus
+colgroup
+collapsiblesidebar
+colm
+comboaddress
+commandline
+committransaction
+conaxis
+config
+configfile
+configname
+configsetting
+configurability
+conntrack
+Conntracking
+Consolas
+constexpr
+controllen
+controlsocket
+coprocess
+coprocesses
+coredumps
+cornercases
+corpit
+CORS
+costypetrisor
+cout
+coverity
+cpp
+cppcheck
+createdb
+createslavedomain
+Cremers
+CRn
+cron
+Cruft
+crv
+cryptokey
+Cryptoki
+cryptopp
+cryptoshop
+css
+csv
+ctime
+ctor
+ctx
+Cuz
+cve
+cvename
+cvs
+cvstrac
+CWD
+CXXFLAGS
+cz
+daa
+dac
+daee
+daemonizing
+daemontools
+daf
+Daganoto
+Danerklint
+dankamongmen
+Darilion
+darix
+Darron
+dataformat
+datasource
+datastore
+datatracker
+Daugaard
+Davids
+Dayneko
+dbaec
+dbe
+dbedfc
+dbf
+dbfile
+dblfilename
+dbname
+dbpf
+dbr
+DBX
+dccc
+dcd
+dcde
+dce
+DCF
+dcobject
+ddaab
+dde
+ddf
+ddns
+DDo
+deactivatedomainkey
+debian
+deboynepollard
+decls
+ded
+Deduktiva
+dedup
+Deduplicate
+defcontent
+defpol
+defttl
+DENIC
+deref
+descclassname
+descname
+Dessel
+dest
+destname
+Detlef
+devicename
+devtoolset
+df
+dfb
+dfd
+dff
+dh
+DHCID
+DHCP
+dhcpd
+dhcpdupdate
+diffs
+DIGESTALGOS
+Digitalus
+dijk
+dilinger
+Directi
+Disqus
+distro
+djbdns
+DKIM
+dlerror
+dlg
+DLLs
+dlmalloc
+DLV
+dmesg
+Dmitry
+dname
+Dnn
+dns
+dnsapi
+dnsbulktest
+dnscache
+dnscrypt
+dnsdemog
+dnsdist
+dnsdistconf
+dnsdistdist
+dnsdistdoc
+dnsdomain
+dnsext
+dnsgram
+dnsheader
+dnskey
+dnsmessage
+dnsname
+dnsnameset
+dnsop
+dnspacket
+dnsparser
+dnspcap
+DNSQ
+dnsquestion
+DNSR
+dnsrecord
+dnsreplay
+dnsresourcerecord
+dnsscan
+dnsscope
+dnssec
+dnssecfromexisting
+DNSSERVER
+dnsspoof
+dnstap
+dnstcpbench
+dnstree
+dnsttl
+dnsupdate
+dnswasher
+dnszone
+dnt
+Dobrawy
+docnamecachelookup
+doctrees
+documentclass
+documentwrapper
+docutils
+doesnotexist
+dofile
+Dohmen
+domaininfo
+domainmetadata
+domainname
+domainrelatedobject
+Donatas
+dontcare
+downsides
+downstreams
+dport
+dq
+drafiei
+Draschl
+droprate
+DRR
+dscontent
+dsrecord
+DSs
+dst
+DTS
+Dufberg
+dumresp
+dynblock
+dynblocklist
+dynblocksref
+dynbpf
+dyndns
+dynhandler
+dynmodules
+eaa
+eac
+eachother
+EAGAIN
+easydns
+eb
+ebaf
+ebd
+ebe
+ebeb
+ebf
+ebfd
+ebpf
+ebpfblocklist
+EBXN
+ecbf
+ecc
+ECCN
+ecdsa
+ECDSAP
+econds
+ECONNRESET
+ecs
+ECSDA
+ecswho
+eda
+edb
+edc
+ede
+edfa
+editline
+edns
+ednsoptions
+ednsoptionview
+ednssubnet
+EDNSTo
+edu
+eea
+eeb
+eec
+EED
+eef
+efb
+efbf
+efc
+efd
+Eieb
+EINTR
+ejones
+EJUGg
+ek
+Ekkelenkamp
+elgoog
+Emph
+endblock
+Enden
+endian
+endif
+endl
+ENOENT
+ENOTCONN
+ent
+entrypoint
+enum
+envoutput
+EOL
+epel
+epoll
+epub
+eqno
+Eriksson
+errlog
+errno
+errorlevels
+esr
+EUI
+EUips
+evildomain
+EVMu
+EWMA
+examplekey
+exceedfuncs
+execfile
+Exort
+externalrefs
+extrahead
+Ezb
+Ezbu
+ezdns
+fabf
+fadec
+FAEEBC
+Faerch
+faf
+failedservers
+failover
+favicon
+FBAE
+fbc
+fbe
+fbf
+fcbd
+fcc
+fcd
+fcde
+fcf
+fcff
+fcgi
+fcontext
+fd
+fda
+fdc
+fdce
+fdd
+fdde
+fdopen
+fedc
+fedoraproject
+feedents
+feedrecord
+feef
+ffaae
+ffb
+ffd
+ffdd
+fff
+ffi
+ffipolicy
+filedescriptor
+Filesystem
+findclientpolicy
+Firefox
+firewalled
+firewalls
+fixednow
+FIXME
+FJZ
+Fki
+FLln
+Florus
+flto
+FNs
+fontname
+footerbgcolor
+footertextcolor
+forfun
+FORMERR
+Fortiguard
+Fortinet's
+forwardzone
+framestream
+freakshow
+freebsd
+freedesktop
+Freenet
+freesans
+freetds
+freshports
+Froemel
+frontend
+fstrm
+fullname
+fulltoc
+func
+Furnell
+Fusl
+FYhvws
+FZq
+gaba
+gacogne
+gatech
+Gavarret
+gcc
+Gci
+gdpr
+Geijn
+genindex
+geobackend
+geoip
+geoipbackend
+geolocated
+Gergely
+Gerritsen
+Gervai
+Gerwin
+getaddrinfo
+getaddrs
+getalldomainmetadata
+getbeforeandafternamesabsolute
+getdomaininfo
+getdomainkeys
+getdomainmetadata
+gethostname
+getlocaladdress
+getn
+getrandom
+getregisteredname
+gettag
+gettext
+gettime
+gettimeofday
+gettsigkey
+Geuze
+GFm
+gh
+Gibheer
+Gieben
+Gillstrom
+github
+githubusercontent
+Gkey
+Gkkq
+glibc
+gmail
+gmake
+Gmb
+gmtime
+GMy
+gmysql
+gmysqlbackend
+gnutls
+godbc
+godbcbackend
+goodmatch
+google
+googleapis
+goracle
+goraclebackend
+GOST
+gouv
+Goxz
+gpgsql
+gpgsqlbackend
+gprof
+gpsqlbackend
+GQj
+GQNy
+grep
+grepping
+grepq
+GSQ
+gsql
+gsqlite
+gss
+gssapi
+gsub
+gtld
+guilabel
+gy
+Gyh
+Gyselinck
+gz
+gzip
+gzipped
+hackerone
+Hakulinen
+Hannu
+haproxy
+hardcode
+hardcoded
+hardcoding
+hardlink
+Harker
+headbgcolor
+headerlink
+headfont
+headlinkcolor
+headtextcolor
+healthcheck
+Heimhilcher
+Helbekkmo
+HELO
+Hendriks
+Henk
+Hensbergen
+Heredoc
+Heuer
+Hev
+hh
+hidesoadetails
+hidettl
+highlighttable
+Hiljanen
+hinfo
+hitrate
+hkraal
+HKSKRWu
+hll
+hlmann
+hmac
+Hmi
+Hoentjen
+Hofstaedtler
+homepage
+Hooimeijer
+hostmaster
+hostname
+Hotmail
+howto
+hpecorp
+hpiers
+hpp
+HPx
+href
+hsm
+htbp
+htm
+html
+htmlescape
+htmlhelp
+http
+httpapi
+httpdomain
+hubert
+hyperlink
+HZQ
+iana
+icann
+ico
+ict
+idprotect
+idq
+idx
+iers
+ietf
+ifdef
+ifportup
+ifurlup
+IFV
+ihsinme
+IJajghd
+IKOYz
+illumos
+img
+Imhard
+incbin
+includeboilerplate
+includerings
+indexa
+indexassociated
+indextable
+inet
+infolog
+infosecinstitute
+ini
+initscript
+Inno
+innodb
+inode
+installable
+interop
+interoperability
+interoperation
+inzk
+iostream
+iowait
+ip
+IPADDRESS
+IPbackend
+ipc
+ipcipher
+ipcom
+ipcrypt
+ipdecrypt
+ipencrypt
+ipfilter
+IPSECKEY
+iptables
+iputils
+ipv
+IQIT
+IQuery
+irc
+isane
+isc
+ismaster
+isoc
+isp
+ispell
+isql
+isse
+issuecomment
+ixfr
+ixfrdist
+ixplore
+Jakub
+Jakum
+janeczku
+Jatko
+Jaury
+Jauvin
+javascript
+JCf
+Jck
+Jeftovic
+Jelte
+Jermar
+Jeroen
+jessie
+jj
+Joaqu
+Jong
+Jorn
+journalctl
+journald
+jp
+jpmens
+jq
+json
+jsondomain
+JSONP
+jsonstat
+ju
+Juergen
+jumpbox
+Juraj
+jye
+Kaminsky
+Kaseorg
+KCtsq
+kd
+Kdhcp
+Kdhcpdupdate
+Kees
+kerberos
+Kerkhof
+KEYBITS
+keyblock
+keydir
+keyfile
+keygen
+keyname
+keypair
+keypairgen
+keyroll
+keysearch
+keysize
+keytab
+KEYTAG
+keytype
+keywordmatches
+kickdaddy
+Kirill
+Klebermass
+koch
+Kockum
+Kolkman
+kom
+Konqueror
+Koos
+koqv
+Kovacic
+kp
+kqueue
+KQZX
+krb
+Krist
+Krul
+ksk
+kskroll
+kskrollcdnskey
+Kuehrer
+KUXs
+kvs
+KX
+Kxxnux
+Kyc
+Ladot
+Lafon
+Lakkas
+largeanswer
+Laros
+lastcheck
+Lastdrager
+lastnotified
+latexpdf
+latlon
+latlonloc
+latomic
+lauch
+Laurient
+Laursen
+Lbackend
+LCUP
+LDA
+ldap
+ldapbackend
+ldflags
+ldif
+ldns
+LDR
+Leen
+Lemoine
+len
+lessthan
+Lesuisse
+lethalgroup
+letsencrypt
+letterpaper
+LFya
+libatomic
+libc
+libcrypto
+libcryptopp
+libcurl
+libdecaf
+libdir
+libedit
+libfstrm
+libgcc
+libgeoip
+libh
+libmaxminddb
+libmongo
+libmysqlclient
+libnss
+libpcap
+libpq
+libpqpp
+libresolv
+libressl
+librt
+libsodium
+libsofthsm
+libssl
+libsystemd
+libtdsodbc
+libyaml
+libzmq
+Lindqvist
+linenos
+linenum
+linkcolor
+lintian
+linux
+linuxnetworks
+Lior
+listinfo
+literalinclude
+llvm
+lmdb
+lmdbbackend
+LMDBKV
+lnsl
+loadbalancer
+loadbalancing
+localaddr
+localhost
+localip
+LOCALSTATEDIR
+localtime
+localtoc
+locaweb
+lochiiconnectivity
+logfile
+loglevel
+logmessage
+logrotate
+lon
+Loopia
+Lorbach
+lordievader
+Louwers
+loweralpha
+lowerroman
+lresolv
+Lrhazi
+lrt
+lsock
+lsocket
+lte
+lua
+luaaction
+luabackend
+luac
+luajit
+luaroundrobin
+luarule
+luawrapper
+Lutter
+Luuk
+Lwc
+Lwz
+LYg
+lz
+Maik
+Maikel
+MAILA
+MAILB
+Majer
+Makefiles
+malcrafted
+malloc
+malware
+Mamane
+Mandriva
+manpage
+mapasync
+mariadb
+Markmann
+maskv
+Massar
+matchtype
+Matthijs
+maxdepth
+MAXINT
+maxlistdepth
+maxmind
+maxqps
+MAXVALUE
+mbed
+mbedtls
+MBOXFW
+mbytes
+MDB
+Meerwald
+Mekking
+MEMLOCK
+Memusage
+menuselection
+metadata
+metadatabase
+metainformation
+metricnames
+metricscarbon
+Meulen
+Michiel
+Microsoft
+Miek
+Miell
+Mieslinger
+Milas
+Mimimization
+minbody
+mindex
+MINFO
+minipatch
+misconfigured
+mjt
+mkuchar
+mmap
+mmdb
+mname
+mnordhoff
+MOADNS
+Modderman
+modifyingpolicydecisions
+modindex
+monshouwer
+Moq
+motherboards
+mozilla
+mplexer
+Mpqhbg
+MQ
+mrtg
+msdcs
+MSDNS
+msphinx
+mssql
+mtasker
+MTEUl
+mthread
+MUar
+Mulholland
+multiline
+multimaster
+multithreading
+mundsson
+munmap
+Muraro
+musl
+mutex
+Mwaikambo
+mx
+mxrecord
+mybackend
+mycompany
+mydns
+mydnsbackend
+mydomain
+MYec
+myhost
+myinstance
+myname
+mypassword
+mypgsql
+mysecretpassword
+myset
+myspecialmetric
+mysql
+mysqlbackend
+mysqld
+mytsigkey
+myuser
+mywebapp
+NAi
+namedroppers
+nameserver
+nameserving
+namespace
+namserver
+naptr
+Nauck
+Navarrete
+nc
+Ndd
+nearmiss
+nearmisses
+Nederlandse
+nedmalloc
+negativetrustanchor
+negcache
+negquery
+neheb
+Nelless
+neosystem
+Netblock
+netfilter
+netherlabs
+netinet
+netmask
+netmaskgroup
+netsnmp
+NETWORKMASK
+Neue
+Neuf
+newcontent
+nextval
+nf
+nic
+nimber
+Nixu
+nkey
+nmg
+nn
+Nncqx
+noaction
+noad
+noall
+nocache
+nocookie
+nodata
+NODELAY
+noedns
+noerrors
+nometasync
+Nominet
+nonexist
+Nonnekes
+noout
+noping
+noport
+nosniff
+nostrip
+NOSUBDIR
+nosync
+Notaras
+NOTAUTH
+NOTIMP
+NOTPARALLEL
+notrack
+NOTZONE
+Novell
+nproxy
+NPTL
+NSCOUNT
+NSCx
+nsd
+NSECx
+NSes
+nsid
+nsis
+nsname
+NSQ
+nsrecord
+NSS
+nsset
+nsspeeds
+NSTTL
+nsupdate
+nta
+ntei
+ntlworld
+nullptr
+NULs
+NUMA
+numreceived
+nx
+nxd
+NXDATA
+nxdomain
+NXRRSET
+oarc
+oauth
+Obermayer
+obidos
+objectclass
+Obser
+obspm
+ocsp
+odbc
+odbcbackend
+odbcinst
+Oddy
+Odintsov
+Oestreicher
+offsite
+Ofpy
+oftc
+OIDs
+Olafur
+Omroep
+openapi
+openbsd
+opendbx
+openpgpkey
+opensc
+openssl
+opensuse
+openwall
+Opmeer
+optcode
+Opteron
+optmem
+optout
+oraclebackend
+ordername
+orsn
+Oservers
+ostringstream
+OSX
+otherdomain
+otherpool
+ou
+OUHTU
+ourname
+ourserial
+ourtime
+OUTFILE
+outpacket
+outputbuffer
+outqueries
+PACKAGEVERSION
+packetcache
+packethandler
+papersize
+paramater
+PARAMKEYWORDS
+params
+passphrase
+passthrough
+passthru
+PATC
+patchlevels
+pathconfig
+pathto
+pawal
+pb
+Pbackend
+pcap
+PCAPFILE
+pdf
+pdns
+pdnsbackend
+pdnscontrol
+pdnsldap
+pdnslog
+pdnsmgrd
+pdnsodbx
+pdnsrandom
+pdnssec
+pdnstest
+pdnsutil
+Peeters
+Pels
+pem
+Penev
+perl
+Perroud
+Pertubation
+pez
+Pfetzing
+pgmysql
+pgmysqlbackend
+pgp
+pgpsql
+pgsql
+phishing
+phonedph
+php
+pickclosest
+pickrandom
+pickwhashed
+pickwrandom
+pid
+piddir
+pidfile
+pidof
+pieter
+pieterlexis
+pilindex
+Pinski
+pipebackend
+pipermail
+PIV
+pkcs
+PKGLIBDIR
+PKI
+PKTINFO
+placeholders
+PLt
+Plusnet
+plzz
+pmtmr
+pnds
+png
+Poelov
+pointsize
+polarssl
+policyactions
+policykinds
+policyname
+pollmplexer
+Ponomarev
+poolers
+poolname
+portnum
+portnumber
+postgre
+postgresql
+postinst
+postresolve
+powerdns
+powerdnsrecursor
+powerdnssec
+powerldap
+powerpc
+ppc
+pragma
+Predota
+preoutquery
+Preproc
+prequery
+prereleases
+prerpz
+presigned
+presignedness
+PRId
+primetime
+princ
+privatekey
+privs
+PRNG
+Proba
+progid
+protobuf
+PROTOC
+providername
+proxyprotocol
+proxyprotocolvalues
+PROXYv
+pseudonymize
+pseudorecord
+psql
+pthread
+ptr
+ptrrecord
+Publieke
+publishdomainkey
+pullreq
+pwfm
+px
+py
+pygments
+pypi
+Pyry
+PYv
+PZFX
+qa
+Qag
+qclass
+qdcount
+qdomain
+qgen
+Qkj
+qlen
+Qlim
+Qll
+qname
+qperq
+Qpkv
+qps
+QPSIP
+qpslimits
+QRate
+qsize
+QSLj
+qthread
+qtype
+qtypelist
+querycache
+querycount
+quickstart
+QYCIHp
+qytpe
+ragel
+randombackend
+randombit
+randombytes
+randomisation
+randomises
+randomloader
+rapidjson
+raspbian
+rb
+RBL
+RBUb
+rcode
+rcodezero
+Rcvbuf
+rcvmmsg
+rdata
+rdqueries
+rdynamic
+readline
+README
+readonly
+realtime
+reconnections
+recursor
+recursordist
+Recursordoc
+Recuweb
+recv
+recvbuf
+recverr
+recvfrom
+recvmmsg
+recvmsg
+redelegations
+redhat
+redjack
+reentrantly
+refactor
+Refactoring
+refcount
+refman
+refreh
+refuseds
+regex
+reid
+reimplementation
+Reinier
+Rejo
+relbar
+relbarbgcolor
+relbarlinkcolor
+relbartextcolor
+relro
+Remco
+remi
+remoteaddr
+remotebackend
+remoteip
+remoting
+removedomainkey
+replacerrset
+requery
+resolv
+respawn
+respawning
+respout
+respsizes
+Resync
+resynchronise
+retransfering
+reuseds
+reuseport
+Reuwiei
+rfc
+rhel
+Rietz
+rightsidebar
+Rijsdijk
+ringbuffer
+rkey
+RLIMIT
+rname
+rng
+rocommunity
+Roel
+Rosmalen
+roundrobin
+rp
+RPATH
+rping
+RPMS
+rpz
+rpzstatistics
+Rqvg
+rr
+rrcontent
+rrd
+rrdata
+rrdtool
+rrname
+rrset
+rrsig
+rrtype
+rsa
+rsasha
+RSP
+rst
+rsync
+ru
+Rueckert
+Rul
+rulesets
+Ruthensteiner
+rv
+Rvd
+rw
+Rwgj
+rwlock
+Rzs
+Sakaguchi
+saltsa
+sandboxing
+Sangwhan
+SASL
+Saunalahti
+saxfr
+SBF
+sbin
+Sbvka
+scalability
+SCHED
+Scheffler
+Schlich
+Scholten
+Schryver
+Schueler
+schwer
+SCn
+scopebits
+scopemask
+scriptable
+scriptlets
+sdb
+sdfoijdfio
+sdig
+secpoll
+securesphere
+securitypolicy
+securitypolling
+seealso
+segfault
+selectmplexer
+selinux
+senderrors
+Sendetzky
+sendmsg
+sensistive
+Sergey
+servername
+serverpools
+serverselection
+servfail
+setaffinity
+setcontent
+setdomainmetadata
+setgid
+seting
+setkey
+setnotified
+SETPIPE
+settting
+setuptools
+setvariable
+Sgs
+Shafir
+shantikulkarni
+shinsterneck
+Shm
+showdetails
+showflags
+Shukla
+sid
+SIddm
+sidebarbgcolor
+sidebarbtncolor
+sidebarbutton
+sidebarlinkcolor
+sidebarlogo
+sidebarsourcelink
+sidebartextcolor
+sidebarwidth
+sidn
+SIGABR
+sigint
+Sigmod
+signedness
+Signingpiper
+signpipe
+signttl
+signzone
+SIGPIPE
+sigs
+SIGTERM
+SIGUSR
+singlethreaded
+Sinstallation
+Sipek
+sizeof
+Sjoerd
+slapd
+slapindex
+slaveness
+SLES
+smartcard
+smb
+smellyspice
+smfgo
+smimea
+smn
+smtp
+Smurthwaite
+Snarked
+sndbuf
+SNI
+snmp
+snmpd
+SNMPv
+snprintf
+soa
+soadata
+soarecord
+sockaddr
+socketdir
+softhsm
+solaris
+SOLc
+Soldaat
+SOMAXCONN
+somedomain
+Sonix
+Soref
+Soroceanu
+sortlist
+sourcecode
+SOURCEDIR
+sourceforge
+sourceware
+Spaans
+Spackages
+spam
+Sparc
+spf
+SPHINXBUILD
+sphinxcontrib
+sphinxjsondomain
+SPHINXOPTS
+SPHINXPROJ
+sphinxsidebar
+sphinxsidebarwrapper
+splitsetup
+sprezzos
+Spruyt
+SQk
+sql
+sqlite
+srandom
+src
+srcname
+SRecord
+Srule
+srv
+SSH
+sshfp
+ssi
+ssl
+sslmode
+sslrootcert
+SSQ
+SSql
+stacksize
+standalone
+starttls
+starttransaction
+Stasic
+statbag
+statbas
+statisticitem
+statm
+stbuehler
+stderr
+stdin
+stdio
+stdout
+Stef
+Steinbuch
+Stichting
+stickysidebar
+Stillaway
+Stirnimann
+stmt
+Stol
+Storbeck
+stou
+stoul
+strcasestr
+stringmatch
+strlen
+strpos
+stubquery
+stubresolver
+stunnel
+Stussy
+stutiredboy
+stylesheet
+subdomain
+subkey
+subnetmask
+sudo
+suffixmatchtree
+supermaster
+supermasterbackend
+supernotification
+supersecretpassword
+superslave
+superslaving
+supervisord
+Surfnet
+SUSE
+swapcontext
+swe
+swoga
+sx
+Symlink
+syncres
+sys
+sysadmin
+syscall
+SYSCONFDIR
+sysctl
+Sysdream
+syslog
+systemcall
+systemctl
+systemd
+sysv
+SYW
+SZ
+szw
+tarball
+Tarjei
+Tarnell
+tbhandler
+tbody
+tcely
+tcp
+tcpdump
+TCPKEEPALIVE
+TCPv
+Tctk
+td
+teeaction
+Telenet
+testrunner
+testsdir
+texinfo
+textcolor
+tfoot
+Tful
+TGJGVc
+thead
+thel
+thelog
+Thessalonikefs
+Thiago
+thinko
+Thomassen
+threadsafe
+throttlemap
+thrysoee
+Thu
+timedipsetrule
+timedout
+timeframe
+timeline
+timesource
+timeval
+timezone
+tinycdb
+tinydns
+tinydnsbackend
+tisr
+TKEY
+tld
+tls
+tlsa
+tmp
+tmpfs
+tobool
+toc
+toctree
+todo
+toint
+tokenuser
+tolower
+Tolstov
+Toshifumi
+tostring
+Travaille
+tribool
+trunc
+trustanchor
+trusteer
+trx
+trxid
+tsc
+tsig
+tsigalgo
+tsigkey
+tsigname
+tsigsecret
+tstamp
+TSU
+tt
+ttl
+Tuinder
+tunables
+tuomi
+Tushuizen
+Tuu
+Tuxis
+TVJRU
+tw
+tylerneylon
+typedefs
+typenames
+tyu
+TZ
+ualberta
+Ubo
+ubuntu
+udp
+udpqueryresponse
+UDPv
+udr
+Ueber
+Ueli
+uid
+uint
+Uisms
+uj
+uk
+ul
+ulimit
+Umq
+unauth
+unbreak
+uncached
+unescaping
+unfresh
+unhash
+unicode
+uninett
+uninitialised
+Uninstaller
+unistd
+unitialized
+unixodbc
+unixtime
+unparseable
+Unprocessable
+unpublish
+unpublishdomainkey
+unreachables
+unregister
+unshadowing
+untruncated
+unzero
+upd
+updatepolicy
+upperalpha
+upperroman
+urandom
+uri
+url
+urlencoded
+usec
+usecase
+useragent
+userbase
+username
+userspace
+usr
+utf
+UUHJWZg
+uuid
+uwaterloo
+Uwcjbp
+Uwi
+Uyypn
+Valentei
+Valentini
+valgrind
+validationstates
+validators
+Valkenburg
+Vandalon
+vandergaast
+Vandoren
+VARCHAR
+varname
+Vasiliy
+VBG
+Vbt
+VDRAW
+VDz
+Veldhuyzen
+venv
+Verheijen
+Verisign
+Verschuren
+versionadded
+versionchanged
+versionmodified
+vh
+viewcode
+virtualenv
+virtualized
+visitedlinkcolor
+vixie
+vm
+Vn
+Voegeli
+Volker
+voxel
+Vranken
+Vsgoi
+Vwgbclzx
+VY
+vzu
+WAITFORONE
+wal
+wallclock
+warnlog
+Wascwa
+Wbq
+webbased
+webdocs
+webhandler
+webpassword
+webserver
+website
+Webspider
+weightparams
+Weimer
+Welzel
+Wessels
+westes
+Wevers
+wextra
+Wgx
+whashed
+whitelist
+Wia
+Wichert
+Wieger
+Wielicki
+Wijk
+Wijnand
+Wijngaards
+wiki
+wikipedia
+wil
+wildcarded
+wildcards
+Willcott
+windr
+Winfried
+wireformat
+wirelength
+Wisiol
+wmc
+Wmissing
+Wojas
+workaround
+Worldnic
+would've
+wouter
+wpad
+wproduction
+wrandom
+Wrange
+Wredundant
+writev
+Wshadow
+wuh
+ww
+www
+WYbw
+Wzqf
+Wzs
+Xander
+xchacha
+Xchange
+xdb
+Xek
+Xeon
+xf
+xfr
+xh
+xhtml
+XM
+xml
+xorbooter
+xpf
+XPFCODE
+XPFDST
+XPFPROTO
+XPFSRC
+XPFVERSION
+Xpw
+XRecord
+XSalsa
+xss
+XYR
+yahttp
+yaml
+yb
+YDyzc
+Yehuda
+YG
+YHk
+YIBo
+Yiu
+Yj
+yk
+YLCOy
+Ylitalo
+yml
+YMMV
+yourcompany
+yourdomain
+yourorganization
+yoursecret
+yp
+YQ
+yubikey
+Yx
+YXDOMAIN
+YXRRSET
+yy
+YYYYMMD
+YYYYMMDD
+YYYYMMDDSS
+Zealey
+zeha
+Zengers
+Zengin
+zeromq
+Zfndz
+Zhf
+zilopbg
+ZJad
+Zmd
+ZNLY
+Zoag
+zonecryptokey
+zonefile
+zonemetadata
+zonename
+zoneparser
+zonetransfer
+Zonneveld
+ZRev
+zsk
+zskroll
+Zt
+Zumstrull
+Zwane
+zz
+zzyzz
--- /dev/null
+/docs/
+^docs/
--- /dev/null
+(?:0[Xx]|U\+|#)[a-f0-9A-FGgRr]{2,}[Uu]?[Ll]{0,2}\b
+addNSECRecordToLW.*DNSName.*powerdnt.com.*QType::NS.*res->d_records
+data:[a-zA-Z=;,/0-9+]+
+BOOST_CHECK_EQUAL\(b64, "[a-zA-Z=;,/0-9+]+"
+\b([A-Za-z])\1{3,}\b
+C(?:XX|)FLAGS="[^"]*"
--- /dev/null
+name: Spell checking
+on:
+ push:
+ branches:
+ - "**"
+ tags-ignore:
+ - "**"
+ paths:
+ - "docs/**"
+ - "**/docs/**"
+ pull_request:
+ branches:
+ - "**"
+ tags-ignore:
+ - "**"
+ paths:
+ - "docs/**"
+ - "**/docs/**"
+ types: ['opened', 'reopened', 'synchronize']
+
+jobs:
+ build:
+ name: Spell checking
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2.0.0
+ with:
+ fetch-depth: 5
+ - uses: check-spelling/check-spelling@0.0.16-alpha
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ bucket: .github/actions
+ project: spell-check
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
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:
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
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.
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
PKG=$2
CMD=$3
+ if [ "$VERSION" = "8" ]; then
+ CENTOS8_FLAGS="--nobest"
+ else
+ CENTOS8_FLAGS=""
+ fi
+
cat <<EOF > Dockerfile.$RELEASE.$OS-$VERSION
FROM $OS:$VERSION
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
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
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
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
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
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
run "cd .."
run "wget http://s3-us-west-1.amazonaws.com/umbrella-static/top-1m.csv.zip"
run "unzip top-1m.csv.zip -d ${TRAVIS_BUILD_DIR}/regression-tests"
- run 'echo -e "deb [arch=amd64] http://repo.powerdns.com/ubuntu trusty-auth-master main" | sudo tee /etc/apt/sources.list.d/pdns.list'
- run 'echo -e "Package: pdns-*\nPin: origin repo.powerdns.com\nPin-Priority: 9001" | sudo tee /etc/apt/preferences.d/pdns'
- run 'curl https://repo.powerdns.com/CBC8B383-pub.asc | sudo apt-key add - '
- run 'sudo apt-get update'
+ run 'wget https://downloads.powerdns.com/tmp/pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty.tar.bz2'
+ run 'tar xf pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty.tar.bz2'
+ run 'sudo dpkg -i pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty/pdns-server_4.2.0~rc2+master.255.g2bee14438-1pdns.trusty_amd64.deb pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty/pdns-tools_4.2.0~rc2+master.255.g2bee14438-1pdns.trusty_amd64.deb pdns-4.2.0-rc2.255.master.g2bee14438-ubuntu-trusty/pdns-backend-bind_4.2.0~rc2+master.255.g2bee14438-1pdns.trusty_amd64.deb ; sudo apt-get -y install -f'
run 'sudo apt-get -y install pdns-server pdns-tools'
run "sudo service pdns stop"
run 'for suffix in {1..40}; do sudo /sbin/ip addr add 10.0.3.$suffix/32 dev lo; done'
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
override_dh_install:
dh_install
- ./pdns/pdns_server --no-config --config | sed \
+ ./pdns/pdns_server --config=default | sed \
-e 's!# module-dir=.*!!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/pdns.d!' \
-e 's!# launch=.*!&\nlaunch=!' \
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
override_dh_install:
dh_install
- ./pdns/pdns_server --no-config --config | sed \
+ ./pdns/pdns_server --config=default | sed \
-e 's!# module-dir=.*!!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/pdns.d!' \
-e 's!# launch=.*!&\nlaunch=!' \
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
override_dh_install:
dh_install
- ./pdns/pdns_server --no-config --config | sed \
+ ./pdns/pdns_server --config=default | sed \
-e 's!# module-dir=.*!!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/pdns.d!' \
-e 's!# launch=.*!&\nlaunch=!' \
install -m 644 -t debian/pdns-recursor/usr/share/pdns-recursor/lua-config debian/lua-config/rootkeys.lua
install -m 644 -t debian/pdns-recursor/etc/powerdns debian/recursor.lua
rm -f debian/pdns-recursor/etc/powerdns/recursor.conf-dist
- ./pdns_recursor --no-config --config | sed \
+ ./pdns_recursor --config=default | sed \
-e 's!# config-dir=.*!config-dir=/etc/powerdns!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/recursor.d!' \
-e 's!# local-address=.*!local-address=127.0.0.1!' \
-e 's!# setgid=.*!setgid=pdns!' \
-e 's!# setuid=.*!setuid=pdns!' \
-e 's!# hint-file=.*!&\nhint-file=/usr/share/dns/root.hints!' \
+ -e '/^# version-string=.*/d' \
> debian/pdns-recursor/etc/powerdns/recursor.conf
override_dh_strip:
install -m 644 -t debian/pdns-recursor/usr/share/pdns-recursor/lua-config debian/lua-config/rootkeys.lua
install -m 644 -t debian/pdns-recursor/etc/powerdns debian/recursor.lua
rm -f debian/tmp/etc/powerdns/recursor.conf-dist
- ./pdns_recursor --no-config --config | sed \
+ ./pdns_recursor --config=default | sed \
-e 's!# config-dir=.*!config-dir=/etc/powerdns!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/recursor.d!' \
-e 's!# local-address=.*!local-address=127.0.0.1!' \
-e 's!# setgid=.*!setgid=pdns!' \
-e 's!# setuid=.*!setuid=pdns!' \
-e 's!# hint-file=.*!&\nhint-file=/usr/share/dns/root.hints!' \
+ -e '/^# version-string=.*/d' \
> debian/tmp/etc/powerdns/recursor.conf
override_dh_strip:
install -m 644 -t debian/pdns-recursor/usr/share/pdns-recursor/lua-config debian/lua-config/rootkeys.lua
install -m 644 -t debian/pdns-recursor/etc/powerdns debian/recursor.lua
rm -f debian/pdns-recursor/etc/powerdns/recursor.conf-dist
- ./pdns_recursor --no-config --config | sed \
+ ./pdns_recursor --config=default | sed \
-e 's!# config-dir=.*!config-dir=/etc/powerdns!' \
-e 's!# include-dir=.*!&\ninclude-dir=/etc/powerdns/recursor.d!' \
-e 's!# local-address=.*!local-address=127.0.0.1!' \
-e 's!# setgid=.*!setgid=pdns!' \
-e 's!# setuid=.*!setuid=pdns!' \
-e 's!# hint-file=.*!&\nhint-file=/usr/share/dns/root.hints!' \
+ -e '/^# version-string=.*/d' \
> debian/pdns-recursor/etc/powerdns/recursor.conf
override_dh_strip:
%{__install} -D -p %{SOURCE1} %{buildroot}%{_initrddir}/pdns
%endif
-%{buildroot}/usr/sbin/pdns_server --no-config --config | sed \
+%{buildroot}/usr/sbin/pdns_server --config=default | sed \
-e 's!# daemon=.*!daemon=no!' \
-e 's!# guardian=.*!guardian=no!' \
-e 's!# launch=.*!&\\nlaunch=!' \
%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
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
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],
_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]}"
End of life statements
======================
+We aim to have a release every six months.
+The latest and previous release receive correctness, stability and security updates.
+The release before that gets critical security updates only.
+Older releases are marked end of life and receive no updates at all.
+Pre-releases do not receive immediate security updates.
+
The currently supported release train of PowerDNS Authoritative Server is 4.3.
PowerDNS Authoritative Server 4.2 will only receive correctness, stability and security updates and will be receiving security updates only after PowerDNS Authoritative Server 4.4 is released.
can receive extended support for releases which are End Of Life. If you are
such a user, these EOL statements do not apply to you.
+.. list-table:: PowerDNS Authoritative Server Release Life Cycle
+ :header-rows: 1
+
+ * - Version
+ - Release date
+ - Security-Only updates
+ - End of Life
+ * - 4.3
+ - April 7 2020
+ - ~ April 2021
+ - ~ October 2021
+ * - 4.2
+ - August 30 2019
+ - ~ October 2020
+ - ~ April 2021
+ * - 4.1
+ - November 30 2017
+ - April 7 2020
+ - ~ October 2020
+ * - 4.0 and older
+ - EOL
+ - EOL
+ - EOL
+
PowerDNS Authoritative Server 3.x
---------------------------------
1st of December 2017
My masters won't allow PowerDNS to access zones as it is using the wrong local IP address
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
By default, PowerDNS lets the kernel pick the source address.
-To set an explicit source address, use the :ref:`setting-query-local-address` and :ref:`setting-query-local-address6` settings.
+To set an explicit source address, use the :ref:`setting-query-local-address` setting.
PowerDNS does not answer queries on all my IP addresses (and I've ignored the warning I got about that at startup)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
======================
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.
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
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
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
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()``!
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
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();
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
-------------
: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
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
--------------
.. 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.
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
^^^^^^^^^^^^^^^
fix records ending up in wrong packet section (Kees Monshouwer)
.. change::
- :tags: Improvents
+ :tags: Improvements
:pullreq: 9003, 8736
cache: strictly enforce maximum size, and improve cleanup routine
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
.. 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
-------------
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::
return false
end
+
+Additional updatepolicy example scripts can be found in our
+`Wiki <https://github.com/PowerDNS/pdns/wiki/Lua-Examples-(Authoritative)>`__.
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.
* :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
----------------
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
.. openapi:: swagger/authoritative-api-swagger.yaml
:definitions: TSIGKey
+
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.
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*
^^^^^^^^^^^^^^^^^^^^^^
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*]..
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
-@ 86400 IN SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020042201 10800 3600 604800 10800
+@ 86400 IN SOA pdns-public-ns1.powerdns.com. pieter\.lexis.powerdns.com. 2020073001 10800 3600 604800 10800
@ 3600 IN NS pdns-public-ns1.powerdns.com.
@ 3600 IN NS pdns-public-ns2.powerdns.com.
auth-4.0.5.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2018-03.html"
auth-4.0.6.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2019-03.html"
auth-4.0.7.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2018-03.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2018-05.html"
-auth-4.0.8.security-status 60 IN TXT "1 OK"
-auth-4.0.9.security-status 60 IN TXT "1 OK"
+auth-4.0.8.security-status 60 IN TXT "2 Unsupported release (EOL)"
+auth-4.0.9.security-status 60 IN TXT "2 Unsupported release (EOL)"
auth-4.1.0-rc1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
auth-4.1.0-rc2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
auth-4.1.0-rc3.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
auth-3.4.1-4_deb8u5.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2016-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
auth-3.4.1-4_deb8u6.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
auth-3.4.1-4_deb8u7.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2017-04.html"
-auth-3.4.1-4_deb8u8.debian.security-status 60 IN TXT "1 OK"
+auth-3.4.1-4_deb8u8.debian.security-status 60 IN TXT "2 Unsupported release (EOL)"
auth-3.4.4-2_bpo8_1.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
auth-3.4.5-1_bpo8_1.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2015-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-03/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-04/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-05/"
auth-4.0.1-7.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-02.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-03.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-04.html https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2016-05.html"
auth-4.0.2-1.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2017-04.html"
auth-4.0.3-1.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/authoritative/security-advisories/powerdns-advisory-2017-04.html"
-auth-4.0.3-1_deb9u2.debian.security-status 60 IN TXT "1 OK"
+auth-4.0.3-1_deb9u2.debian.security-status 60 IN TXT "2 Unsupported release (EOL)"
; From here on security polling has been disabled for this distribution.
; Auth Ubuntu
recursor-4.1.6.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2018-09.html"
recursor-4.1.7.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2018-09.html"
recursor-4.1.8.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2019-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2019-02.html"
-recursor-4.1.9.security-status 60 IN TXT "1 OK"
-recursor-4.1.10.security-status 60 IN TXT "1 OK"
-recursor-4.1.11.security-status 60 IN TXT "1 OK"
-recursor-4.1.12.security-status 60 IN TXT "1 OK"
-recursor-4.1.13.security-status 60 IN TXT "1 OK"
-recursor-4.1.14.security-status 60 IN TXT "1 OK"
-recursor-4.1.15.security-status 60 IN TXT "1 OK"
-recursor-4.2.0-alpha1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0-beta1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0-rc1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0-rc2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.2.0.security-status 60 IN TXT "1 OK"
-recursor-4.2.1.security-status 60 IN TXT "1 OK"
-recursor-4.3.0-alpha1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-alpha2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-alpha3.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-beta1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-beta2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-rc1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0-rc2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-recursor-4.3.0.security-status 60 IN TXT "1 OK"
-recursor-4.4.0-alpha1.security-status 60 IN TXT "1 OK"
+recursor-4.1.9.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.10.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.11.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.12.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.13.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.14.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.15.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.1.16.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-04.html"
+recursor-4.1.17.security-status 60 IN TXT "1 OK"
+recursor-4.2.0-alpha1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0-beta1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0-rc1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0-rc2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.2.0.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.2.1.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.2.2.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-04.html"
+recursor-4.2.3.security-status 60 IN TXT "1 OK"
+recursor-4.2.4.security-status 60 IN TXT "1 OK"
+recursor-4.3.0-alpha1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-alpha2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-alpha3.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-beta1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-beta2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-rc1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0-rc2.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.3.0.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-01.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-02.html https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-03.html"
+recursor-4.3.1.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/recursor/security-advisories/powerdns-advisory-2020-04.html"
+recursor-4.3.2.security-status 60 IN TXT "1 OK"
+recursor-4.3.3.security-status 60 IN TXT "1 OK"
+recursor-4.4.0-alpha1.security-status 60 IN TXT "3 Unsupported pre-release (known vulnerabilities)"
+recursor-4.4.0-alpha2.security-status 60 IN TXT "1 OK"
; Recursor Debian
recursor-3.6.2-2.debian.security-status 60 IN TXT "3 Upgrade now, see https://doc.powerdns.com/3/security/powerdns-advisory-2015-01/ and https://doc.powerdns.com/3/security/powerdns-advisory-2016-02/"
dnsdist-1.4.0-rc5.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
dnsdist-1.4.0.security-status 60 IN TXT "1 OK"
dnsdist-1.5.0-alpha1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
-dnsdist-1.5.0-rc1.security-status 60 IN TXT "1 OK"
+dnsdist-1.5.0-rc1.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc2.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc3.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0-rc4.security-status 60 IN TXT "2 Unsupported pre-release (no known vulnerabilities)"
+dnsdist-1.5.0.security-status 60 IN TXT "1 OK"
- 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:
``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:
- 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:
``query-local-address``
-----------------------
+.. versionchanged:: 4.4.0
+ Accepts both IPv4 and IPv6 addresses. Also accept more than one address per
+ address family.
-- IPv4 Address
-- Default: 0.0.0.0
+- IP addresses, separated by spaces or commas
+- Default: 0.0.0.0 ::
-The IP address to use as a source address for sending queries. Useful if
+The IP addresses to use as a source address for sending queries. Useful if
you have multiple IPs and PowerDNS is not bound to the IP address your
operating system uses by default for outgoing packets.
+PowerDNS will pick the correct address family based on the remote's address (v4
+for outgoing v4, v6 for outgoing v6). However, addresses are selected at random
+without taking into account ip subnet reachability. It is highly recommended to
+use the defaults in that case (the kernel will pick the right source address for
+the network).
+
.. _setting-query-local-address6:
``query-local-address6``
------------------------
+.. deprecated:: 4.4.0
+ Use :ref:`setting-query-local-address`. The default has been changed
+ from '::' to unset.
- IPv6 Address
-- Default: '::'
+- Default: unset
Source IP address for sending IPv6 queries.
$ 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>`:
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
--------------
#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>
#include <boost/utility/string_ref.hpp>
using string_view = boost::string_ref;
#endif
-#else // C++17
-using std::string_view;
#endif
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
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;
}
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","");
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);
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) {
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);
}
}
}
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);
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) {
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);
}
};
getArgAsNum("timeout"),
mustDo("thread-cleanup"),
mustDo("ssl")));
+ allocateStatements();
}
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");
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)");
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);
}
throw PDNSException( "Unable to launch " + mode + " connection: " + e.txtReason());
}
+ allocateStatements();
+
g_log << Logger::Warning << mode << " Connection successful" << std::endl;
}
}
//! 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");
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)");
}
//! Constructs a new gODBCBackend object.
- DNSBackend *make(const string & suffix = "" )
+ DNSBackend *make(const string & suffix = "" ) override
{
return new gODBCBackend( d_mode, suffix );
}
{
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;
}
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","");
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");
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)");
}
- DNSBackend *make(const string &suffix="")
+ DNSBackend *make(const string &suffix="") override
{
return new gPgSQLBackend(d_mode,suffix);
}
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")); }
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));
}
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
#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
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);
}
}
d_residx++;
if (d_residx >= d_resnum) {
PQclear(d_res);
- d_res = NULL;
+ d_res = nullptr;
nextResult();
}
return this;
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)); }
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;
}
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);
}
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;
d_connectstr+=" password="+password;
}
+ d_use_prepared = use_prepared;
+
d_db=PQconnectdb(d_connectstr.c_str());
if (!d_db || PQstatus(d_db)==CONNECTION_BAD) {
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() {
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();
PGconn* db() { return d_db; }
bool in_trx() const { return d_in_trx; }
+ bool usePrepared() { return d_use_prepared; }
private:
PGconn* d_db;
string d_connectlogstr;
static bool s_dolog;
bool d_in_trx;
+ bool d_use_prepared;
+ unsigned int d_nstatements;
};
--- /dev/null
+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;
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
{
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"));
}
}
//! 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", "");
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)");
}
//! Constructs a new gSQLite3Backend object.
- DNSBackend *make( const string & suffix = "" )
+ DNSBackend *make( const string & suffix = "" ) override
{
return new gSQLite3Backend( d_mode, suffix );
}
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 (
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);
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" );
}
- DNSBackend* make( const string &suffix="" )
+ DNSBackend* make( const string &suffix="" ) override
{
return new LdapBackend( suffix );
}
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);
}
}
-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;
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);
{
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");
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);
}
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;
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
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);
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)
void UnixRemote::receive(string& line)
{
line.clear();
- stringfgets(d_fp, line);
+ stringfgets(d_fp.get(), line);
trim_right(line);
}
{
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);
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");
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);
}
{
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);
}
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)
if (d_socket == nullptr ) return -1; // cannot receive :(
char buffer[4096];
int rd = -1;
- bool fail = false;
time_t t0;
arl.initialize(&resp);
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;
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)
* 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);
}
/**
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()));
+ }
}
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);
}
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());
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");
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);
}
};
packethandler.cc packethandler.hh \
pdnsexception.hh \
qtype.cc qtype.hh \
+ query-local-address.hh query-local-address.cc \
rcpgenerator.cc \
receiver.cc \
resolver.cc resolver.hh \
+ axfr-retriever.cc axfr-retriever.hh \
responsestats.cc responsestats.hh responsestats-auth.cc \
rfc2136handler.cc \
secpoll.cc secpoll.hh \
misc.cc misc.hh \
mplexer.hh \
nsecrecords.cc \
+ query-local-address.hh query-local-address.cc \
qtype.cc \
rcpgenerator.cc rcpgenerator.hh \
resolver.cc \
+ axfr-retriever.cc \
pollmplexer.cc \
sillyrecords.cc \
sstuff.hh \
logger.cc \
misc.cc misc.hh \
nsecrecords.cc \
+ query-local-address.hh query-local-address.cc \
qtype.cc \
rcpgenerator.cc rcpgenerator.hh \
resolver.cc \
+ axfr-retriever.cc \
ixfr.cc ixfr.hh \
ixfrutils.cc ixfrutils.hh \
ixplore.cc \
endif
tsig_tests_SOURCES = \
+ axfr-retriever.cc \
arguments.cc \
base32.cc \
base64.cc base64.hh \
dnssecinfra.cc \
dnswriter.cc dnswriter.hh \
gss_context.cc gss_context.hh \
+ iputils.cc \
logger.cc \
misc.cc misc.hh \
nsecrecords.cc \
+ query-local-address.cc \
qtype.cc \
rcpgenerator.cc rcpgenerator.hh \
resolver.cc \
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 \
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
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)
string namespace_name=arg()["carbon-namespace"];
string hostname=arg()["carbon-ourname"];
- if(hostname.empty()) {
- char tmp[80];
- memset(tmp, 0, sizeof(tmp));
- gethostname(tmp, sizeof(tmp));
- char *p = strchr(tmp, '.');
- if(p) *p=0;
- hostname=tmp;
- boost::replace_all(hostname, ".", "_");
+ if (hostname.empty()) {
+ try {
+ hostname = getCarbonHostName();
+ }
+ catch(const std::exception& e) {
+ throw std::runtime_error(std::string("The 'carbon-ourname' setting has not been set and we are unable to determine the system's hostname: ") + e.what());
+ }
}
string instance_name=arg()["carbon-instance"];
{
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");
{
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");
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "axfr-retriever.hh"
+#include "arguments.hh"
+#include "dns_random.hh"
+#include "utility.hh"
+#include "resolver.hh"
+#include "query-local-address.hh"
+
+using pdns::resolver::parseResult;
+
+AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
+ const DNSName& domain,
+ const TSIGTriplet& tt,
+ const ComboAddress* laddr,
+ size_t maxReceivedBytes,
+ uint16_t timeout)
+ : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
+{
+ ComboAddress local;
+ if (laddr != nullptr) {
+ local = ComboAddress(*laddr);
+ } else {
+ if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
+ throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". Address family is not configured for outgoing queries");
+ }
+ local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
+ }
+ d_sock = -1;
+ try {
+ d_sock = makeQuerySocket(local, false); // make a TCP socket
+ if (d_sock < 0)
+ throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort());
+ d_buf = shared_array<char>(new char[65536]);
+ d_remote = remote; // mostly for error reporting
+ this->connect(timeout);
+ d_soacount = 0;
+
+ vector<uint8_t> packet;
+ DNSPacketWriter pw(packet, domain, QType::AXFR);
+ pw.getHeader()->id = dns_random_uint16();
+
+ if(!tt.name.empty()) {
+ if (tt.algo == DNSName("hmac-md5"))
+ d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int");
+ else
+ d_trc.d_algoName = tt.algo;
+ d_trc.d_time = time(0);
+ d_trc.d_fudge = 300;
+ d_trc.d_origID=ntohs(pw.getHeader()->id);
+ d_trc.d_eRcode=0;
+ addTSIG(pw, d_trc, tt.name, tt.secret, "", false);
+ }
+
+ uint16_t replen=htons(packet.size());
+ Utility::iovec iov[2];
+ iov[0].iov_base=reinterpret_cast<char*>(&replen);
+ iov[0].iov_len=2;
+ iov[1].iov_base=packet.data();
+ iov[1].iov_len=packet.size();
+
+ int ret=Utility::writev(d_sock, iov, 2);
+ if(ret < 0)
+ throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
+ if(ret != (int)(2+packet.size())) {
+ throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
+ }
+
+ int res = waitForData(d_sock, timeout, 0);
+
+ if(!res)
+ throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
+ if(res<0)
+ throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
+ }
+ catch(...) {
+ if(d_sock >= 0)
+ close(d_sock);
+ d_sock = -1;
+ throw;
+ }
+}
+
+AXFRRetriever::~AXFRRetriever()
+{
+ close(d_sock);
+}
+
+
+
+int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed.
+{
+ if(d_soacount > 1)
+ return false;
+
+ // d_sock is connected and is about to spit out a packet
+ int len=getLength(timeout);
+ if(len<0)
+ throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
+
+ if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
+ throw ResolverException("Reached the maximum number of received bytes during AXFR");
+
+ timeoutReadn(len, timeout);
+
+ d_receivedBytes += (uint16_t) len;
+
+ MOADNSParser mdp(false, d_buf.get(), len);
+
+ int err = mdp.d_header.rcode;
+
+ if(err) {
+ throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
+ }
+
+ try {
+ d_tsigVerifier.check(std::string(d_buf.get(), len), mdp);
+ }
+ catch(const std::runtime_error& re) {
+ throw ResolverException(re.what());
+ }
+
+ if(!records) {
+ err = parseResult(mdp, DNSName(), 0, 0, &res);
+
+ if (!err) {
+ for(const auto& answer : mdp.d_answers)
+ if (answer.first.d_type == QType::SOA)
+ d_soacount++;
+ }
+ }
+ else {
+ records->clear();
+ records->reserve(mdp.d_answers.size());
+
+ for(auto& r: mdp.d_answers) {
+ if (r.first.d_type == QType::SOA) {
+ d_soacount++;
+ }
+
+ records->push_back(std::move(r.first));
+ }
+ }
+
+ return true;
+}
+
+void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec)
+{
+ time_t start=time(nullptr);
+ int n=0;
+ int numread;
+ while(n<bytes) {
+ int res=waitForData(d_sock, timeoutsec-(time(nullptr)-start));
+ if(res<0)
+ throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
+ if(!res)
+ throw ResolverException("Timeout while reading data from remote nameserver over TCP");
+
+ numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
+ if(numread<0)
+ throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
+ if(numread==0)
+ throw ResolverException("Remote nameserver closed TCP connection");
+ n+=numread;
+ }
+}
+
+void AXFRRetriever::connect(uint16_t timeout)
+{
+ setNonBlocking( d_sock );
+
+ int err;
+
+ if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
+ try {
+ closesocket(d_sock);
+ }
+ catch(const PDNSException& e) {
+ d_sock=-1;
+ throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
+ }
+
+ throw ResolverException("connect: "+stringerror());
+ }
+
+ if(!err)
+ goto done;
+
+ err=waitForRWData(d_sock, false, timeout, 0); // wait for writeability
+
+ if(!err) {
+ try {
+ closesocket(d_sock); // timeout
+ }
+ catch(const PDNSException& e) {
+ d_sock=-1;
+ throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
+ }
+
+ d_sock=-1;
+ errno=ETIMEDOUT;
+
+ throw ResolverException("Timeout connecting to server");
+ }
+ else if(err < 0) {
+ throw ResolverException("Error connecting: "+stringerror());
+ }
+ else {
+ Utility::socklen_t len=sizeof(err);
+ if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
+ throw ResolverException("Error connecting: "+stringerror()); // Solaris
+
+ if(err)
+ throw ResolverException("Error connecting: "+string(strerror(err)));
+ }
+
+ done:
+ setBlocking( d_sock );
+ // d_sock now connected
+}
+
+int AXFRRetriever::getLength(uint16_t timeout)
+{
+ timeoutReadn(2, timeout);
+ return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
+}
+
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+#include <boost/utility.hpp>
+
+#include "iputils.hh"
+#include "dnsname.hh"
+#include "resolver.hh"
+
+class AXFRRetriever : public boost::noncopyable
+{
+ public:
+ AXFRRetriever(const ComboAddress& remote,
+ const DNSName& zone,
+ const TSIGTriplet& tt = TSIGTriplet(),
+ const ComboAddress* laddr = NULL,
+ size_t maxReceivedBytes=0,
+ uint16_t timeout=10);
+ ~AXFRRetriever();
+ int getChunk(Resolver::res_t &res, vector<DNSRecord>* records=0, uint16_t timeout=10);
+
+ private:
+ void connect(uint16_t timeout);
+ int getLength(uint16_t timeout);
+ void timeoutReadn(uint16_t bytes, uint16_t timeoutsec=10);
+
+ shared_array<char> d_buf;
+ string d_domain;
+ int d_sock;
+ int d_soacount;
+ ComboAddress d_remote;
+ TSIGTCPVerifier d_tsigVerifier;
+
+ size_t d_receivedBytes;
+ size_t d_maxReceivedBytes;
+ TSIGRecordContent d_trc;
+};
GSQLBackend::GSQLBackend(const string &mode, const string &suffix)
{
setArgPrefix(mode+suffix);
- d_db=0;
+ d_db = nullptr;
d_logprefix="["+mode+"Backend"+suffix+"] ";
try
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");
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;
}
}
-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;
}
}
}
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;
}
}
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
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();
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
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());
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);
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);
}
}
- void freeStatements() {
+ virtual void freeStatements() {
d_NoIdQuery_stmt.reset();
d_IdQuery_stmt.reset();
d_ANYNoIdQuery_stmt.reset();
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();
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;
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;
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);
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;
string d_SuperMasterInfoQuery;
string d_GetSuperMasterName;
string d_GetSuperMasterIPs;
+ string d_AddSuperMaster;
string d_InsertZoneQuery;
string d_InsertRecordQuery;
string d_SearchRecordsQuery;
string d_SearchCommentsQuery;
- unique_ptr<SSqlStatement>* d_query_stmt;
unique_ptr<SSqlStatement> d_NoIdQuery_stmt;
unique_ptr<SSqlStatement> d_IdQuery_stmt;
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;
// 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);
}
}
toTrim -= totErased;
- while (toTrim > 0) {
+ while (true) {
size_t pershard = toTrim / maps_size + 1;
for (auto& mc : maps) {
const typename C::lock l(mc);
totErased++;
toTrim--;
if (toTrim == 0) {
- break;
+ return totErased;
}
}
}
}
+ // Not reached
return totErased;
}
#include "dnsseckeeper.hh"
#include "threadname.hh"
#include "misc.hh"
+#include "query-local-address.hh"
#include <thread>
{
::arg().set("config-dir","Location of configuration directory (pdns.conf)")=SYSCONFDIR;
::arg().set("config-name","Name of this virtual configuration - will rename the binary image")="";
- ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns when unset and not chrooted" )="";
+ ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns when unset and not chrooted"
+#ifdef HAVE_SYSTEMD
+ + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")="";
+ auto runtimeDir = getenv("RUNTIME_DIRECTORY");
+ if (runtimeDir != nullptr) {
+ ::arg().set("socket-dir") = runtimeDir;
+ }
+#else
+ )="";
+#endif
::arg().set("module-dir","Default directory for modules")=PKGLIBDIR;
::arg().set("chroot","If set, chroot to this directory for more security")="";
::arg().set("logging-facility","Log under a specific facility")="";
::arg().setSwitch("local-address-nonexist-fail","Fail to start if one or more of the local-address's do not exist on this server")="yes";
::arg().setSwitch("non-local-bind", "Enable binding to non-local addresses by using FREEBIND / BINDANY socket options")="no";
::arg().setSwitch("reuseport","Enable higher performance on compliant kernels by using SO_REUSEPORT allowing each receiver thread to open its own socket")="no";
- ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
- ::arg().set("query-local-address6","Source IPv6 address for sending queries")="::";
+ ::arg().set("query-local-address","Source IP addresses for sending queries")="0.0.0.0 ::";
+ ::arg().set("query-local-address6","DEPRECATED: Use query-local-address. Source IPv6 address for sending queries")="";
::arg().set("overload-queue-length","Maximum queuelength moving to packetcache only")="0";
::arg().set("max-queue-length","Maximum queuelength before considering situation lost")="5000";
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.");
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");
}
}
+ pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
+ if (!::arg()["query-local-address6"].empty()) {
+ g_log<<Logger::Warning<<"query-local-address6 is deprecated and will be removed in a future version. Please use query-local-address for IPv6 addresses as well"<<endl;
+ pdns::parseQueryLocalAddress(::arg()["query-local-address6"]);
+ }
+
// NOW SAFE TO CREATE THREADS!
dl->go();
#include "dnsbackend.hh"
#include "ueberbackend.hh"
#include "packethandler.hh"
-#include "resolver.hh"
#include "logger.hh"
#include "dns.hh"
#include "arguments.hh"
sr=d_suckdomains.front();
d_suckdomains.pop_front();
}
- suck(sr.domain, sr.master);
+ suck(sr.domain, sr.master, sr.force);
}
}
{
DNSName domain;
ComboAddress master;
+ bool force;
bool operator<(const SuckRequest& b) const
{
return tie(domain, master) < tie(b.domain, b.master);
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);
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);
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);
}
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)
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()
{
}
}
-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;
}
{
}
- //! 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;
}
//! 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)
{
}
//! 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;
}
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();
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);
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;
}
const auto& server = conf.server;
const std::string& namespace_name = conf.namespace_name;
std::string hostname = conf.ourname;
- if(hostname.empty()) {
- char tmp[80];
- memset(tmp, 0, sizeof(tmp));
- gethostname(tmp, sizeof(tmp));
- char *p = strchr(tmp, '.');
- if(p) *p=0;
- hostname=tmp;
- boost::replace_all(hostname, ".", "_");
+ if (hostname.empty()) {
+ try {
+ hostname = getCarbonHostName();
+ }
+ catch(const std::exception& e) {
+ throw std::runtime_error(std::string("The 'ourname' setting in 'carbonServer()' has not been set and we are unable to determine the system's hostname: ") + e.what());
+ }
}
const std::string& instance_name = conf.instance_name;
}
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";
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) {
#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
{ "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'" },
{ "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" },
/* 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;
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);
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);
#endif /* LUAJIT_VERSION */
#include "dnsdist-rings.hh"
#include "dnsdist-secpoll.hh"
+#include "dnsdist-web.hh"
#include "base64.hh"
#include "dnswriter.hh"
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));
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; });
});
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) {
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 {
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();
};
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"));
}
string servers;
- for (const auto& server: pool->getServers()) {
+ const auto poolServers = pool->getServers();
+ for (const auto& server: *poolServers) {
if (!servers.empty()) {
servers += ", ";
}
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);
* 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;
{ "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") },
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");
}
// Latency histogram buckets
- output << "# HELP dnsdist_latency Histogram of responses by latency (in miliseconds)\n";
+ output << "# HELP dnsdist_latency Histogram of responses by latency (in milliseconds)\n";
output << "# TYPE dnsdist_latency histogram\n";
uint64_t latency_amounts = g_stats.latency0_1;
output << "dnsdist_latency_bucket{le=\"1\"} " << latency_amounts << "\n";
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;
}
}
+ 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";
}
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);
{
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());
}
}
#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>
std::vector<int> sockets;
sockets.reserve(dss->sockets.size());
- for(;;) {
+ for(; !dss->isStopped(); ) {
dnsheader* dh = reinterpret_cast<struct dnsheader*>(packet);
try {
pickBackendSocketsReadyForReceiving(dss, sockets);
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;
#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
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;
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;
{"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},
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;
std::atomic_flag threadStarted;
bool tcpFastOpen{false};
bool ipBindAddrNoPort{true};
+ bool reconnectOnUp{false};
bool isUp() const
{
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)
{
private:
std::string name;
std::string nameWithAddr;
+ bool d_stopped{false};
};
using servers_t =vector<std::shared_ptr<DownstreamState>>;
struct ServerPool
{
- ServerPool()
+ ServerPool(): d_servers(std::make_shared<ServerPolicy::NumberedServerVector>())
{
}
+
~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++;
}
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;
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) */
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};
};
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);
+-- == 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)
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 \
endif
dnsdist@.service: dnsdist.service
- $(AM_V_GEN)sed -e 's!/dnsdist !&--config $(sysconfdir)/dnsdist-%i.conf !' < $< >$@
+ $(AM_V_GEN)sed -e 's!/dnsdist !&--config $(sysconfdir)/dnsdist-%i.conf !' \
+ -e 's!RuntimeDirectory=.*!&-%i!' \
+ < $< >$@
systemdsystemunitdir = $(SYSTEMD_DIR)
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])
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],
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);
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();
+ }
}
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()) {
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;
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
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
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();
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));
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;
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) {
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";
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();
}
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);
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")));
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();
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());
}
if (releaseVersion) {
- warnlog("Could not retrieve security status update for '%s' on %s", pkgv, queriedName);
+ warnlog("Failed to retrieve security status update for '%s' on %s", pkgv, queriedName);
}
else if (!g_secPollDone) {
infolog("Not validating response for security status update, this is a non-release version.");
--- /dev/null
+#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);
Changelog
=========
+.. changelog::
+ :version: 1.5.0
+ :released: 30th of July 2020
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9231
+
+ Use explicit flag for the specific version of c++ we are targeting.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9320
+
+ Prevent a possible overflow via large Proxy Protocol values. (Valentei Sergey)
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9348
+ :tickets: 9279
+
+ Avoid name clashes on Solaris derived systems.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9343
+
+ Resize hostname to final size in getCarbonHostname(). (Aki Tuomi)
+
+ .. change::
+ :tags: Bug Fixes, DNS over HTTPS
+ :pullreq: 9344
+
+ Fix compilation with h2o_socket_get_ssl_server_name().
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9346
+
+ Fix compilation on OpenBSD/amd64.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9356
+
+ Handle calling PacketCache methods on a nil object.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9360
+
+ Prevent a copy of a pool's backends when selecting a server.
+
+.. changelog::
+ :version: 1.5.0-rc4
+ :released: 7th of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9278
+
+ Prevent a race between the DoH handling threads
+
+.. changelog::
+ :version: 1.5.0-rc3
+ :released: 18th of June 2020
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9100
+
+ Less negatives in secpoll error messages improves readability.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9127
+ :tickets: 9125
+
+ Fix compilation on systems that do not define HOST_NAME_MAX
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9207
+
+ Use std::string_view when available (Rosen Penev)
+
+ .. change::
+ :tags: Bug Fixes, DNS over HTTPS
+ :pullreq: 9211
+ :tickets: 9206
+
+ Use non-blocking pipes to pass DoH queries/responses around
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9213
+
+ Do not use `using namespace std;`
+
+ .. change::
+ :tags: New Features
+ :pullreq: 9229
+
+ Implement an ACL in the internal web server
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9238
+ :tickets: 8038
+
+ Clean up dnsdistconf.lua as a default configuration file
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9244
+
+ Add optional masks to KeyValueLookupKeySourceIP
+
+.. changelog::
+ :version: 1.5.0-rc2
+ :released: 13th of May 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9031
+ :tickets: 9025
+
+ Fix compilation of the ports event multiplexer
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9042
+
+ Avoid copies in for loops
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9054
+
+ Build with -Wmissing-declarations -Wredundant-decls
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9016
+ :tickets: 9004
+
+ Use std::shuffle instead of std::random_shuffle
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9053
+
+ Get rid of a naked pointer in the /dev/poll event multiplexer
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9059
+
+ A few warnings fixed, reported by clang on OpenBSD
+
+ .. change::
+ :tags: Bug Fixes, DNS over HTTPS
+ :pullreq: 9068
+
+ Fix duplicated HTTP/1 counter in 'showDOHFrontends()'
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9057
+
+ Gracefully handle a failure to remove FD on (re)-connection
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9067
+
+ Wrap pthread objects
+
+ .. change::
+ :tags: Improvements, Metrics
+ :pullreq: 9084
+
+ Add the unit to the help for latency buckets
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9078
+
+ NetmaskTree: do not test node for null, the loop guarantees node is not null.
+
.. changelog::
:version: 1.5.0-rc1
:released: 16th of April 2020
:tags: Improvements
:pullreq: 8440
- Fix -WShadow warnings (Aki Tuomi)
+ Fix -Wshadow warnings (Aki Tuomi)
.. change::
:tags: Improvements
: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
:pullreq: 7585
:tickets: 7534
- Prevent 0-ttl cache hits
+ Prevent 0-ttl cache hits
.. change::
:tags: Improvements
: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
:pullreq: 7064
:tickets: 7060
- Wrap GnuTLS and OpenSSL pointers in smart pointers
+ Wrap GnuTLS and OpenSSL pointers in smart pointers
.. change::
:tags: New Features
:pullreq: 6523
:tickets: 6430
- Tests: avoid failure on not-so-optimal distribution
+ Tests: avoid failure on not-so-optimal distribution
.. change::
:tags: New Features
:tags: Bug Fixes
:pullreq: 6672
- Fix reconnection handling
+ Fix reconnection handling
.. change::
:tags: Improvements
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"}}
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::
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
-------------------------
.. 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.
.. 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.
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:
* ``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])
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.
: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])
.. 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.
* ``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
~~~~~~~~~~~~~~~~~~~~
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.
: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.
: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()
.. 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.
: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)
.. 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
~~~~~~~~~~~~~~~~
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.
: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
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.
.. 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`),
.. 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`).
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.
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
--------------
#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
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.
// 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;
}
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) {
/* 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;
}
}
}
+/* 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;
/*
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)
{
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;
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);
}
/* 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;
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) {
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);
}
}
+/* 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;
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";
}
/*
- 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.
*/
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);
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=");
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
}
return 0;
}
-catch(const exception& e)
+ catch(const std::exception& e)
{
errlog("DOH Handler function failed with error %s", e.what());
return 0;
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;
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;
}
}
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;
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);
}
}
/* 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;
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);
// 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();
}
}
-/* 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))
{
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));
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);
{
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 {
}
}
+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
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");
#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);
}
}
+ /* 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.");
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");
{
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();
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";
{
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();
#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>
#include <boost/utility/string_ref.hpp>
using string_view = boost::string_ref;
#endif
-#else // C++17
-using std::string_view;
#endif
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); }
#include "namespaces.hh"
PcapPacketReader::PcapPacketReader(const string& fname) : d_fname(fname)
{
- d_fp=fopen(fname.c_str(),"r");
- if(!d_fp)
+ d_fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(fname.c_str(), "r"), fclose);
+ if (!d_fp) {
unixDie("Unable to open file " + fname);
-
- int flags=fcntl(fileno(d_fp),F_GETFL,0);
- fcntl(fileno(d_fp), F_SETFL,flags&(~O_NONBLOCK)); // bsd needs this in stdin (??)
-
+ }
+
+ int flags = fcntl(fileno(d_fp.get()), F_GETFL, 0);
+ fcntl(fileno(d_fp.get()), F_SETFL, flags & (~O_NONBLOCK)); // bsd needs this in stdin (??)
+
checkedFread(&d_pfh);
-
- if(d_pfh.magic != 2712847316UL)
+
+ if (d_pfh.magic != 2712847316UL) {
throw runtime_error((format("PCAP file %s has bad magic %x, should be %x") % fname % d_pfh.magic % 2712847316UL).str());
-
+ }
+
if( d_pfh.linktype==1) {
d_skipMediaHeader=sizeof(struct ether_header);
}
+ else if( d_pfh.linktype==12) { // LOOP
+ d_skipMediaHeader=4;
+ }
else if(d_pfh.linktype==101) {
d_skipMediaHeader=0;
}
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();
d_ether=reinterpret_cast<struct ether_header*>(d_buffer);
contentCode=ntohs(d_ether->ether_type);
}
+ else if(d_pfh.linktype == 12) { // LOOP
+ if (d_pheader.caplen < (d_skipMediaHeader + sizeof(*d_ip))) {
+ d_runts++;
+ continue;
+ }
+ if(d_ip->ip_v == 4)
+ contentCode = 0x0800;
+ else
+ contentCode = 0x86dd;
+ }
else if(d_pfh.linktype==101) {
if (d_pheader.caplen < (d_skipMediaHeader + sizeof(*d_ip))) {
d_runts++;
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()
}
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());
}
PcapPacketReader(const string& fname);
- ~PcapPacketReader();
-
template<typename T>
void checkedFread(T* ptr)
{
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;
};
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};
};
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);
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;
RRSIGRecordContent::report();
DSRecordContent::report();
CDSRecordContent::report();
+ IPSECKEYRecordContent::report();
SSHFPRecordContent::report();
CERTRecordContent::report();
NSECRecordContent::report();
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;
static void setMaxEntries(size_t maxEntries);
+ typedef std::map<std::string, std::vector<std::string> > METAValues;
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{};
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;
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));
}
HTTPVersionStats d_http1Stats;
HTTPVersionStats d_http2Stats;
+ uint32_t d_internalPipeBufferSize{0};
bool d_sendCacheControlHeaders{true};
bool d_trustForwardedForHeader{false};
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
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));
}
}
else {
+ g_log<<Logger::Warning<<"Cache clear request received from operator"<<endl;
ret = purgeAuthCaches();
DNSSECKeeper::clearAllCaches();
}
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
{
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 {
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)
std::unordered_set<std::string> d_tags;
std::string d_name;
Priority d_priority{maximumPriority};
+ bool d_policyOverridesGettag{true};
};
struct Policy
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;
{
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;
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 = 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)
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;
}
#include <condition_variable>
#include "ixfr.hh"
#include "ixfrutils.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include "dns_random.hh"
#include "sstuff.hh"
#include "mplexer.hh"
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);
#include "dns_random.hh"
#include "gss_context.hh"
#include <boost/multi_index_container.hpp>
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include <fstream>
#include "ixfr.hh"
#include "ixfrutils.hh"
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;
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);
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", []() {
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);
fmt % labels[i];
fmt % dashed;
- for(const auto& quad : quads)
- fmt % quad;
+ for(const auto& lquad : quads)
+ fmt % lquad;
return fmt.str();
}
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);
}
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);
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});
deviceName = *deviceNameret;
}
- const auto routingTagret = std::get<6>(ret);
- if (routingTagret) {
- routingTag = *routingTagret;
+ const auto routingTarget = std::get<6>(ret);
+ if (routingTarget) {
+ routingTag = *routingTarget;
}
return std::get<0>(ret);
std::string requestorId;
std::string deviceId;
std::string deviceName;
- vState validationState{Indeterminate};
+ vState validationState{vState::Indeterminate};
bool& variable;
bool& wantsRPZ;
bool& logResponse;
#include <boost/algorithm/string.hpp>
#include "validate-recursor.hh"
#include "ednssubnet.hh"
+#include "query-local-address.hh"
#ifdef HAVE_PROTOBUF
#ifdef HAVE_FSTRM
#include "rec-dnstap.hh"
#include "fstrm_logger.hh"
+
+
bool g_syslog;
static bool isEnabledForQueries(const std::shared_ptr<std::vector<std::unique_ptr<FrameStreamLogger>>>& fstreamLoggers)
Socket s(ip.sin4.sin_family, SOCK_STREAM);
s.setNonBlocking();
- ComboAddress local = getQueryLocalAddress(ip.sin4.sin_family, 0);
+ ComboAddress local = pdns::getQueryLocalAddress(ip.sin4.sin_family, 0);
s.bind(local);
#include "packetcache.hh"
#include "base64.hh"
#include "namespaces.hh"
+#include "query-local-address.hh"
void CommunicatorClass::queueNotifyDomain(const DomainInfo& di, UeberBackend* B)
ComboAddress remote(ip, 53); // default to 53
if((d_nsock6 < 0 && remote.sin4.sin_family == AF_INET6) ||
(d_nsock4 < 0 && remote.sin4.sin_family == AF_INET)) {
- g_log<<Logger::Warning<<"Unable to notify "<<remote.toStringWithPort()<<" for domain '"<<domain<<"', address family is disabled. Is query-local-address"<<(remote.sin4.sin_family == AF_INET ? "" : "6")<<" unset?"<<endl;
+ g_log<<Logger::Warning<<"Unable to notify "<<remote.toStringWithPort()<<" for domain '"<<domain<<"', address family is disabled. Is an IPv"<<(remote.sin4.sin_family == AF_INET ? "4" : "6")<<" address set in query-local-address?"<<endl;
d_nq.removeIf(remote.toStringWithPort(), id, domain); // Remove, we'll never be able to notify
continue; // don't try to notify what we can't!
}
void CommunicatorClass::makeNotifySockets()
{
- if(!::arg()["query-local-address"].empty()) {
- d_nsock4 = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind"));
+ if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+ d_nsock4 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind"));
} else {
d_nsock4 = -1;
}
- if(!::arg()["query-local-address6"].empty()) {
- d_nsock6 = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind"));
+ if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
+ d_nsock6 = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind"));
} else {
d_nsock6 = -1;
}
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
+#include <limits.h>
#ifdef __FreeBSD__
# include <pthread_np.h>
#endif
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;
+}
std::vector<ComboAddress> getResolvers(const std::string& resolvConfPath);
DNSName reverseNameFromIP(const ComboAddress& ip);
+
+std::string getCarbonHostName();
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();
}
}
-unsigned long openssl_pthreads_id_callback()
+static unsigned long openssl_pthreads_id_callback(void)
{
return (unsigned long)pthread_self();
}
{
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!
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());
[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
#ifdef NOD_ENABLED
#include "nod.hh"
#endif /* NOD_ENABLED */
+#include "query-local-address.hh"
#include "rec-protobuf.hh"
#include "rec-snmp.hh"
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) */
typedef vector<int> tcpListenSockets_t;
typedef map<int, ComboAddress> listenSocketsAddresses_t; // is shared across all threads right now
-static const ComboAddress g_local4("0.0.0.0"), g_local6("::");
static listenSocketsAddresses_t g_listenSocketsAddresses; // is shared across all threads right now
static set<int> g_fromtosockets; // listen sockets that use 'sendfromto()' mechanism
-static vector<ComboAddress> g_localQueryAddresses4, g_localQueryAddresses6;
static AtomicCounter counter;
static std::shared_ptr<SyncRes::domainmap_t> g_initialDomainMap; // new threads needs this to be setup
static std::shared_ptr<NetmaskGroup> g_initialAllowFrom; // new thread needs to be setup with this
{
Socket s(dest.sin4.sin_family, SOCK_DGRAM);
s.setNonBlocking();
- ComboAddress local = getQueryLocalAddress(dest.sin4.sin_family, 0);
+ ComboAddress local = pdns::getQueryLocalAddress(dest.sin4.sin_family, 0);
s.bind(local);
s.connect(dest);
return data;
}
-//! pick a random query local address
-ComboAddress getQueryLocalAddress(int family, uint16_t port)
-{
- ComboAddress ret;
- if(family==AF_INET) {
- if(g_localQueryAddresses4.empty())
- ret = g_local4;
- else
- ret = g_localQueryAddresses4[dns_random(g_localQueryAddresses4.size())];
- ret.sin4.sin_port = htons(port);
- }
- else {
- if(g_localQueryAddresses6.empty())
- ret = g_local6;
- else
- ret = g_localQueryAddresses6[dns_random(g_localQueryAddresses6.size())];
-
- ret.sin6.sin6_port = htons(port);
- }
- return ret;
-}
-
static void handleUDPServerResponse(int fd, FDMultiplexer::funcparam_t&);
static void setSocketBuffer(int fd, int optname, uint32_t size)
while (s_avoidUdpSourcePorts.count(port));
}
- sin=getQueryLocalAddress(family, port); // does htons for us
+ sin=pdns::getQueryLocalAddress(family, port); // does htons for us
if (::bind(ret, (struct sockaddr *)&sin, sin.getSocklen()) >= 0)
break;
#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)) {
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;
}
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;
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;
}
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;
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);
}
}
}
+ // 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)) {
}
}
- 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) {
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;
}
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;
}
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)
#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
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--;
}
}
}
+
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) {
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) {
}
if (cacheHit) {
- if(valState == Bogus) {
+ if(valState == vState::Bogus) {
if(t_bogusremotes)
t_bogusremotes->push_back(source);
if(t_bogusqueryring)
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;
+ }
}
}
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;
}
}
}
}
- 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()
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"));
checkLinuxIPv6Limits();
try {
- vector<string> addrs;
- if(!::arg()["query-local-address6"].empty()) {
- SyncRes::s_doIPv6=true;
- g_log<<Logger::Warning<<"Enabling IPv6 transport for outgoing queries"<<endl;
-
- stringtok(addrs, ::arg()["query-local-address6"], ", ;");
- for(const string& addr : addrs) {
- g_localQueryAddresses6.push_back(ComboAddress(addr));
- }
- }
- else {
- g_log<<Logger::Warning<<"NOT using IPv6 for outgoing queries - set 'query-local-address6=::' to enable"<<endl;
- }
- addrs.clear();
- stringtok(addrs, ::arg()["query-local-address"], ", ;");
- for(const string& addr : addrs) {
- g_localQueryAddresses4.push_back(ComboAddress(addr));
+ pdns::parseQueryLocalAddress(::arg()["query-local-address"]);
+ if (!::arg()["query-local-address6"].empty()) {
+ // TODO remove in 4.5.0
+ g_log<<Logger::Warning<<"query-local-address6 is deprecated and will be removed in a future version. Please use query-local-address for IPv6 addresses as well"<<endl;
+ pdns::parseQueryLocalAddress(::arg()["query-local-address6"]);
}
}
catch(std::exception& e) {
exit(99);
}
+ if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+ SyncRes::s_doIPv4=true;
+ g_log<<Logger::Warning<<"Enabling IPv4 transport for outgoing queries"<<endl;
+ }
+ else {
+ g_log<<Logger::Warning<<"NOT using IPv4 for outgoing queries - add an IPv4 address (like '0.0.0.0') to query-local-address to enable"<<endl;
+ }
+
+
+ if(pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
+ SyncRes::s_doIPv6=true;
+ g_log<<Logger::Warning<<"Enabling IPv6 transport for outgoing queries"<<endl;
+ }
+ else {
+ g_log<<Logger::Warning<<"NOT using IPv6 for outgoing queries - add an IPv6 address (like '::') to query-local-address to enable"<<endl;
+ }
+
+ if (!SyncRes::s_doIPv6 && !SyncRes::s_doIPv4) {
+ g_log<<Logger::Error<<"No outgoing addresses configured! Can not continue"<<endl;
+ exit(99);
+ }
+
// keep this ABOVE loadRecursorLuaConfig!
if(::arg()["dnssec"]=="off")
g_dnssecmode=DNSSECMode::Off;
SyncRes::s_serverdownthrottletime=::arg().asNum("server-down-throttle-time");
SyncRes::s_serverID=::arg()["server-id"];
SyncRes::s_maxqperq=::arg().asNum("max-qperq");
+ SyncRes::s_maxnsaddressqperq=::arg().asNum("max-ns-address-qperq");
SyncRes::s_maxtotusec=1000*::arg().asNum("max-total-msec");
SyncRes::s_maxdepth=::arg().asNum("max-recursion-depth");
SyncRes::s_rootNXTrust = ::arg().mustDo( "root-nx-trust");
SyncRes::setECSScopeZeroAddress(Netmask(scopeZero, scopeZero.isIPv4() ? 32 : 128));
}
else {
- bool found = false;
- for (const auto& addr : g_localQueryAddresses4) {
- if (!IsAnyAddress(addr)) {
- SyncRes::setECSScopeZeroAddress(Netmask(addr, 32));
- found = true;
- break;
- }
+ Netmask nm;
+ bool done = false;
+
+ auto addr = pdns::getNonAnyQueryLocalAddress(AF_INET);
+ if (addr.sin4.sin_family != 0) {
+ nm = Netmask(addr, 32);
+ done = true;
}
- if (!found) {
- for (const auto& addr : g_localQueryAddresses6) {
- if (!IsAnyAddress(addr)) {
- SyncRes::setECSScopeZeroAddress(Netmask(addr, 128));
- found = true;
- break;
- }
- }
- if (!found) {
- SyncRes::setECSScopeZeroAddress(Netmask("127.0.0.1/32"));
+ if (!done) {
+ addr = pdns::getNonAnyQueryLocalAddress(AF_INET6);
+ if (addr.sin4.sin_family != 0) {
+ nm = Netmask(addr, 128);
+ done = true;
}
}
+ if (!done) {
+ nm = Netmask(ComboAddress("127.0.0.1"), 32);
+ }
+ SyncRes::setECSScopeZeroAddress(nm);
}
SyncRes::parseEDNSSubnetWhitelist(::arg()["edns-subnet-whitelist"]);
recursorThread(currentThreadId++, "worker");
handlerInfos.thread.join();
+ if (handlerInfos.exitCode != 0) {
+ ret = handlerInfos.exitCode;
+ }
}
else {
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)
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)
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);
}
::arg().set("socket-group","Group of socket")="";
::arg().set("socket-mode", "Permissions for socket")="";
- ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns-recursor when unset and not chrooted" )="";
+ ::arg().set("socket-dir",string("Where the controlsocket will live, ")+LOCALSTATEDIR+"/pdns-recursor when unset and not chrooted"
+#ifdef HAVE_SYSTEMD
+ + ". Set to the RUNTIME_DIRECTORY environment variable when that variable has a value (e.g. under systemd).")="";
+ auto runtimeDir = getenv("RUNTIME_DIRECTORY");
+ if (runtimeDir != nullptr) {
+ ::arg().set("socket-dir") = runtimeDir;
+ }
+#else
+ )="";
+#endif
::arg().set("delegation-only","Which domains we only accept delegations from")="";
::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
- ::arg().set("query-local-address6","Source IPv6 address for sending queries. IF UNSET, IPv6 WILL NOT BE USED FOR OUTGOING QUERIES")="";
+ ::arg().set("query-local-address6","DEPRECATED: Use query-local-address for IPv6 as well. Source IPv6 address for sending queries. IF UNSET, IPv6 WILL NOT BE USED FOR OUTGOING QUERIES")="";
::arg().set("client-tcp-timeout","Timeout in seconds when talking to TCP clients")="2";
::arg().set("max-mthreads", "Maximum number of simultaneous Mtasker threads")="2048";
::arg().set("max-tcp-clients","Maximum number of simultaneous TCP clients")="128";
::arg().set("edns-outgoing-bufsize", "Outgoing EDNS buffer size")="1232";
::arg().set("minimum-ttl-override", "Set under adverse conditions, a minimum TTL")="0";
::arg().set("max-qperq", "Maximum outgoing queries per query")="60";
+ ::arg().set("max-ns-address-qperq", "Maximum outgoing NS address queries per query")="10";
::arg().set("max-total-msec", "Maximum total wall-clock time per query in milliseconds, 0 for unlimited")="7000";
::arg().set("max-recursion-depth", "Maximum number of internal recursion calls per query, 0 for unlimited")="40";
::arg().set("max-udp-queries-per-round", "Maximum number of UDP queries processed per recvmsg() round, before returning back to normal processing")="10000";
::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";
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");
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;
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+
+#include <fcntl.h>
+
#include "dnsseckeeper.hh"
#include "dnssecinfra.hh"
#include "statbag.hh"
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;
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) {
}
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;
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;
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;
}
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) {
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_)
{
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) {
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;
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;
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;
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;
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 {
} 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) {
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)
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) {
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) {
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)
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) {
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) {
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) {
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) {
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) {
}
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) {
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) {
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];
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];
dpk.d_flags = 257;
else {
cerr<<"Unknown key flag '"<<cmds[4]<<"'"<<endl;
- exit(1);
+ return 1;
}
}
else
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;
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];
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;
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]);
return 1;
}
- cerr << "Key of size " << bits << " created" << std::endl;
+ cerr << "Key of size " << dke->getBits() << " created" << std::endl;
return 0;
}
#else
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;
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)
(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
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;
}
}
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;
}
}
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
}
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");
}
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;
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "query-local-address.hh"
+#include "iputils.hh"
+#include "dns_random.hh"
+
+namespace pdns {
+ static const ComboAddress local4("0.0.0.0");
+ static const ComboAddress local6("::");
+
+ static vector<ComboAddress> g_localQueryAddresses4;
+ static vector<ComboAddress> g_localQueryAddresses6;
+
+ ComboAddress getQueryLocalAddress(const sa_family_t family, const in_port_t port) {
+ ComboAddress ret;
+ if (family==AF_INET) {
+ if (g_localQueryAddresses4.empty()) {
+ ret = local4;
+ } else if (g_localQueryAddresses4.size() == 1) {
+ ret = g_localQueryAddresses4.at(0);
+ } else {
+ ret = g_localQueryAddresses4[dns_random(g_localQueryAddresses4.size())];
+ }
+ ret.sin4.sin_port = htons(port);
+ }
+ else {
+ if (g_localQueryAddresses6.empty()) {
+ ret = local6;
+ } else if (g_localQueryAddresses6.size() == 1) {
+ ret = g_localQueryAddresses6.at(0);
+ } else {
+ ret = g_localQueryAddresses6[dns_random(g_localQueryAddresses6.size())];
+ }
+ ret.sin6.sin6_port = htons(port);
+ }
+ return ret;
+ }
+
+ ComboAddress getNonAnyQueryLocalAddress(const sa_family_t family) {
+ if (family == AF_INET) {
+ for (const auto& addr : pdns::g_localQueryAddresses4) {
+ if (!IsAnyAddress(addr)) {
+ return addr;
+ }
+ }
+ }
+ if (family == AF_INET6) {
+ for (const auto& addr : pdns::g_localQueryAddresses6) {
+ if (!IsAnyAddress(addr)) {
+ return addr;
+ }
+ }
+ }
+ ComboAddress ret("0.0.0.0");
+ ret.reset(); // Ensure all is zero, even the addr family
+ return ret;
+ }
+
+ void parseQueryLocalAddress(const std::string &qla) {
+ vector<string> addrs;
+ stringtok(addrs, qla, ", ;");
+ for(const string& addr : addrs) {
+ ComboAddress tmp(addr);
+ if (tmp.isIPv4()) {
+ g_localQueryAddresses4.push_back(tmp);
+ continue;
+ }
+ g_localQueryAddresses6.push_back(tmp);
+ }
+ }
+
+ bool isQueryLocalAddressFamilyEnabled(const sa_family_t family) {
+ if (family == AF_INET) {
+ return !g_localQueryAddresses4.empty();
+ }
+ if (family == AF_INET6) {
+ return !g_localQueryAddresses6.empty();
+ }
+ return false;
+ }
+} // namespace pdns
--- /dev/null
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "iputils.hh"
+
+namespace pdns {
+ /*! pick a random query local address for family
+ *
+ * Will always return a ComboAddress.
+ *
+ * @param family Address Family, only AF_INET and AF_INET6 are supported
+ * @param port Port to set in the returned ComboAddress
+ */
+ ComboAddress getQueryLocalAddress(const sa_family_t family, const in_port_t port);
+
+ /*! Returns a non-Any address QLA, or an empty QLA when the QLA is any
+ *
+ * @param family Address Family
+ */
+ ComboAddress getNonAnyQueryLocalAddress(const sa_family_t family);
+
+ /*! Populate the query local address vectors
+ *
+ * Will throw when an address can't be parsed
+ *
+ * @param qla A string of one or more ip addresses, separated by
+ * spaces, semi-colons or commas
+ */
+ void parseQueryLocalAddress(const std::string &qla);
+
+ /*! Is the address family explicitly enabled
+ *
+ * i.e. was there an address parsed by parseQueryLocalAddress belonging
+ * to this family
+ *
+ * @param family Address Family, only AF_INET and AF_INET6 are supported
+ */
+ bool isQueryLocalAddressFamilyEnabled(const sa_family_t family);
+} // namespace pdns
if(namespace_name.empty()) {
namespace_name="pdns";
}
- if(hostname.empty()) {
- char tmp[80];
- memset(tmp, 0, sizeof(tmp));
- gethostname(tmp, sizeof(tmp));
- char *p = strchr(tmp, '.');
- if(p) *p=0;
-
- hostname=tmp;
- boost::replace_all(hostname, ".", "_");
+ if (hostname.empty()) {
+ try {
+ hostname = getCarbonHostName();
+ }
+ catch(const std::exception& e) {
+ throw std::runtime_error(std::string("The 'carbon-ourname' setting has not been set and we are unable to determine the system's hostname: ") + e.what());
+ }
}
if(instance_name.empty()) {
instance_name="recursor";
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"]);
tags.insert(tag.second);
}
}
+ if (have.count("overridesGettag")) {
+ overridesGettag = boost::get<bool>(have["overridesGettag"]);
+ }
}
#if HAVE_PROTOBUF
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);
}
}
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;
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"])));
#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]);
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>");
return sum;
}
-void RecursorPacketCache::doPruneTo(unsigned int maxCached)
+void RecursorPacketCache::doPruneTo(size_t maxCached)
{
pruneCollection<SequencedTag>(*this, d_packetCache, maxCached);
}
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);
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());
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);
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)
{
}
pubsuffix.hh pubsuffix.cc \
pubsuffixloader.cc \
qtype.hh qtype.cc \
+ query-local-address.hh query-local-address.cc \
rcpgenerator.cc rcpgenerator.hh \
rec-carbon.cc \
rec-lua-conf.hh rec-lua-conf.cc \
reczones.cc \
remote_logger.cc remote_logger.hh \
resolver.hh resolver.cc \
+ axfr-retriever.hh axfr-retriever.cc \
resolve-context.hh \
responsestats.hh responsestats.cc \
root-addresses.hh \
testrunner_SOURCES = \
arguments.cc \
+ axfr-retriever.hh axfr-retriever.cc \
base32.cc \
base64.cc base64.hh \
circular_buffer.hh \
pollmplexer.cc \
protobuf.cc protobuf.hh \
qtype.cc qtype.hh \
+ query-local-address.hh query-local-address.cc \
rcpgenerator.cc \
rec-protobuf.cc rec-protobuf.hh \
recpacketcache.cc recpacketcache.hh \
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 \
pdns-recursor@.service: pdns-recursor.service
$(AM_V_GEN)sed -e 's!/pdns_recursor!& --config-name=%i!' \
-e 's!Recursor!& %i!' \
+ -e 's!RuntimeDirectory=.*!&-%i!' \
< $< > $@
systemdsystemunitdir = $(SYSTEMD_DIR)
--- /dev/null
+../axfr-retriever.cc
\ No newline at end of file
--- /dev/null
+../axfr-retriever.hh
\ No newline at end of file
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],
# 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
End of life statements
======================
+We aim to have a release every six months.
+The latest and previous release receive correctness, stability and security updates.
+The release before that gets critical security updates only.
+Older releases are marked end of life and receive no updates at all.
+Pre-releases do not receive immediate security updates.
+
The currently supported release train of the PowerDNS Recursor is 4.3.
PowerDNS Recursor 4.2 will only receive correctness, stability and
Note: Users with a commercial agreement with PowerDNS.COM BV or Open-Xchange
can receive extended support for releases which are End Of Life. If you are
such a user, these EOL statements do not apply to you.
+
+.. list-table:: PowerDNS Recursor Release Life Cycle
+ :header-rows: 1
+
+ * - Version
+ - Release date
+ - Security-Only updates
+ - End of Life
+ * - 4.3
+ - March 3 2020
+ - ~ March 2021
+ - ~ September 2021
+ * - 4.2
+ - July 16 2019
+ - ~ September 2020
+ - ~ March 2021
+ * - 4.1
+ - December 4 2017
+ - March 3 2020
+ - ~ September 2020
+ * - 4.0 and older
+ - EOL
+ - EOL
+ - EOL
+
====================
.. changelog::
- :version: 4.1.15
+ :version: 4.1.17
+ :released: 1st of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9283
+
+ Backport of CVE-2020-14196: Enforce webserver ACL.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9129
+ :tickets: 9127, 8640
+
+ Fix compilation on systems that do not define HOST_NAME_MAX.
+
+
+.. changelog::
+ :version: 4.1.16
+ :released: 19th of May 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9117
+
+ Backport of security fixes for CVE-2020-10995, CVE-2020-12244 and
+ CVE-2020-10030, plus avoid a crash when loading an invalid RPZ.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8809
+
+ Update python dependencies for docs generation.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8868
+
+ Only log qname parsing errors when 'log-common-errors' is set.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8753
+
+ Update boost.m4.
+
+.. changelog::
+ :version: 4.1.15
:released: 6th of December 2019
.. change::
Changelogs for 4.2.x
====================
+.. changelog::
+ :version: 4.2.4
+ :released: 17th of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9334
+ :tickets: 9309
+
+ Validate cached DNSKEYs against the DSs, not the RRSIGs only.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9333
+ :tickets: 9297
+
+ Ignore cache-only for DNSKEYS/DS retrieval.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9332
+ :tickets: 9292
+
+ A ServFail while retrieving DS/DNSKEY records is just that.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9331
+ :tickets: 9188
+
+ Refuse DS records received from child zones.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9306
+ :tickets: 9268
+
+ Better exception handling in housekeeping/handlePolicyHit.
+
+.. changelog::
+ :version: 4.2.3
+ :released: 1st of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9284
+
+ Backport of CVE-2020-14196: Enforce webserver ACL.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9261
+ :tickets: 9251
+
+ Copy the negative cache entry before validating it.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9133
+ :tickets: 9127
+
+ Fix compilation on systems that do not define HOST_NAME_MAX.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9123
+ :tickets: 8640
+
+ Fix build with gcc-10
+
+.. changelog::
+ :version: 4.2.2
+ :released: 19th of May 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9116
+
+ Backport of security fixes for CVE-2020-10995, CVE-2020-12244 and
+ CVE-2020-10030, plus avoid a crash when loading an invalid RPZ.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9081
+
+ Add ubuntu focal target.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8988
+
+ Update gen-version to use latest tag for version number.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8964, 8752
+ :tickets: 8875
+
+ Update boost.m4.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 8869
+
+ Only log qname parsing errors when 'log-common-errors' is set.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8832
+
+ Refuse NSEC records with a bitmap length > 32.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8802
+
+ Avoid startup race by setting the state of a tread before starting it.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8696
+
+ Better detection of Bogus zone cuts for DNSSEC validation.
+
+ .. change::
+ :tags: Bug Fixes.
+ :pullreq: 8674
+
+ Debian postinst / do not fail on user creation if it already exists.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 8686
+
+ Fix parsing `dont-throttle-names` and `dont-throttle-netmasks` as comma separated lists.
.. changelog::
:version: 4.2.1
Changelogs for 4.3.x
====================
+.. changelog::
+ :version: 4.3.3
+ :released: 17th of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9330
+ :tickets: 9309
+
+ Validate cached DNSKEYs against the DSs, not the RRSIGs only.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9329
+ :tickets: 9297
+
+ Ignore cache-only for DNSKEYs and DS retrieval.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9328
+ :tickets: 9292
+
+ A ServFail while retrieving DS/DNSKEY records is just that.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9327
+ :tickets: 9188
+
+ Refuse DS records received from child zones.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9305
+ :tickets: 9268
+
+ Better exception handling in houseKeeping/handlePolicyHit.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9304
+ :tickets: 9299, 9301
+
+ Take initial refresh time from loaded zone.
+
+.. changelog::
+ :version: 4.3.2
+ :released: 1st of July 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9285
+
+ Backport of CVE-2020-14196: Enforce webserver ACL.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9262
+ :tickets: 9251
+
+ Copy the negative cache entry before validating it.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9242
+ :tickets: 9031
+
+ Fix compilation of the ports event multiplexer.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9243
+ :tickets: 9142
+
+ Defer the NOD lookup until after the response has been sent.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9245
+ :tickets: 9151
+
+ Fix the handling of DS queries for the root.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9246
+ :tickets: 9172
+
+ Fix RPZ removals when an update has several deltas.
+
+ .. change::
+ :tags: Bug Fixes.
+ :pullreq: 9247
+ :tickets: 9192, 9184
+
+ Correct depth increments.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9248
+ :tickets: 9194, 9202, 9216
+
+ CNAME loop detection.
+
+ .. change::
+ :tags: Bug Fixes.
+ :pullreq: 9249
+ :tickets: 9205
+
+ Limit the TTL of RRSIG records as well
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9128
+ :tickets: 9127
+
+ Fix compilation on systems that do not define HOST_NAME_MAX.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9122
+ :tickets: 8640
+
+ Fix build with gcc-10.
+
+.. changelog::
+ :version: 4.3.1
+ :released: 19th of May 2020
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9115
+
+ Backport of security fixes for CVE-2020-10995, CVE-2020-12244 and
+ CVE-2020-10030, plus avoid a crash when loading an invalid RPZ.
+
+ .. change::
+ :tags: Improvements
+ :pullreq: 9082
+
+ Add ubuntu focal target.
+
+ .. change::
+ :tags: Bug Fixes
+ :pullreq: 9048
+ :tickets: 8778
+
+ RPZ dumpFile/seedFile: store/get SOA refresh on dump/load.
+
+ .. change::
+ :tags: Internals
+ :pullreq: 8963
+ :tickets: 8875
+
+ Update boost.m4.
.. changelog::
:version: 4.3.0
:tags: Improvements
:pullreq: 8440
- Fix -WShadow warnings (Aki Tuomi)
+ Fix -Wshadow warnings (Aki Tuomi)
.. change::
:tags: Improvements
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
Configuring DNSSEC key material must be done in the :ref:`setting-lua-config-file`, using :func:`addTA`.
This function takes 2 arguments: the node in the DNS-tree and the data of the corresponding DS record.
-To e.g. add a trust anchor for the root and powerdns.com, use the following config in the Lua file:
+To e.g. add a trust anchor for the root and example.com, use the following config in the Lua file:
.. code:: Lua
addTA('.', "63149 13 1 a59da3f5c1b97fcd5fa2b3b2b0ac91d38a60d33a") -- This is not an ICANN root
- addTA('powerdns.com', "44030 8 2 D4C3D5552B8679FAEEBC317E5F048B614B2E5F607DC57F1553182D49 AB2179F7")
+ addTA('example.com', "44030 8 2 D4C3D5552B8679FAEEBC317E5F048B614B2E5F607DC57F1553182D49 AB2179F7")
For PowerDNS Recursor 4.1.x and below, use the :func:`addDS` function instead.
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.
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!
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>`_.
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.
localAddress
^^^^^^^^^^^^
The source IP address to use when transferring the RPZ.
-When unset, :ref:`setting-query-local-address` and :ref:`setting-query-local-address6` are used.
+When unset, :ref:`setting-query-local-address` is used.
axfrTimeout
^^^^^^^^^^^
.. 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
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`)
: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.
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
Maximum number of simultaneous TCP clients.
--max-tcp-per-client=<num>
If set, maximum number of TCP sessions per client (IP address).
---query-local-address=<address>
+--query-local-address=<address[,address...]>
Use *address* as Source IP address when sending queries.
---query-local-address6=<address>
- Send out local IPv6 queries from *address*. Disabled by default,
- which also disables outgoing IPv6 support. A useful setting is
- '::0'.
--quiet
Suppress logging of questions and answers.
--server-id=<text>
--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
--------
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``
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
^^^^^^^^^^^
--- /dev/null
+PowerDNS Security Advisory 2020-01: Denial of Service
+=====================================================
+
+- CVE: CVE-2020-10995
+- Date: May 19th 2020
+- Affects: PowerDNS Recursor from 4.1.0 up to and including 4.3.0
+- Not affected: 4.1.16, 4.2.2, 4.3.1
+- Severity: Medium
+- Impact: Degraded Service
+- Exploit: This problem can be triggered via a crafted reply
+- Risk of system compromise: No
+- Solution: Upgrade to a non-affected version
+- Workaround: None
+
+An issue in the DNS protocol has been found that allow malicious
+parties to use recursive DNS services to attack third party
+authoritative name servers. The attack uses a crafted reply by an
+authoritative name server to amplify the resulting traffic between the
+recursive and other authoritative name servers. Both types of service
+can suffer degraded performance as an effect.
+
+This issue has been assigned CVE-2020-10995.
+
+PowerDNS Recursor from 4.1.0 up to and including 4.3.0 is
+affected. PowerDNS Recursor 4.1.16, 4.2.2 and 4.3.1 contain a
+mitigation to limit the impact of this DNS protocol issue.
+
+Please note that at the time of writing, PowerDNS Recursor 4.0 and
+below are no longer supported, as described in
+https://doc.powerdns.com/recursor/appendices/EOL.html.
+
+We would like to thank Lior Shafir, Yehuda Afek and Anat Bremler-Barr
+for finding and subsequently reporting this issue!
--- /dev/null
+PowerDNS Security Advisory 2020-02: Insufficient validation of DNSSEC signatures
+================================================================================
+
+- CVE: CVE-2020-12244
+- Date: May 19th 2020
+- Affects: PowerDNS Recursor from 4.1.0 up to and including 4.3.0
+- Not affected: 4.3.1, 4.2.2, 4.1.16
+- Severity: Medium
+- Impact: Denial of existence spoofing
+- Exploit: This problem can be triggered by an attacker in position
+ of man-in-the-middle
+- Risk of system compromise: No
+- Solution: Upgrade to a non-affected version
+- Workaround: None
+
+An issue has been found in PowerDNS Recursor 4.1.0 through 4.3.0 where
+records in the answer section of a NXDOMAIN response lacking an SOA
+were not properly validated in SyncRes::processAnswer. This would
+allow an attacker in position of man-in-the-middle to send a NXDOMAIN
+answer for a name that does exist, bypassing DNSSEC validation.
+
+This issue has been assigned CVE-2020-12244.
+
+PowerDNS Recursor from 4.1.0 up to and including 4.3.0 is affected.
+
+Please note that at the time of writing, PowerDNS Authoritative 4.0 and
+below are no longer supported, as described in
+https://doc.powerdns.com/authoritative/appendices/EOL.html.
+
+We would like to thank Matt Nordhoff for finding and subsequently
+reporting this issue!
+
--- /dev/null
+PowerDNS Security Advisory 2020-03: Information disclosure
+==========================================================
+
+- CVE: CVE-2020-10030
+- Date: May 19th 2020
+- Affects: PowerDNS Recursor from 4.1.0 up to and including 4.3.0
+- Not affected: 4.3.1, 4.2.2, 4.1.16
+- Severity: Low
+- Impact: Information Disclosure, Denial of Service
+- Exploit: This problem can be triggered via a crafted hostname
+- Risk of system compromise: No
+- Solution: Upgrade to a non-affected version
+- Workaround: None
+
+An issue has been found in PowerDNS Recursor allowing an
+attacker with enough privileges to change the system's hostname to
+cause disclosure of uninitialized memory content via a stack-based
+out-of-bounds read.
+It only occurs on systems where gethostname() does not null-terminate
+the returned string if the hostname is larger than the supplied buffer.
+Linux systems are not affected because the buffer is always large enough.
+OpenBSD systems are not affected because the returned hostname is always
+null-terminated.
+Under some conditions this issue can lead to the writing of one null-byte
+out-of-bounds on the stack, causing a denial of service or possibly
+arbitrary code execution.
+
+This issue has been assigned CVE-2020-10030.
+
+PowerDNS Recursor from 4.1.0 up to and including 4.3.0 is affected.
+
+Please note that at the time of writing, PowerDNS Recursor 4.0 and
+below are no longer supported, as described in
+https://doc.powerdns.com/recursor/appendices/EOL.html.
+
+We would like to thank Valentei Sergey for finding and subsequently
+reporting this issue!
+
--- /dev/null
+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.
+
``allow-from``
--------------
-- IP ranges, separated by commas
-- Default: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16
+- IP addresses or netmasks, separated by commas
+- Default: 127.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10, 169.254.0.0/16, 192.168.0.0/16, 172.16.0.0/12, ::1/128, fc00::/7, fe80::/10
Netmasks (both IPv4 and IPv6) that are allowed to use the server.
The default allows access only from :rfc:`1918` private IP addresses.
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``
--------------
- 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
`edns-subnet-whitelist`_ when `use-incoming-edns-subnet`_ is set and the query has
an ECS source prefix-length set to 0.
The default is to look for the first usable (not an ``any`` one) address in
-`query-local-address`_ then `query-local-address6`_. If no suitable address is
+`query-local-address`_ (starting with IPv4). If no suitable address is
found, the recursor fallbacks to sending 127.0.0.1.
.. _setting-edns-outgoing-bufsize:
``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:
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``
----------------------------------------------
If qname-minimization is enabled, the number will be forced to be 100
at a minimum to allow for the extra queries qname-minimization generates when the cache is empty.
+.. _setting-max-ns-address-qperq:
+
+``max-ns-address-qperq``
+------------------------
+.. versionadded:: 4.1.16
+.. versionadded:: 4.2.2
+.. versionadded:: 4.3.1
+
+- Integer
+- Default: 10
+
+The maximum number of outgoing queries with empty replies for
+resolving nameserver names to addresses we allow during the resolution
+of a single client query. If IPv6 is enabled, an A and a AAAA query
+for a name counts as 1. If a zone publishes more than this number of
+NS records, the limit is further reduced for that zone by lowering
+it by the number of NS records found above the
+`max-ns-address-qperq`_ value. The limit wil not be reduced to a
+number lower than 5.
+
.. _setting-max-negative-ttl:
``max-negative-ttl``
- 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:
-----------------------
.. 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.
``query-local-address``
-----------------------
-- IPv4 Address, comma separated
+.. versionchanged:: 4.4.0
+ IPv6 addresses can be set with this option as well.
+
+- IP addresses, comma separated
- Default: 0.0.0.0
-Send out local queries from this address, or addresses, by adding multiple addresses, increased spoofing resilience is achieved.
+Send out local queries from this address, or addresses. By adding multiple
+addresses, increased spoofing resilience is achieved. When no address of a certain
+address family is configured, there are *no* queries sent with that address family.
+In the default configuration this means that IPv6 is not used for outgoing queries.
.. _setting-query-local-address6:
``query-local-address6``
------------------------
+.. deprecated:: 4.4.0
+ Use :ref:`setting-query-local-address` for IPv4 and IPv6.
+
- IPv6 addresses, comma separated
- Default: unset
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``
- 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:
``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:
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:
------------------
.. versionadded:: 4.2.0
-- IP ranges, separated by commas
+- IP addresses or netmasks, separated by commas
- Default: empty
.. note::
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
--------------
* \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())
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;
}
* \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);
if ((uint32_t)now.tv_sec < ni->d_ttd) {
// Not expired
- *ne = &(*ni);
+ ne = *ni;
moveCacheItemToBack<SequenceTag>(d_negcache, firstIndexIterator);
return true;
}
*
* \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);
}
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;
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;
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);
--- /dev/null
+../query-local-address.cc
\ No newline at end of file
--- /dev/null
+../query-local-address.hh
\ No newline at end of file
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)
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)
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)
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)
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)
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)
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)
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)
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)
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);
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)
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)
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);
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);
last allocation if any. */
free(line);
}
-
- fclose(fp);
}
BOOST_AUTO_TEST_CASE(test_count)
BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("127.0.0.1"), boost::none), 0);
BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
- // if no tag given and no-non-tagged entries matches nothing shoudl be returned
+ // if no tag given and no-non-tagged entries matches nothing should be returned
BOOST_CHECK_LT(MRC.get(now, power, QType(QType::A), false, &retrieved, ComboAddress("192.0.2.2"), boost::none), 0);
BOOST_REQUIRE_EQUAL(retrieved.size(), 0U);
// Provide stubs for some symbols
bool g_logRPZChanges{false};
-ComboAddress getQueryLocalAddress(int family, uint16_t port)
-{
- cerr << "getQueryLocalAddress() STUBBED IN TEST!" << endl;
- BOOST_ASSERT(false);
- return ComboAddress();
-}
BOOST_AUTO_TEST_SUITE(rpzloader_cc)
static bool checkBasicMessage2(const PDNSException& ex)
{
- BOOST_CHECK_EQUAL(ex.reason, "RCODE was not NOERROR but " + RCode::to_s(1));
+ BOOST_CHECK_EQUAL(ex.reason, "RCODE was " + RCode::to_s(1));
return true;
}
return theArg;
}
-void primeRootNSZones(bool)
+void primeRootNSZones(bool, unsigned int)
{
}
#include "root-addresses.hh"
-void primeHints(void)
+bool primeHints(void)
{
vector<DNSRecord> nsset;
if (!s_RC)
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()
s_RC = std::unique_ptr<MemRecursorCache>(new MemRecursorCache());
SyncRes::s_maxqperq = 50;
+ SyncRes::s_maxnsaddressqperq = 10;
SyncRes::s_maxtotusec = 1000 * 7000;
SyncRes::s_maxdepth = 40;
SyncRes::s_maxnegttl = 3600;
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;
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);
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)
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);
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);
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);
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);
--- /dev/null
+#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()
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);
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);
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);
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);
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);
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);
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);
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);
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);
BOOST_CHECK_EQUAL(queriesCount, 5U);
}
+BOOST_AUTO_TEST_CASE(test_completely_flawed_big_nsset)
+{
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr);
+
+ primeHints();
+
+ const DNSName target("powerdns.com.");
+ size_t queriesCount = 0;
+
+ sr->setAsyncCallback([&queriesCount, target](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+ queriesCount++;
+
+ if (isRootServer(ip) && domain == target) {
+ setLWResult(res, 0, false, false, true);
+ // 20 NS records
+ for (int i = 0; i < 20; i++) {
+ string n = string("pdns-public-ns") + std::to_string(i) + string(".powerdns.com.");
+ addRecordToLW(res, domain, QType::NS, n, DNSResourceRecord::AUTHORITY, 172800);
+ }
+ return 1;
+ }
+ else if (domain.toString().length() > 14 && domain.toString().substr(0, 14) == "pdns-public-ns") {
+ setLWResult(res, 0, true, false, true);
+ addRecordToLW(res, ".", QType::SOA, "a.root-servers.net. nstld.verisign-grs.com. 2017032800 1800 900 604800 86400", DNSResourceRecord::AUTHORITY, 86400);
+ return 1;
+ }
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ try {
+ sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK(0);
+ }
+ catch (const ImmediateServFailException& ex) {
+ BOOST_CHECK_EQUAL(ret.size(), 0U);
+ // one query to get NSs, then A and AAAA for each NS, 5th NS hits the limit
+ // limit is reduced to 5, because zone publishes many (20) NS
+ BOOST_CHECK_EQUAL(queriesCount, 11U);
+ }
+}
+
BOOST_AUTO_TEST_CASE(test_cache_hit)
{
std::unique_ptr<SyncRes> sr;
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);
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);
}
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);
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);
}
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);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 0U);
/* we don't store empty results */
BOOST_CHECK_EQUAL(queriesCount, 4U);
BOOST_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();
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();
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)
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();
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();
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)
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)
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);
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);
}
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);
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);
}
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);
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;
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);
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)
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);
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);
}
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 */
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) {
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 */
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);
}
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 */
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);
}
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);
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);
}
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);
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);
}
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);
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);
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);
}
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);
}
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);
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);
}
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);
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);
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);
}
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);
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
}
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);
}
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 {
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()
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);
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);
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);
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 */
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);
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);
}
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);
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);
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);
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);
}
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);
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);
}
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
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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)
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
}
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);
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);
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);
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);
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);
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);
}
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);
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);
}
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);
ret.clear();
res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
BOOST_CHECK_EQUAL(res, RCode::NoError);
- BOOST_CHECK_EQUAL(sr->getValidationState(), Bogus);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+ BOOST_REQUIRE_EQUAL(ret.size(), 0U);
+ /* we don't store empty results */
+ BOOST_CHECK_EQUAL(queriesCount, 4U);
+}
+
+BOOST_AUTO_TEST_CASE(test_dnssec_bogus_nxdomain)
+{
+ std::unique_ptr<SyncRes> sr;
+ initSR(sr, true);
+
+ setDNSSECValidation(sr, DNSSECMode::ValidateAll);
+
+ primeHints();
+ const DNSName target("powerdns.com.");
+ testkeysset_t keys;
+
+ auto luaconfsCopy = g_luaconfs.getCopy();
+ luaconfsCopy.dsAnchors.clear();
+ generateKeyMaterial(DNSName("."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys, luaconfsCopy.dsAnchors);
+ generateKeyMaterial(DNSName("powerdns.com."), DNSSECKeeper::ECDSA256, DNSSECKeeper::DIGEST_SHA256, keys);
+ g_luaconfs.setState(luaconfsCopy);
+
+ size_t queriesCount = 0;
+
+ sr->setAsyncCallback([target, &queriesCount, keys](const ComboAddress& ip, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, int EDNS0Level, struct timeval* now, boost::optional<Netmask>& srcmask, boost::optional<const ResolveContext&> context, LWResult* res, bool* chained) {
+ queriesCount++;
+
+ if (type == QType::DS || type == QType::DNSKEY) {
+ return genericDSAndDNSKEYHandler(res, domain, domain, type, keys);
+ }
+ else {
+
+ setLWResult(res, RCode::NXDomain, true, false, true);
+ return 1;
+ }
+
+ return 0;
+ });
+
+ vector<DNSRecord> ret;
+ int res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NXDomain);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
+ BOOST_REQUIRE_EQUAL(ret.size(), 0U);
+ /* com|NS, powerdns.com|NS, powerdns.com|A */
+ BOOST_CHECK_EQUAL(queriesCount, 3U);
+
+ /* again, to test the cache */
+ ret.clear();
+ res = sr->beginResolve(target, QType(QType::A), QClass::IN, ret);
+ BOOST_CHECK_EQUAL(res, RCode::NXDomain);
+ BOOST_CHECK_EQUAL(sr->getValidationState(), vState::Bogus);
BOOST_REQUIRE_EQUAL(ret.size(), 0U);
/* we don't store empty results */
BOOST_CHECK_EQUAL(queriesCount, 4U);
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)
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)
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)
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)
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)
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)
/* 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)
/* 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)
/* 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)
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)
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)
/* 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();
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)
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 */
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)
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)
/* 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)
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);
}
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);
}
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);
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);
}
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);
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);
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);
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);
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);
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);
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);
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);
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)
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);
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);
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);
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 */
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!)
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)
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);
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);
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)
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);
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);
}
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)
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) {
}
}
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
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)
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);
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);
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);
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);
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 {
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)
}
}
-void primeHints(void)
+bool primeHints(void)
{
// prime root cache
- const vState validationState = Insecure;
+ const vState validationState = vState::Insecure;
vector<DNSRecord> nsset;
t_rootNSZones.clear();
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;
}
// 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);
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);
}
}
#include "base64.hh"
#include "dnswriter.hh"
#include "dnsparser.hh"
-
+#include "query-local-address.hh"
#include "dns_random.hh"
#include <poll.h>
#include "gss_context.hh"
#include "namespaces.hh"
+using pdns::resolver::parseResult;
+
int makeQuerySocket(const ComboAddress& local, bool udpOrTCP, bool nonLocalBind)
{
ComboAddress ourLocal(local);
locals["default4"] = -1;
locals["default6"] = -1;
try {
- if(!::arg()["query-local-address"].empty())
- locals["default4"] = makeQuerySocket(ComboAddress(::arg()["query-local-address"]), true, ::arg().mustDo("non-local-bind"));
- if(!::arg()["query-local-address6"].empty())
- locals["default6"] = makeQuerySocket(ComboAddress(::arg()["query-local-address6"]), true, ::arg().mustDo("non-local-bind"));
+ if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET)) {
+ locals["default4"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET, 0), true, ::arg().mustDo("non-local-bind"));
+ }
+ if (pdns::isQueryLocalAddressFamilyEnabled(AF_INET6)) {
+ locals["default6"] = makeQuerySocket(pdns::getQueryLocalAddress(AF_INET6, 0), true, ::arg().mustDo("non-local-bind"));
+ }
}
catch(...) {
if(locals["default4"]>=0)
// choose socket based on local
if (local.sin4.sin_family == 0) {
// up to us.
- sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
- if (sock == -1) {
+ if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
string ipv = remote.sin4.sin_family == AF_INET ? "4" : "6";
- string qla = remote.sin4.sin_family == AF_INET ? "" : "6";
- throw ResolverException("No IPv" + ipv + " socket available, is query-local-address" + qla + " unset?");
+ throw ResolverException("No IPv" + ipv + " socket available, is such an address configured in query-local-address?");
}
+ sock = remote.sin4.sin_family == AF_INET ? locals["default4"] : locals["default6"];
} else {
std::string lstr = local.toString();
std::map<std::string, int>::iterator lptr;
return randomid;
}
-static int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
-{
- result->clear();
-
- if(mdp.d_header.rcode)
- return mdp.d_header.rcode;
-
- if(origQname.countLabels()) { // not AXFR
- if(mdp.d_header.id != id)
- throw ResolverException("Remote nameserver replied with wrong id");
- if(mdp.d_header.qdcount != 1)
- throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")");
- if(mdp.d_qname != origQname)
- throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)");
- }
+namespace pdns {
+ namespace resolver {
+ int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result)
+ {
+ result->clear();
+
+ if(mdp.d_header.rcode)
+ return mdp.d_header.rcode;
+
+ if(origQname.countLabels()) { // not AXFR
+ if(mdp.d_header.id != id)
+ throw ResolverException("Remote nameserver replied with wrong id");
+ if(mdp.d_header.qdcount != 1)
+ throw ResolverException("resolver: received answer with wrong number of questions ("+itoa(mdp.d_header.qdcount)+")");
+ if(mdp.d_qname != origQname)
+ throw ResolverException(string("resolver: received an answer to another question (")+mdp.d_qname.toLogString()+"!="+ origQname.toLogString()+".)");
+ }
- vector<DNSResourceRecord> ret;
- DNSResourceRecord rr;
- result->reserve(mdp.d_answers.size());
+ vector<DNSResourceRecord> ret;
+ DNSResourceRecord rr;
+ result->reserve(mdp.d_answers.size());
- for (const auto& i: mdp.d_answers) {
- rr.qname = i.first.d_name;
- rr.qtype = i.first.d_type;
- rr.ttl = i.first.d_ttl;
- rr.content = i.first.d_content->getZoneRepresentation(true);
- result->push_back(rr);
- }
+ for (const auto& i: mdp.d_answers) {
+ rr.qname = i.first.d_name;
+ rr.qtype = i.first.d_type;
+ rr.ttl = i.first.d_ttl;
+ rr.content = i.first.d_content->getZoneRepresentation(true);
+ result->push_back(rr);
+ }
- return 0;
-}
+ return 0;
+ }
+
+ } // namespace resolver
+} // namespace pdns
bool Resolver::tryGetSOASerial(DNSName *domain, ComboAddress* remote, uint32_t *theirSerial, uint32_t *theirInception, uint32_t *theirExpire, uint16_t* id)
{
}
}
-AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
- const DNSName& domain,
- const TSIGTriplet& tt,
- const ComboAddress* laddr,
- size_t maxReceivedBytes,
- uint16_t timeout)
- : d_tsigVerifier(tt, remote, d_trc), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes)
-{
- ComboAddress local;
- if (laddr != nullptr) {
- local = ComboAddress(*laddr);
- } else {
- string qlas = remote.sin4.sin_family == AF_INET ? "query-local-address" : "query-local-address6";
- if (::arg()[qlas].empty()) {
- throw ResolverException("Unable to determine source address for AXFR request to " + remote.toStringWithPort() + " for " + domain.toLogString() + ". " + qlas + " is unset");
- }
- local=ComboAddress(::arg()[qlas]);
- }
- d_sock = -1;
- try {
- d_sock = makeQuerySocket(local, false); // make a TCP socket
- if (d_sock < 0)
- throw ResolverException("Error creating socket for AXFR request to "+d_remote.toStringWithPort());
- d_buf = shared_array<char>(new char[65536]);
- d_remote = remote; // mostly for error reporting
- this->connect(timeout);
- d_soacount = 0;
-
- vector<uint8_t> packet;
- DNSPacketWriter pw(packet, domain, QType::AXFR);
- pw.getHeader()->id = dns_random_uint16();
-
- if(!tt.name.empty()) {
- if (tt.algo == DNSName("hmac-md5"))
- d_trc.d_algoName = tt.algo + DNSName("sig-alg.reg.int");
- else
- d_trc.d_algoName = tt.algo;
- d_trc.d_time = time(0);
- d_trc.d_fudge = 300;
- d_trc.d_origID=ntohs(pw.getHeader()->id);
- d_trc.d_eRcode=0;
- addTSIG(pw, d_trc, tt.name, tt.secret, "", false);
- }
-
- uint16_t replen=htons(packet.size());
- Utility::iovec iov[2];
- iov[0].iov_base=reinterpret_cast<char*>(&replen);
- iov[0].iov_len=2;
- iov[1].iov_base=packet.data();
- iov[1].iov_len=packet.size();
-
- int ret=Utility::writev(d_sock, iov, 2);
- if(ret < 0)
- throw ResolverException("Error sending question to "+d_remote.toStringWithPort()+": "+stringerror());
- if(ret != (int)(2+packet.size())) {
- throw ResolverException("Partial write on AXFR request to "+d_remote.toStringWithPort());
- }
-
- int res = waitForData(d_sock, timeout, 0);
-
- if(!res)
- throw ResolverException("Timeout waiting for answer from "+d_remote.toStringWithPort()+" during AXFR");
- if(res<0)
- throw ResolverException("Error waiting for answer from "+d_remote.toStringWithPort()+": "+stringerror());
- }
- catch(...) {
- if(d_sock >= 0)
- close(d_sock);
- d_sock = -1;
- throw;
- }
-}
-
-AXFRRetriever::~AXFRRetriever()
-{
- close(d_sock);
-}
-
-
-
-int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records, uint16_t timeout) // Implementation is making sure RFC2845 4.4 is followed.
-{
- if(d_soacount > 1)
- return false;
-
- // d_sock is connected and is about to spit out a packet
- int len=getLength(timeout);
- if(len<0)
- throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
-
- if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
- throw ResolverException("Reached the maximum number of received bytes during AXFR");
-
- timeoutReadn(len, timeout);
-
- d_receivedBytes += (uint16_t) len;
-
- MOADNSParser mdp(false, d_buf.get(), len);
-
- int err = mdp.d_header.rcode;
-
- if(err) {
- throw ResolverException("AXFR chunk error: " + RCode::to_s(err));
- }
-
- try {
- d_tsigVerifier.check(std::string(d_buf.get(), len), mdp);
- }
- catch(const std::runtime_error& re) {
- throw ResolverException(re.what());
- }
-
- if(!records) {
- err = parseResult(mdp, DNSName(), 0, 0, &res);
-
- if (!err) {
- for(const auto& answer : mdp.d_answers)
- if (answer.first.d_type == QType::SOA)
- d_soacount++;
- }
- }
- else {
- records->clear();
- records->reserve(mdp.d_answers.size());
-
- for(auto& r: mdp.d_answers) {
- if (r.first.d_type == QType::SOA) {
- d_soacount++;
- }
-
- records->push_back(std::move(r.first));
- }
- }
-
- return true;
-}
-
-void AXFRRetriever::timeoutReadn(uint16_t bytes, uint16_t timeoutsec)
-{
- time_t start=time(nullptr);
- int n=0;
- int numread;
- while(n<bytes) {
- int res=waitForData(d_sock, timeoutsec-(time(nullptr)-start));
- if(res<0)
- throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
- if(!res)
- throw ResolverException("Timeout while reading data from remote nameserver over TCP");
-
- numread=recv(d_sock, d_buf.get()+n, bytes-n, 0);
- if(numread<0)
- throw ResolverException("Reading data from remote nameserver over TCP: "+stringerror());
- if(numread==0)
- throw ResolverException("Remote nameserver closed TCP connection");
- n+=numread;
- }
-}
-
-void AXFRRetriever::connect(uint16_t timeout)
-{
- setNonBlocking( d_sock );
-
- int err;
-
- if((err=::connect(d_sock,(struct sockaddr*)&d_remote, d_remote.getSocklen()))<0 && errno!=EINPROGRESS) {
- try {
- closesocket(d_sock);
- }
- catch(const PDNSException& e) {
- d_sock=-1;
- throw ResolverException("Error closing AXFR socket after connect() failed: "+e.reason);
- }
-
- throw ResolverException("connect: "+stringerror());
- }
-
- if(!err)
- goto done;
-
- err=waitForRWData(d_sock, false, timeout, 0); // wait for writeability
-
- if(!err) {
- try {
- closesocket(d_sock); // timeout
- }
- catch(const PDNSException& e) {
- d_sock=-1;
- throw ResolverException("Error closing AXFR socket after timeout: "+e.reason);
- }
-
- d_sock=-1;
- errno=ETIMEDOUT;
-
- throw ResolverException("Timeout connecting to server");
- }
- else if(err < 0) {
- throw ResolverException("Error connecting: "+stringerror());
- }
- else {
- Utility::socklen_t len=sizeof(err);
- if(getsockopt(d_sock, SOL_SOCKET,SO_ERROR,(char *)&err,&len)<0)
- throw ResolverException("Error connecting: "+stringerror()); // Solaris
-
- if(err)
- throw ResolverException("Error connecting: "+string(strerror(err)));
- }
-
- done:
- setBlocking( d_sock );
- // d_sock now connected
-}
-
-int AXFRRetriever::getLength(uint16_t timeout)
-{
- timeoutReadn(2, timeout);
- return (unsigned char)d_buf[0]*256+(unsigned char)d_buf[1];
-}
-
std::map<std::string, int> locals;
};
-class AXFRRetriever : public boost::noncopyable
-{
- public:
- AXFRRetriever(const ComboAddress& remote,
- const DNSName& zone,
- const TSIGTriplet& tt = TSIGTriplet(),
- const ComboAddress* laddr = NULL,
- size_t maxReceivedBytes=0,
- uint16_t timeout=10);
- ~AXFRRetriever();
- int getChunk(Resolver::res_t &res, vector<DNSRecord>* records=0, uint16_t timeout=10);
-
- private:
- void connect(uint16_t timeout);
- int getLength(uint16_t timeout);
- void timeoutReadn(uint16_t bytes, uint16_t timeoutsec=10);
-
- shared_array<char> d_buf;
- string d_domain;
- int d_sock;
- int d_soacount;
- ComboAddress d_remote;
- TSIGTCPVerifier d_tsigVerifier;
-
- size_t d_receivedBytes;
- size_t d_maxReceivedBytes;
- TSIGRecordContent d_trc;
-};
+namespace pdns {
+ namespace resolver {
+ int parseResult(MOADNSParser& mdp, const DNSName& origQname, uint16_t origQtype, uint16_t id, Resolver::res_t* result);
+ } // namespace resolver
+} // namespace pdns
#include "dns_random.hh"
#include "backends/gsql/ssql.hh"
#include "communicator.hh"
+#include "query-local-address.hh"
extern StatBag S;
extern CommunicatorClass Communicator;
for(const auto& remote : di.masters) {
g_log<<Logger::Notice<<msgPrefix<<"Forwarding packet to master "<<remote<<endl;
- ComboAddress local;
- if (remote.sin4.sin_family == AF_INET && !::arg()["query-local-address"].empty()) {
- local = ComboAddress(::arg()["query-local-address"]);
- } else if(remote.sin4.sin_family == AF_INET6 && !::arg()["query-local-address6"].empty()) {
- local = ComboAddress(::arg()["query-local-address6"]);
- } else {
+ if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
continue;
}
+ auto local = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
int sock = makeQuerySocket(local, false); // create TCP socket. RFC2136 section 6.2 seems to be ok with this.
if(sock < 0) {
g_log<<Logger::Error<<msgPrefix<<"Error creating socket: "<<stringerror()<<endl;
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;
}
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;
#include "dnsrecords.hh"
#include "ixfr.hh"
#include "syncres.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include "logger.hh"
#include "rec-lua-conf.hh"
#include "rpzloader.hh"
#include "zoneparser-tng.hh"
#include "threadname.hh"
+#include "query-local-address.hh"
Netmask makeNetmaskFromRPZ(const DNSName& name)
{
ComboAddress local(localAddress);
if (local == ComboAddress())
- local = getQueryLocalAddress(master.sin4.sin_family, 0);
+ local = pdns::getQueryLocalAddress(master.sin4.sin_family, 0);
AXFRRetriever axfr(master, zoneName, tt, &local, maxReceivedBytes, axfrTimeout);
unsigned int nrecords=0;
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;
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;
}
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) {
ComboAddress local(localAddress);
if (local == ComboAddress()) {
- local = getQueryLocalAddress(master.sin4.sin_family, 0);
+ local = pdns::getQueryLocalAddress(master.sin4.sin_family, 0);
}
try {
continue;
}
- g_log<<Logger::Info<<"Processing "<<deltas.size()<<" delta"<<addS(deltas)<<" for RPZ "<<zoneName<<endl;
-
- oldZone = luaconfsLocal->dfe.getZone(zoneIdx);
- /* we need to make a _full copy_ of the zone we are going to work on */
- std::shared_ptr<DNSFilterEngine::Zone> newZone = std::make_shared<DNSFilterEngine::Zone>(*oldZone);
+ try {
+ g_log<<Logger::Info<<"Processing "<<deltas.size()<<" delta"<<addS(deltas)<<" for RPZ "<<zoneName<<endl;
+
+ oldZone = luaconfsLocal->dfe.getZone(zoneIdx);
+ /* we need to make a _full copy_ of the zone we are going to work on */
+ std::shared_ptr<DNSFilterEngine::Zone> newZone = std::make_shared<DNSFilterEngine::Zone>(*oldZone);
+ /* initialize the current serial to the last one */
+ std::shared_ptr<SOARecordContent> currentSR = sr;
+
+ int totremove=0, totadd=0;
+ bool fullUpdate = false;
+ for(const auto& delta : deltas) {
+ const auto& remove = delta.first;
+ const auto& add = delta.second;
+ if(remove.empty()) {
+ g_log<<Logger::Warning<<"IXFR update is a whole new zone"<<endl;
+ newZone->clear();
+ fullUpdate = true;
+ }
+ for(const auto& rr : remove) { // should always contain the SOA
+ if(rr.d_type == QType::NS)
+ continue;
+ if(rr.d_type == QType::SOA) {
+ auto oldsr = getRR<SOARecordContent>(rr);
+ if (oldsr && oldsr->d_st.serial == currentSR->d_st.serial) {
+ // cout<<"Got good removal of SOA serial "<<oldsr->d_st.serial<<endl;
+ }
+ else {
+ if (!oldsr) {
+ throw std::runtime_error("Unable to extract serial from SOA record while processing the removal part of an update");
+ }
+ else {
+ throw std::runtime_error("Received an unexpected serial (" + std::to_string(oldsr->d_st.serial) + ", expecting " + std::to_string(currentSR->d_st.serial) + ") from SOA record while processing the removal part of an update");
+ }
+ }
+ }
+ else {
+ totremove++;
+ g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had removal of "<<rr.d_name<<" from RPZ zone "<<zoneName<<endl;
+ RPZRecordToPolicy(rr, newZone, false, defpol, defpolOverrideLocal, maxTTL);
+ }
+ }
- int totremove=0, totadd=0;
- bool fullUpdate = false;
- for(const auto& delta : deltas) {
- const auto& remove = delta.first;
- const auto& add = delta.second;
- if(remove.empty()) {
- g_log<<Logger::Warning<<"IXFR update is a whole new zone"<<endl;
- newZone->clear();
- fullUpdate = true;
+ for(const auto& rr : add) { // should always contain the new SOA
+ if(rr.d_type == QType::NS)
+ continue;
+ if(rr.d_type == QType::SOA) {
+ auto tempSR = getRR<SOARecordContent>(rr);
+ // g_log<<Logger::Info<<"New SOA serial for "<<zoneName<<": "<<currentSR->d_st.serial<<endl;
+ if (tempSR) {
+ currentSR = tempSR;
+ }
+ }
+ else {
+ totadd++;
+ g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had addition of "<<rr.d_name<<" to RPZ zone "<<zoneName<<endl;
+ RPZRecordToPolicy(rr, newZone, true, defpol, defpolOverrideLocal, maxTTL);
+ }
+ }
}
- for(const auto& rr : remove) { // should always contain the SOA
- if(rr.d_type == QType::NS)
- continue;
- if(rr.d_type == QType::SOA) {
- auto oldsr = getRR<SOARecordContent>(rr);
- if(oldsr && oldsr->d_st.serial == sr->d_st.serial) {
- // cout<<"Got good removal of SOA serial "<<oldsr->d_st.serial<<endl;
- }
- else
- g_log<<Logger::Error<<"GOT WRONG SOA SERIAL REMOVAL, SHOULD TRIGGER WHOLE RELOAD"<<endl;
- }
- else {
- totremove++;
- g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had removal of "<<rr.d_name<<" from RPZ zone "<<zoneName<<endl;
- RPZRecordToPolicy(rr, newZone, false, defpol, defpolOverrideLocal, maxTTL);
- }
+
+ /* only update sr now that all changes have been converted */
+ if (currentSR) {
+ sr = currentSR;
}
+ g_log<<Logger::Info<<"Had "<<totremove<<" RPZ removal"<<addS(totremove)<<", "<<totadd<<" addition"<<addS(totadd)<<" for "<<zoneName<<" New serial: "<<sr->d_st.serial<<endl;
+ newZone->setSerial(sr->d_st.serial);
+ newZone->setRefresh(sr->d_st.refresh);
+ setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), fullUpdate);
+
+ /* we need to replace the existing zone with the new one,
+ but we don't want to touch anything else, especially other zones,
+ since they might have been updated by another RPZ IXFR tracker thread.
+ */
+ g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
+ lci.dfe.setZone(zoneIdx, newZone);
+ });
- for(const auto& rr : add) { // should always contain the new SOA
- if(rr.d_type == QType::NS)
- continue;
- if(rr.d_type == QType::SOA) {
- auto newsr = getRR<SOARecordContent>(rr);
- // g_log<<Logger::Info<<"New SOA serial for "<<zoneName<<": "<<newsr->d_st.serial<<endl;
- if (newsr) {
- sr = newsr;
- }
- }
- else {
- totadd++;
- g_log<<(g_logRPZChanges ? Logger::Info : Logger::Debug)<<"Had addition of "<<rr.d_name<<" to RPZ zone "<<zoneName<<endl;
- RPZRecordToPolicy(rr, newZone, true, defpol, defpolOverrideLocal, maxTTL);
- }
+ if (!dumpZoneFileName.empty()) {
+ dumpZoneToDisk(zoneName, newZone, dumpZoneFileName);
}
+ refresh = std::max(refreshFromConf ? refreshFromConf : newZone->getRefresh(), 1U);
}
- g_log<<Logger::Info<<"Had "<<totremove<<" RPZ removal"<<addS(totremove)<<", "<<totadd<<" addition"<<addS(totadd)<<" for "<<zoneName<<" New serial: "<<sr->d_st.serial<<endl;
- newZone->setSerial(sr->d_st.serial);
- newZone->setRefresh(sr->d_st.refresh);
- setRPZZoneNewState(polName, sr->d_st.serial, newZone->size(), fullUpdate);
-
- /* we need to replace the existing zone with the new one,
- but we don't want to touch anything else, especially other zones,
- since they might have been updated by another RPZ IXFR tracker thread.
- */
- g_luaconfs.modify([zoneIdx, &newZone](LuaConfigItems& lci) {
- lci.dfe.setZone(zoneIdx, newZone);
- });
-
- if (!dumpZoneFileName.empty()) {
- dumpZoneToDisk(zoneName, newZone, dumpZoneFileName);
+ catch (const std::exception& e) {
+ g_log << Logger::Error << "Error while applying the update received over XFR for "<<zoneName<<", skipping the update: "<< e.what() <<endl;
}
- refresh = std::max(refreshFromConf ? refreshFromConf : newZone->getRefresh(), 1U);
}
}
processSecPoll(res, ret, security_status, security_message);
} catch(const PDNSException &pe) {
S.set("security-status", security_status);
- g_log<<Logger::Warning<<"Could not retrieve security status update for '" + pkgv + "' on '"+ query + "': "<<pe.reason<<endl;
+ g_log<<Logger::Warning<<"Failed to retrieve security status update for '" + pkgv + "' on '"+ query + "': "<<pe.reason<<endl;
return;
}
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);
state = sr.getValidationState();
}
- if(state == Bogus) {
- g_log<<Logger::Error<<"Could not retrieve security status update for '" +pkgv+ "' on '"<<query<<"', DNSSEC validation result was Bogus!"<<endl;
+ if(state == vState::Bogus) {
+ g_log<<Logger::Error<<"Failed to retrieve security status update for '" +pkgv+ "' on '"<<query<<"', DNSSEC validation result was Bogus!"<<endl;
if(g_security_status == 1) // If we were OK, go to unknown
g_security_status = 0;
return;
processSecPoll(res, ret, security_status, security_message);
} catch(const PDNSException &pe) {
g_security_status = security_status;
- g_log<<Logger::Warning<<"Could not retrieve security status update for '" << pkgv << "' on '"<< query << "': "<<pe.reason<<endl;
+ g_log<<Logger::Warning<<"Failed to retrieve security status update for '" << pkgv << "' on '"<< query << "': "<<pe.reason<<endl;
return;
}
secPollMessage.clear();
if (res != 0) { // not NOERROR
setSecPollToUnknownOnOK(secPollStatus);
- throw PDNSException("RCODE was not NOERROR but " + RCode::to_s(res));
+ throw PDNSException("RCODE was " + RCode::to_s(res));
}
if (ret.empty()) { // empty NOERROR... wat?
{
std::vector<DNSZoneRecord>::iterator first, second;
- // We assume the CNAMES are listed firts in the ANSWWER section and the the other records
+ // We assume the CNAMES are listed first in the ANSWER section and the the other records
// and we want to shuffle the other records only
// First we scan for the first non-CNAME ANSWER record
break;
}
}
- // And then for one past the last ANSWER recordd
+ // And then for one past the last ANSWER record
for (second = first; second != rrs.end(); ++second)
if (second->dr.d_place != DNSResourceRecord::ANSWER)
break;
#include "dnsbackend.hh"
#include "ueberbackend.hh"
#include "packethandler.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include "logger.hh"
#include "dns.hh"
#include "arguments.hh"
#include "inflighter.cc"
#include "namespaces.hh"
#include "common_startup.hh"
+#include "query-local-address.hh"
#include "ixfr.hh"
-void CommunicatorClass::addSuckRequest(const DNSName &domain, const ComboAddress& master)
+void CommunicatorClass::addSuckRequest(const DNSName &domain, const ComboAddress& master, bool force)
{
std::lock_guard<std::mutex> l(d_lock);
SuckRequest sr;
sr.domain = domain;
sr.master = master;
+ sr.force = force;
pair<UniQueue::iterator, bool> res;
res=d_suckdomains.push_back(sr);
}
-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);
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
return;
}
} else {
- if(remote.sin4.sin_family == AF_INET && !::arg()["query-local-address"].empty()) {
- laddr = ComboAddress(::arg()["query-local-address"]);
- } else if(remote.sin4.sin_family == AF_INET6 && !::arg()["query-local-address6"].empty()) {
- laddr = ComboAddress(::arg()["query-local-address6"]);
- } else {
- bool isv6 = remote.sin4.sin_family == AF_INET6;
- g_log<<Logger::Error<<"Unable to AXFR, destination address is IPv" << (isv6 ? "6" : "4") << ", but query-local-address"<< (isv6 ? "6" : "") << " is unset!"<<endl;
+ if (!pdns::isQueryLocalAddressFamilyEnabled(remote.sin4.sin_family)) {
+ bool isV6 = remote.sin4.sin_family == AF_INET6;
+ g_log<<Logger::Error<<"Unable to AXFR, destination address is "<<remote<<" (IPv"<< (isV6 ? "6" : "4") <<
+ ", but that address family is not enabled for outgoing traffic (query-local-address)"<<endl;
return;
}
+ laddr = pdns::getQueryLocalAddress(remote.sin4.sin_family, 0);
}
bool hadDnssecZone = false;
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;
}
}
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;
}
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) {
}
}
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);
}
}
}
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;
}
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)
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;
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);
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
unsigned int SyncRes::s_maxbogusttl;
unsigned int SyncRes::s_maxcachettl;
unsigned int SyncRes::s_maxqperq;
+unsigned int SyncRes::s_maxnsaddressqperq;
unsigned int SyncRes::s_maxtotusec;
unsigned int SyncRes::s_maxdepth;
unsigned int SyncRes::s_minimumTTL;
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;
}
/** 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;
}
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);
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)
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);
}
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");
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) {
// 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;
}
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");
// 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++;
* \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()) {
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();
*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);
}
}
- 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;
}
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;
/** This function explicitly goes out for A or AAAA addresses
*/
-vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly)
+vector<ComboAddress> SyncRes::getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS)
{
typedef vector<DNSRecord> res_t;
typedef vector<ComboAddress> ret_t;
bool oldCacheOnly = setCacheOnly(cacheOnly);
bool oldRequireAuthData = d_requireAuthData;
bool oldValidationRequested = d_DNSSECValidationRequested;
+ const unsigned int startqueries = d_outqueries;
d_requireAuthData = false;
d_DNSSECValidationRequested = false;
try {
- vState newState = Indeterminate;
+ vState newState = vState::Indeterminate;
res_t resv4;
// If IPv4 ever becomes second class, we should revisit this
- if (doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) { // this consults cache, OR goes out
+ if (s_doIPv4 && doResolve(qname, QType::A, resv4, depth+1, beenthere, newState) == 0) { // this consults cache, OR goes out
for (auto const &i : resv4) {
if (i.d_type == QType::A) {
if (auto rec = getRR<ARecordContent>(i)) {
}
}
}
- 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) {
of a NS and keep processing the current query */
}
+ if (ret.empty() && d_outqueries > startqueries) {
+ // We did 1 or more outgoing queries to resolve this NS name but returned empty handed
+ addressQueriesForNS++;
+ }
d_requireAuthData = oldRequireAuthData;
d_DNSSECValidationRequested = oldValidationRequested;
setCacheOnly(oldCacheOnly);
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);
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());
/** 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 {
}
}
+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;
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;
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 */
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);
}
}
- 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;
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) {
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;
* \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 */
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
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);
}
}
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) {
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;
}
}
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;
}
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. */
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);
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;
}
return false;
}
-vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly)
+vector<ComboAddress> SyncRes::retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, std::vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly, unsigned int &retrieveAddressesForNS)
{
vector<ComboAddress> result;
if(!tns->first.empty()) {
LOG(prefix<<qname<<": Trying to resolve NS '"<<tns->first<< "' ("<<1+tns-rnameservers.begin()<<"/"<<(unsigned int)rnameservers.size()<<")"<<endl);
- result = getAddrs(tns->first, depth+2, beenthere, cacheOnly);
+ result = getAddrs(tns->first, depth, beenthere, cacheOnly, retrieveAddressesForNS);
pierceDontQuery=false;
}
else {
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)
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);
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)
{
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;
- 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) {
}
LOG(d_prefix<<": returning Bogus state from "<<__func__<<"("<<zone<<")"<<endl);
- return Bogus;
+ return vState::Bogus;
}
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;
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;
}
}
{
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;
}
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()) {
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;
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;
/* 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;
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 {
}
}
- 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);
if (!signer.empty() && zone.isPartOf(signer)) {
vState state = getDSRecords(signer, ds, false, depth);
- if (state != Secure) {
+ if (state != vState::Secure) {
return state;
}
}
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)
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);
}
}
}
- 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)
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;
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)
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;
}
}
}
}
- 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
}
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;
}
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) {
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) {
}
}
- 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));
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
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);
}
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)
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);
}
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.
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 {
}
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;
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);
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);
}
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;
}
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;
}
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);
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;
}
if(lwr.d_rcode == RCode::NXDomain) {
LOG(prefix<<qname<<": status=NXDOMAIN, we are done "<<(negindic ? "(have negative SOA)" : "")<<endl);
+ if (state == vState::Secure && (lwr.d_aabit || sendRDQuery) && !negindic) {
+ updateValidationState(state, vState::Bogus);
+ }
+
if(d_doDNSSEC)
addNXNSECS(ret, lwr.d_records);
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)
LOG(endl);
+ unsigned int addressQueriesForNS = 0;
for(;;) { // we may get more specific nameservers
auto rnameservers = shuffleInSpeedOrder(nameservers, doLog() ? (prefix+qname.toString()+": ") : string() );
+ // We allow s_maxnsaddressqperq (default 10) queries with empty responses when resolving NS names.
+ // If a zone publishes many (more than s_maxnsaddressqperq) NS records, we allow less.
+ // This is to "punish" zones that publish many non-resolving NS names.
+ // We always allow 5 NS name resolving attempts with empty results.
+ unsigned int nsLimit = s_maxnsaddressqperq;
+ if (rnameservers.size() > nsLimit) {
+ int newLimit = static_cast<int>(nsLimit) - (rnameservers.size() - nsLimit);
+ nsLimit = std::max(5, newLimit);
+ }
+
for(auto tns=rnameservers.cbegin();;++tns) {
+ if (addressQueriesForNS >= nsLimit) {
+ throw ImmediateServFailException(std::to_string(nsLimit)+" (adjusted max-ns-address-qperq) or more queries with empty results for NS addresses sent resolving "+qname.toLogString());
+ }
if(tns==rnameservers.cend()) {
LOG(prefix<<qname<<": Failed to resolve via any of the "<<(unsigned int)rnameservers.size()<<" offered NS at level '"<<auth<<"'"<<endl);
if(!auth.isRoot() && flawedNSSet) {
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;
}
else {
/* if tns is empty, retrieveAddressesForNS() knows we have hardcoded servers (i.e. "forwards") */
- remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly);
+ remoteIPs = retrieveAddressesForNS(prefix, qname, tns, depth, beenthere, rnameservers, nameservers, sendRDQuery, pierceDontQuery, flawedNSSet, cacheOnly, addressQueriesForNS);
if(remoteIPs.empty()) {
LOG(prefix<<qname<<": Failed to get IP for NS "<<tns->first<<", trying next if available"<<endl);
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();
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;
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();
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())
return d_now;
}
- void setSkipCNAMECheck(bool skip = false)
- {
- d_skipCNAMECheck = skip;
- }
-
void setQuerySource(const ComboAddress& requestor, boost::optional<const EDNSSubnetOpts&> incomingECS);
#ifdef HAVE_PROTOBUF
static unsigned int s_minimumTTL;
static unsigned int s_minimumECSTTL;
static unsigned int s_maxqperq;
+ static unsigned int s_maxnsaddressqperq;
static unsigned int s_maxtotusec;
static unsigned int s_maxdepth;
static unsigned int s_maxnegttl;
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;
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);
inline vector<std::pair<DNSName, float>> shuffleInSpeedOrder(NsSet &nameservers, const string &prefix);
inline vector<ComboAddress> shuffleForwardSpeed(const vector<ComboAddress> &rnameservers, const string &prefix, const bool wasRd);
bool moreSpecificThan(const DNSName& a, const DNSName &b) const;
- vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly);
+ vector<ComboAddress> getAddrs(const DNSName &qname, unsigned int depth, set<GetBestNSAnswer>& beenthere, bool cacheOnly, unsigned int& addressQueriesForNS);
bool nameserversBlockedByRPZ(const DNSFilterEngine& dfe, const NsSet& nameservers);
bool nameserverIPBlockedByRPZ(const DNSFilterEngine& dfe, const ComboAddress&);
bool throttledOrBlocked(const std::string& prefix, const ComboAddress& remoteIP, const DNSName& qname, const QType& qtype, bool pierceDontQuery);
- vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly);
+ vector<ComboAddress> retrieveAddressesForNS(const std::string& prefix, const DNSName& qname, vector<std::pair<DNSName, float>>::const_iterator& tns, const unsigned int depth, set<GetBestNSAnswer>& beenthere, const vector<std::pair<DNSName, float>>& rnameservers, NsSet& nameservers, bool& sendRDQuery, bool& pierceDontQuery, bool& flawedNSSet, bool cacheOnly, unsigned int& addressQueriesForNS);
void sanitizeRecords(const std::string& prefix, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, bool rdQuery);
RCode::rcodes_ updateCacheFromRecords(unsigned int depth, LWResult& lwr, const DNSName& qname, const QType& qtype, const DNSName& auth, bool wasForwarded, const boost::optional<Netmask>, vState& state, bool& needWildcardProof, bool& gatherWildcardProof, unsigned int& wildcardLabelsCount, bool sendRDQuery);
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);
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
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};
std::string reloadAuthAndForwards();
ComboAddress parseIPAndPort(const std::string& input, uint16_t port);
-ComboAddress getQueryLocalAddress(int family, uint16_t port);
typedef boost::function<void*(void)> pipefunc_t;
void broadcastFunction(const pipefunc_t& func);
void distributeAsyncFunction(const std::string& question, const pipefunc_t& func);
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;
#include "common_startup.hh"
#include "packethandler.hh"
#include "statbag.hh"
-#include "resolver.hh"
#include "communicator.hh"
#include "namespaces.hh"
#include "signingpipe.hh"
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
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()) {
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;
}
}
}
- UeberBackend signatureDB;
-
// SOA *must* go out first, our signing pipe might reorder
DLOG(g_log<<"Sending out SOA"<<endl);
DNSZoneRecord soa = makeEditedDNSZRFromSOAData(dk, sd);
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())
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);
}
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()
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);
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 */
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 */
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 */
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 */
--- /dev/null
+#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();
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));
}
}
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;
#include "digests.hh"
#include "base64.hh"
#include "dnssecinfra.hh"
-#include "resolver.hh"
+#include "axfr-retriever.hh"
#include "arguments.hh"
#include "dns_random.hh"
+#include "query-local-address.hh"
StatBag S;
int main(int argc, char** argv)
try
{
- ::arg().set("query-local-address","Source IP address for sending queries")="0.0.0.0";
- ::arg().set("query-local-address6","Source IPv6 address for sending queries")="::";
+ pdns::parseQueryLocalAddress(":: 0.0.0.0");
reportAllTypes();
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;
}
}
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) {
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;
}
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)
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");
}
#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,
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
*/
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);
#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;
}
/*
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.
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);
/* RFC 6840 section 4.3 */
if (nsec->isSet(QType::CNAME)) {
LOG("However a CNAME exists"<<endl);
- return NODATA;
+ return dState::NODENIAL;
}
/*
*/
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
}
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);
string h = getHashFromNSEC3(qname, nsec3);
if (h.empty()) {
LOG("Unsupported hash, ignoring"<<endl);
- return INSECURE;
+ return dState::INSECURE;
}
nsec3Seen = true;
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);
/* RFC 6840 section 4.3 */
if (nsec3->isSet(QType::CNAME)) {
LOG("However a CNAME exists"<<endl);
- return NODATA;
+ return dState::NODENIAL;
}
/*
*/
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;
}
}
}
/* if we have no NSEC3 records, we are done */
if (!nsec3Seen) {
- return NODATA;
+ return dState::NODENIAL;
}
DNSName closestEncloser(qname);
string h = getHashFromNSEC3(closestEncloser, nsec3);
if (h.empty()) {
- return INSECURE;
+ return dState::INSECURE;
}
string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
string h = getHashFromNSEC3(nextCloser, nsec3);
if (h.empty()) {
- return INSECURE;
+ return dState::INSECURE;
}
string beginHash=fromBase32Hex(v.first.first.getRawLabels()[0]);
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;
}
/*
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;
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(".");
*/
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);
}
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
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;
}
/*
}
}
// There were no zone cuts (aka, we should never get here)
- return Bogus;
+ return vState::Bogus;
}
bool isSupportedDS(const DSRecordContent& ds)
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;
+}
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
{
+ "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());
}
}
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");
}
catch (const JsonException&) {}
+ try {
+ nsec3paramDocVal = stringFromJson(document, "nsec3param");
+ nsec3paramInJSON = true;
+ }
+ catch (const JsonException&) {}
+
+
bool isDNSSECZone = dk.isSecuredZone(zonename);
if (dnssecInJSON) {
}
}
- 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.");
+ }
}
}
}
}
+ 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))
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;
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"));
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).
});
}
+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";
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();
}
*/
#pragma once
#include <string>
+#include <tuple>
#include <map>
#include <time.h>
#include <pthread.h>
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;
// 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);
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);
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
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')
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 = {
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 = []
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)
_healthCheckCounter = 0
_answerUnexpected = True
_checkConfigExpectedOutput = None
+ _verboseMode = False
@classmethod
def startResponders(cls):
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))
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__))
'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)
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)
setMaxTCPConnectionDuration(%s)
"""
_config_params = ['_testServerPort', '_tcpIdleTimeout', '_maxTCPQueriesPerConn', '_maxTCPConnsPerClient', '_maxTCPConnDuration']
+ _verboseMode = True
def testTCPQueriesPerConn(self):
"""
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)
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
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
_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"
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')
--- /dev/null
+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
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
nameECSInvalidScope = 'invalid-scope.ecs-echo.example.'
ttlECS = 60
ecsReactorRunning = False
+ecsReactorv6Running = False
class ECSTest(RecursorTest):
_config_template_default = """
@classmethod
def startResponders(cls):
global ecsReactorRunning
+ global ecsReactorv6Running
print("Launching responders..")
address = cls._PREFIX + '.21'
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)
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'
ecs-ipv6-bits=128
ecs-ipv4-cache-bits=32
ecs-ipv6-cache-bits=128
-forward-zones=ecs-echo.example=%s.21
-query-local-address6=::1
- """ % (os.environ['PREFIX'])
+query-local-address=::1
+forward-zones=ecs-echo.example=[::1]:53000
+ """
def testSendECS(self):
expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '2001:db8::1/128')
self.sendECSQuery(query, expected, ttlECS)
def testRequireNoECS(self):
- # we should get ::1/128 because neither ecs-scope-zero-addr nor query-local-address are set,
- # but query-local-address6 is set to ::1
+ # we should get ::1/128 because ecs-scope-zero-addr is unset and query-local-address is set to ::1
expected = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', "::1/128")
ecso = clientsubnetoption.ClientSubnetOption('0.0.0.0', 0)
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
elif message.question[0].rdtype == dns.rdatatype.IXFR:
oldSerial = message.authority[0][0].serial
- if oldSerial != self._currentSerial:
+ # special case for the 9th update, which might get skipped
+ if oldSerial != self._currentSerial and self._currentSerial != 9:
print('Received an IXFR query with an unexpected serial %d, expected %d' % (oldSerial, self._currentSerial))
return (None, self._currentSerial)
dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
]
+ elif newSerial == 9:
+ # IXFR inserting a duplicate, we should not crash and skip it
+ records = [
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('dup.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-passthru.'),
+ dns.rrset.from_text('dup.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.CNAME, 'rpz-passthru.'),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
+ ]
+ elif newSerial == 10:
+ # full AXFR to make sure we are removing the duplicate, adding a record, to check that the update was correctly applied
+ records = [
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial)
+ ]
+ elif newSerial == 11:
+ # IXFR with two deltas, the first one adding a 'g' and the second one removing 'f'
+ records = [
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1)),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % oldSerial),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('g.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % newSerial),
+ dns.rrset.from_text('f.example.zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.A, '192.0.2.1'),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1)),
+ dns.rrset.from_text('zone.rpz.', 60, dns.rdataclass.IN, dns.rdatatype.SOA, 'ns.zone.rpz. hostmaster.zone.rpz. %d 3600 3600 3600 1' % (newSerial + 1))
+ ]
+ # this one has two updates in one
+ newSerial = newSerial + 1
+ self._targetSerial = self._targetSerial + 1
response.answer = records
return (newSerial, response)
self.checkNXD('tc.example.')
self.checkNXD('drop.example.')
+ # 9th zone is a duplicate, it might get skipped
+ global rpzServer
+ rpzServer.moveToSerial(9)
+ time.sleep(3)
+ self.waitUntilCorrectSerialIsLoaded(10)
+ self.checkRPZStats(10, 1, 4, self._xfrDone)
+ self.checkNotBlocked('a.example.')
+ self.checkNotBlocked('b.example.')
+ self.checkNotBlocked('c.example.')
+ self.checkNotBlocked('d.example.')
+ self.checkNotBlocked('e.example.')
+ self.checkBlocked('f.example.')
+ self.checkNXD('tc.example.')
+ self.checkNXD('drop.example.')
+
+ # the next update will update the zone twice
+ rpzServer.moveToSerial(11)
+ time.sleep(3)
+ self.waitUntilCorrectSerialIsLoaded(12)
+ self.checkRPZStats(12, 1, 4, self._xfrDone)
+ self.checkNotBlocked('a.example.')
+ self.checkNotBlocked('b.example.')
+ self.checkNotBlocked('c.example.')
+ self.checkNotBlocked('d.example.')
+ self.checkNotBlocked('e.example.')
+ self.checkNXD('f.example.')
+ self.checkBlocked('g.example.')
+ self.checkNXD('tc.example.')
+ self.checkNXD('drop.example.')
+
class RPZFileRecursorTest(RPZRecursorTest):
"""
This test makes sure that we correctly load RPZ zones from a file
query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected1)
- # Now check a cache hit withg the same routingTag (but no ECS)
+ # Now check a cache hit with the same routingTag (but no ECS)
query = dns.message.make_query(nameECS, 'TXT', 'IN')
self.checkECSQueryHit(query, expected1)
query = dns.message.make_query(nameECS, 'TXT', 'IN')
self.sendECSQuery(query, expected2)
- # And see if a *no* tag does *not* hit the firts one
+ # And see if a *no* tag does *not* hit the first one
expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.3.0/24')
self.setRoutingTag(None)
ecso = clientsubnetoption.ClientSubnetOption('192.0.3.1', 32)
'--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)
query = dns.message.make_query(nameECS, 'TXT', 'IN', use_edns=True, options=[ecso], payload=512)
self.sendECSQuery(query, expected1)
- # Now check a cache hit withg the same routingTag (but no ECS)
+ # Now check a cache hit with the same routingTag (but no ECS)
query = dns.message.make_query(nameECS, 'TXT', 'IN')
self.checkECSQueryHit(query, expected1)
query = dns.message.make_query(nameECS, 'TXT', 'IN')
self.sendECSQuery(query, expected2)
- # And see if a *no* tag does *not* hit the firts one
+ # And see if a *no* tag does *not* hit the first one
expected3 = dns.rrset.from_text(nameECS, ttlECS, dns.rdataclass.IN, 'TXT', '192.0.3.0/24')
self.setRoutingTag(None)
ecso = clientsubnetoption.ClientSubnetOption('192.0.3.1', 32)
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
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
if [ $IPv6 = 1 ]
then
- QLA6="::"
+ QLA6=" ::"
else
QLA6=""
fi
<measurement><name>system CPU seconds</name><value>%S</value></measurement>
<measurement><name>wallclock seconds</name><value>%e</value></measurement>
<measurement><name>%% CPU used</name><value>%P</value></measurement>
-' ${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --max-mthreads=$mthreads --query-local-address6="${QLA6}" --threads=$threads --cache-shards=$shards --disable-packetcache > recursor.log 2>&1 &
+' ${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --max-mthreads=$mthreads --query-local-address="0.0.0.0${QLA6}" --threads=$threads --record-cache-shards=$shards --disable-packetcache > recursor.log 2>&1 &
sleep 3
# warm up the cache
if [ $IPv6 = 1 ]
then
- QLA6="::"
+ QLA6=" ::"
else
QLA6=""
fi
rm -f recursor.pid pdns_recursor.pid
-${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --local-address=0.0.0.0 --allow-from=0.0.0.0/0 --query-local-address6="${QLA6}" > recursor.log 2>&1 &
+${RECURSOR} --daemon=no --local-port=$port --socket-dir=./ --trace=$TRACE --config-dir=. --local-address=0.0.0.0 --allow-from=0.0.0.0/0 --query-local-address="0.0.0.0${QLA6}" > recursor.log 2>&1 &
sleep 3
./dnsbulktest -qe 37.252.127.190 $port $limit < ${CSV} > bulktest.results
kill $(cat pdns_recursor.pid)
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
}