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